@douglasneuroinformatics/libui 4.9.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.d.ts +5 -48
- package/dist/components.js +1110 -723
- package/dist/components.js.map +1 -1
- package/dist/hooks.d.ts +4 -2
- package/dist/{types-9zYgx7C8.d.ts → types-CQ7qbFhC.d.ts} +57 -1
- package/package.json +3 -2
- package/src/components/DataTable/DataTable.stories.tsx +207 -37
- package/src/components/DataTable/DataTable.tsx +22 -279
- package/src/components/DataTable/DataTableBody.tsx +69 -0
- package/src/components/DataTable/DataTableContent.tsx +36 -0
- package/src/components/DataTable/DataTableControls.tsx +55 -0
- package/src/components/DataTable/DataTableEmptyState.tsx +25 -0
- package/src/components/DataTable/DataTableHead.tsx +58 -0
- package/src/components/DataTable/DataTablePagination.tsx +62 -0
- package/src/components/DataTable/DataTableRowActionCell.tsx +67 -0
- package/src/components/DataTable/__tests__/DataTable.spec.tsx +60 -0
- package/src/components/DataTable/constants.ts +7 -0
- package/src/components/DataTable/context.ts +5 -0
- package/src/components/DataTable/hooks.ts +60 -0
- package/src/components/DataTable/store.ts +203 -0
- package/src/components/DataTable/types.ts +99 -0
- package/src/components/DataTable/utils.tsx +138 -0
- package/src/hooks/useDestructiveAction/useDestructiveActionStore.test.ts +2 -7
- package/src/hooks/useNotificationsStore/useNotificationsStore.test.ts +1 -8
- package/src/testing/setup-tests.ts +1 -3
- package/src/components/DataTable/DestructiveActionDialog.tsx +0 -67
- package/src/components/DataTable/RowActionsDropdown.tsx +0 -64
package/dist/hooks.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { C as ChartConfig, U as UseStorageOptions } from './types-
|
|
2
|
-
export {
|
|
1
|
+
import { C as ChartConfig, U as UseStorageOptions } from './types-CQ7qbFhC.js';
|
|
2
|
+
export { a as DEFAULT_THEME, b as SYS_DARK_MEDIA_QUERY, S as StorageName, c as THEME_ATTRIBUTE, d as THEME_KEY, T as Theme, u as useStorage, e as useTheme } from './types-CQ7qbFhC.js';
|
|
3
3
|
import { Promisable } from 'type-fest';
|
|
4
4
|
import { RefObject, useEffect, Dispatch, SetStateAction } from 'react';
|
|
5
5
|
import * as zustand from 'zustand';
|
|
6
6
|
import { T as TranslatorType, a as TranslationKey, b as TranslationNamespace, c as TranslationKeyForNamespace } from './types-CwW4nA_v.js';
|
|
7
|
+
import '@tanstack/table-core';
|
|
8
|
+
import 'lucide-react';
|
|
7
9
|
|
|
8
10
|
declare function useChart(): {
|
|
9
11
|
config: ChartConfig;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { RowData, Table, ColumnDef, ColumnFiltersState, ColumnPinningState, SortingState, TableMeta } from '@tanstack/table-core';
|
|
2
|
+
import { Promisable } from 'type-fest';
|
|
1
3
|
import { Dispatch, SetStateAction } from 'react';
|
|
4
|
+
import { LucideIcon } from 'lucide-react';
|
|
2
5
|
|
|
3
6
|
type StorageName = 'localStorage' | 'sessionStorage';
|
|
4
7
|
type StorageEventMap = {
|
|
@@ -72,4 +75,57 @@ type ChartConfig = {
|
|
|
72
75
|
});
|
|
73
76
|
};
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
declare const ROW_ACTIONS_METADATA_KEY: unique symbol;
|
|
79
|
+
declare const TABLE_NAME_METADATA_KEY: unique symbol;
|
|
80
|
+
|
|
81
|
+
type DataTableEmptyStateProps = {
|
|
82
|
+
className?: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
icon?: LucideIcon;
|
|
85
|
+
title: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
declare module '@tanstack/table-core' {
|
|
89
|
+
interface ColumnMeta<TData, TValue> {
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
}
|
|
92
|
+
type GlobalFilter = string | undefined;
|
|
93
|
+
interface TableMeta<TData extends RowData> {
|
|
94
|
+
[key: string]: unknown;
|
|
95
|
+
[ROW_ACTIONS_METADATA_KEY]?: DataTableRowAction<TData>[];
|
|
96
|
+
[TABLE_NAME_METADATA_KEY]?: string;
|
|
97
|
+
}
|
|
98
|
+
interface TableState {
|
|
99
|
+
globalFilter: GlobalFilter;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
type DataTableInitialState = {
|
|
103
|
+
columnFilters?: ColumnFiltersState;
|
|
104
|
+
columnPinning?: ColumnPinningState;
|
|
105
|
+
sorting?: SortingState;
|
|
106
|
+
};
|
|
107
|
+
type DataTableProps<T extends RowData> = DataTableContentProps<T> & DataTableStoreParams<T>;
|
|
108
|
+
type DataTableContentProps<T extends RowData> = {
|
|
109
|
+
emptyStateProps?: Partial<DataTableEmptyStateProps>;
|
|
110
|
+
onSearchChange?: SearchChangeHandler<NoInfer<T>>;
|
|
111
|
+
togglesComponent?: React.FC<{
|
|
112
|
+
table: Table<T>;
|
|
113
|
+
}>;
|
|
114
|
+
};
|
|
115
|
+
type DataTableRowAction<T extends RowData> = {
|
|
116
|
+
destructive?: boolean;
|
|
117
|
+
disabled?: ((row: T) => boolean) | boolean;
|
|
118
|
+
label: string;
|
|
119
|
+
onSelect: (row: T, table: Table<T>) => Promisable<void>;
|
|
120
|
+
};
|
|
121
|
+
type DataTableStoreParams<T extends RowData> = {
|
|
122
|
+
columns: ColumnDef<NoInfer<T>>[];
|
|
123
|
+
data: T[];
|
|
124
|
+
initialState?: DataTableInitialState;
|
|
125
|
+
meta?: TableMeta<NoInfer<T>>;
|
|
126
|
+
rowActions?: DataTableRowAction<NoInfer<T>>[];
|
|
127
|
+
tableName?: string;
|
|
128
|
+
};
|
|
129
|
+
type SearchChangeHandler<T = any> = (value: string, table: Table<T>) => void;
|
|
130
|
+
|
|
131
|
+
export { type ChartConfig as C, type DataTableProps as D, type StorageName as S, type Theme as T, type UseStorageOptions as U, DEFAULT_THEME as a, SYS_DARK_MEDIA_QUERY as b, THEME_ATTRIBUTE as c, THEME_KEY as d, useTheme as e, useStorage as u };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douglasneuroinformatics/libui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"packageManager": "pnpm@10.7.1",
|
|
6
6
|
"description": "Generic UI components for DNP projects, built using React and Tailwind CSS",
|
|
7
7
|
"author": "Joshua Unrau",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"types": "./dist/i18n.d.ts",
|
|
32
32
|
"import": "./dist/i18n.js"
|
|
33
33
|
},
|
|
34
|
+
"./package.json": "./package.json",
|
|
34
35
|
"./providers": {
|
|
35
36
|
"types": "./dist/providers.d.ts",
|
|
36
37
|
"import": "./dist/providers.js"
|
|
37
38
|
},
|
|
38
|
-
"./package.json": "./package.json",
|
|
39
39
|
"./tailwind/globals.css": "./dist/tailwind/globals.css",
|
|
40
40
|
"./utils": {
|
|
41
41
|
"types": "./dist/utils.d.ts",
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"@radix-ui/react-tabs": "^1.1.3",
|
|
98
98
|
"@radix-ui/react-tooltip": "^1.1.8",
|
|
99
99
|
"@tanstack/react-table": "^8.21.3",
|
|
100
|
+
"@tanstack/table-core": "^8.21.3",
|
|
100
101
|
"class-variance-authority": "^0.7.1",
|
|
101
102
|
"clsx": "^2.1.1",
|
|
102
103
|
"cmdk": "^1.1.1",
|
|
@@ -1,76 +1,246 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
2
3
|
import { faker } from '@faker-js/faker';
|
|
3
4
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
5
|
+
import type { ColumnDef } from '@tanstack/table-core';
|
|
6
|
+
import { range } from 'lodash-es';
|
|
7
|
+
import { ChevronDownIcon } from 'lucide-react';
|
|
4
8
|
|
|
9
|
+
import { Button } from '../Button';
|
|
10
|
+
import { DropdownMenu } from '../DropdownMenu';
|
|
5
11
|
import { DataTable } from './DataTable';
|
|
12
|
+
import { useDataTableHandle } from './hooks';
|
|
6
13
|
|
|
7
|
-
|
|
14
|
+
type PaymentStatus = 'failed' | 'pending' | 'processing' | 'success';
|
|
8
15
|
|
|
9
|
-
type
|
|
10
|
-
|
|
16
|
+
type Payment = {
|
|
17
|
+
amount: number;
|
|
11
18
|
email: string;
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
id: string;
|
|
20
|
+
status: PaymentStatus;
|
|
14
21
|
};
|
|
15
22
|
|
|
16
|
-
type Story = StoryObj<typeof DataTable<
|
|
23
|
+
type Story = StoryObj<typeof DataTable<Payment>>;
|
|
17
24
|
|
|
18
|
-
const columns:
|
|
25
|
+
const columns: ColumnDef<Payment>[] = [
|
|
19
26
|
{
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
accessorKey: 'status',
|
|
28
|
+
enableSorting: false,
|
|
29
|
+
filterFn: (row, id, filter: PaymentStatus[]) => {
|
|
30
|
+
return filter.includes(row.getValue(id));
|
|
22
31
|
},
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
header: 'Status'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
accessorKey: 'email',
|
|
36
|
+
header: 'Email'
|
|
25
37
|
},
|
|
26
38
|
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
label: 'Email',
|
|
30
|
-
sortable: true
|
|
39
|
+
accessorKey: 'amount',
|
|
40
|
+
header: 'Amount'
|
|
31
41
|
}
|
|
32
42
|
];
|
|
33
43
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const statuses: readonly PaymentStatus[] = Object.freeze(['failed', 'pending', 'processing', 'success']);
|
|
45
|
+
|
|
46
|
+
const createData = (n: number): Payment[] => {
|
|
47
|
+
return range(n).map((i) => ({
|
|
48
|
+
amount: faker.number.int({ max: 100, min: 0 }),
|
|
49
|
+
email: faker.internet.email(),
|
|
50
|
+
id: String(i + 1),
|
|
51
|
+
status: faker.helpers.arrayElement(statuses)
|
|
52
|
+
}));
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const Toggles = () => {
|
|
56
|
+
const table = useDataTableHandle('table', true);
|
|
57
|
+
const columns = table.getAllColumns();
|
|
58
|
+
const statusColumn = columns.find((column) => column.id === 'status')!;
|
|
59
|
+
|
|
60
|
+
const filterValue = statusColumn.getFilterValue() as PaymentStatus[];
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<DropdownMenu>
|
|
65
|
+
<DropdownMenu.Trigger asChild>
|
|
66
|
+
<Button className="flex items-center gap-2" variant="outline">
|
|
67
|
+
Columns
|
|
68
|
+
<ChevronDownIcon />
|
|
69
|
+
</Button>
|
|
70
|
+
</DropdownMenu.Trigger>
|
|
71
|
+
<DropdownMenu.Content align="end">
|
|
72
|
+
{columns
|
|
73
|
+
.filter((column) => column.getCanHide())
|
|
74
|
+
.map((column) => {
|
|
75
|
+
return (
|
|
76
|
+
<DropdownMenu.CheckboxItem
|
|
77
|
+
checked={column.getIsVisible()}
|
|
78
|
+
className="capitalize"
|
|
79
|
+
key={column.id}
|
|
80
|
+
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
|
81
|
+
>
|
|
82
|
+
{column.id}
|
|
83
|
+
</DropdownMenu.CheckboxItem>
|
|
84
|
+
);
|
|
85
|
+
})}
|
|
86
|
+
</DropdownMenu.Content>
|
|
87
|
+
</DropdownMenu>
|
|
88
|
+
<DropdownMenu>
|
|
89
|
+
<DropdownMenu.Trigger asChild>
|
|
90
|
+
<Button className="flex items-center gap-2" variant="outline">
|
|
91
|
+
Filters
|
|
92
|
+
<ChevronDownIcon />
|
|
93
|
+
</Button>
|
|
94
|
+
</DropdownMenu.Trigger>
|
|
95
|
+
<DropdownMenu.Content widthFull align="start">
|
|
96
|
+
{statuses.map((option) => (
|
|
97
|
+
<DropdownMenu.CheckboxItem
|
|
98
|
+
checked={filterValue.includes(option)}
|
|
99
|
+
className="capitalize"
|
|
100
|
+
key={option}
|
|
101
|
+
onCheckedChange={(checked) => {
|
|
102
|
+
statusColumn.setFilterValue((prevValue: PaymentStatus[]) => {
|
|
103
|
+
if (checked) {
|
|
104
|
+
return [...prevValue, option];
|
|
105
|
+
}
|
|
106
|
+
return prevValue.filter((item) => item !== option);
|
|
107
|
+
});
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{option}
|
|
111
|
+
</DropdownMenu.CheckboxItem>
|
|
112
|
+
))}
|
|
113
|
+
</DropdownMenu.Content>
|
|
114
|
+
</DropdownMenu>
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
40
118
|
|
|
41
|
-
export default {
|
|
119
|
+
export default {
|
|
120
|
+
component: DataTable
|
|
121
|
+
} as Meta<typeof DataTable>;
|
|
42
122
|
|
|
43
123
|
export const Default: Story = {
|
|
124
|
+
decorators: [
|
|
125
|
+
(Story) => {
|
|
126
|
+
const [tableData, setTableData] = useState(createData(10));
|
|
127
|
+
return (
|
|
128
|
+
<div>
|
|
129
|
+
<Story
|
|
130
|
+
args={{
|
|
131
|
+
columns,
|
|
132
|
+
data: tableData
|
|
133
|
+
}}
|
|
134
|
+
/>
|
|
135
|
+
<div className="fixed bottom-0 py-2">
|
|
136
|
+
<button
|
|
137
|
+
className="rounded-md border px-2 py-1.5 text-sm"
|
|
138
|
+
type="button"
|
|
139
|
+
onClick={() => setTableData(createData(10))}
|
|
140
|
+
>
|
|
141
|
+
New Data
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const WithActions: Story = {
|
|
44
151
|
args: {
|
|
45
|
-
columns
|
|
46
|
-
|
|
47
|
-
headerActions: [
|
|
152
|
+
columns: [
|
|
153
|
+
...columns,
|
|
48
154
|
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
alert('Something!');
|
|
52
|
-
}
|
|
155
|
+
accessorKey: 'notes',
|
|
156
|
+
header: 'Notes'
|
|
53
157
|
}
|
|
54
158
|
],
|
|
159
|
+
data: createData(100).map((payment) => ({ ...payment, notes: faker.lorem.paragraph() })),
|
|
160
|
+
onSearchChange: () => {
|
|
161
|
+
return;
|
|
162
|
+
},
|
|
55
163
|
rowActions: [
|
|
164
|
+
{
|
|
165
|
+
label: 'Modify',
|
|
166
|
+
onSelect: () => {
|
|
167
|
+
alert('Modify');
|
|
168
|
+
}
|
|
169
|
+
},
|
|
56
170
|
{
|
|
57
171
|
destructive: true,
|
|
58
172
|
label: 'Delete',
|
|
59
|
-
onSelect: (
|
|
60
|
-
alert(
|
|
173
|
+
onSelect: () => {
|
|
174
|
+
alert('Delete');
|
|
61
175
|
}
|
|
62
176
|
}
|
|
63
177
|
],
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
178
|
+
tableName: 'action-table'
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export const WithToggles: Story = {
|
|
183
|
+
args: {
|
|
184
|
+
columns,
|
|
185
|
+
data: createData(100),
|
|
186
|
+
initialState: {
|
|
187
|
+
columnFilters: [
|
|
188
|
+
{
|
|
189
|
+
id: 'status',
|
|
190
|
+
value: [...statuses]
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
onSearchChange: () => {
|
|
195
|
+
return;
|
|
196
|
+
},
|
|
197
|
+
togglesComponent: Toggles
|
|
68
198
|
}
|
|
69
199
|
};
|
|
70
200
|
|
|
71
201
|
export const Empty: Story = {
|
|
72
202
|
args: {
|
|
73
203
|
columns,
|
|
74
|
-
data: []
|
|
204
|
+
data: [],
|
|
205
|
+
onSearchChange: () => {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const Grouped: Story = {
|
|
212
|
+
args: {
|
|
213
|
+
columns: [
|
|
214
|
+
{
|
|
215
|
+
columns: [
|
|
216
|
+
{
|
|
217
|
+
accessorKey: 'id',
|
|
218
|
+
header: 'ID'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
accessorKey: 'status',
|
|
222
|
+
header: 'Status'
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
header: 'Internal'
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
columns: [
|
|
229
|
+
{
|
|
230
|
+
accessorKey: 'email',
|
|
231
|
+
header: 'Email'
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
accessorKey: 'amount',
|
|
235
|
+
header: 'Amount'
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
header: 'Details'
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
data: createData(100),
|
|
242
|
+
onSearchChange: () => {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
75
245
|
}
|
|
76
246
|
};
|