mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-11-24 09:45:35 +08:00
feat(rules): renderInTwoColumns
This commit is contained in:
parent
9e556d959e
commit
6743ebde8f
@ -55,7 +55,6 @@
|
|||||||
"prettier-plugin-organize-imports": "^3.2.3",
|
"prettier-plugin-organize-imports": "^3.2.3",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.4",
|
"prettier-plugin-tailwindcss": "^0.5.4",
|
||||||
"solid-apexcharts": "^0.3.2",
|
"solid-apexcharts": "^0.3.2",
|
||||||
"solid-infinite-scroll": "^1.0.1",
|
|
||||||
"solid-js": "^1.7.11",
|
"solid-js": "^1.7.11",
|
||||||
"solid-toast": "^0.5.0",
|
"solid-toast": "^0.5.0",
|
||||||
"sort-package-json": "^2.5.1",
|
"sort-package-json": "^2.5.1",
|
||||||
|
@ -131,9 +131,6 @@ dependencies:
|
|||||||
solid-apexcharts:
|
solid-apexcharts:
|
||||||
specifier: ^0.3.2
|
specifier: ^0.3.2
|
||||||
version: 0.3.2(apexcharts@3.42.0)(solid-js@1.7.11)
|
version: 0.3.2(apexcharts@3.42.0)(solid-js@1.7.11)
|
||||||
solid-infinite-scroll:
|
|
||||||
specifier: ^1.0.1
|
|
||||||
version: 1.0.1
|
|
||||||
solid-js:
|
solid-js:
|
||||||
specifier: ^1.7.11
|
specifier: ^1.7.11
|
||||||
version: 1.7.11
|
version: 1.7.11
|
||||||
@ -5230,12 +5227,6 @@ packages:
|
|||||||
solid-js: 1.7.11
|
solid-js: 1.7.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/solid-infinite-scroll@1.0.1:
|
|
||||||
resolution: {integrity: sha512-yIDyr61316jyHSxuQqidMUTcauKDQyPIzIAKawTrTadqxij2wOcM3ei6/MR6WPBCcR0ZMJ8E0aQInFSnL9w2Hg==}
|
|
||||||
dependencies:
|
|
||||||
solid-js: 1.7.11
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/solid-js@1.7.11:
|
/solid-js@1.7.11:
|
||||||
resolution: {integrity: sha512-JkuvsHt8jqy7USsy9xJtT18aF9r2pFO+GB8JQ2XGTvtF49rGTObB46iebD25sE3qVNvIbwglXOXdALnJq9IHtQ==}
|
resolution: {integrity: sha512-JkuvsHt8jqy7USsy9xJtT18aF9r2pFO+GB8JQ2XGTvtF49rGTObB46iebD25sE3qVNvIbwglXOXdALnJq9IHtQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { JSX, ParentComponent, Show, createMemo } from 'solid-js'
|
import { JSX, ParentComponent, Show, createMemo } from 'solid-js'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { renderInTwoColumns } from '~/signals'
|
import { proxiesRenderInTwoColumns } from '~/signals'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: JSX.Element
|
title: JSX.Element
|
||||||
@ -27,7 +27,7 @@ export const Collapse: ParentComponent<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mediaQueryClassName = createMemo(() => {
|
const mediaQueryClassName = createMemo(() => {
|
||||||
if (renderInTwoColumns()) {
|
if (proxiesRenderInTwoColumns()) {
|
||||||
return 'lg:grid-cols-3 xl:grid-cols-4'
|
return 'lg:grid-cols-3 xl:grid-cols-4'
|
||||||
} else {
|
} else {
|
||||||
return 'sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7'
|
return 'sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7'
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { createWindowSize } from '@solid-primitives/resize-observer'
|
import { createWindowSize } from '@solid-primitives/resize-observer'
|
||||||
import { JSX, Show, createMemo } from 'solid-js'
|
import { JSX, Show, createMemo } from 'solid-js'
|
||||||
import { renderInTwoColumns } from '~/signals'
|
import { proxiesRenderInTwoColumns } from '~/signals'
|
||||||
|
|
||||||
export const ForTwoColumns = (props: { subChild: JSX.Element[] }) => {
|
export const ForTwoColumns = (props: { subChild: JSX.Element[] }) => {
|
||||||
const windowSize = createWindowSize()
|
const windowSize = createWindowSize()
|
||||||
const isShowTwoColumns = createMemo(
|
const isShowTwoColumns = createMemo(
|
||||||
() => windowSize.width >= 640 && renderInTwoColumns(),
|
() => windowSize.width >= 640 && proxiesRenderInTwoColumns(),
|
||||||
) // 640 is sm size in daisyui
|
) // 640 is sm size in daisyui
|
||||||
const leftColumns = createMemo(() =>
|
const leftColumns = createMemo(() =>
|
||||||
props.subChild.filter((_, index) => index % 2 === 0 || !isShowTwoColumns()),
|
props.subChild.filter((_, index) => index % 2 === 0 || !isShowTwoColumns()),
|
||||||
|
@ -8,11 +8,13 @@ import {
|
|||||||
latencyTestTimeoutDuration,
|
latencyTestTimeoutDuration,
|
||||||
proxiesOrderingType,
|
proxiesOrderingType,
|
||||||
proxiesPreviewType,
|
proxiesPreviewType,
|
||||||
|
proxiesRenderInTwoColumns,
|
||||||
setAutoCloseConns,
|
setAutoCloseConns,
|
||||||
setHideUnAvailableProxies,
|
setHideUnAvailableProxies,
|
||||||
setLatencyTestTimeoutDuration,
|
setLatencyTestTimeoutDuration,
|
||||||
setProxiesOrderingType,
|
setProxiesOrderingType,
|
||||||
setProxiesPreviewType,
|
setProxiesPreviewType,
|
||||||
|
setProxiesRenderInTwoColumns,
|
||||||
setUrlForLatencyTest,
|
setUrlForLatencyTest,
|
||||||
urlForLatencyTest,
|
urlForLatencyTest,
|
||||||
} from '~/signals'
|
} from '~/signals'
|
||||||
@ -112,6 +114,19 @@ export const ProxiesSettingsModal = () => {
|
|||||||
</For>
|
</For>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ConfigTitle withDivider>{t('renderInTwoColumns')}</ConfigTitle>
|
||||||
|
|
||||||
|
<div class="flex w-full justify-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle"
|
||||||
|
checked={proxiesRenderInTwoColumns()}
|
||||||
|
onChange={(e) => setProxiesRenderInTwoColumns(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="dialog" class="modal-backdrop">
|
<form method="dialog" class="modal-backdrop">
|
||||||
|
34
src/components/RulesSettingsModal.tsx
Normal file
34
src/components/RulesSettingsModal.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { useI18n } from '@solid-primitives/i18n'
|
||||||
|
import { ConfigTitle } from '~/components'
|
||||||
|
import { MODAL } from '~/constants'
|
||||||
|
import { rulesRenderInTwoColumns, setRulesRenderInTwoColumns } from '~/signals'
|
||||||
|
|
||||||
|
export const RulesSettingsModal = () => {
|
||||||
|
const [t] = useI18n()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<dialog
|
||||||
|
id={MODAL.RULES_SETTINGS}
|
||||||
|
class="modal modal-bottom sm:modal-middle"
|
||||||
|
>
|
||||||
|
<div class="modal-box flex flex-col gap-4">
|
||||||
|
<div>
|
||||||
|
<ConfigTitle withDivider>{t('renderInTwoColumns')}</ConfigTitle>
|
||||||
|
|
||||||
|
<div class="flex w-full justify-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle"
|
||||||
|
checked={rulesRenderInTwoColumns()}
|
||||||
|
onChange={(e) => setRulesRenderInTwoColumns(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="dialog" class="modal-backdrop">
|
||||||
|
<button />
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
)
|
||||||
|
}
|
@ -14,4 +14,5 @@ export * from './ProxyNodeCard'
|
|||||||
export * from './ProxyNodePreview'
|
export * from './ProxyNodePreview'
|
||||||
export * from './ProxyPreviewBar'
|
export * from './ProxyPreviewBar'
|
||||||
export * from './ProxyPreviewDots'
|
export * from './ProxyPreviewDots'
|
||||||
|
export * from './RulesSettingsModal'
|
||||||
export * from './SubscriptionInfo'
|
export * from './SubscriptionInfo'
|
||||||
|
@ -22,12 +22,10 @@ import {
|
|||||||
backendConfig,
|
backendConfig,
|
||||||
favDayTheme,
|
favDayTheme,
|
||||||
favNightTheme,
|
favNightTheme,
|
||||||
renderInTwoColumns,
|
|
||||||
setAutoSwitchTheme,
|
setAutoSwitchTheme,
|
||||||
setBackendConfig,
|
setBackendConfig,
|
||||||
setFavDayTheme,
|
setFavDayTheme,
|
||||||
setFavNightTheme,
|
setFavNightTheme,
|
||||||
setRenderInTwoColumns,
|
|
||||||
setSelectedEndpoint,
|
setSelectedEndpoint,
|
||||||
setTwemoji,
|
setTwemoji,
|
||||||
useRequest,
|
useRequest,
|
||||||
@ -238,11 +236,6 @@ const ConfigForXd = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const checkboxList = [
|
const checkboxList = [
|
||||||
{
|
|
||||||
label: t('renderInTwoColumns'),
|
|
||||||
value: renderInTwoColumns,
|
|
||||||
onChange: setRenderInTwoColumns,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t('autoSwitchTheme'),
|
label: t('autoSwitchTheme'),
|
||||||
value: autoSwitchTheme,
|
value: autoSwitchTheme,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useI18n } from '@solid-primitives/i18n'
|
import { useI18n } from '@solid-primitives/i18n'
|
||||||
import { IconReload } from '@tabler/icons-solidjs'
|
import { IconReload, IconSettings } from '@tabler/icons-solidjs'
|
||||||
import InfiniteScroll from 'solid-infinite-scroll'
|
import { For, Show, createSignal, onMount } from 'solid-js'
|
||||||
import { For, Show, createMemo, createSignal, onMount } from 'solid-js'
|
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { Button, ForTwoColumns } from '~/components'
|
import { Button, RulesSettingsModal } from '~/components'
|
||||||
|
import { MODAL } from '~/constants'
|
||||||
import { formatTimeFromNow, useStringBooleanMap } from '~/helpers'
|
import { formatTimeFromNow, useStringBooleanMap } from '~/helpers'
|
||||||
import { useRules } from '~/signals'
|
import { rulesRenderInTwoColumns, useRules } from '~/signals'
|
||||||
|
|
||||||
enum ActiveTab {
|
enum ActiveTab {
|
||||||
ruleProviders = 'ruleProviders',
|
ruleProviders = 'ruleProviders',
|
||||||
@ -21,8 +21,6 @@ export default () => {
|
|||||||
updateAllRuleProvider,
|
updateAllRuleProvider,
|
||||||
updateRuleProviderByName,
|
updateRuleProviderByName,
|
||||||
} = useRules()
|
} = useRules()
|
||||||
const [maxRuleRender, setMaxRuleRender] = createSignal(100)
|
|
||||||
const renderRules = createMemo(() => rules().slice(0, maxRuleRender()))
|
|
||||||
|
|
||||||
onMount(updateRules)
|
onMount(updateRules)
|
||||||
|
|
||||||
@ -61,55 +59,102 @@ export default () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex h-full flex-col gap-2">
|
<div class="flex h-full flex-col gap-2">
|
||||||
<Show when={ruleProviders().length > 0}>
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="tabs-boxed tabs gap-2">
|
||||||
<div class="tabs tabs-boxed gap-2">
|
<For each={tabs()}>
|
||||||
<For each={tabs()}>
|
{(tab) => (
|
||||||
{(tab) => (
|
<button
|
||||||
<button
|
class={twMerge(
|
||||||
class={twMerge(
|
activeTab() === tab.type && 'tab-active',
|
||||||
activeTab() === tab.type && 'tab-active',
|
'tab tab-sm gap-2 px-2 md:tab-md',
|
||||||
'tab tab-sm gap-2 px-2 md:tab-md',
|
)}
|
||||||
)}
|
onClick={() => setActiveTab(tab.type)}
|
||||||
onClick={() => setActiveTab(tab.type)}
|
>
|
||||||
>
|
<span>{tab.name}</span>
|
||||||
<span>{tab.name}</span>
|
<div class="badge badge-sm">{tab.count}</div>
|
||||||
<div class="badge badge-sm">{tab.count}</div>
|
</button>
|
||||||
</button>
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
||||||
|
<Button
|
||||||
|
class="btn btn-circle btn-sm"
|
||||||
|
disabled={allProviderIsUpdating()}
|
||||||
|
onClick={(e) => onUpdateAllProviderClick(e)}
|
||||||
|
>
|
||||||
|
<IconReload
|
||||||
|
class={twMerge(
|
||||||
|
allProviderIsUpdating() && 'animate-spin text-success',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<div class="ml-auto">
|
||||||
|
<Button
|
||||||
|
class="btn-circle btn-sm sm:btn-md"
|
||||||
|
onClick={() => {
|
||||||
|
const modal = document.querySelector(
|
||||||
|
`#${MODAL.RULES_SETTINGS}`,
|
||||||
|
) as HTMLDialogElement | null
|
||||||
|
|
||||||
|
modal?.showModal()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconSettings />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 overflow-y-auto">
|
||||||
|
<Show when={activeTab() === ActiveTab.rules}>
|
||||||
|
<div
|
||||||
|
class="grid gap-2"
|
||||||
|
classList={{
|
||||||
|
'grid-cols-2': rulesRenderInTwoColumns(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={rules()}>
|
||||||
|
{(rule) => (
|
||||||
|
<div class="card card-bordered card-compact bg-base-200 p-4">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="break-all">{rule.payload}</span>
|
||||||
|
<Show
|
||||||
|
when={typeof rule.size === 'number' && rule.size !== -1}
|
||||||
|
>
|
||||||
|
<div class="badge badge-sm">{rule.size}</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-slate-500">
|
||||||
|
{rule.type} :: {rule.proxy}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
|
||||||
<Button
|
|
||||||
class="btn btn-circle btn-sm"
|
|
||||||
disabled={allProviderIsUpdating()}
|
|
||||||
onClick={(e) => onUpdateAllProviderClick(e)}
|
|
||||||
>
|
|
||||||
<IconReload
|
|
||||||
class={twMerge(
|
|
||||||
allProviderIsUpdating() && 'animate-spin text-success',
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<div class="flex-1 overflow-y-auto">
|
|
||||||
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
||||||
<ForTwoColumns
|
<div
|
||||||
subChild={ruleProviders().map((ruleProvider) => {
|
class="grid gap-2"
|
||||||
return (
|
classList={{
|
||||||
<div class="card card-bordered card-compact mb-2 bg-base-200 p-4">
|
'grid-cols-2': rulesRenderInTwoColumns(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<For each={ruleProviders()}>
|
||||||
|
{(ruleProvider) => (
|
||||||
|
<div class="card card-bordered card-compact bg-base-200 p-4">
|
||||||
<div class="flex items-center gap-2 pr-8">
|
<div class="flex items-center gap-2 pr-8">
|
||||||
<span class="break-all">{ruleProvider.name}</span>
|
<span class="break-all">{ruleProvider.name}</span>
|
||||||
<div class="badge badge-sm">{ruleProvider.ruleCount}</div>
|
<div class="badge badge-sm">{ruleProvider.ruleCount}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-xs text-slate-500">
|
<div class="text-xs text-slate-500">
|
||||||
{ruleProvider.vehicleType} / {ruleProvider.behavior} /
|
{ruleProvider.vehicleType} / {ruleProvider.behavior} /
|
||||||
{t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
|
{t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4"
|
class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4"
|
||||||
disabled={updatingMap()[ruleProvider.name]}
|
disabled={updatingMap()[ruleProvider.name]}
|
||||||
@ -123,35 +168,13 @@ export default () => {
|
|||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
})}
|
</For>
|
||||||
/>
|
</div>
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Show when={activeTab() === ActiveTab.rules}>
|
|
||||||
<InfiniteScroll
|
|
||||||
each={renderRules()}
|
|
||||||
hasMore={renderRules().length < rules().length}
|
|
||||||
next={() => setMaxRuleRender(maxRuleRender() + 100)}
|
|
||||||
>
|
|
||||||
{(rule) => (
|
|
||||||
<div class="card card-bordered card-compact mb-2 bg-base-200 p-4">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="break-all">{rule.payload}</span>
|
|
||||||
<Show
|
|
||||||
when={typeof rule.size === 'number' && rule.size !== -1}
|
|
||||||
>
|
|
||||||
<div class="badge badge-sm">{rule.size}</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-slate-500">
|
|
||||||
{rule.type} :: {rule.proxy}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</InfiniteScroll>
|
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<RulesSettingsModal />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,16 @@ export const [favNightTheme, setFavNightTheme] = makePersisted(
|
|||||||
createSignal('night'),
|
createSignal('night'),
|
||||||
{ name: 'favNightTheme', storage: localStorage },
|
{ name: 'favNightTheme', storage: localStorage },
|
||||||
)
|
)
|
||||||
export const [renderInTwoColumns, setRenderInTwoColumns] = makePersisted(
|
export const [proxiesRenderInTwoColumns, setProxiesRenderInTwoColumns] =
|
||||||
createSignal(true),
|
makePersisted(createSignal(true), {
|
||||||
{ name: 'renderInTwoColumn', storage: localStorage },
|
name: 'proxiesRenderInTwoColumns',
|
||||||
)
|
storage: localStorage,
|
||||||
|
})
|
||||||
|
export const [rulesRenderInTwoColumns, setRulesRenderInTwoColumns] =
|
||||||
|
makePersisted(createSignal(true), {
|
||||||
|
name: 'rulesRenderInTwoColumns',
|
||||||
|
storage: localStorage,
|
||||||
|
})
|
||||||
export const [connectionsTableSize, setConnectionsTableSize] = makePersisted(
|
export const [connectionsTableSize, setConnectionsTableSize] = makePersisted(
|
||||||
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
|
createSignal<TAILWINDCSS_SIZE>(TAILWINDCSS_SIZE.XS),
|
||||||
{ name: 'connectionsTableSize', storage: localStorage },
|
{ name: 'connectionsTableSize', storage: localStorage },
|
||||||
|
Loading…
Reference in New Issue
Block a user