@wheelhouse/ui 0.2.3 → 0.2.5
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/blocks/floating-menu-widget/floating-menu-widget.d.ts +26 -0
- package/dist/blocks/floating-menu-widget/floating-menu-widget.d.ts.map +1 -0
- package/dist/blocks/floating-menu-widget/floating-menu-widget.js +200 -0
- package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.d.ts +15 -0
- package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.d.ts.map +1 -0
- package/dist/blocks/floating-menu-widget/floating-menu-widget.stories.js +22 -0
- package/dist/blocks/floating-menu-widget/index.d.ts +3 -0
- package/dist/blocks/floating-menu-widget/index.d.ts.map +1 -0
- package/dist/blocks/floating-menu-widget/index.js +1 -0
- package/dist/blocks/index.d.ts +1 -0
- package/dist/blocks/index.d.ts.map +1 -1
- package/dist/blocks/index.js +1 -0
- package/dist/components/button/button.d.ts +18 -11
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +27 -14
- package/dist/components/button/button.stories.d.ts +11 -0
- package/dist/components/button/button.stories.d.ts.map +1 -1
- package/dist/components/button/button.stories.js +84 -0
- 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/filters/filter-date-metric-value.d.ts.map +1 -1
- package/dist/components/filters/filter-date-metric-value.js +83 -8
- package/dist/components/filters/filter-fields-listing-demo.d.ts +12 -0
- package/dist/components/filters/filter-fields-listing-demo.d.ts.map +1 -0
- package/dist/components/filters/filter-fields-listing-demo.js +565 -0
- package/dist/components/filters/filters-types.d.ts +7 -0
- package/dist/components/filters/filters-types.d.ts.map +1 -1
- package/dist/components/filters/filters.stories.d.ts.map +1 -1
- package/dist/components/filters/filters.stories.js +8 -149
- package/dist/components/filters/index.d.ts +1 -0
- package/dist/components/filters/index.d.ts.map +1 -1
- package/dist/components/filters/index.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -0
- package/dist/components/popover/index.d.ts +1 -0
- package/dist/components/popover/index.d.ts.map +1 -1
- package/dist/components/popover/index.js +1 -0
- package/dist/components/popover/popover-handle.d.ts +6 -0
- package/dist/components/popover/popover-handle.d.ts.map +1 -0
- package/dist/components/popover/popover-handle.js +6 -0
- package/dist/components/popover/popover.d.ts +41 -7
- package/dist/components/popover/popover.d.ts.map +1 -1
- package/dist/components/popover/popover.js +50 -3
- package/dist/components/progress/progress.js +1 -1
- package/dist/components/progress/progress.stories.d.ts +11 -2
- package/dist/components/progress/progress.stories.d.ts.map +1 -1
- package/dist/components/progress/progress.stories.js +77 -4
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.d.ts.map +1 -0
- package/dist/components/sidebar/index.js +1 -0
- package/dist/components/sidebar/sidebar.d.ts +64 -0
- package/dist/components/sidebar/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.js +255 -0
- package/dist/components/sidebar/sidebar.stories.d.ts +20 -0
- package/dist/components/sidebar/sidebar.stories.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.stories.js +181 -0
- package/dist/components/skeleton/index.d.ts +3 -0
- package/dist/components/skeleton/index.d.ts.map +1 -0
- package/dist/components/skeleton/index.js +1 -0
- package/dist/components/skeleton/skeleton.d.ts +7 -0
- package/dist/components/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton/skeleton.js +8 -0
- 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/llms.txt +1 -1
- package/package.json +7 -4
- package/src/styles/globals.css +26 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-column-header.d.ts","sourceRoot":"","sources":["../../../src/components/data-grid/data-grid-column-header.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AA8BpD,UAAU,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAE,SAAQ,cAAc,CAAC,cAAc,CAAC;IACrF,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC9B,kHAAkH;IAClH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,iBAAS,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAkB,EAAE,EAAE,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,2CA0QzJ;AAED,QAAA,MAAM,oBAAoB,EAAsC,OAAO,yBAAyB,CAAC;AAEjG,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,CAAC"}
|
|
@@ -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"}
|