feat: settings per-page

This commit is contained in:
kunish 2023-09-15 23:43:55 +08:00
parent c607a31070
commit d77626058a
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
14 changed files with 340 additions and 214 deletions

View File

@ -0,0 +1,14 @@
import { children, ParentComponent } from 'solid-js'
export const ConfigTitle: ParentComponent<{ withDivider?: boolean }> = (
props,
) => (
<div
class="pb-4 text-lg font-semibold"
classList={{
divider: props.withDivider,
}}
>
{children(() => props.children)()}
</div>
)

View File

@ -14,17 +14,20 @@ import {
useDragDropContext, useDragDropContext,
} from '@thisbeyond/solid-dnd' } from '@thisbeyond/solid-dnd'
import { Component, For, Show, createSignal } from 'solid-js' import { Component, For, Show, createSignal } from 'solid-js'
import { Button } from '~/components' import { Button, ConfigTitle } from '~/components'
import { import {
CONNECTIONS_TABLE_ACCESSOR_KEY, CONNECTIONS_TABLE_ACCESSOR_KEY,
CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER, CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER,
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY, CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
MODAL,
TAILWINDCSS_SIZE,
} from '~/constants' } from '~/constants'
import { connectionsTableSize, setConnectionsTableSize } from '~/signals'
type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>> type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>>
type ColumnOrder = CONNECTIONS_TABLE_ACCESSOR_KEY[] type ColumnOrder = CONNECTIONS_TABLE_ACCESSOR_KEY[]
export const ConnectionsTableOrderingModal = (props: { export const ConnectionsSettingsModal = (props: {
order: ColumnOrder order: ColumnOrder
visible: ColumnVisibility visible: ColumnVisibility
onOrderChange: (value: ColumnOrder) => void onOrderChange: (value: ColumnOrder) => void
@ -76,8 +79,9 @@ export const ConnectionsTableOrderingModal = (props: {
'transition-transform': !!state.active.draggable, 'transition-transform': !!state.active.draggable,
}} }}
> >
<div class="my-1 flex cursor-grab justify-between p-1"> <div class="flex cursor-grab justify-between py-2">
<span class="select-none">{t(key)}</span> <span class="select-none">{t(key)}</span>
<input <input
type="checkbox" type="checkbox"
class="toggle" class="toggle"
@ -96,27 +100,50 @@ export const ConnectionsTableOrderingModal = (props: {
return ( return (
<dialog <dialog
id="connections-table-ordering-modal" id={MODAL.CONNECTIONS_SETTINGS}
class="modal modal-bottom sm:modal-middle" class="modal modal-bottom sm:modal-middle"
> >
<div class="modal-box" onContextMenu={(e) => e.preventDefault()}> <div
<DragDropProvider class="modal-box flex flex-col gap-4"
onDragStart={onDragStart} onContextMenu={(e) => e.preventDefault()}
onDragEnd={onDragEnd as DragEventHandler} >
collisionDetector={closestCenter} <div>
> <ConfigTitle withDivider>{t('tableSize')}</ConfigTitle>
<DragDropSensors />
<SortableProvider ids={props.order}> <select
<For each={props.order}>{(key) => <FormRow key={key} />}</For> class="select select-bordered w-full"
</SortableProvider> value={connectionsTableSize()}
onChange={(e) =>
setConnectionsTableSize(e.target.value as TAILWINDCSS_SIZE)
}
>
<For each={Object.values(TAILWINDCSS_SIZE)}>
{(value) => <option value={value}>{t(value)}</option>}
</For>
</select>
</div>
<DragOverlay> <div>
<Show when={activeKey()}> <ConfigTitle withDivider>{t('sort')}</ConfigTitle>
<div>{t(activeKey()!)}</div>
</Show> <DragDropProvider
</DragOverlay> onDragStart={onDragStart}
</DragDropProvider> onDragEnd={onDragEnd as DragEventHandler}
collisionDetector={closestCenter}
>
<DragDropSensors />
<SortableProvider ids={props.order}>
<For each={props.order}>{(key) => <FormRow key={key} />}</For>
</SortableProvider>
<DragOverlay>
<Show when={activeKey()}>
<div>{t(activeKey()!)}</div>
</Show>
</DragOverlay>
</DragDropProvider>
</div>
<div class="modal-action"> <div class="modal-action">
<Button <Button

View File

@ -1,4 +1,5 @@
import { Component, Show } from 'solid-js' import { Component, Show } from 'solid-js'
import { MODAL } from '~/constants'
import { allConnections } from '~/signals' import { allConnections } from '~/signals'
export const ConnectionsTableDetailsModal: Component<{ export const ConnectionsTableDetailsModal: Component<{
@ -6,7 +7,7 @@ export const ConnectionsTableDetailsModal: Component<{
}> = (props) => { }> = (props) => {
return ( return (
<dialog <dialog
id="connections-table-details-modal" id={MODAL.CONNECTIONS_TABLE_DETAILS}
class="modal modal-bottom sm:modal-middle" class="modal modal-bottom sm:modal-middle"
> >
<div class="modal-box"> <div class="modal-box">

View File

@ -0,0 +1,81 @@
import { useI18n } from '@solid-primitives/i18n'
import { For } from 'solid-js'
import { ConfigTitle } from '~/components'
import {
LOG_LEVEL,
LOGS_TABLE_MAX_ROWS_LIST,
MODAL,
TAILWINDCSS_SIZE,
} from '~/constants'
import {
logMaxRows,
logsTableSize,
setLogLevel,
setLogMaxRows,
setLogsTableSize,
} from '~/signals'
export const LogsSettingsModal = () => {
const [t] = useI18n()
return (
<dialog id={MODAL.LOGS_SETTINGS} class="modal modal-bottom sm:modal-middle">
<div class="modal-box flex flex-col gap-4">
<div>
<ConfigTitle withDivider>{t('tableSize')}</ConfigTitle>
<select
class="select select-bordered w-full"
value={logsTableSize()}
onChange={(e) =>
setLogsTableSize(e.target.value as TAILWINDCSS_SIZE)
}
>
<For each={Object.values(TAILWINDCSS_SIZE)}>
{(value) => <option value={value}>{t(value)}</option>}
</For>
</select>
</div>
<div>
<ConfigTitle withDivider>{t('logLevel')}</ConfigTitle>
<select
class="select select-bordered w-full"
onChange={(e) => setLogLevel(e.target.value as LOG_LEVEL)}
>
<For
each={[
LOG_LEVEL.Info,
LOG_LEVEL.Error,
LOG_LEVEL.Warning,
LOG_LEVEL.Debug,
LOG_LEVEL.Silent,
]}
>
{(level) => <option value={level}>{t(level)}</option>}
</For>
</select>
</div>
<div>
<ConfigTitle withDivider>{t('logMaxRows')}</ConfigTitle>
<select
class="select select-bordered w-full"
value={logMaxRows()}
onChange={(e) => setLogMaxRows(parseInt(e.target.value))}
>
<For each={LOGS_TABLE_MAX_ROWS_LIST}>
{(rows) => <option value={rows}>{rows}</option>}
</For>
</select>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button />
</form>
</dialog>
)
}

View File

@ -0,0 +1,107 @@
import { useI18n } from '@solid-primitives/i18n'
import { For } from 'solid-js'
import { ConfigTitle } from '~/components'
import { MODAL, PROXIES_ORDERING_TYPE, PROXIES_PREVIEW_TYPE } from '~/constants'
import {
autoCloseConns,
latencyTestTimeoutDuration,
proxiesOrderingType,
proxiesPreviewType,
setAutoCloseConns,
setLatencyTestTimeoutDuration,
setProxiesOrderingType,
setProxiesPreviewType,
setUrlForLatencyTest,
urlForLatencyTest,
} from '~/signals'
export const ProxiesSettingsModal = () => {
const [t] = useI18n()
return (
<dialog
id={MODAL.PROXIES_SETTINGS}
class="modal modal-bottom sm:modal-middle"
>
<div class="modal-box flex flex-col gap-4">
<div>
<ConfigTitle withDivider>{t('autoCloseConns')}</ConfigTitle>
<div class="flex w-full justify-center">
<input
class="toggle"
type="checkbox"
checked={autoCloseConns()}
onChange={(e) => setAutoCloseConns(e.target.checked)}
/>
</div>
</div>
<div class="flex flex-col">
<ConfigTitle withDivider>{t('urlForLatencyTest')}</ConfigTitle>
<input
class="input input-bordered w-full"
value={urlForLatencyTest()}
onChange={(e) => setUrlForLatencyTest(e.target.value)}
/>
</div>
<div>
<ConfigTitle withDivider>
{t('latencyTestTimeoutDuration')} ({t('ms')})
</ConfigTitle>
<input
type="number"
class="input input-bordered w-full"
value={latencyTestTimeoutDuration()}
onChange={(e) =>
setLatencyTestTimeoutDuration(Number(e.target.value))
}
/>
</div>
<div>
<ConfigTitle withDivider>{t('proxiesSorting')}</ConfigTitle>
<select
class="select select-bordered w-full"
value={proxiesOrderingType()}
onChange={(e) =>
setProxiesOrderingType(e.target.value as PROXIES_ORDERING_TYPE)
}
>
<For each={Object.values(PROXIES_ORDERING_TYPE)}>
{(value) => (
<option class="flex items-center gap-2" value={value}>
{t(value)}
</option>
)}
</For>
</select>
</div>
<div>
<ConfigTitle withDivider>{t('proxiesPreviewType')}</ConfigTitle>
<select
class="select select-bordered w-full"
value={proxiesPreviewType()}
onChange={(e) =>
setProxiesPreviewType(e.target.value as PROXIES_PREVIEW_TYPE)
}
>
<For each={Object.values(PROXIES_PREVIEW_TYPE)}>
{(value) => <option value={value}>{t(value)}</option>}
</For>
</select>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button />
</form>
</dialog>
)
}

View File

@ -1,11 +1,14 @@
export * from './Button' export * from './Button'
export * from './Collapse' export * from './Collapse'
export * from './ConfigTitle'
export * from './ConnectionsSettingsModal'
export * from './ConnectionsTableDetailsModal' export * from './ConnectionsTableDetailsModal'
export * from './ConnectionsTableOrderingModal'
export * from './ForTwoColumns' export * from './ForTwoColumns'
export * from './Header' export * from './Header'
export * from './Latency' export * from './Latency'
export * from './LogoText' export * from './LogoText'
export * from './LogsSettingsModal'
export * from './ProxiesSettingsModal'
export * from './ProxyCardGroups' export * from './ProxyCardGroups'
export * from './ProxyNodeCard' export * from './ProxyNodeCard'
export * from './ProxyNodePreview' export * from './ProxyNodePreview'

View File

@ -163,3 +163,11 @@ export enum LOG_LEVEL {
} }
export const LOGS_TABLE_MAX_ROWS_LIST = [200, 300, 500, 800, 1000] export const LOGS_TABLE_MAX_ROWS_LIST = [200, 300, 500, 800, 1000]
export enum MODAL {
PROXIES_SETTINGS = 'proxies-settings',
RULES_SETTINGS = 'rules-settings',
CONNECTIONS_SETTINGS = 'connections-settings',
CONNECTIONS_TABLE_DETAILS = 'connections-table-details',
LOGS_SETTINGS = 'logs-settings',
}

View File

@ -59,7 +59,6 @@ export default {
orderName_desc: 'By name alphabetically (Z-A)', orderName_desc: 'By name alphabetically (Z-A)',
ms: 'ms', ms: 'ms',
updated: 'Updated', updated: 'Updated',
renderProxiesInSamePage: 'Render proxies and proxy provider in same page',
tableSize: 'Table size', tableSize: 'Table size',
logLevel: 'Log Level', logLevel: 'Log Level',
info: 'info', info: 'info',
@ -67,7 +66,7 @@ export default {
debug: 'debug', debug: 'debug',
warning: 'warning', warning: 'warning',
error: 'error', error: 'error',
logMaxRows: 'Log Maxinum Reserved Rows', logMaxRows: 'Log Maximum Reserved Rows',
xs: 'Extra small size', xs: 'Extra small size',
sm: 'Small size', sm: 'Small size',
md: 'Normal size', md: 'Normal size',
@ -86,4 +85,5 @@ export default {
direct: 'Direct', direct: 'Direct',
active: 'Active', active: 'Active',
closed: 'Closed', closed: 'Closed',
sort: 'Sort',
} }

View File

@ -59,7 +59,6 @@ export default {
orderName_desc: '按名称字母排序 (Z-A)', orderName_desc: '按名称字母排序 (Z-A)',
ms: '毫秒', ms: '毫秒',
updated: '更新于', updated: '更新于',
renderProxiesInSamePage: '将代理和代理提供者显示在同一页',
tableSize: '表格大小', tableSize: '表格大小',
logLevel: '日志等级', logLevel: '日志等级',
info: '信息', info: '信息',
@ -86,4 +85,5 @@ export default {
direct: '直连', direct: '直连',
active: '活动', active: '活动',
closed: '已关闭', closed: '已关闭',
sort: '排序',
} }

View File

@ -2,14 +2,7 @@ import { createForm } from '@felte/solid'
import { validator } from '@felte/validator-zod' import { validator } from '@felte/validator-zod'
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { useNavigate } from '@solidjs/router' import { useNavigate } from '@solidjs/router'
import { import { For, Show, createSignal, onMount } from 'solid-js'
For,
ParentComponent,
Show,
children,
createSignal,
onMount,
} from 'solid-js'
import { z } from 'zod' import { z } from 'zod'
import { import {
fetchBackendConfigAPI, fetchBackendConfigAPI,
@ -22,47 +15,21 @@ import {
upgradeBackendAPI, upgradeBackendAPI,
upgradingBackend, upgradingBackend,
} from '~/apis' } from '~/apis'
import { Button } from '~/components' import { Button, ConfigTitle } from '~/components'
import { LANG, MODE_OPTIONS, ROUTES, themes } from '~/constants'
import { import {
LANG,
LOGS_TABLE_MAX_ROWS_LIST,
LOG_LEVEL,
MODE_OPTIONS,
PROXIES_ORDERING_TYPE,
PROXIES_PREVIEW_TYPE,
ROUTES,
TAILWINDCSS_SIZE,
themes,
} from '~/constants'
import {
autoCloseConns,
autoSwitchTheme, autoSwitchTheme,
backendConfig, backendConfig,
favDayTheme, favDayTheme,
favNightTheme, favNightTheme,
latencyTestTimeoutDuration,
logLevel,
logMaxRows,
proxiesOrderingType,
proxiesPreviewType,
renderInTwoColumns, renderInTwoColumns,
setAutoCloseConns,
setAutoSwitchTheme, setAutoSwitchTheme,
setBackendConfig, setBackendConfig,
setFavDayTheme, setFavDayTheme,
setFavNightTheme, setFavNightTheme,
setLatencyTestTimeoutDuration,
setLogLevel,
setLogMaxRows,
setProxiesOrderingType,
setProxiesPreviewType,
setRenderInTwoColumns, setRenderInTwoColumns,
setSelectedEndpoint, setSelectedEndpoint,
setTableSize,
setTwemoji, setTwemoji,
setUrlForLatencyTest,
tableSize,
urlForLatencyTest,
useRequest, useRequest,
useTwemoji, useTwemoji,
} from '~/signals' } from '~/signals'
@ -73,12 +40,6 @@ const dnsQueryFormSchema = z.object({
type: z.string(), type: z.string(),
}) })
const ConfigTitle: ParentComponent = (props) => (
<div class="pb-4 text-lg font-semibold">
{children(() => props.children)()}
</div>
)
const DNSQueryForm = () => { const DNSQueryForm = () => {
const [t] = useI18n() const [t] = useI18n()
const request = useRequest() const request = useRequest()
@ -147,7 +108,7 @@ const ConfigForm = () => {
const portsList = [ const portsList = [
{ {
label: 'Http Port', label: 'HTTP Port',
key: 'port', key: 'port',
}, },
{ {
@ -159,7 +120,7 @@ const ConfigForm = () => {
key: 'redir-port', key: 'redir-port',
}, },
{ {
label: 'Tproxy Port', label: 'TProxy Port',
key: 'tproxy-port', key: 'tproxy-port',
}, },
{ {
@ -226,42 +187,6 @@ const ConfigForm = () => {
{t('restartCore')} {t('restartCore')}
</Button> </Button>
</div> </div>
<div class="flex flex-col">
<ConfigTitle>{t('urlForLatencyTest')}</ConfigTitle>
<input
class="input input-bordered max-w-md"
value={urlForLatencyTest()}
onChange={(e) => setUrlForLatencyTest(e.target.value)}
/>
</div>
<div>
<ConfigTitle>
{t('latencyTestTimeoutDuration')} ({t('ms')})
</ConfigTitle>
<input
type="number"
class="input input-bordered w-full max-w-md"
value={latencyTestTimeoutDuration()}
onChange={(e) =>
setLatencyTestTimeoutDuration(Number(e.target.value))
}
/>
</div>
<div>
<ConfigTitle>{t('autoCloseConns')}</ConfigTitle>
<input
class="toggle"
type="checkbox"
checked={autoCloseConns()}
onChange={(e) => setAutoCloseConns(e.target.checked)}
/>
</div>
</div> </div>
) )
} }
@ -355,97 +280,6 @@ const ConfigForXd = () => {
)} )}
</For> </For>
<div>
<ConfigTitle>{t('proxiesPreviewType')}</ConfigTitle>
<select
class="select select-bordered w-full max-w-xs"
value={proxiesPreviewType()}
onChange={(e) =>
setProxiesPreviewType(e.target.value as PROXIES_PREVIEW_TYPE)
}
>
<For each={Object.values(PROXIES_PREVIEW_TYPE)}>
{(value) => <option value={value}>{t(value)}</option>}
</For>
</select>
</div>
<div>
<ConfigTitle>{t('proxiesSorting')}</ConfigTitle>
<select
class="select select-bordered w-full max-w-xs"
value={proxiesOrderingType()}
onChange={(e) =>
setProxiesOrderingType(e.target.value as PROXIES_ORDERING_TYPE)
}
>
<For each={Object.values(PROXIES_ORDERING_TYPE)}>
{(value) => (
<option class="flex items-center gap-2" value={value}>
{t(value)}
</option>
)}
</For>
</select>
</div>
<div>
<ConfigTitle>{t('tableSize')}</ConfigTitle>
<select
class="select select-bordered w-full max-w-xs"
value={tableSize()}
onChange={(e) => setTableSize(e.target.value as TAILWINDCSS_SIZE)}
>
<For each={Object.values(TAILWINDCSS_SIZE)}>
{(value) => <option value={value}>{t(value)}</option>}
</For>
</select>
</div>
<div>
<ConfigTitle>{t('logLevel')}</ConfigTitle>
<select
class="select select-bordered w-full max-w-xs"
onChange={(e) => {
setLogLevel(e.target.value as LOG_LEVEL)
}}
>
<For
each={[
LOG_LEVEL.Info,
LOG_LEVEL.Error,
LOG_LEVEL.Warning,
LOG_LEVEL.Debug,
LOG_LEVEL.Silent,
]}
>
{(level) => (
<option selected={logLevel() === level} value={level}>
{t(level)}
</option>
)}
</For>
</select>
</div>
<div>
<ConfigTitle>{t('logMaxRows')}</ConfigTitle>
<select
class="select select-bordered w-full max-w-xs"
value={logMaxRows()}
onChange={(e) => setLogMaxRows(parseInt(e.target.value))}
>
<For each={LOGS_TABLE_MAX_ROWS_LIST}>
{(rows) => <option value={rows}>{rows}</option>}
</For>
</select>
</div>
<div> <div>
<Button <Button
onClick={() => { onClick={() => {

View File

@ -33,16 +33,21 @@ import { twMerge } from 'tailwind-merge'
import { closeAllConnectionsAPI, closeSingleConnectionAPI } from '~/apis' import { closeAllConnectionsAPI, closeSingleConnectionAPI } from '~/apis'
import { import {
Button, Button,
ConnectionsSettingsModal,
ConnectionsTableDetailsModal, ConnectionsTableDetailsModal,
ConnectionsTableOrderingModal,
} from '~/components' } from '~/components'
import { import {
CONNECTIONS_TABLE_ACCESSOR_KEY, CONNECTIONS_TABLE_ACCESSOR_KEY,
CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER, CONNECTIONS_TABLE_INITIAL_COLUMN_ORDER,
CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY, CONNECTIONS_TABLE_INITIAL_COLUMN_VISIBILITY,
MODAL,
} from '~/constants' } from '~/constants'
import { formatTimeFromNow } from '~/helpers' import { formatTimeFromNow } from '~/helpers'
import { tableSize, tableSizeClassName, useConnections } from '~/signals' import {
connectionsTableSize,
tableSizeClassName,
useConnections,
} from '~/signals'
import type { Connection } from '~/types' import type { Connection } from '~/types'
type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>> type ColumnVisibility = Partial<Record<CONNECTIONS_TABLE_ACCESSOR_KEY, boolean>>
@ -107,7 +112,7 @@ export default () => {
setSelectedConnectionID(row.original.id) setSelectedConnectionID(row.original.id)
const modal = document.querySelector( const modal = document.querySelector(
'#connections-table-details-modal', `#${MODAL.CONNECTIONS_TABLE_DETAILS}`,
) as HTMLDialogElement | null ) as HTMLDialogElement | null
modal?.showModal() modal?.showModal()
@ -357,7 +362,7 @@ export default () => {
class="btn join-item btn-sm sm:btn-md" class="btn join-item btn-sm sm:btn-md"
onClick={() => { onClick={() => {
const modal = document.querySelector( const modal = document.querySelector(
'#connections-table-ordering-modal', `#${MODAL.CONNECTIONS_SETTINGS}`,
) as HTMLDialogElement | null ) as HTMLDialogElement | null
modal?.showModal() modal?.showModal()
@ -371,7 +376,7 @@ export default () => {
<div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300"> <div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300">
<table <table
class={twMerge( class={twMerge(
tableSizeClassName(tableSize()), tableSizeClassName(connectionsTableSize()),
'table table-zebra relative rounded-none', 'table table-zebra relative rounded-none',
)} )}
> >
@ -488,7 +493,7 @@ export default () => {
</table> </table>
</div> </div>
<ConnectionsTableOrderingModal <ConnectionsSettingsModal
order={columnOrder()} order={columnOrder()}
visible={columnVisibility()} visible={columnVisibility()}
onOrderChange={(data: ColumnOrder) => setColumnOrder(data)} onOrderChange={(data: ColumnOrder) => setColumnOrder(data)}

View File

@ -1,4 +1,5 @@
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { IconSettings } from '@tabler/icons-solidjs'
import { import {
ColumnDef, ColumnDef,
createSolidTable, createSolidTable,
@ -7,8 +8,9 @@ import {
} from '@tanstack/solid-table' } from '@tanstack/solid-table'
import { For, Index, createEffect, createSignal } from 'solid-js' import { For, Index, createEffect, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { LOG_LEVEL } from '~/constants' import { Button, LogsSettingsModal } from '~/components'
import { tableSize, tableSizeClassName, useWsRequest } from '~/signals' import { LOG_LEVEL, MODAL } from '~/constants'
import { logsTableSize, tableSizeClassName, useWsRequest } from '~/signals'
import { logLevel, logMaxRows } from '~/signals/config' import { logLevel, logMaxRows } from '~/signals/config'
import { Log } from '~/types' import { Log } from '~/types'
@ -85,17 +87,32 @@ export default () => {
return ( return (
<div class="flex h-full flex-col gap-4 p-1"> <div class="flex h-full flex-col gap-4 p-1">
<input <div class="join w-full">
type="search" <input
class="input input-primary input-sm flex-shrink-0 sm:input-md" type="search"
placeholder={t('search')} class="input join-item input-primary input-sm flex-1 flex-shrink-0 sm:input-md"
onInput={(e) => setSearch(e.target.value)} placeholder={t('search')}
/> onInput={(e) => setSearch(e.target.value)}
/>
<Button
class="join-item btn-sm sm:btn-md"
onClick={() => {
const modal = document.querySelector(
`#${MODAL.LOGS_SETTINGS}`,
) as HTMLDialogElement | null
modal?.showModal()
}}
>
<IconSettings />
</Button>
</div>
<div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300"> <div class="overflow-x-auto whitespace-nowrap rounded-md bg-base-300">
<table <table
class={twMerge( class={twMerge(
tableSizeClassName(tableSize()), tableSizeClassName(logsTableSize()),
'table relative rounded-none', 'table relative rounded-none',
)} )}
> >
@ -150,6 +167,8 @@ export default () => {
</tbody> </tbody>
</table> </table>
</div> </div>
<LogsSettingsModal />
</div> </div>
) )
} }

View File

@ -1,15 +1,21 @@
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { IconBrandSpeedtest, IconReload } from '@tabler/icons-solidjs' import {
IconBrandSpeedtest,
IconReload,
IconSettings,
} from '@tabler/icons-solidjs'
import { For, Show, createSignal } from 'solid-js' import { For, Show, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import { import {
Button, Button,
Collapse, Collapse,
ForTwoColumns, ForTwoColumns,
ProxiesSettingsModal,
ProxyCardGroups, ProxyCardGroups,
ProxyNodePreview, ProxyNodePreview,
SubscriptionInfo, SubscriptionInfo,
} from '~/components' } from '~/components'
import { MODAL } from '~/constants'
import { import {
formatTimeFromNow, formatTimeFromNow,
sortProxiesByOrderingType, sortProxiesByOrderingType,
@ -123,6 +129,21 @@ export default () => {
/> />
</Button> </Button>
</Show> </Show>
<div class="ml-auto">
<Button
class="btn-circle btn-sm sm:btn-md"
onClick={() => {
const modal = document.querySelector(
`#${MODAL.PROXIES_SETTINGS}`,
) as HTMLDialogElement | null
modal?.showModal()
}}
>
<IconSettings />
</Button>
</div>
</div> </div>
</Show> </Show>
@ -274,6 +295,8 @@ export default () => {
/> />
</Show> </Show>
</div> </div>
<ProxiesSettingsModal />
</div> </div>
) )
} }

View File

@ -48,9 +48,13 @@ export const [renderInTwoColumns, setRenderInTwoColumns] = makePersisted(
createSignal(true), createSignal(true),
{ name: 'renderInTwoColumn', storage: localStorage }, { name: 'renderInTwoColumn', storage: localStorage },
) )
export const [tableSize, setTableSize] = makePersisted( export const [connectionsTableSize, setConnectionsTableSize] = makePersisted(
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS), createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
{ name: 'tableSize', storage: localStorage }, { name: 'connectionsTableSize', storage: localStorage },
)
export const [logsTableSize, setLogsTableSize] = makePersisted(
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
{ name: 'logsTableSize', storage: localStorage },
) )
export const [logLevel, setLogLevel] = makePersisted( export const [logLevel, setLogLevel] = makePersisted(