@wheelhouse/ui 0.2.4 → 0.2.6
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/dist/blocks/columns/columns-types.d.ts +25 -1
- package/dist/blocks/columns/columns-types.d.ts.map +1 -1
- package/dist/blocks/columns/columns-types.js +4 -0
- package/dist/blocks/columns/columns-utils.d.ts +31 -1
- package/dist/blocks/columns/columns-utils.d.ts.map +1 -1
- package/dist/blocks/columns/columns-utils.js +86 -1
- package/dist/blocks/columns/columns.d.ts +1 -1
- package/dist/blocks/columns/columns.d.ts.map +1 -1
- package/dist/blocks/columns/columns.js +81 -20
- package/dist/blocks/columns/columns.stories.d.ts +1 -0
- package/dist/blocks/columns/columns.stories.d.ts.map +1 -1
- package/dist/blocks/columns/columns.stories.js +21 -1
- package/dist/blocks/columns/index.d.ts +1 -1
- package/dist/blocks/columns/index.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid-column-filter.d.ts +15 -0
- package/dist/components/data-grid/data-grid-column-filter.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-column-filter.js +36 -0
- package/dist/components/data-grid/data-grid-column-header.d.ts +15 -0
- package/dist/components/data-grid/data-grid-column-header.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-column-header.js +137 -0
- package/dist/components/data-grid/data-grid-column-visibility.d.ts +8 -0
- package/dist/components/data-grid/data-grid-column-visibility.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-column-visibility.js +13 -0
- package/dist/components/data-grid/data-grid-pagination.d.ts +20 -0
- package/dist/components/data-grid/data-grid-pagination.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-pagination.js +76 -0
- package/dist/components/data-grid/data-grid-scroll-area.d.ts +11 -0
- package/dist/components/data-grid/data-grid-scroll-area.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-scroll-area.js +218 -0
- package/dist/components/data-grid/data-grid-table-dnd-rows.d.ts +12 -0
- package/dist/components/data-grid/data-grid-table-dnd-rows.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-table-dnd-rows.js +91 -0
- package/dist/components/data-grid/data-grid-table-dnd.d.ts +8 -0
- package/dist/components/data-grid/data-grid-table-dnd.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-table-dnd.js +95 -0
- package/dist/components/data-grid/data-grid-table-virtual.d.ts +28 -0
- package/dist/components/data-grid/data-grid-table-virtual.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-table-virtual.js +133 -0
- package/dist/components/data-grid/data-grid-table.d.ts +98 -0
- package/dist/components/data-grid/data-grid-table.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid-table.js +560 -0
- package/dist/components/data-grid/data-grid.d.ts +94 -0
- package/dist/components/data-grid/data-grid.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid.js +123 -0
- package/dist/components/data-grid/data-grid.stories.d.ts +14 -0
- package/dist/components/data-grid/data-grid.stories.d.ts.map +1 -0
- package/dist/components/data-grid/data-grid.stories.js +47 -0
- package/dist/components/data-grid/index.d.ts +14 -0
- package/dist/components/data-grid/index.d.ts.map +1 -0
- package/dist/components/data-grid/index.js +10 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/sidebar/sidebar.stories.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar.stories.js +2 -5
- package/dist/components/sortable/sortable.d.ts +4 -2
- package/dist/components/sortable/sortable.d.ts.map +1 -1
- package/dist/components/sortable/sortable.js +4 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -4
- package/src/styles/globals.css +26 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { getColumnHeaderLabel, useDataGrid } from './data-grid';
|
|
5
|
+
import { Button } from '../button';
|
|
6
|
+
import { cn } from '../../lib/utils';
|
|
7
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '../dropdown-menu';
|
|
8
|
+
import { ArrowDownIcon, ArrowLeftIcon, ArrowLeftToLineIcon, ArrowRightIcon, ArrowRightToLineIcon, ArrowUpIcon, CheckIcon, ChevronsUpDownIcon, PinOffIcon, Settings2Icon, } from 'lucide-react';
|
|
9
|
+
function DataGridColumnHeaderInner({ column, title, icon, className, filter, visibility = false }) {
|
|
10
|
+
const { isLoading, table, props, recordCount } = useDataGrid();
|
|
11
|
+
const resolvedTitle = title ?? getColumnHeaderLabel(column);
|
|
12
|
+
const columnOrder = table.getState().columnOrder;
|
|
13
|
+
const columnVisibilityKey = JSON.stringify(table.getState().columnVisibility);
|
|
14
|
+
const isSorted = column.getIsSorted();
|
|
15
|
+
const isPinned = column.getIsPinned();
|
|
16
|
+
const canSort = column.getCanSort();
|
|
17
|
+
const canPin = column.getCanPin();
|
|
18
|
+
const canResize = column.getCanResize();
|
|
19
|
+
const columnIndex = columnOrder.indexOf(column.id);
|
|
20
|
+
const canMoveLeft = columnIndex > 0;
|
|
21
|
+
const canMoveRight = columnIndex < columnOrder.length - 1;
|
|
22
|
+
const handleSort = () => {
|
|
23
|
+
if (isSorted === 'asc') {
|
|
24
|
+
column.toggleSorting(true);
|
|
25
|
+
}
|
|
26
|
+
else if (isSorted === 'desc') {
|
|
27
|
+
column.clearSorting();
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
column.toggleSorting(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const headerLabelClassName = cn('inline-flex h-full items-center gap-1.5 text-[0.8125rem] leading-[calc(1.125/0.8125)] font-normal text-secondary-foreground/80 [&_svg]:size-3.5 [&_svg]:opacity-60', className);
|
|
34
|
+
const headerButtonClassName = cn('-ms-2 h-6 rounded-lg px-2 font-normal text-secondary-foreground/80 hover:bg-secondary hover:text-foreground data-[state=open]:bg-secondary data-[state=open]:text-foreground', className);
|
|
35
|
+
const sortIcon = canSort &&
|
|
36
|
+
(isSorted === 'desc' ? (_jsx(ArrowDownIcon, { className: "size-3.25" })) : isSorted === 'asc' ? (_jsx(ArrowUpIcon, { className: "size-3.25" })) : (_jsx(ChevronsUpDownIcon, { className: "mt-px size-3.25" })));
|
|
37
|
+
const hasControls = props.tableLayout?.columnsMovable || (props.tableLayout?.columnsVisibility && visibility) || (props.tableLayout?.columnsPinnable && canPin) || filter;
|
|
38
|
+
const menuItems = useMemo(() => {
|
|
39
|
+
const items = [];
|
|
40
|
+
let hasPreviousSection = false;
|
|
41
|
+
// Filter section
|
|
42
|
+
if (filter) {
|
|
43
|
+
items.push(_jsx(DropdownMenuGroup, { children: _jsx(DropdownMenuLabel, { children: filter }, "filter") }, "group-filter"));
|
|
44
|
+
hasPreviousSection = true;
|
|
45
|
+
}
|
|
46
|
+
// Sort section
|
|
47
|
+
if (canSort) {
|
|
48
|
+
if (hasPreviousSection) {
|
|
49
|
+
items.push(_jsx(DropdownMenuSeparator, {}, "sep-sort"));
|
|
50
|
+
}
|
|
51
|
+
items.push(_jsxs(DropdownMenuItem, { onClick: () => {
|
|
52
|
+
if (isSorted === 'asc') {
|
|
53
|
+
column.clearSorting();
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
column.toggleSorting(false);
|
|
57
|
+
}
|
|
58
|
+
}, disabled: !canSort, children: [_jsx(ArrowUpIcon, { className: "size-3.5!" }), _jsx("span", { className: "grow", children: "Asc" }), isSorted === 'asc' && _jsx(CheckIcon, { className: "size-4 text-primary opacity-100!" })] }, "sort-asc"), _jsxs(DropdownMenuItem, { onClick: () => {
|
|
59
|
+
if (isSorted === 'desc') {
|
|
60
|
+
column.clearSorting();
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
column.toggleSorting(true);
|
|
64
|
+
}
|
|
65
|
+
}, disabled: !canSort, children: [_jsx(ArrowDownIcon, { className: "size-3.5!" }), _jsx("span", { className: "grow", children: "Desc" }), isSorted === 'desc' && _jsx(CheckIcon, { className: "size-4 text-primary opacity-100!" })] }, "sort-desc"));
|
|
66
|
+
hasPreviousSection = true;
|
|
67
|
+
}
|
|
68
|
+
// Pin section
|
|
69
|
+
if (props.tableLayout?.columnsPinnable && canPin) {
|
|
70
|
+
if (hasPreviousSection) {
|
|
71
|
+
items.push(_jsx(DropdownMenuSeparator, {}, "sep-pin"));
|
|
72
|
+
}
|
|
73
|
+
items.push(_jsxs(DropdownMenuItem, { onClick: () => column.pin(isPinned === 'left' ? false : 'left'), children: [_jsx(ArrowLeftToLineIcon, { className: "size-3.5!", "aria-hidden": "true" }), _jsx("span", { className: "grow", children: "Pin to left" }), isPinned === 'left' && _jsx(CheckIcon, { className: "size-4 text-primary opacity-100!" })] }, "pin-left"), _jsxs(DropdownMenuItem, { onClick: () => column.pin(isPinned === 'right' ? false : 'right'), children: [_jsx(ArrowRightToLineIcon, { className: "size-3.5!", "aria-hidden": "true" }), _jsx("span", { className: "grow", children: "Pin to right" }), isPinned === 'right' && _jsx(CheckIcon, { className: "size-4 text-primary opacity-100!" })] }, "pin-right"));
|
|
74
|
+
hasPreviousSection = true;
|
|
75
|
+
}
|
|
76
|
+
// Move section
|
|
77
|
+
if (props.tableLayout?.columnsMovable) {
|
|
78
|
+
if (hasPreviousSection) {
|
|
79
|
+
items.push(_jsx(DropdownMenuSeparator, {}, "sep-move"));
|
|
80
|
+
}
|
|
81
|
+
items.push(_jsxs(DropdownMenuItem, { onClick: () => {
|
|
82
|
+
if (columnIndex > 0) {
|
|
83
|
+
const newOrder = [...columnOrder];
|
|
84
|
+
const [movedColumn] = newOrder.splice(columnIndex, 1);
|
|
85
|
+
newOrder.splice(columnIndex - 1, 0, movedColumn);
|
|
86
|
+
table.setColumnOrder(newOrder);
|
|
87
|
+
}
|
|
88
|
+
}, disabled: !canMoveLeft || isPinned !== false, children: [_jsx(ArrowLeftIcon, { className: "size-3.5!", "aria-hidden": "true" }), _jsx("span", { children: "Move to Left" })] }, "move-left"), _jsxs(DropdownMenuItem, { onClick: () => {
|
|
89
|
+
if (columnIndex < columnOrder.length - 1) {
|
|
90
|
+
const newOrder = [...columnOrder];
|
|
91
|
+
const [movedColumn] = newOrder.splice(columnIndex, 1);
|
|
92
|
+
newOrder.splice(columnIndex + 1, 0, movedColumn);
|
|
93
|
+
table.setColumnOrder(newOrder);
|
|
94
|
+
}
|
|
95
|
+
}, disabled: !canMoveRight || isPinned !== false, children: [_jsx(ArrowRightIcon, { className: "size-3.5!", "aria-hidden": "true" }), _jsx("span", { children: "Move to Right" })] }, "move-right"));
|
|
96
|
+
hasPreviousSection = true;
|
|
97
|
+
}
|
|
98
|
+
// Visibility section
|
|
99
|
+
if (props.tableLayout?.columnsVisibility && visibility) {
|
|
100
|
+
if (hasPreviousSection) {
|
|
101
|
+
items.push(_jsx(DropdownMenuSeparator, {}, "sep-visibility"));
|
|
102
|
+
}
|
|
103
|
+
items.push(_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { children: [_jsx(Settings2Icon, { className: "size-3.5!" }), _jsx("span", { children: "Columns" })] }), _jsx(DropdownMenuSubContent, { side: "right", children: table
|
|
104
|
+
.getAllColumns()
|
|
105
|
+
.filter((col) => col.getCanHide())
|
|
106
|
+
.map((col) => (_jsx(DropdownMenuCheckboxItem, { checked: col.getIsVisible(), onSelect: (event) => event.preventDefault(), onCheckedChange: (value) => col.toggleVisibility(!!value), className: "capitalize", children: getColumnHeaderLabel(col) }, col.id))) })] }, "visibility"));
|
|
107
|
+
}
|
|
108
|
+
return items;
|
|
109
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
110
|
+
}, [
|
|
111
|
+
filter,
|
|
112
|
+
canSort,
|
|
113
|
+
isSorted,
|
|
114
|
+
column,
|
|
115
|
+
props.tableLayout?.columnsPinnable,
|
|
116
|
+
props.tableLayout?.columnsMovable,
|
|
117
|
+
props.tableLayout?.columnsVisibility,
|
|
118
|
+
canPin,
|
|
119
|
+
isPinned,
|
|
120
|
+
canMoveLeft,
|
|
121
|
+
canMoveRight,
|
|
122
|
+
visibility,
|
|
123
|
+
table,
|
|
124
|
+
columnIndex,
|
|
125
|
+
columnOrder,
|
|
126
|
+
columnVisibilityKey, // Needed to update checkbox states when visibility changes
|
|
127
|
+
]);
|
|
128
|
+
if (hasControls) {
|
|
129
|
+
return (_jsxs("div", { className: "flex h-full items-center justify-between gap-1.5", children: [_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { render: _jsxs(Button, { variant: "ghost", className: headerButtonClassName, disabled: isLoading || recordCount === 0, children: [icon && icon, resolvedTitle, sortIcon] }) }), _jsx(DropdownMenuContent, { className: "w-40", align: "start", children: menuItems })] }), props.tableLayout?.columnsPinnable && canPin && isPinned && (_jsx(Button, { size: "icon-sm", variant: "ghost", className: "-me-1 size-7 rounded-md", onClick: () => column.pin(false), "aria-label": `Unpin ${resolvedTitle} column`, title: `Unpin ${resolvedTitle} column`, children: _jsx(PinOffIcon, { className: "size-3.5! opacity-50!", "aria-hidden": "true" }) }))] }));
|
|
130
|
+
}
|
|
131
|
+
if (canSort || (props.tableLayout?.columnsResizable && canResize)) {
|
|
132
|
+
return (_jsx("div", { className: "flex h-full items-center", children: _jsxs(Button, { variant: "ghost", className: headerButtonClassName, disabled: isLoading || recordCount === 0, onClick: handleSort, children: [icon && icon, resolvedTitle, sortIcon] }) }));
|
|
133
|
+
}
|
|
134
|
+
return (_jsxs("div", { className: headerLabelClassName, children: [icon && icon, resolvedTitle] }));
|
|
135
|
+
}
|
|
136
|
+
const DataGridColumnHeader = memo(DataGridColumnHeaderInner);
|
|
137
|
+
export { DataGridColumnHeader };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { Table } from '@tanstack/react-table';
|
|
3
|
+
declare function DataGridColumnVisibility<TData>({ table, trigger }: {
|
|
4
|
+
table: Table<TData>;
|
|
5
|
+
trigger: ReactElement<Record<string, unknown>>;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export { DataGridColumnVisibility };
|
|
8
|
+
//# sourceMappingURL=data-grid-column-visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-column-visibility.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-column-visibility.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAInD,iBAAS,wBAAwB,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAAC,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,2CA2BnI;AAED,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { getColumnHeaderLabel } from './data-grid';
|
|
4
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuTrigger } from '../dropdown-menu';
|
|
5
|
+
function DataGridColumnVisibility({ table, trigger }) {
|
|
6
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { render: trigger }), _jsx(DropdownMenuContent, { align: "end", className: "min-w-[150px]", children: _jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { className: "font-medium", children: "Toggle Columns" }), table
|
|
7
|
+
.getAllColumns()
|
|
8
|
+
.filter((column) => column.getCanHide())
|
|
9
|
+
.map((column) => {
|
|
10
|
+
return (_jsx(DropdownMenuCheckboxItem, { className: "capitalize", checked: column.getIsVisible(), onSelect: (event) => event.preventDefault(), onCheckedChange: (value) => column.toggleVisibility(!!value), children: getColumnHeaderLabel(column) }, column.id));
|
|
11
|
+
})] }) })] }));
|
|
12
|
+
}
|
|
13
|
+
export { DataGridColumnVisibility };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
interface DataGridPaginationProps {
|
|
3
|
+
sizes?: number[];
|
|
4
|
+
sizesInfo?: string;
|
|
5
|
+
sizesLabel?: string;
|
|
6
|
+
sizesDescription?: string;
|
|
7
|
+
sizesSkeleton?: ReactNode;
|
|
8
|
+
more?: boolean;
|
|
9
|
+
moreLimit?: number;
|
|
10
|
+
info?: string;
|
|
11
|
+
infoSkeleton?: ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
rowsPerPageLabel?: string;
|
|
14
|
+
previousPageLabel?: string;
|
|
15
|
+
nextPageLabel?: string;
|
|
16
|
+
ellipsisText?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function DataGridPagination(props: DataGridPaginationProps): React.JSX.Element;
|
|
19
|
+
export { DataGridPagination, type DataGridPaginationProps };
|
|
20
|
+
//# sourceMappingURL=data-grid-pagination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-pagination.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-pagination.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAS9C,UAAU,uBAAuB;IAC7B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,iBAAS,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAmK7E;AAED,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, {} from 'react';
|
|
4
|
+
import { useDataGrid } from './data-grid';
|
|
5
|
+
import { Button } from '../button';
|
|
6
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
|
7
|
+
import { Skeleton } from '../skeleton';
|
|
8
|
+
import { cn } from '../../lib/utils';
|
|
9
|
+
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react';
|
|
10
|
+
function DataGridPagination(props) {
|
|
11
|
+
const { table, recordCount, isLoading } = useDataGrid();
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
sizes: [5, 10, 25, 50, 100],
|
|
14
|
+
sizesLabel: 'Show',
|
|
15
|
+
sizesDescription: 'per page',
|
|
16
|
+
sizesSkeleton: _jsx(Skeleton, { className: "h-8 w-44" }),
|
|
17
|
+
moreLimit: 5,
|
|
18
|
+
more: false,
|
|
19
|
+
info: '{from} - {to} of {count}',
|
|
20
|
+
infoSkeleton: _jsx(Skeleton, { className: "h-8 w-60" }),
|
|
21
|
+
rowsPerPageLabel: 'Rows per page',
|
|
22
|
+
previousPageLabel: 'Go to previous page',
|
|
23
|
+
nextPageLabel: 'Go to next page',
|
|
24
|
+
ellipsisText: '...',
|
|
25
|
+
};
|
|
26
|
+
const mergedProps = { ...defaultProps, ...props };
|
|
27
|
+
const btnBaseClasses = 'size-7 p-0 text-sm';
|
|
28
|
+
const btnArrowClasses = btnBaseClasses + ' rtl:transform rtl:rotate-180';
|
|
29
|
+
const pageIndex = table.getState().pagination.pageIndex;
|
|
30
|
+
const pageSize = table.getState().pagination.pageSize;
|
|
31
|
+
const from = pageIndex * pageSize + 1;
|
|
32
|
+
const to = Math.min((pageIndex + 1) * pageSize, recordCount);
|
|
33
|
+
const pageCount = table.getPageCount();
|
|
34
|
+
// Replace placeholders in paginationInfo
|
|
35
|
+
const paginationInfo = mergedProps?.info
|
|
36
|
+
? mergedProps.info.replace('{from}', from.toString()).replace('{to}', to.toString()).replace('{count}', recordCount.toString())
|
|
37
|
+
: `${from} - ${to} of ${recordCount}`;
|
|
38
|
+
// Pagination limit logic
|
|
39
|
+
const paginationMoreLimit = mergedProps?.moreLimit || 5;
|
|
40
|
+
// Determine the start and end of the pagination group
|
|
41
|
+
const currentGroupStart = Math.floor(pageIndex / paginationMoreLimit) * paginationMoreLimit;
|
|
42
|
+
const currentGroupEnd = Math.min(currentGroupStart + paginationMoreLimit, pageCount);
|
|
43
|
+
// Render page buttons based on the current group
|
|
44
|
+
const renderPageButtons = () => {
|
|
45
|
+
const buttons = [];
|
|
46
|
+
for (let i = currentGroupStart; i < currentGroupEnd; i++) {
|
|
47
|
+
buttons.push(_jsx(Button, { size: "icon-sm", variant: "ghost", className: cn(btnBaseClasses, 'text-muted-foreground', {
|
|
48
|
+
'bg-accent text-accent-foreground': pageIndex === i,
|
|
49
|
+
}), onClick: () => {
|
|
50
|
+
if (pageIndex !== i) {
|
|
51
|
+
table.setPageIndex(i);
|
|
52
|
+
}
|
|
53
|
+
}, children: i + 1 }, i));
|
|
54
|
+
}
|
|
55
|
+
return buttons;
|
|
56
|
+
};
|
|
57
|
+
// Render a "previous" ellipsis button if there are previous pages to show
|
|
58
|
+
const renderEllipsisPrevButton = () => {
|
|
59
|
+
if (currentGroupStart > 0) {
|
|
60
|
+
return (_jsx(Button, { size: "icon-sm", className: btnBaseClasses, variant: "ghost", onClick: () => table.setPageIndex(currentGroupStart - 1), children: mergedProps.ellipsisText }));
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
64
|
+
// Render a "next" ellipsis button if there are more pages to show after the current group
|
|
65
|
+
const renderEllipsisNextButton = () => {
|
|
66
|
+
if (currentGroupEnd < pageCount) {
|
|
67
|
+
return (_jsx(Button, { className: btnBaseClasses, variant: "ghost", size: "icon-sm", onClick: () => table.setPageIndex(currentGroupEnd), children: mergedProps.ellipsisText }));
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
return (_jsxs("div", { "data-slot": "data-grid-pagination", className: cn('flex grow flex-col flex-wrap items-center justify-between gap-2.5 py-2.5 sm:flex-row sm:py-0', mergedProps?.className), children: [_jsx("div", { className: "order-2 flex flex-wrap items-center space-x-2.5 pb-2.5 sm:order-1 sm:pb-0", children: isLoading ? (mergedProps?.sizesSkeleton) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-sm text-muted-foreground", children: mergedProps.rowsPerPageLabel }), _jsxs(Select, { value: `${pageSize}`, onValueChange: (value) => {
|
|
72
|
+
const newPageSize = Number(value);
|
|
73
|
+
table.setPageSize(newPageSize);
|
|
74
|
+
}, children: [_jsx(SelectTrigger, { className: "w-14", size: "sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { side: "top", className: "min-w-18", children: mergedProps?.sizes?.map((size) => (_jsx(SelectItem, { value: `${size}`, children: size }, size))) })] })] })) }), _jsx("div", { className: "order-1 flex flex-col items-center justify-center gap-2.5 pt-2.5 sm:order-2 sm:flex-row sm:justify-end sm:pt-0", children: isLoading ? (mergedProps?.infoSkeleton) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "order-2 text-sm text-nowrap text-muted-foreground sm:order-1", children: paginationInfo }), pageCount > 1 && (_jsxs("div", { className: "order-1 flex items-center space-x-1 sm:order-2", children: [_jsxs(Button, { size: "icon-sm", variant: "ghost", className: btnArrowClasses, onClick: () => table.previousPage(), disabled: !table.getCanPreviousPage(), children: [_jsx("span", { className: "sr-only", children: mergedProps.previousPageLabel }), _jsx(ChevronLeftIcon, { className: "size-4" })] }), renderEllipsisPrevButton(), renderPageButtons(), renderEllipsisNextButton(), _jsxs(Button, { size: "icon-sm", variant: "ghost", className: btnArrowClasses, onClick: () => table.nextPage(), disabled: !table.getCanNextPage(), children: [_jsx("span", { className: "sr-only", children: mergedProps.nextPageLabel }), _jsx(ChevronRightIcon, { className: "size-4" })] })] }))] })) })] }));
|
|
75
|
+
}
|
|
76
|
+
export { DataGridPagination };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { ScrollArea as ScrollAreaPrimitive } from '@base-ui/react/scroll-area';
|
|
3
|
+
type DataGridScrollAreaOrientation = 'horizontal' | 'vertical' | 'both';
|
|
4
|
+
type DataGridScrollAreaProps = Omit<ScrollAreaPrimitive.Root.Props, 'children'> & {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
orientation?: DataGridScrollAreaOrientation;
|
|
7
|
+
};
|
|
8
|
+
declare function DataGridScrollArea({ children, className, orientation, ...props }: DataGridScrollAreaProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { DataGridScrollArea };
|
|
10
|
+
export type { DataGridScrollAreaOrientation, DataGridScrollAreaProps };
|
|
11
|
+
//# sourceMappingURL=data-grid-scroll-area.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-scroll-area.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-scroll-area.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,EAAE,UAAU,IAAI,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAgB/E,KAAK,6BAA6B,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,CAAC;AAkBxE,KAAK,uBAAuB,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG;IAC9E,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,6BAA6B,CAAC;CAC/C,CAAC;AAwBF,iBAAS,kBAAkB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAoB,EAAE,GAAG,KAAK,EAAE,EAAE,uBAAuB,2CAmR3G;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EAAE,6BAA6B,EAAE,uBAAuB,EAAE,CAAC"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { useDataGrid } from './data-grid';
|
|
5
|
+
import { ScrollArea as ScrollAreaPrimitive } from '@base-ui/react/scroll-area';
|
|
6
|
+
import { cn } from '../../lib/utils';
|
|
7
|
+
const MIN_THUMB_SIZE = 24;
|
|
8
|
+
const FALLBACK_SCROLLBAR_SIZE = 12;
|
|
9
|
+
const INITIAL_METRICS = {
|
|
10
|
+
hasVerticalOverflow: false,
|
|
11
|
+
headerHeight: 0,
|
|
12
|
+
horizontalScrollbarSize: 0,
|
|
13
|
+
thumbHeight: 0,
|
|
14
|
+
thumbTop: 0,
|
|
15
|
+
trackHeight: 0,
|
|
16
|
+
};
|
|
17
|
+
function clamp(value, min, max) {
|
|
18
|
+
return Math.min(max, Math.max(min, value));
|
|
19
|
+
}
|
|
20
|
+
function areMetricsEqual(next, prev) {
|
|
21
|
+
return (next.hasVerticalOverflow === prev.hasVerticalOverflow &&
|
|
22
|
+
next.headerHeight === prev.headerHeight &&
|
|
23
|
+
next.horizontalScrollbarSize === prev.horizontalScrollbarSize &&
|
|
24
|
+
next.thumbHeight === prev.thumbHeight &&
|
|
25
|
+
next.thumbTop === prev.thumbTop &&
|
|
26
|
+
next.trackHeight === prev.trackHeight);
|
|
27
|
+
}
|
|
28
|
+
function applyMetrics(element, metrics) {
|
|
29
|
+
element.style.setProperty('--data-grid-scrollbar-header-height', `${metrics.headerHeight}px`);
|
|
30
|
+
element.style.setProperty('--data-grid-scrollbar-thumb-height', `${metrics.thumbHeight}px`);
|
|
31
|
+
element.style.setProperty('--data-grid-scrollbar-thumb-top', `${metrics.thumbTop}px`);
|
|
32
|
+
element.style.setProperty('--data-grid-scrollbar-track-height', `${metrics.trackHeight}px`);
|
|
33
|
+
}
|
|
34
|
+
function DataGridScrollArea({ children, className, orientation = 'both', ...props }) {
|
|
35
|
+
const { props: dataGridProps } = useDataGrid();
|
|
36
|
+
const containerRef = useRef(null);
|
|
37
|
+
const viewportRef = useRef(null);
|
|
38
|
+
const dragRef = useRef(null);
|
|
39
|
+
const metricsRef = useRef(INITIAL_METRICS);
|
|
40
|
+
const observedElementsRef = useRef({
|
|
41
|
+
header: null,
|
|
42
|
+
horizontalScrollbar: null,
|
|
43
|
+
table: null,
|
|
44
|
+
tableViewport: null,
|
|
45
|
+
});
|
|
46
|
+
const showHorizontal = orientation !== 'vertical';
|
|
47
|
+
const showVertical = orientation !== 'horizontal';
|
|
48
|
+
const usesCustomVerticalScrollbar = showVertical && !!dataGridProps.tableLayout?.headerSticky;
|
|
49
|
+
const [hasCustomVerticalOverflow, setHasCustomVerticalOverflow] = useState(false);
|
|
50
|
+
const clearDragState = useCallback(() => {
|
|
51
|
+
dragRef.current = null;
|
|
52
|
+
document.body.style.userSelect = '';
|
|
53
|
+
document.body.style.webkitUserSelect = '';
|
|
54
|
+
}, []);
|
|
55
|
+
const applyInitialMetricsToContainer = useCallback(() => {
|
|
56
|
+
const container = containerRef.current;
|
|
57
|
+
if (container && !areMetricsEqual(INITIAL_METRICS, metricsRef.current)) {
|
|
58
|
+
applyMetrics(container, INITIAL_METRICS);
|
|
59
|
+
metricsRef.current = INITIAL_METRICS;
|
|
60
|
+
}
|
|
61
|
+
}, []);
|
|
62
|
+
const resetMetrics = useCallback(() => {
|
|
63
|
+
applyInitialMetricsToContainer();
|
|
64
|
+
setHasCustomVerticalOverflow((prev) => (prev ? false : prev));
|
|
65
|
+
}, [applyInitialMetricsToContainer]);
|
|
66
|
+
const syncCustomVerticalScrollbar = useCallback(() => {
|
|
67
|
+
const container = containerRef.current;
|
|
68
|
+
const viewport = viewportRef.current;
|
|
69
|
+
if (!container || !viewport || !usesCustomVerticalScrollbar) {
|
|
70
|
+
resetMetrics();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { header, horizontalScrollbar } = observedElementsRef.current;
|
|
74
|
+
const headerHeight = header?.getBoundingClientRect().height ?? 0;
|
|
75
|
+
const viewportHeight = viewport.clientHeight;
|
|
76
|
+
const viewportWidth = viewport.clientWidth;
|
|
77
|
+
const scrollHeight = viewport.scrollHeight;
|
|
78
|
+
const scrollWidth = viewport.scrollWidth;
|
|
79
|
+
const hasHorizontalOverflow = showHorizontal && scrollWidth > viewportWidth + 0.5;
|
|
80
|
+
const horizontalScrollbarSize = hasHorizontalOverflow ? horizontalScrollbar?.offsetHeight || FALLBACK_SCROLLBAR_SIZE : 0;
|
|
81
|
+
const trackHeight = Math.max(0, viewportHeight - headerHeight - horizontalScrollbarSize);
|
|
82
|
+
const maxScroll = Math.max(0, scrollHeight - viewportHeight);
|
|
83
|
+
let nextMetrics;
|
|
84
|
+
if (trackHeight === 0 || maxScroll === 0) {
|
|
85
|
+
nextMetrics = {
|
|
86
|
+
hasVerticalOverflow: false,
|
|
87
|
+
headerHeight,
|
|
88
|
+
horizontalScrollbarSize,
|
|
89
|
+
thumbHeight: trackHeight,
|
|
90
|
+
thumbTop: 0,
|
|
91
|
+
trackHeight,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const bodyContentHeight = Math.max(trackHeight, scrollHeight - headerHeight);
|
|
96
|
+
const thumbHeight = clamp(trackHeight * (trackHeight / bodyContentHeight), MIN_THUMB_SIZE, trackHeight);
|
|
97
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
98
|
+
const thumbTop = maxThumbTop > 0 ? (viewport.scrollTop / maxScroll) * maxThumbTop : 0;
|
|
99
|
+
nextMetrics = {
|
|
100
|
+
hasVerticalOverflow: true,
|
|
101
|
+
headerHeight,
|
|
102
|
+
horizontalScrollbarSize,
|
|
103
|
+
thumbHeight,
|
|
104
|
+
thumbTop,
|
|
105
|
+
trackHeight,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (!areMetricsEqual(nextMetrics, metricsRef.current)) {
|
|
109
|
+
applyMetrics(container, nextMetrics);
|
|
110
|
+
metricsRef.current = nextMetrics;
|
|
111
|
+
}
|
|
112
|
+
setHasCustomVerticalOverflow((prev) => (prev === nextMetrics.hasVerticalOverflow ? prev : nextMetrics.hasVerticalOverflow));
|
|
113
|
+
}, [resetMetrics, showHorizontal, usesCustomVerticalScrollbar]);
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
const container = containerRef.current;
|
|
116
|
+
const viewport = viewportRef.current;
|
|
117
|
+
if (!container || !viewport)
|
|
118
|
+
return;
|
|
119
|
+
if (!usesCustomVerticalScrollbar) {
|
|
120
|
+
applyInitialMetricsToContainer();
|
|
121
|
+
queueMicrotask(() => {
|
|
122
|
+
setHasCustomVerticalOverflow((prev) => (prev ? false : prev));
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
observedElementsRef.current = {
|
|
127
|
+
header: container.querySelector('[data-slot="data-grid-table"] thead'),
|
|
128
|
+
horizontalScrollbar: container.querySelector('[data-slot="data-grid-scrollbar"][data-orientation="horizontal"]'),
|
|
129
|
+
table: container.querySelector('[data-slot="data-grid-table"]'),
|
|
130
|
+
tableViewport: container.querySelector('[data-slot="data-grid-table-viewport"]'),
|
|
131
|
+
};
|
|
132
|
+
let frame = 0;
|
|
133
|
+
const scheduleSync = () => {
|
|
134
|
+
cancelAnimationFrame(frame);
|
|
135
|
+
frame = window.requestAnimationFrame(syncCustomVerticalScrollbar);
|
|
136
|
+
};
|
|
137
|
+
scheduleSync();
|
|
138
|
+
viewport.addEventListener('scroll', scheduleSync, { passive: true });
|
|
139
|
+
const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(scheduleSync);
|
|
140
|
+
if (observer) {
|
|
141
|
+
observer.observe(viewport);
|
|
142
|
+
const { header, table, tableViewport } = observedElementsRef.current;
|
|
143
|
+
if (header)
|
|
144
|
+
observer.observe(header);
|
|
145
|
+
if (table)
|
|
146
|
+
observer.observe(table);
|
|
147
|
+
if (tableViewport)
|
|
148
|
+
observer.observe(tableViewport);
|
|
149
|
+
}
|
|
150
|
+
return () => {
|
|
151
|
+
cancelAnimationFrame(frame);
|
|
152
|
+
observer?.disconnect();
|
|
153
|
+
viewport.removeEventListener('scroll', scheduleSync);
|
|
154
|
+
clearDragState();
|
|
155
|
+
};
|
|
156
|
+
}, [applyInitialMetricsToContainer, clearDragState, resetMetrics, syncCustomVerticalScrollbar, usesCustomVerticalScrollbar]);
|
|
157
|
+
const scrollToThumbOffset = (nextThumbTop) => {
|
|
158
|
+
const viewport = viewportRef.current;
|
|
159
|
+
const { thumbHeight, trackHeight } = metricsRef.current;
|
|
160
|
+
if (!viewport)
|
|
161
|
+
return;
|
|
162
|
+
const maxScroll = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
|
|
163
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
164
|
+
if (maxScroll === 0 || maxThumbTop === 0) {
|
|
165
|
+
viewport.scrollTop = 0;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const ratio = clamp(nextThumbTop, 0, maxThumbTop) / maxThumbTop;
|
|
169
|
+
viewport.scrollTop = ratio * maxScroll;
|
|
170
|
+
};
|
|
171
|
+
const handleThumbPointerDown = (event) => {
|
|
172
|
+
const viewport = viewportRef.current;
|
|
173
|
+
if (!viewport)
|
|
174
|
+
return;
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
event.stopPropagation();
|
|
177
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
178
|
+
dragRef.current = {
|
|
179
|
+
pointerId: event.pointerId,
|
|
180
|
+
startScrollTop: viewport.scrollTop,
|
|
181
|
+
startY: event.clientY,
|
|
182
|
+
};
|
|
183
|
+
document.body.style.userSelect = 'none';
|
|
184
|
+
document.body.style.webkitUserSelect = 'none';
|
|
185
|
+
};
|
|
186
|
+
const handleThumbPointerMove = (event) => {
|
|
187
|
+
const viewport = viewportRef.current;
|
|
188
|
+
const dragState = dragRef.current;
|
|
189
|
+
const { thumbHeight, trackHeight } = metricsRef.current;
|
|
190
|
+
if (!viewport || !dragState || dragState.pointerId !== event.pointerId) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
|
|
194
|
+
const maxScroll = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
|
|
195
|
+
if (maxThumbTop === 0 || maxScroll === 0)
|
|
196
|
+
return;
|
|
197
|
+
const deltaY = event.clientY - dragState.startY;
|
|
198
|
+
const nextScrollTop = dragState.startScrollTop + (deltaY / maxThumbTop) * maxScroll;
|
|
199
|
+
viewport.scrollTop = clamp(nextScrollTop, 0, maxScroll);
|
|
200
|
+
};
|
|
201
|
+
const handleThumbPointerUp = (event) => {
|
|
202
|
+
if (dragRef.current?.pointerId !== event.pointerId)
|
|
203
|
+
return;
|
|
204
|
+
clearDragState();
|
|
205
|
+
};
|
|
206
|
+
const handleTrackPointerDown = (event) => {
|
|
207
|
+
const { thumbHeight } = metricsRef.current;
|
|
208
|
+
if (event.target !== event.currentTarget)
|
|
209
|
+
return;
|
|
210
|
+
event.preventDefault();
|
|
211
|
+
event.stopPropagation();
|
|
212
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
213
|
+
const offsetY = event.clientY - rect.top - thumbHeight / 2;
|
|
214
|
+
scrollToThumbOffset(offsetY);
|
|
215
|
+
};
|
|
216
|
+
return (_jsxs("div", { ref: containerRef, className: "relative", children: [_jsxs(ScrollAreaPrimitive.Root, { "data-slot": "data-grid-scroll-area", className: cn('relative', className), ...props, children: [_jsx(ScrollAreaPrimitive.Viewport, { ref: viewportRef, "data-slot": "scroll-area-viewport", className: "size-full", children: _jsx(ScrollAreaPrimitive.Content, { "data-slot": "scroll-area-content", children: children }) }), showHorizontal && (_jsx(ScrollAreaPrimitive.Scrollbar, { "data-slot": "data-grid-scrollbar", "data-orientation": "horizontal", orientation: "horizontal", className: "flex touch-none p-px transition-colors select-none data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2 data-[orientation=vertical]:border-s data-[orientation=vertical]:border-s-transparent", children: _jsx(ScrollAreaPrimitive.Thumb, { "data-slot": "data-grid-thumb", className: "relative flex-1 rounded-full bg-border" }) })), showVertical && !usesCustomVerticalScrollbar && (_jsx(ScrollAreaPrimitive.Scrollbar, { "data-slot": "data-grid-scrollbar", "data-orientation": "vertical", orientation: "vertical", className: "flex touch-none p-px transition-colors select-none data-[orientation=horizontal]:h-2.5 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2 data-[orientation=vertical]:border-s data-[orientation=vertical]:border-s-transparent", children: _jsx(ScrollAreaPrimitive.Thumb, { "data-slot": "data-grid-thumb", className: "relative flex-1 rounded-full bg-border" }) }))] }), usesCustomVerticalScrollbar && hasCustomVerticalOverflow && (_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-e-0 top-(--data-grid-scrollbar-header-height) z-20 h-(--data-grid-scrollbar-track-height)", children: _jsx("div", { className: "pointer-events-auto relative h-full w-2 touch-none p-px", onPointerDown: handleTrackPointerDown, children: _jsx("div", { className: cn('absolute end-px w-2 bg-border', 'top-(--data-grid-scrollbar-thumb-top) h-(--data-grid-scrollbar-thumb-height)', 'rounded-full'), onLostPointerCapture: clearDragState, onPointerCancel: handleThumbPointerUp, onPointerDown: handleThumbPointerDown, onPointerMove: handleThumbPointerMove, onPointerUp: handleThumbPointerUp }) }) }))] }));
|
|
217
|
+
}
|
|
218
|
+
export { DataGridScrollArea };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type DragEndEvent, type UniqueIdentifier } from '@dnd-kit/core';
|
|
3
|
+
declare function DataGridTableDndRowHandle({ className }: {
|
|
4
|
+
className?: string;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function DataGridTableDndRows<TData>({ handleDragEnd, dataIds, footerContent, }: {
|
|
7
|
+
handleDragEnd: (event: DragEndEvent) => void;
|
|
8
|
+
dataIds: UniqueIdentifier[];
|
|
9
|
+
footerContent?: ReactNode;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export { DataGridTableDndRowHandle, DataGridTableDndRows };
|
|
12
|
+
//# sourceMappingURL=data-grid-table-dnd-rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-table-dnd-rows.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-table-dnd-rows.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqC,KAAK,SAAS,EAA2D,MAAM,OAAO,CAAC;AAkBnI,OAAO,EAGH,KAAK,YAAY,EAKjB,KAAK,gBAAgB,EAGxB,MAAM,eAAe,CAAC;AAcvB,iBAAS,yBAAyB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CA4BvE;AA+BD,iBAAS,oBAAoB,CAAC,KAAK,EAAE,EACjC,aAAa,EACb,OAAO,EACP,aAAa,GAChB,EAAE;IACC,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC7C,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,SAAS,CAAC;CAC7B,2CAuHA;AAED,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useDataGrid } from './data-grid';
|
|
5
|
+
import { DataGridTableBase, DataGridTableBody, DataGridTableBodyRow, DataGridTableBodyRowCell, DataGridTableBodyRowSkeleton, DataGridTableBodyRowSkeletonCell, DataGridTableEmpty, DataGridTableFoot, DataGridTableHead, DataGridTableHeadRow, DataGridTableHeadRowCell, DataGridTableHeadRowCellResize, DataGridTableRowSpacer, DataGridTableViewport, } from './data-grid-table';
|
|
6
|
+
import { closestCenter, DndContext, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors, } from '@dnd-kit/core';
|
|
7
|
+
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
|
8
|
+
import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
9
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
10
|
+
import { flexRender } from '@tanstack/react-table';
|
|
11
|
+
import { Button } from '../button';
|
|
12
|
+
import { cn } from '../../lib/utils';
|
|
13
|
+
import { GripHorizontalIcon } from 'lucide-react';
|
|
14
|
+
const SortableRowContext = createContext(null);
|
|
15
|
+
function DataGridTableDndRowHandle({ className }) {
|
|
16
|
+
const context = useContext(SortableRowContext);
|
|
17
|
+
if (!context) {
|
|
18
|
+
// Fallback if context is not available (shouldn't happen in normal usage)
|
|
19
|
+
return (_jsx(Button, { variant: "ghost", size: "icon-sm", className: cn('size-7 cursor-grab opacity-70 hover:bg-transparent hover:opacity-100 active:cursor-grabbing', className), disabled: true, children: _jsx(GripHorizontalIcon, {}) }));
|
|
20
|
+
}
|
|
21
|
+
return (_jsx(Button, { variant: "ghost", size: "icon-sm", className: cn('size-7 cursor-grab opacity-70 hover:bg-transparent hover:opacity-100 active:cursor-grabbing', className), ...context.attributes, ...context.listeners, children: _jsx(GripHorizontalIcon, {}) }));
|
|
22
|
+
}
|
|
23
|
+
function DataGridTableDndRow({ row }) {
|
|
24
|
+
const { transform, transition, setNodeRef, isDragging, attributes, listeners } = useSortable({
|
|
25
|
+
id: row.id,
|
|
26
|
+
});
|
|
27
|
+
const style = {
|
|
28
|
+
transform: CSS.Transform.toString(transform),
|
|
29
|
+
transition: transition,
|
|
30
|
+
opacity: isDragging ? 0.8 : 1,
|
|
31
|
+
zIndex: isDragging ? 1 : 0,
|
|
32
|
+
position: 'relative',
|
|
33
|
+
cursor: isDragging ? 'grabbing' : undefined,
|
|
34
|
+
};
|
|
35
|
+
return (_jsx(SortableRowContext.Provider, { value: { attributes, listeners }, children: _jsx(DataGridTableBodyRow, { row: row, dndRef: setNodeRef, dndStyle: style, children: row.getVisibleCells().map((cell, colIndex) => {
|
|
36
|
+
return (_jsx(DataGridTableBodyRowCell, { cell: cell, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, colIndex));
|
|
37
|
+
}) }, row.id) }));
|
|
38
|
+
}
|
|
39
|
+
function DataGridTableDndRows({ handleDragEnd, dataIds, footerContent, }) {
|
|
40
|
+
const { table, isLoading, props } = useDataGrid();
|
|
41
|
+
const pagination = table.getState().pagination;
|
|
42
|
+
const tableContainerRef = useRef(null);
|
|
43
|
+
const [isDraggingRow, setIsDraggingRow] = useState(false);
|
|
44
|
+
const sensors = useSensors(useSensor(MouseSensor, {}), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {}));
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!isDraggingRow)
|
|
47
|
+
return;
|
|
48
|
+
const { body, documentElement } = document;
|
|
49
|
+
const previousBodyCursor = body.style.cursor;
|
|
50
|
+
const previousDocumentCursor = documentElement.style.cursor;
|
|
51
|
+
body.style.cursor = 'grabbing';
|
|
52
|
+
documentElement.style.cursor = 'grabbing';
|
|
53
|
+
return () => {
|
|
54
|
+
body.style.cursor = previousBodyCursor;
|
|
55
|
+
documentElement.style.cursor = previousDocumentCursor;
|
|
56
|
+
};
|
|
57
|
+
}, [isDraggingRow]);
|
|
58
|
+
const modifiers = useMemo(() => {
|
|
59
|
+
const restrictToTableContainer = ({ transform, draggingNodeRect }) => {
|
|
60
|
+
if (!tableContainerRef.current || !draggingNodeRect) {
|
|
61
|
+
return transform;
|
|
62
|
+
}
|
|
63
|
+
const containerRect = tableContainerRef.current.getBoundingClientRect();
|
|
64
|
+
const { x, y } = transform;
|
|
65
|
+
const minX = containerRect.left - draggingNodeRect.left;
|
|
66
|
+
const maxX = containerRect.right - draggingNodeRect.right;
|
|
67
|
+
const minY = containerRect.top - draggingNodeRect.top;
|
|
68
|
+
const maxY = containerRect.bottom - draggingNodeRect.bottom;
|
|
69
|
+
return {
|
|
70
|
+
...transform,
|
|
71
|
+
x: Math.max(minX, Math.min(maxX, x)),
|
|
72
|
+
y: Math.max(minY, Math.min(maxY, y)),
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
return [restrictToVerticalAxis, restrictToTableContainer];
|
|
76
|
+
}, []);
|
|
77
|
+
return (_jsx(DndContext, { id: useId(), collisionDetection: closestCenter, modifiers: modifiers, onDragCancel: () => setIsDraggingRow(false), onDragEnd: (event) => {
|
|
78
|
+
setIsDraggingRow(false);
|
|
79
|
+
handleDragEnd(event);
|
|
80
|
+
}, onDragStart: () => setIsDraggingRow(true), sensors: sensors, children: _jsx(DataGridTableViewport, { viewportRef: tableContainerRef, className: isDraggingRow ? 'relative cursor-grabbing [&_*]:cursor-grabbing!' : 'relative', children: _jsxs(DataGridTableBase, { children: [_jsx(DataGridTableHead, { children: table.getHeaderGroups().map((headerGroup, index) => {
|
|
81
|
+
return (_jsx(DataGridTableHeadRow, { headerGroup: headerGroup, children: headerGroup.headers.map((header, index) => {
|
|
82
|
+
const { column } = header;
|
|
83
|
+
return (_jsxs(DataGridTableHeadRowCell, { header: header, children: [header.isPlaceholder ? null : props.tableLayout?.columnsResizable && column.getCanResize() ? (_jsx("div", { className: "truncate", children: flexRender(header.column.columnDef.header, header.getContext()) })) : (flexRender(header.column.columnDef.header, header.getContext())), props.tableLayout?.columnsResizable && column.getCanResize() && (_jsx(DataGridTableHeadRowCellResize, { header: header }))] }, index));
|
|
84
|
+
}) }, index));
|
|
85
|
+
}) }), (props.tableLayout?.stripped || !props.tableLayout?.rowBorder) && _jsx(DataGridTableRowSpacer, {}), _jsx(DataGridTableBody, { children: props.loadingMode === 'skeleton' && isLoading && pagination?.pageSize ? (Array.from({ length: pagination.pageSize }).map((_, rowIndex) => (_jsx(DataGridTableBodyRowSkeleton, { children: table.getVisibleFlatColumns().map((column, colIndex) => {
|
|
86
|
+
return (_jsx(DataGridTableBodyRowSkeletonCell, { column: column, children: column.columnDef.meta?.skeleton }, colIndex));
|
|
87
|
+
}) }, rowIndex)))) : table.getRowModel().rows.length ? (_jsx(SortableContext, { items: dataIds, strategy: verticalListSortingStrategy, children: table.getRowModel().rows.map((row) => {
|
|
88
|
+
return _jsx(DataGridTableDndRow, { row: row }, row.id);
|
|
89
|
+
}) })) : (_jsx(DataGridTableEmpty, {})) }), footerContent && _jsx(DataGridTableFoot, { children: footerContent })] }) }) }));
|
|
90
|
+
}
|
|
91
|
+
export { DataGridTableDndRowHandle, DataGridTableDndRows };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type DragEndEvent } from '@dnd-kit/core';
|
|
3
|
+
declare function DataGridTableDnd<TData>({ handleDragEnd, footerContent }: {
|
|
4
|
+
handleDragEnd: (event: DragEndEvent) => void;
|
|
5
|
+
footerContent?: ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export { DataGridTableDnd };
|
|
8
|
+
//# sourceMappingURL=data-grid-table-dnd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-table-dnd.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-table-dnd.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAgC,KAAK,SAAS,EAAsC,MAAM,OAAO,CAAC;AAmBzG,OAAO,EAA6B,KAAK,YAAY,EAAkF,MAAM,eAAe,CAAC;AA2E7J,iBAAS,gBAAgB,CAAC,KAAK,EAAE,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE;IAAE,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAA;CAAE,2CAsH7I;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|