mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-11-10 05:15:35 +08:00
feat(rule): implement virtual scroll on rules and rule providers, fixes #285
This commit is contained in:
parent
f8bf02e9c7
commit
7145cec62f
@ -32,7 +32,8 @@
|
|||||||
"@tabler/icons-solidjs": "^2.35.0",
|
"@tabler/icons-solidjs": "^2.35.0",
|
||||||
"@tanstack/match-sorter-utils": "^8.8.4",
|
"@tanstack/match-sorter-utils": "^8.8.4",
|
||||||
"@tanstack/solid-table": "^8.10.3",
|
"@tanstack/solid-table": "^8.10.3",
|
||||||
"@tanstack/solid-virtual": "3.0.0-beta.6",
|
"@tanstack/solid-virtual": "3.0.0-beta.60",
|
||||||
|
"@tanstack/virtual-core": "3.0.0-beta.60",
|
||||||
"@thisbeyond/solid-dnd": "^0.7.4",
|
"@thisbeyond/solid-dnd": "^0.7.4",
|
||||||
"@types/byte-size": "^8.1.0",
|
"@types/byte-size": "^8.1.0",
|
||||||
"@types/lodash": "^4.14.199",
|
"@types/lodash": "^4.14.199",
|
||||||
|
@ -60,8 +60,11 @@ dependencies:
|
|||||||
specifier: ^8.10.3
|
specifier: ^8.10.3
|
||||||
version: 8.10.3(solid-js@1.7.12)
|
version: 8.10.3(solid-js@1.7.12)
|
||||||
'@tanstack/solid-virtual':
|
'@tanstack/solid-virtual':
|
||||||
specifier: 3.0.0-beta.6
|
specifier: 3.0.0-beta.60
|
||||||
version: 3.0.0-beta.6
|
version: 3.0.0-beta.60(solid-js@1.7.12)
|
||||||
|
'@tanstack/virtual-core':
|
||||||
|
specifier: 3.0.0-beta.60
|
||||||
|
version: 3.0.0-beta.60
|
||||||
'@thisbeyond/solid-dnd':
|
'@thisbeyond/solid-dnd':
|
||||||
specifier: ^0.7.4
|
specifier: ^0.7.4
|
||||||
version: 0.7.4(solid-js@1.7.12)
|
version: 0.7.4(solid-js@1.7.12)
|
||||||
@ -1968,10 +1971,6 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@reach/observe-rect@1.2.0:
|
|
||||||
resolution: {integrity: sha512-Ba7HmkFgfQxZqqaeIWWkNK0rEhpxVQHIoVyW1YDSkGsGIXzcaW4deC8B0pZrNSSyLTdIk7y+5olKt5+g0GmFIQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@rollup/plugin-babel@5.3.1(@babel/core@7.22.20)(rollup@2.79.1):
|
/@rollup/plugin-babel@5.3.1(@babel/core@7.22.20)(rollup@2.79.1):
|
||||||
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
|
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
@ -2194,11 +2193,13 @@ packages:
|
|||||||
solid-js: 1.7.12
|
solid-js: 1.7.12
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@tanstack/solid-virtual@3.0.0-beta.6:
|
/@tanstack/solid-virtual@3.0.0-beta.60(solid-js@1.7.12):
|
||||||
resolution: {integrity: sha512-/HjeHZb4UZxxFSAkICUEWOozGwHQpKlvtnUoS5uSMSuLOz0XM5vFq6zR6ENwAczKWDtkh8ntddk+zXAhyXOlEw==}
|
resolution: {integrity: sha512-SN007vDq1AeRq5w2rK0E47CeI8rv+oPZ5IjtOgLynt1MKNUsSsJ7d+igK2jUPQ24QrZf5NLrN4FRQRdxvakvIg==}
|
||||||
engines: {node: '>=12'}
|
peerDependencies:
|
||||||
|
solid-js: ^1.3.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@reach/observe-rect': 1.2.0
|
'@tanstack/virtual-core': 3.0.0-beta.60
|
||||||
|
solid-js: 1.7.12
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@tanstack/table-core@8.10.3:
|
/@tanstack/table-core@8.10.3:
|
||||||
@ -2206,6 +2207,10 @@ packages:
|
|||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tanstack/virtual-core@3.0.0-beta.60:
|
||||||
|
resolution: {integrity: sha512-QlCdhsV1+JIf0c0U6ge6SQmpwsyAT0oQaOSZk50AtEeAyQl9tQrd6qCHAslxQpgphrfe945abvKG8uYvw3hIGA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@thisbeyond/solid-dnd@0.7.4(solid-js@1.7.12):
|
/@thisbeyond/solid-dnd@0.7.4(solid-js@1.7.12):
|
||||||
resolution: {integrity: sha512-jgV9EtR3gAtVsILG8p1OAGrhHIgnK4W04YxpyLgJRCDKEFYQWuDrMdUe8F5Kc6pcVXlC4IMXr4cB8fS2Ut3/Ow==}
|
resolution: {integrity: sha512-jgV9EtR3gAtVsILG8p1OAGrhHIgnK4W04YxpyLgJRCDKEFYQWuDrMdUe8F5Kc6pcVXlC4IMXr4cB8fS2Ut3/Ow==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { IconReload } from '@tabler/icons-solidjs'
|
import { IconReload } from '@tabler/icons-solidjs'
|
||||||
|
import { createVirtualizer } from '@tanstack/solid-virtual'
|
||||||
import { For, Show, createSignal, onMount } from 'solid-js'
|
import { For, Show, createSignal, onMount } from 'solid-js'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { Button } from '~/components'
|
import { Button } from '~/components'
|
||||||
@ -56,6 +57,30 @@ export default () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let parentRef: HTMLDivElement | undefined
|
||||||
|
|
||||||
|
const ruleVirtualizer = createVirtualizer({
|
||||||
|
get count() {
|
||||||
|
return rules().length
|
||||||
|
},
|
||||||
|
getScrollElement: () => parentRef!,
|
||||||
|
estimateSize: () => 74,
|
||||||
|
overscan: 5,
|
||||||
|
})
|
||||||
|
|
||||||
|
const ruleVirtualizerItems = ruleVirtualizer.getVirtualItems()
|
||||||
|
|
||||||
|
const ruleProviderVirtualizer = createVirtualizer({
|
||||||
|
get count() {
|
||||||
|
return ruleProviders().length
|
||||||
|
},
|
||||||
|
getScrollElement: () => parentRef!,
|
||||||
|
estimateSize: () => 74,
|
||||||
|
overscan: 5,
|
||||||
|
})
|
||||||
|
|
||||||
|
const ruleProviderVirtualizerItems = ruleProviderVirtualizer.getVirtualItems()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex h-full flex-col gap-2">
|
<div class="flex h-full flex-col gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@ -92,60 +117,91 @@ export default () => {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 overflow-y-auto">
|
<div ref={(ref) => (parentRef = ref)} class="flex-1 overflow-y-auto">
|
||||||
<Show when={activeTab() === ActiveTab.rules}>
|
<Show when={activeTab() === ActiveTab.rules}>
|
||||||
<div class="grid gap-2">
|
<div
|
||||||
<For each={rules()}>
|
class="relative"
|
||||||
{(rule) => (
|
style={{ height: `${ruleVirtualizer.getTotalSize()}px` }}
|
||||||
<div class="card card-bordered card-compact bg-base-200 p-4">
|
>
|
||||||
<div class="flex items-center gap-2">
|
{ruleVirtualizerItems.map((virtualizerItem) => {
|
||||||
<span class="break-all">{rule.payload}</span>
|
const rule = rules()[virtualizerItem.index]
|
||||||
<Show
|
|
||||||
when={typeof rule.size === 'number' && rule.size !== -1}
|
return (
|
||||||
>
|
<div
|
||||||
<div class="badge badge-sm">{rule.size}</div>
|
ref={(el) =>
|
||||||
</Show>
|
onMount(() => ruleVirtualizer.measureElement(el))
|
||||||
</div>
|
}
|
||||||
<div class="text-xs text-slate-500">
|
data-index={virtualizerItem.index}
|
||||||
{rule.type} :: {rule.proxy}
|
class="absolute inset-x-0 top-0 pb-2 last:pb-0"
|
||||||
|
style={{
|
||||||
|
transform: `translateY(${virtualizerItem.start}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<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={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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
</For>
|
})}
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
<Show when={activeTab() === ActiveTab.ruleProviders}>
|
||||||
<div class="grid gap-2">
|
<div class="relative">
|
||||||
<For each={ruleProviders()}>
|
{ruleProviderVirtualizerItems.map((virtualizerItem) => {
|
||||||
{(ruleProvider) => (
|
const ruleProvider = ruleProviders()[virtualizerItem.index]
|
||||||
<div class="card card-bordered card-compact bg-base-200 p-4">
|
|
||||||
<div class="flex items-center gap-2 pr-8">
|
|
||||||
<span class="break-all">{ruleProvider.name}</span>
|
|
||||||
<div class="badge badge-sm">{ruleProvider.ruleCount}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-xs text-slate-500">
|
return (
|
||||||
{ruleProvider.vehicleType} / {ruleProvider.behavior} /
|
<div
|
||||||
{t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
|
ref={(el) =>
|
||||||
</div>
|
onMount(() => ruleProviderVirtualizer.measureElement(el))
|
||||||
|
}
|
||||||
|
data-index={virtualizerItem.index}
|
||||||
|
class="absolute inset-x-0 top-0 pb-2 last:pb-0"
|
||||||
|
style={{
|
||||||
|
transform: `translateY(${virtualizerItem.start}px)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="card card-bordered card-compact bg-base-200 p-4">
|
||||||
|
<div class="flex items-center gap-2 pr-8">
|
||||||
|
<span class="break-all">{ruleProvider.name}</span>
|
||||||
|
<div class="badge badge-sm">{ruleProvider.ruleCount}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button
|
<div class="text-xs text-slate-500">
|
||||||
class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4"
|
{ruleProvider.vehicleType} / {ruleProvider.behavior} /
|
||||||
disabled={updatingMap()[ruleProvider.name]}
|
{t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
|
||||||
onClick={(e) => onUpdateProviderClick(e, ruleProvider.name)}
|
</div>
|
||||||
icon={
|
|
||||||
<IconReload
|
<Button
|
||||||
class={twMerge(
|
class="btn-circle btn-sm absolute right-2 top-2 mr-2 h-4"
|
||||||
updatingMap()[ruleProvider.name] &&
|
disabled={updatingMap()[ruleProvider.name]}
|
||||||
'animate-spin text-success',
|
onClick={(e) =>
|
||||||
)}
|
onUpdateProviderClick(e, ruleProvider.name)
|
||||||
/>
|
}
|
||||||
}
|
icon={
|
||||||
/>
|
<IconReload
|
||||||
|
class={twMerge(
|
||||||
|
updatingMap()[ruleProvider.name] &&
|
||||||
|
'animate-spin text-success',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
</For>
|
})}
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user