From 3f4dcf2c4f97e5b9a0936be146604fc3a4610310 Mon Sep 17 00:00:00 2001 From: polina4096 Date: Fri, 24 Jan 2025 20:18:16 +0300 Subject: [PATCH] Improve tdata import dialog --- packages/repl/package.json | 1 + .../import/tdata/TdataImportDialog.tsx | 59 +++--- .../settings/import/tdata/TdataTable.tsx | 177 ++++++++++++++++++ packages/repl/src/lib/components/ui/table.tsx | 94 ++++++++++ pnpm-lock.yaml | 20 ++ 5 files changed, 315 insertions(+), 36 deletions(-) create mode 100644 packages/repl/src/components/settings/import/tdata/TdataTable.tsx create mode 100644 packages/repl/src/lib/components/ui/table.tsx diff --git a/packages/repl/package.json b/packages/repl/package.json index efb7cec..64eace8 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -18,6 +18,7 @@ "@mtcute/convert": "^0.19.8", "@mtcute/web": "^0.19.5", "@nanostores/persistent": "^0.10.2", + "@tanstack/solid-table": "^8.20.5", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "filesize": "^10.1.6", diff --git a/packages/repl/src/components/settings/import/tdata/TdataImportDialog.tsx b/packages/repl/src/components/settings/import/tdata/TdataImportDialog.tsx index 3747118..44ec15b 100644 --- a/packages/repl/src/components/settings/import/tdata/TdataImportDialog.tsx +++ b/packages/repl/src/components/settings/import/tdata/TdataImportDialog.tsx @@ -1,12 +1,12 @@ import type { Tdata } from '@mtcute/convert' import { hex } from '@fuman/utils' import { workerInvoke } from 'mtcute-repl-worker/client' -import { createEffect, createSignal, For, on, Show } from 'solid-js' +import { createEffect, createSignal, on, Show } from 'solid-js' import { Button } from '../../../../lib/components/ui/button.tsx' -import { Checkbox, CheckboxControl, CheckboxLabel } from '../../../../lib/components/ui/checkbox.tsx' import { Dialog, DialogContent, DialogDescription, DialogHeader } from '../../../../lib/components/ui/dialog.tsx' import { Spinner } from '../../../../lib/components/ui/spinner.tsx' import { $accounts } from '../../../../store/accounts.ts' +import { TdataDataTable } from './TdataTable.tsx' interface TdataAccount { telegramId: number @@ -191,41 +191,28 @@ export function TdataImportDialog(props: { )} > - - {account => ( - setAccounts( - accounts().map(it => it.index === account.index ? { - ...it, - toImport: checked, - } : it), - )} - disabled={accountExists(account.telegramId)} - > - - -
- ID - {' '} - {account.telegramId} -
-
- (DC - {' '} - {account.dcId} - , index - {' '} - {account.index} - ) -
-
-
- )} -
+ accounts().map(e => ({ + index: e.index, + dcId: e.dcId, + telegramId: e.telegramId, + disabled: accountExists(e.telegramId) ?? false, + }))} + onChange={(s) => { + const nextAccounts = accounts().map(e => ({ ...e, toImport: false })) + for (const [idx, v] of Object.entries(s)) { + const acc = nextAccounts.find(e => e.index === Number(idx)) + if (acc === undefined) continue + if (accountExists(acc.telegramId)) continue + + acc.toImport = v + } + + setAccounts(nextAccounts) + }} + /> {error() && ( -
+
{error()}
)} diff --git a/packages/repl/src/components/settings/import/tdata/TdataTable.tsx b/packages/repl/src/components/settings/import/tdata/TdataTable.tsx new file mode 100644 index 0000000..7dab9f2 --- /dev/null +++ b/packages/repl/src/components/settings/import/tdata/TdataTable.tsx @@ -0,0 +1,177 @@ +import type { ColumnDef, RowSelectionState } from '@tanstack/solid-table' +import type { Accessor } from 'solid-js' +import { createSolidTable, flexRender, getCoreRowModel } from '@tanstack/solid-table' +import { createSignal, For, Show, splitProps } from 'solid-js' +import { Checkbox, CheckboxControl } from '../../../../lib/components/ui/checkbox' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '../../../../lib/components/ui/table' + +interface TdataDataTableProps { + data: Accessor + onChange?: (selection: RowSelectionState) => void +} + +export interface TdataAccountModel { + index: number + dcId: number + telegramId: number + disabled: boolean +} + +export const columns: ColumnDef[] = [ + { + id: 'select', + header: props => ( + { + return props.table.getCoreRowModel?.().rows.reduce((acc, e) => acc && (e.getIsSelected() || e.original.disabled), true) + })()} + onChange={(value) => { + props.table.toggleAllPageRowsSelected(!!value) + props.table.setRowSelection((selection) => { + const next = { ...selection } + for (const row of props.table.getCoreRowModel().rows) { + if (row.original.disabled) { + next[row.original.index] = true + } + } + + return next + }) + }} + aria-label="Select all" + > + + + ), + cell: props => ( + { + props.row.toggleSelected(!!value) + props.table.setRowSelection((selection) => { + const next = { ...selection } + for (const row of props.table.getCoreRowModel().rows) { + if (row.original.disabled) { + next[row.original.index] = true + } + } + + return next + }) + }} + disabled={props.row.original.disabled} + aria-label="Select row" + > + + + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: 'index', + header: 'Index', + }, + { + accessorKey: 'dcId', + header: 'DC Id', + }, + { + accessorKey: 'telegramId', + header: 'Telegram Id', + }, +] + +export function TdataDataTable(props: TdataDataTableProps) { + const [local] = splitProps(props, ['data', 'onChange']) + + const initialSelection: RowSelectionState = {} + // eslint-disable-next-line solid/reactivity + for (const [idx, _] of (local.data?.() ?? []).entries()) { + initialSelection[idx] = true + } + + const [rowSelection, setRowSelection] = createSignal(initialSelection) + + const table = createSolidTable({ + get data() { + return local.data() || [] + }, + columns, + onRowSelectionChange: (s) => { + const selection = setRowSelection(s) + local.onChange?.(selection) + }, + getRowId: (e) => { + return e.index.toString() + }, + state: { + get rowSelection() { + return rowSelection() + }, + }, + getCoreRowModel: getCoreRowModel(), + }) + + return ( +
+ + + + {headerGroup => ( + + + {(header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} + + ) + }} + + + )} + + + + + + No results. + + + )} + > + + {row => ( + + + {cell => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + )} + + + )} + + + +
+
+ ) +} diff --git a/packages/repl/src/lib/components/ui/table.tsx b/packages/repl/src/lib/components/ui/table.tsx new file mode 100644 index 0000000..b4225b3 --- /dev/null +++ b/packages/repl/src/lib/components/ui/table.tsx @@ -0,0 +1,94 @@ +import { type ComponentProps, splitProps } from 'solid-js' + +import { cn } from '../../utils.ts' + +export function Table(props: ComponentProps<'table'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( +
+ + + ) +} + +export function TableHeader(props: ComponentProps<'thead'>) { + const [local, rest] = splitProps(props, ['class']) + + return +} + +export function TableBody(props: ComponentProps<'tbody'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( + + ) +} + +export function TableFooter(props: ComponentProps<'tfoot'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( + + ) +} + +export function TableRow(props: ComponentProps<'tr'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( + + ) +} + +export function TableHead(props: ComponentProps<'th'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( +
[role=checkbox]]:translate-y-[2px]', + local.class, + )} + {...rest} + /> + ) +} + +export function TableCell(props: ComponentProps<'td'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( + [role=checkbox]]:translate-y-[2px]', + local.class, + )} + {...rest} + /> + ) +} + +export function TableCaption(props: ComponentProps<'caption'>) { + const [local, rest] = splitProps(props, ['class']) + + return ( +
+ ) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34c9e76..46ef213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,9 @@ importers: '@nanostores/persistent': specifier: ^0.10.2 version: 0.10.2(nanostores@0.11.3) + '@tanstack/solid-table': + specifier: ^8.20.5 + version: 8.20.5(solid-js@1.9.4) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -1168,6 +1171,16 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tanstack/solid-table@8.20.5': + resolution: {integrity: sha512-LsB/g/24CjBpccOcok+u+tfyqtU9SIQg5wf7ne54jRdEsy5YQnrpb5ATWZileHBduIG0p/1oE7UOA+DyjtnbDQ==} + engines: {node: '>=12'} + peerDependencies: + solid-js: '>=1.3' + + '@tanstack/table-core@8.20.5': + resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==} + engines: {node: '>=12'} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -3721,6 +3734,13 @@ snapshots: dependencies: tslib: 2.8.1 + '@tanstack/solid-table@8.20.5(solid-js@1.9.4)': + dependencies: + '@tanstack/table-core': 8.20.5 + solid-js: 1.9.4 + + '@tanstack/table-core@8.20.5': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.26.5