@sqlrooms/data-table 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-dev.log +7 -0
- package/.turbo/turbo-lint.log +31 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE.md +9 -0
- package/dist/DataTableModal.d.ts +12 -0
- package/dist/DataTableModal.d.ts.map +1 -0
- package/dist/DataTableModal.js +7 -0
- package/dist/DataTablePaginated.d.ts +16 -0
- package/dist/DataTablePaginated.d.ts.map +1 -0
- package/dist/DataTablePaginated.js +65 -0
- package/dist/DataTableVirtualized.d.ts +15 -0
- package/dist/DataTableVirtualized.d.ts.map +1 -0
- package/dist/DataTableVirtualized.js +60 -0
- package/dist/QueryDataTable.d.ts +8 -0
- package/dist/QueryDataTable.d.ts.map +1 -0
- package/dist/QueryDataTable.js +79 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/useArrowDataTable.d.ts +13 -0
- package/dist/useArrowDataTable.d.ts.map +1 -0
- package/dist/useArrowDataTable.js +65 -0
- package/docs/architecture.md +18 -0
- package/eslint.config.js +4 -0
- package/package.json +32 -0
- package/src/DataTableModal.tsx +44 -0
- package/src/DataTablePaginated.tsx +296 -0
- package/src/DataTableVirtualized.tsx +190 -0
- package/src/QueryDataTable.tsx +117 -0
- package/src/index.ts +6 -0
- package/src/useArrowDataTable.tsx +112 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
> @sqlrooms/data-table@0.0.0 lint /Users/ilya/Workspace/sqlrooms/packages/data-table
|
|
3
|
+
> eslint .
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/Users/ilya/Workspace/sqlrooms/packages/data-table/src/DataTablePaginated.tsx
|
|
7
|
+
41:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
8
|
+
68:36 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
9
|
+
|
|
10
|
+
/Users/ilya/Workspace/sqlrooms/packages/data-table/src/DataTableVirtualized.tsx
|
|
11
|
+
26:29 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
12
|
+
28:11 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
13
|
+
34:28 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
14
|
+
83:66 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
15
|
+
139:66 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
16
|
+
186:27 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
17
|
+
|
|
18
|
+
/Users/ilya/Workspace/sqlrooms/packages/data-table/src/QueryDataTable.tsx
|
|
19
|
+
16:24 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
20
|
+
28:36 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
21
|
+
|
|
22
|
+
/Users/ilya/Workspace/sqlrooms/packages/data-table/src/useArrowDataTable.tsx
|
|
23
|
+
14:19 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
24
|
+
15:22 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
25
|
+
15:27 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
26
|
+
25:53 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
27
|
+
61:30 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
28
|
+
61:35 warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
|
|
29
|
+
|
|
30
|
+
✖ 16 problems (0 errors, 16 warnings)
|
|
31
|
+
|
package/CHANGELOG.md
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Ilya Boyandin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
type Props = {
|
|
3
|
+
title: string | undefined;
|
|
4
|
+
query: string | undefined;
|
|
5
|
+
tableModal: {
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
declare const DataTableModal: FC<Props>;
|
|
11
|
+
export default DataTableModal;
|
|
12
|
+
//# sourceMappingURL=DataTableModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTableModal.d.ts","sourceRoot":"","sources":["../src/DataTableModal.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAGzB,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,UAAU,EAAE;QACV,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,MAAM,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAEF,QAAA,MAAM,cAAc,EAAE,EAAE,CAAC,KAAK,CAqB7B,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '@sqlrooms/ui';
|
|
3
|
+
import QueryDataTable from './QueryDataTable';
|
|
4
|
+
const DataTableModal = ({ title, query, tableModal }) => {
|
|
5
|
+
return (_jsx(Dialog, { open: tableModal.isOpen, onOpenChange: (isOpen) => !isOpen && tableModal.onClose(), children: _jsxs(DialogContent, { className: "h-[80vh] max-w-[75vw]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: title ? `Table "${title}"` : '' }) }), _jsx("div", { className: "flex-1 bg-muted overflow-hidden", children: tableModal.isOpen && query ? _jsx(QueryDataTable, { query: query }) : null }), _jsx(DialogFooter, { children: _jsx(Button, { variant: "outline", onClick: tableModal.onClose, children: "Close" }) })] }) }));
|
|
6
|
+
};
|
|
7
|
+
export default DataTableModal;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ColumnDef, PaginationState, SortingState } from '@tanstack/react-table';
|
|
2
|
+
export type DataTablePaginatedProps<Data extends object> = {
|
|
3
|
+
data?: ArrayLike<Data> | undefined;
|
|
4
|
+
columns?: ColumnDef<Data, any>[] | undefined;
|
|
5
|
+
pageCount?: number | undefined;
|
|
6
|
+
numRows?: number | undefined;
|
|
7
|
+
isFetching?: boolean;
|
|
8
|
+
isExporting?: boolean;
|
|
9
|
+
pagination?: PaginationState;
|
|
10
|
+
sorting?: SortingState;
|
|
11
|
+
onPaginationChange?: (pagination: PaginationState) => void;
|
|
12
|
+
onSortingChange?: (sorting: SortingState) => void;
|
|
13
|
+
onExport?: () => void;
|
|
14
|
+
};
|
|
15
|
+
export default function DataTablePaginated<Data extends object>({ data, columns, pageCount, numRows, pagination, sorting, onPaginationChange, onSortingChange, onExport, isExporting, isFetching, }: DataTablePaginatedProps<Data>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=DataTablePaginated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTablePaginated.d.ts","sourceRoot":"","sources":["../src/DataTablePaginated.tsx"],"names":[],"mappings":"AA0BA,OAAO,EACL,SAAS,EACT,eAAe,EACf,YAAY,EAKb,MAAM,uBAAuB,CAAC;AAI/B,MAAM,MAAM,uBAAuB,CAAC,IAAI,SAAS,MAAM,IAAI;IACzD,IAAI,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,SAAS,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3D,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,IAAI,SAAS,MAAM,EAAE,EAC9D,IAAI,EACJ,OAAO,EACP,SAAS,EACT,OAAO,EACP,UAAU,EACV,OAAO,EACP,kBAAkB,EAClB,eAAe,EACf,QAAQ,EACR,WAAW,EACX,UAAU,GACX,EAAE,uBAAuB,CAAC,IAAI,CAAC,2CAuO/B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon, ChevronUpIcon, ArrowDownIcon, ChevronDoubleLeftIcon, ChevronDoubleRightIcon, ChevronLeftIcon, ChevronRightIcon, } from '@heroicons/react/24/solid';
|
|
3
|
+
import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge, } from '@sqlrooms/ui';
|
|
4
|
+
import { formatCount } from '@sqlrooms/utils';
|
|
5
|
+
import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
export default function DataTablePaginated({ data, columns, pageCount, numRows, pagination, sorting, onPaginationChange, onSortingChange, onExport, isExporting, isFetching, }) {
|
|
8
|
+
const defaultData = useMemo(() => [], []);
|
|
9
|
+
const table = useReactTable({
|
|
10
|
+
data: (data ?? defaultData),
|
|
11
|
+
columns: columns ?? [],
|
|
12
|
+
pageCount: pageCount ?? -1,
|
|
13
|
+
getSortedRowModel: getSortedRowModel(),
|
|
14
|
+
onSortingChange: (update) => {
|
|
15
|
+
if (onSortingChange && sorting && typeof update === 'function') {
|
|
16
|
+
onSortingChange(update(sorting));
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
onPaginationChange: (update) => {
|
|
20
|
+
if (onPaginationChange && pagination && typeof update === 'function') {
|
|
21
|
+
onPaginationChange(update(pagination));
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
getCoreRowModel: getCoreRowModel(),
|
|
25
|
+
manualPagination: true,
|
|
26
|
+
state: {
|
|
27
|
+
pagination,
|
|
28
|
+
sorting,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const [internalPageIndex, setInternalPageIndex] = useState(pagination?.pageIndex ?? 0);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setInternalPageIndex(pagination?.pageIndex ?? 0);
|
|
34
|
+
}, [pagination?.pageIndex]);
|
|
35
|
+
return (_jsxs("div", { className: "relative w-full h-full flex flex-col", children: [_jsx("div", { className: "flex-1 overflow-hidden border border-border font-mono", children: _jsx("div", { className: "overflow-auto h-full", children: _jsxs(Table, { disableWrapper: true, children: [_jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => (_jsxs(TableRow, { children: [_jsx(TableHead, { className: `
|
|
36
|
+
sticky top-0 left-0 w-auto whitespace-nowrap py-2
|
|
37
|
+
bg-background border-r text-center z-20
|
|
38
|
+
`, children: isFetching ? (_jsx("div", { className: "animate-spin h-4 w-4 border-2 border-primary border-t-transparent rounded-full" })) : null }), headerGroup.headers.map((header) => {
|
|
39
|
+
const meta = header.column.columnDef
|
|
40
|
+
.meta;
|
|
41
|
+
return (_jsx(TableHead, { colSpan: header.colSpan, className: `
|
|
42
|
+
sticky top-0 w-auto whitespace-nowrap cursor-pointer py-2
|
|
43
|
+
bg-background border-r hover:bg-muted/80 z-10
|
|
44
|
+
${meta?.isNumeric ? 'text-right' : 'text-left'}
|
|
45
|
+
`, onClick: header.column.getToggleSortingHandler(), children: _jsxs("div", { className: "flex gap-2 items-center", children: [header.isPlaceholder ? null : (_jsx("div", { children: flexRender(header.column.columnDef.header, header.getContext()) })), header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? (_jsx(ChevronDownIcon, { className: "h-4 w-4" })) : (_jsx(ChevronUpIcon, { className: "h-4 w-4" }))) : null, _jsx("div", { className: "flex-1" }), _jsx(Badge, { variant: "outline", className: "opacity-30 text-[9px] max-w-[70px] truncate", children: String(meta?.type) })] }) }, header.id));
|
|
46
|
+
}), _jsx(TableHead, { className: "sticky top-0 w-full whitespace-nowrap py-2 bg-background border-r border-t" })] }, headerGroup.id))) }), _jsx(TableBody, { children: table.getRowModel().rows.map((row, i) => (_jsxs(TableRow, { className: "hover:bg-muted/50", children: [_jsx(TableCell, { className: "text-xs border-r bg-muted text-center text-muted-foreground sticky left-0", children: pagination
|
|
47
|
+
? `${pagination.pageIndex * pagination.pageSize + i + 1}`
|
|
48
|
+
: '' }), row.getVisibleCells().map((cell) => {
|
|
49
|
+
const meta = cell.column.columnDef.meta;
|
|
50
|
+
return (_jsx(TableCell, { className: `
|
|
51
|
+
text-[11px] border-r max-w-[500px] overflow-hidden truncate px-7
|
|
52
|
+
${meta?.isNumeric ? 'text-right' : 'text-left'}
|
|
53
|
+
`, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
54
|
+
}), _jsx(TableCell, { className: "border-r", children: "\u00A0" })] }, row.id))) })] }) }) }), _jsxs("div", { className: "sticky bottom-0 left-0 bg-background p-2 flex gap-2 items-center flex-wrap border border-t-0", children: [_jsx(Button, { variant: "outline", size: "icon", onClick: () => table.setPageIndex(0), disabled: !table.getCanPreviousPage(), children: _jsx(ChevronDoubleLeftIcon, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: _jsx(ChevronLeftIcon, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex items-center text-sm ml-1 gap-1", children: [_jsx("div", { children: "Page" }), _jsx(Input, { type: "number", min: 1, max: table.getPageCount(), className: "w-16 h-8", value: internalPageIndex + 1, onChange: (e) => {
|
|
55
|
+
const value = e.target.value;
|
|
56
|
+
if (value) {
|
|
57
|
+
const page = Math.max(0, Math.min(table.getPageCount() - 1, Number(value) - 1));
|
|
58
|
+
setInternalPageIndex(page);
|
|
59
|
+
}
|
|
60
|
+
}, onBlur: () => {
|
|
61
|
+
if (internalPageIndex !== pagination?.pageIndex) {
|
|
62
|
+
table.setPageIndex(internalPageIndex);
|
|
63
|
+
}
|
|
64
|
+
} }), _jsx("div", { children: `of ${formatCount(table.getPageCount())}` })] }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: _jsx(ChevronRightIcon, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => table.setPageIndex(table.getPageCount() - 1), disabled: !table.getCanNextPage(), children: _jsx(ChevronDoubleRightIcon, { className: "h-4 w-4" }) }), _jsxs(Select, { value: String(table.getState().pagination.pageSize), onValueChange: (value) => table.setPageSize(Number(value)), children: [_jsx(SelectTrigger, { className: "w-[110px] h-8", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: [10, 50, 100, 500, 1000].map((pageSize) => (_jsx(SelectItem, { value: String(pageSize), children: `${pageSize} rows` }, pageSize))) })] }), _jsx("div", { className: "flex-1" }), numRows !== undefined && isFinite(numRows) ? (_jsx("div", { className: "text-sm font-normal", children: `${formatCount(numRows)} rows` })) : null, onExport ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: onExport, disabled: isExporting, children: [isExporting ? (_jsx("div", { className: "animate-spin h-4 w-4 border-2 border-primary border-t-transparent rounded-full mr-2" })) : (_jsx(ArrowDownIcon, { className: "h-4 w-4 mr-2" })), "Export CSV"] })) : null] }), isFetching ? (_jsx("div", { className: "absolute inset-0 bg-background/80 animate-pulse" })) : null] }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ColumnDef } from '@tanstack/react-table';
|
|
2
|
+
export type Props<Data extends object> = {
|
|
3
|
+
data?: ArrayLike<Data>;
|
|
4
|
+
columns?: ColumnDef<Data, any>[];
|
|
5
|
+
isFetching?: boolean;
|
|
6
|
+
error?: any;
|
|
7
|
+
isPreview?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type DataTableProps<Data extends object> = {
|
|
10
|
+
data: ArrayLike<Data>;
|
|
11
|
+
columns: ColumnDef<Data, any>[];
|
|
12
|
+
isPreview?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export default function DataTableWithLoader<Data extends object>(props: Props<Data>): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
//# sourceMappingURL=DataTableVirtualized.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataTableVirtualized.d.ts","sourceRoot":"","sources":["../src/DataTableVirtualized.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,SAAS,EAMV,MAAM,uBAAuB,CAAC;AAI/B,MAAM,MAAM,KAAK,CAAC,IAAI,SAAS,MAAM,IAAI;IACvC,IAAI,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,MAAM,IAAI;IAChD,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA0IF,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,IAAI,SAAS,MAAM,EAC7D,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,kDAenB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/solid';
|
|
3
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Badge, } from '@sqlrooms/ui';
|
|
4
|
+
import { ErrorPane, SpinnerPane } from '@sqlrooms/ui';
|
|
5
|
+
import { formatCount } from '@sqlrooms/utils';
|
|
6
|
+
import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable, } from '@tanstack/react-table';
|
|
7
|
+
import * as React from 'react';
|
|
8
|
+
import { useVirtual } from 'react-virtual';
|
|
9
|
+
const DataTableVirtualized = React.memo(function DataTableVirtualized({ data, columns, isPreview }) {
|
|
10
|
+
const [sorting, setSorting] = React.useState([]);
|
|
11
|
+
const table = useReactTable({
|
|
12
|
+
columns,
|
|
13
|
+
data: data,
|
|
14
|
+
onSortingChange: setSorting,
|
|
15
|
+
getCoreRowModel: getCoreRowModel(),
|
|
16
|
+
getSortedRowModel: getSortedRowModel(),
|
|
17
|
+
state: {
|
|
18
|
+
sorting,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const tableContainerRef = React.useRef(null);
|
|
22
|
+
const { rows } = table.getRowModel();
|
|
23
|
+
const rowVirtualizer = useVirtual({
|
|
24
|
+
parentRef: tableContainerRef,
|
|
25
|
+
size: rows.length,
|
|
26
|
+
overscan: 20,
|
|
27
|
+
});
|
|
28
|
+
const { virtualItems: virtualRows, totalSize } = rowVirtualizer;
|
|
29
|
+
const paddingTop = virtualRows.length > 0 ? virtualRows?.[0]?.start || 0 : 0;
|
|
30
|
+
const paddingBottom = virtualRows.length > 0
|
|
31
|
+
? totalSize - (virtualRows?.[virtualRows.length - 1]?.end || 0)
|
|
32
|
+
: 0;
|
|
33
|
+
return (_jsxs("div", { className: "flex flex-col overflow-hidden", children: [_jsx("div", { className: "overflow-hidden border border-border", children: _jsx("div", { ref: tableContainerRef, className: "overflow-auto h-full", children: _jsxs(Table, { disableWrapper: true, children: [_jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => (_jsxs(TableRow, { children: [_jsx(TableHead, { className: `
|
|
34
|
+
sticky top-0 left-0 w-auto whitespace-nowrap py-2
|
|
35
|
+
bg-background border-r text-center z-20
|
|
36
|
+
` }), headerGroup.headers.map((header) => {
|
|
37
|
+
const meta = header.column.columnDef.meta;
|
|
38
|
+
return (_jsx(TableHead, { className: `
|
|
39
|
+
sticky top-0 font-mono whitespace-nowrap cursor-pointer px-7 py-2
|
|
40
|
+
bg-background border-r hover:bg-muted/80 z-10
|
|
41
|
+
${meta?.isNumeric ? 'text-right' : 'text-left'}
|
|
42
|
+
`, onClick: header.column.getToggleSortingHandler(), children: _jsxs("div", { className: `flex items-center gap-1 ${meta?.isNumeric ? 'justify-end' : 'justify-start'}`, children: [flexRender(header.column.columnDef.header, header.getContext()), header.column.getIsSorted() ? (header.column.getIsSorted() === 'desc' ? (_jsx(ChevronDownIcon, { className: "h-4 w-4" })) : (_jsx(ChevronUpIcon, { className: "h-4 w-4" }))) : null, _jsx("div", { className: "flex-1" }), _jsx(Badge, { variant: "outline", className: "opacity-30 text-[9px]", children: String(meta?.type) })] }) }, header.id));
|
|
43
|
+
})] }, headerGroup.id))) }), _jsxs(TableBody, { children: [paddingTop > 0 && (_jsx(TableRow, { children: _jsx(TableCell, { style: { height: `${paddingTop}px` } }) })), virtualRows.map((virtualRow) => {
|
|
44
|
+
const row = rows[virtualRow.index];
|
|
45
|
+
if (!row)
|
|
46
|
+
return null;
|
|
47
|
+
return (_jsxs(TableRow, { className: "hover:bg-muted/50", children: [_jsx(TableCell, { className: "text-xs border-r bg-muted text-center text-muted-foreground sticky left-0", children: virtualRow.index + 1 }), row.getVisibleCells().map((cell) => {
|
|
48
|
+
const meta = cell.column.columnDef.meta;
|
|
49
|
+
return (_jsx(TableCell, { className: `
|
|
50
|
+
text-xs border-r max-w-[500px] overflow-hidden truncate px-7
|
|
51
|
+
${meta?.isNumeric ? 'text-right' : 'text-left'}
|
|
52
|
+
`, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
|
|
53
|
+
})] }, row.id));
|
|
54
|
+
}), paddingBottom > 0 && (_jsx(TableRow, { children: _jsx(TableCell, { style: { height: `${paddingBottom}px` } }) }))] })] }) }) }), _jsx("div", { className: "sticky bottom-0 left-0 py-2 px-4 text-xs font-mono bg-background border border-t-0", children: `${isPreview ? 'Preview of the first ' : ''}${formatCount(data.length)} rows` })] }));
|
|
55
|
+
});
|
|
56
|
+
export default function DataTableWithLoader(props) {
|
|
57
|
+
const { isPreview, isFetching, error, ...rest } = props;
|
|
58
|
+
const { data, columns } = rest;
|
|
59
|
+
return error ? (_jsx(ErrorPane, { error: error })) : isFetching ? (_jsx(SpinnerPane, { h: "100%" })) : data && columns ? (_jsx(DataTableVirtualized, { data: data, columns: columns, isPreview: isPreview })) : null;
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueryDataTable.d.ts","sourceRoot":"","sources":["../src/QueryDataTable.tsx"],"names":[],"mappings":"AASA,OAAO,EAAC,EAAE,EAAgC,MAAM,OAAO,CAAC;AAIxD,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC;CAC5B,CAAC;AAyFF,QAAA,MAAM,0BAA0B,EAAE,EAAE,CAAC,KAAK,CASzC,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createElement as _createElement } from "react";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SpinnerPane } from '@sqlrooms/ui';
|
|
4
|
+
import { escapeId, exportToCsv, getColValAsNumber, useDuckDb, } from '@sqlrooms/duckdb';
|
|
5
|
+
import { genRandomStr } from '@sqlrooms/utils';
|
|
6
|
+
import { Suspense, useEffect, useState } from 'react';
|
|
7
|
+
import DataTablePaginated from './DataTablePaginated';
|
|
8
|
+
import useArrowDataTable from './useArrowDataTable';
|
|
9
|
+
const QueryDataTable = ({ query }) => {
|
|
10
|
+
const { conn } = useDuckDb();
|
|
11
|
+
const [sorting, setSorting] = useState([]);
|
|
12
|
+
const [pagination, setPagination] = useState({
|
|
13
|
+
pageIndex: 0,
|
|
14
|
+
pageSize: 100,
|
|
15
|
+
});
|
|
16
|
+
const [count, setCount] = useState(undefined);
|
|
17
|
+
const [data, setData] = useState(null);
|
|
18
|
+
const [isFetching, setIsFetching] = useState(false);
|
|
19
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
20
|
+
// Fetch row count
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const fetchCount = async () => {
|
|
23
|
+
try {
|
|
24
|
+
setIsFetching(true);
|
|
25
|
+
const result = await conn.query(`SELECT COUNT(*)::int FROM (${query})`);
|
|
26
|
+
setCount(getColValAsNumber(result));
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Error fetching count:', error);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
setIsFetching(false);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
fetchCount();
|
|
36
|
+
}, [query, conn]);
|
|
37
|
+
// Fetch data
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const fetchData = async () => {
|
|
40
|
+
try {
|
|
41
|
+
setIsFetching(true);
|
|
42
|
+
const result = await conn.query(`SELECT * FROM (
|
|
43
|
+
${query}
|
|
44
|
+
) ${sorting.length > 0
|
|
45
|
+
? `ORDER BY ${sorting
|
|
46
|
+
.map((d) => `${escapeId(d.id)}${d.desc ? ' DESC' : ''}`)
|
|
47
|
+
.join(', ')}`
|
|
48
|
+
: ''}
|
|
49
|
+
OFFSET ${pagination.pageIndex * pagination.pageSize}
|
|
50
|
+
LIMIT ${pagination.pageSize}`);
|
|
51
|
+
setData(result);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('Error fetching data:', error);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
setIsFetching(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
fetchData();
|
|
61
|
+
}, [query, pagination, sorting, conn]);
|
|
62
|
+
const arrowTableData = useArrowDataTable(data);
|
|
63
|
+
const handleExport = async () => {
|
|
64
|
+
if (!query)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
setIsExporting(true);
|
|
68
|
+
await exportToCsv(query, `export-${genRandomStr(5)}.csv`);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
setIsExporting(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return (_jsx(DataTablePaginated, { ...arrowTableData, pageCount: Math.ceil((count ?? 0) / pagination.pageSize), numRows: count, isFetching: isFetching, pagination: pagination, onPaginationChange: setPagination, sorting: sorting, onSortingChange: setSorting, onExport: handleExport, isExporting: isExporting }));
|
|
75
|
+
};
|
|
76
|
+
const QueryDataTableWithSuspense = (props) => {
|
|
77
|
+
return (_jsx(Suspense, { fallback: _jsx(SpinnerPane, { className: "w-full h-full" }), children: _createElement(QueryDataTable, { ...props, key: props.query }) }));
|
|
78
|
+
};
|
|
79
|
+
export default QueryDataTableWithSuspense;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as DataTableModal } from './DataTableModal';
|
|
2
|
+
export * from './DataTablePaginated';
|
|
3
|
+
export { default as DataTablePaginated } from './DataTablePaginated';
|
|
4
|
+
export { default as DataTableVirtualized } from './DataTableVirtualized';
|
|
5
|
+
export { default as QueryDataTable } from './QueryDataTable';
|
|
6
|
+
export { default as useArrowDataTable } from './useArrowDataTable';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,IAAI,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAC3D,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAC,OAAO,IAAI,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAC,OAAO,IAAI,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAC,OAAO,IAAI,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAC,OAAO,IAAI,iBAAiB,EAAC,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as DataTableModal } from './DataTableModal';
|
|
2
|
+
export * from './DataTablePaginated';
|
|
3
|
+
export { default as DataTablePaginated } from './DataTablePaginated';
|
|
4
|
+
export { default as DataTableVirtualized } from './DataTableVirtualized';
|
|
5
|
+
export { default as QueryDataTable } from './QueryDataTable';
|
|
6
|
+
export { default as useArrowDataTable } from './useArrowDataTable';
|