feat: columns hide

This commit is contained in:
Zephyruso 2023-08-31 22:49:34 +08:00
parent adfa4324ef
commit cb9970c29e
5 changed files with 140 additions and 45 deletions

View File

@ -0,0 +1,46 @@
import { For } from 'solid-js'
import { AccessorKey } from '~/config/connection'
type ColumnVisibility = Partial<Record<AccessorKey, boolean>>
export default (props: {
data: ColumnVisibility
onChange: (value: ColumnVisibility) => void
}) => {
const { onChange } = props
return (
<>
<input type="checkbox" id="connection-modal" class="modal-toggle" />
<div class="modal">
<div class="modal-box w-80">
<For
each={Object.values(AccessorKey).filter(
(i) => ![AccessorKey.Close, AccessorKey.ID].includes(i),
)}
>
{(key) => (
<div class="m-1 flex justify-between p-1">
{key}
<input
type="checkbox"
class="toggle"
checked={props.data[key]}
onChange={(e) => {
onChange({
...props.data,
[key]: e.target.checked,
})
}}
/>
</div>
)}
</For>
</div>
<label class="modal-backdrop" htmlFor="connection-modal">
Close
</label>
</div>
</>
)
}

View File

@ -32,8 +32,8 @@ export default (props: {
const formatProxyType = (type: string) => { const formatProxyType = (type: string) => {
const t = type.toLowerCase() const t = type.toLowerCase()
if (t === 'shadowsocks') { if (t.includes('shadowsocks')) {
return 'ss' return t.replace('shadowsocks', 'ss')
} }
return t return t
@ -42,7 +42,7 @@ export default (props: {
return ( return (
<div <div
class={twMerge( class={twMerge(
'card card-bordered tooltip tooltip-bottom card-compact flex gap-1 p-4', 'card card-bordered tooltip tooltip-bottom card-compact flex gap-1 p-3',
isSelected isSelected
? 'border-primary bg-success-content text-success' ? 'border-primary bg-success-content text-success'
: 'border-secondary', : 'border-secondary',

15
src/config/connection.ts Normal file
View File

@ -0,0 +1,15 @@
export enum AccessorKey {
Close = 'Close',
ID = 'ID',
Type = 'Type',
Process = 'Process',
Host = 'Host',
Rule = 'Rule',
Chains = 'Chains',
DlSpeed = 'DL Speed',
ULSpeed = 'UL Speed',
Download = 'DL',
Upload = 'UL',
Source = 'Source',
Destination = 'Destination',
}

View File

@ -1,7 +1,9 @@
import { createEventSignal } from '@solid-primitives/event-listener' import { createEventSignal } from '@solid-primitives/event-listener'
import { makePersisted } from '@solid-primitives/storage'
import { createReconnectingWS } from '@solid-primitives/websocket' import { createReconnectingWS } from '@solid-primitives/websocket'
import { import {
IconCircleX, IconCircleX,
IconSettings,
IconSortAscending, IconSortAscending,
IconSortDescending, IconSortDescending,
} from '@tabler/icons-solidjs' } from '@tabler/icons-solidjs'
@ -17,6 +19,8 @@ import byteSize from 'byte-size'
import { isIPv6 } from 'is-ip' import { isIPv6 } from 'is-ip'
import { For, createEffect, createSignal } from 'solid-js' import { For, createEffect, createSignal } from 'solid-js'
import { twMerge } from 'tailwind-merge' import { twMerge } from 'tailwind-merge'
import ConnectionsModal from '~/components/ConnectionsModal'
import { AccessorKey } from '~/config/connection'
import { secret, useRequest, wsEndpointURL } from '~/signals' import { secret, useRequest, wsEndpointURL } from '~/signals'
import type { Connection } from '~/types' import type { Connection } from '~/types'
@ -25,7 +29,22 @@ type ConnectionWithSpeed = Connection & {
uploadSpeed: number uploadSpeed: number
} }
type ColumnVisibility = Partial<Record<AccessorKey, boolean>>
const initColumnVisibility = {
...Object.fromEntries(Object.values(AccessorKey).map((i) => [i, true])),
[AccessorKey.ID]: false,
}
export default () => { export default () => {
const [columnVisibility, setColumnVisibility] = makePersisted(
createSignal<ColumnVisibility>(initColumnVisibility),
{
name: 'columnVisibility',
storage: localStorage,
},
)
const request = useRequest() const request = useRequest()
const [search, setSearch] = createSignal('') const [search, setSearch] = createSignal('')
@ -80,7 +99,7 @@ export default () => {
const columns: ColumnDef<ConnectionWithSpeed>[] = [ const columns: ColumnDef<ConnectionWithSpeed>[] = [
{ {
id: 'close', accessorKey: AccessorKey.Close,
header: () => ( header: () => (
<div class="flex h-full items-center"> <div class="flex h-full items-center">
<button <button
@ -103,63 +122,63 @@ export default () => {
), ),
}, },
{ {
accessorKey: 'ID', accessorKey: AccessorKey.ID,
accessorFn: (row) => row.id, accessorFn: (row) => row.id,
}, },
{ {
accessorKey: 'Type', accessorKey: AccessorKey.Type,
accessorFn: (row) => `${row.metadata.type}(${row.metadata.network})`, accessorFn: (row) => `${row.metadata.type}(${row.metadata.network})`,
}, },
{ {
accessorKey: 'Process', accessorKey: AccessorKey.Process,
accessorFn: (row) => row.metadata.process || '-', accessorFn: (row) => row.metadata.process || '-',
}, },
{ {
accessorKey: 'Host', accessorKey: AccessorKey.Host,
accessorFn: (row) => accessorFn: (row) =>
row.metadata.host ? row.metadata.host : row.metadata.destinationIP, row.metadata.host ? row.metadata.host : row.metadata.destinationIP,
}, },
{ {
accessorKey: 'Rule', accessorKey: AccessorKey.Rule,
accessorFn: (row) => accessorFn: (row) =>
!row.rulePayload ? row.rule : `${row.rule} :: ${row.rulePayload}`, !row.rulePayload ? row.rule : `${row.rule} :: ${row.rulePayload}`,
}, },
{ {
accessorKey: 'Chains', accessorKey: AccessorKey.Chains,
accessorFn: (row) => row.chains.slice().reverse().join(' :: '), accessorFn: (row) => row.chains.slice().reverse().join(' :: '),
}, },
{ {
accessorKey: 'DL Speed', accessorKey: AccessorKey.DlSpeed,
accessorFn: (row) => `${byteSize(row.downloadSpeed)}/s`, accessorFn: (row) => `${byteSize(row.downloadSpeed)}/s`,
sortingFn: (prev, next) => sortingFn: (prev, next) =>
prev.original.downloadSpeed - next.original.downloadSpeed, prev.original.downloadSpeed - next.original.downloadSpeed,
}, },
{ {
accessorKey: 'UL Speed', accessorKey: AccessorKey.ULSpeed,
accessorFn: (row) => `${byteSize(row.uploadSpeed)}/s`, accessorFn: (row) => `${byteSize(row.uploadSpeed)}/s`,
sortingFn: (prev, next) => sortingFn: (prev, next) =>
prev.original.uploadSpeed - next.original.uploadSpeed, prev.original.uploadSpeed - next.original.uploadSpeed,
}, },
{ {
accessorKey: 'DL', accessorKey: AccessorKey.Download,
accessorFn: (row) => byteSize(row.download), accessorFn: (row) => byteSize(row.download),
sortingFn: (prev, next) => sortingFn: (prev, next) =>
prev.original.download - next.original.download, prev.original.download - next.original.download,
}, },
{ {
accessorKey: 'UL', accessorKey: AccessorKey.Upload,
accessorFn: (row) => byteSize(row.upload), accessorFn: (row) => byteSize(row.upload),
sortingFn: (prev, next) => prev.original.upload - next.original.upload, sortingFn: (prev, next) => prev.original.upload - next.original.upload,
}, },
{ {
accessorKey: 'Source', accessorKey: AccessorKey.Source,
accessorFn: (row) => accessorFn: (row) =>
isIPv6(row.metadata.sourceIP) isIPv6(row.metadata.sourceIP)
? `[${row.metadata.sourceIP}]:${row.metadata.sourcePort}` ? `[${row.metadata.sourceIP}]:${row.metadata.sourcePort}`
: `${row.metadata.sourceIP}:${row.metadata.sourcePort}`, : `${row.metadata.sourceIP}:${row.metadata.sourcePort}`,
}, },
{ {
accessorKey: 'Destination', accessorKey: AccessorKey.Destination,
accessorFn: (row) => accessorFn: (row) =>
isIPv6(row.metadata.destinationIP) isIPv6(row.metadata.destinationIP)
? `[${row.metadata.destinationIP}]:${row.metadata.destinationPort}` ? `[${row.metadata.destinationIP}]:${row.metadata.destinationPort}`
@ -176,8 +195,8 @@ export default () => {
get sorting() { get sorting() {
return sorting() return sorting()
}, },
columnVisibility: { get columnVisibility() {
ID: false, return columnVisibility()
}, },
}, },
get data() { get data() {
@ -200,11 +219,22 @@ export default () => {
return ( return (
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<input <div class="flex w-full">
class="input input-primary" <input
placeholder="Search" class="input input-primary mr-4 w-40 flex-1"
onInput={(e) => setSearch(e.target.value)} placeholder="Search"
/> onInput={(e) => setSearch(e.target.value)}
/>
<label htmlFor="connection-modal" class="btn">
<IconSettings />
</label>
<ConnectionsModal
data={columnVisibility()}
onChange={(data: ColumnVisibility) =>
setColumnVisibility({ ...data })
}
/>
</div>
<div class="overflow-x-auto whitespace-nowrap"> <div class="overflow-x-auto whitespace-nowrap">
<table class="table table-xs"> <table class="table table-xs">

View File

@ -84,17 +84,19 @@ export default () => {
) )
return ( return (
<Collapse <div>
isOpen={collapsedMap()[`group-${proxy.name}`]} <Collapse
title={title} isOpen={collapsedMap()[`group-${proxy.name}`]}
content={content} title={title}
onCollapse={(val) => content={content}
setCollapsedMap({ onCollapse={(val) =>
...collapsedMap(), setCollapsedMap({
[`group-${proxy.name}`]: val, ...collapsedMap(),
}) [`group-${proxy.name}`]: val,
} })
/> }
/>
</div>
) )
}} }}
</For> </For>
@ -143,17 +145,19 @@ export default () => {
) )
return ( return (
<Collapse <div>
isOpen={collapsedMap()[`provider-${proxyProvider.name}`]} <Collapse
title={title} isOpen={collapsedMap()[`provider-${proxyProvider.name}`]}
content={content} title={title}
onCollapse={(val) => content={content}
setCollapsedMap({ onCollapse={(val) =>
...collapsedMap(), setCollapsedMap({
[`provider-${proxyProvider.name}`]: val, ...collapsedMap(),
}) [`provider-${proxyProvider.name}`]: val,
} })
/> }
/>
</div>
) )
}} }}
</For> </For>