@blenx-dev/core 0.1.0 → 0.2.3

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/iconify.config.ts +23 -0
  3. package/package.json +17 -7
  4. package/scripts/generate-icons.ts +82 -0
  5. package/src/components/Accordion/accordion.tsx +2 -2
  6. package/src/components/Autocomplete/autocomplete.tsx +2 -2
  7. package/src/components/Breadcrumbs/breadcrumbs.tsx +3 -3
  8. package/src/components/Checkbox/checkbox.tsx +2 -15
  9. package/src/components/CloseButton/close-button.tsx +2 -5
  10. package/src/components/Combobox/combobox.tsx +5 -5
  11. package/src/components/Command/command.tsx +2 -2
  12. package/src/components/CopyButton/copy-button.tsx +3 -11
  13. package/src/components/Drawer/drawer.tsx +3 -29
  14. package/src/components/Icon/icon.tsx +3 -3
  15. package/src/components/Popover/popover.tsx +24 -14
  16. package/src/components/Select/select.tsx +5 -5
  17. package/src/components/Spinner/spinner.tsx +3 -5
  18. package/src/components/Stack/stack.tsx +5 -15
  19. package/src/components/Text/text.tsx +1 -1
  20. package/src/components/index.ts +0 -3
  21. package/src/icons/ArrowRightIcon.tsx +20 -0
  22. package/src/icons/CalendarIcon.tsx +20 -0
  23. package/src/icons/CheckIcon.tsx +20 -0
  24. package/src/icons/ChevronDownIcon.tsx +20 -0
  25. package/src/icons/ChevronLeftIcon.tsx +20 -0
  26. package/src/icons/ChevronRightIcon.tsx +20 -0
  27. package/src/icons/ChevronUpIcon.tsx +20 -0
  28. package/src/icons/CircleAlertIcon.tsx +20 -0
  29. package/src/icons/CopyIcon.tsx +20 -0
  30. package/src/icons/EllipsisIcon.tsx +20 -0
  31. package/src/icons/FolderOpenIcon.tsx +20 -0
  32. package/src/icons/ListIcon.tsx +20 -0
  33. package/src/icons/LoaderCircleIcon.tsx +20 -0
  34. package/src/icons/SearchIcon.tsx +20 -0
  35. package/src/icons/SquareCheckIcon.tsx +20 -0
  36. package/src/icons/XIcon.tsx +20 -0
  37. package/src/icons/index.ts +17 -0
  38. package/src/utils/sprinkles.css.ts +18 -4
  39. package/src/DataTable/data-table-column-toggle.tsx +0 -73
  40. package/src/DataTable/data-table-empty.tsx +0 -27
  41. package/src/DataTable/data-table-error.tsx +0 -25
  42. package/src/DataTable/data-table-infinite-loader.tsx +0 -73
  43. package/src/DataTable/data-table-loading.tsx +0 -67
  44. package/src/DataTable/data-table-pagination.tsx +0 -80
  45. package/src/DataTable/data-table-toolbar.tsx +0 -62
  46. package/src/DataTable/data-table.css.ts +0 -420
  47. package/src/DataTable/data-table.tsx +0 -507
  48. package/src/DataTable/index.ts +0 -24
  49. package/src/DataTable/types.ts +0 -169
  50. package/src/DataTable/use-infinite-scroll.ts +0 -67
  51. package/src/components/Calendar/calendar.css.ts +0 -187
  52. package/src/components/Calendar/calendar.tsx +0 -143
  53. package/src/components/Calendar/index.ts +0 -1
  54. package/src/components/ColorPicker/color-picker.tsx +0 -123
  55. package/src/components/ColorPicker/index.ts +0 -1
  56. package/src/components/DatePicker/date-picker.tsx +0 -75
  57. package/src/components/DatePicker/index.ts +0 -1
  58. package/src/components/Stack/stack.css.ts +0 -42
@@ -1,507 +0,0 @@
1
- import { CaretDownIcon, CaretUpIcon } from "@phosphor-icons/react";
2
- import clsx from "clsx";
3
- import {
4
- type ColumnDef,
5
- type ColumnFiltersState,
6
- flexRender,
7
- getCoreRowModel,
8
- getExpandedRowModel,
9
- getFacetedRowModel,
10
- getFacetedUniqueValues,
11
- getFilteredRowModel,
12
- getPaginationRowModel,
13
- getSortedRowModel,
14
- type PaginationState,
15
- type Row,
16
- type RowSelectionState,
17
- type SortingState,
18
- useReactTable,
19
- type VisibilityState,
20
- } from "@tanstack/react-table";
21
- import { type UIEvent, useCallback, useMemo, useRef, useState } from "react";
22
- import { DataTableEmpty } from "./data-table-empty";
23
- import { DataTableError } from "./data-table-error";
24
- import { DataTableInfiniteLoader } from "./data-table-infinite-loader";
25
- import { DataTableLoading } from "./data-table-loading";
26
- import { DataTablePagination } from "./data-table-pagination";
27
- import { DataTableToolbar } from "./data-table-toolbar";
28
- import type { DataTableProps, RowAction, TableFeatures } from "./types";
29
- import { Button, Spinner } from "../components";
30
- import * as styles from "./data-table.css";
31
-
32
- const DEFAULT_FEATURES: TableFeatures = {
33
- sorting: true,
34
- globalSearch: true,
35
- pagination: true,
36
- columnVisibility: true,
37
- rowSelection: false,
38
- columnResizing: false,
39
- columnPinning: false,
40
- stickyHeader: false,
41
- rowActions: false,
42
- bulkActions: false,
43
- };
44
-
45
- function IndeterminateCheckbox({
46
- checked,
47
- indeterminate,
48
- onChange,
49
- label,
50
- }: {
51
- checked: boolean;
52
- indeterminate: boolean;
53
- onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
54
- label: string;
55
- }) {
56
- const ref = useRef<HTMLInputElement>(null);
57
- useMemo(() => {
58
- if (ref.current) {
59
- ref.current.indeterminate = indeterminate;
60
- }
61
- }, [indeterminate]);
62
-
63
- return (
64
- <input
65
- ref={ref}
66
- type="checkbox"
67
- checked={checked}
68
- onChange={onChange}
69
- aria-label={label}
70
- className={styles.checkbox}
71
- />
72
- );
73
- }
74
-
75
- function useDataTableStates<TData extends Record<string, unknown>>(
76
- props: Partial<DataTableProps<TData>>,
77
- ) {
78
- const [sorting, setSorting] = useState<SortingState>(props.initialSorting ?? []);
79
- const [pagination, setPagination] = useState<PaginationState>(
80
- props.initialPagination ?? { pageIndex: 0, pageSize: 10 },
81
- );
82
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
83
- props.initialColumnFilters ?? [],
84
- );
85
- const [globalFilter, setGlobalFilter] = useState(props.initialGlobalFilter ?? "");
86
- const [rowSelection, setRowSelection] = useState<RowSelectionState>(
87
- props.initialRowSelection ?? {},
88
- );
89
-
90
- const handlePaginationChange = useCallback(
91
- (v: PaginationState | ((old: PaginationState) => PaginationState)) => {
92
- const next = typeof v === "function" ? v(pagination) : v;
93
- setPagination(next);
94
- props.callbacks?.onPaginationChange?.(next);
95
- },
96
- [pagination, props.callbacks],
97
- );
98
- const handleSortingChange = useCallback(
99
- (v: SortingState | ((old: SortingState) => SortingState)) => {
100
- const next = typeof v === "function" ? v(sorting) : v;
101
- setSorting(next);
102
- props.callbacks?.onSortingChange?.(next);
103
- if (props.mode === "server") setPagination((p) => ({ ...p, pageIndex: 0 }));
104
- },
105
- [sorting, props.callbacks, props.mode],
106
- );
107
-
108
- const handleGlobalFilterChange = useCallback(
109
- (value: string) => {
110
- setGlobalFilter(value);
111
- props.callbacks?.onGlobalFilterChange?.(value);
112
- setPagination((p) => ({ ...p, pageIndex: 0 }));
113
- },
114
- [props.callbacks],
115
- );
116
-
117
- const handleColumnFiltersChange = useCallback(
118
- (v: ColumnFiltersState | ((old: ColumnFiltersState) => ColumnFiltersState)) => {
119
- const next = typeof v === "function" ? v(columnFilters) : v;
120
- setColumnFilters(next);
121
- props.callbacks?.onColumnFiltersChange?.(next);
122
- },
123
- [columnFilters, props.callbacks],
124
- );
125
-
126
- const handleRowSelectionChange = useCallback(
127
- (v: RowSelectionState | ((old: RowSelectionState) => RowSelectionState)) => {
128
- const next = typeof v === "function" ? v(rowSelection) : v;
129
- setRowSelection(next);
130
- props.callbacks?.onRowSelectionChange?.(next);
131
- },
132
- [rowSelection, props.callbacks],
133
- );
134
- return {
135
- sorting,
136
- handleSortingChange,
137
- columnFilters,
138
- handleColumnFiltersChange,
139
- pagination,
140
- handlePaginationChange,
141
- globalFilter,
142
- handleGlobalFilterChange,
143
- rowSelection,
144
- handleRowSelectionChange,
145
- };
146
- }
147
- export function DataTable<TData extends Record<string, unknown>>({
148
- columns,
149
- data,
150
- pageCount,
151
- mode = "client",
152
- isLoading,
153
- isFetching,
154
- isError,
155
- errorMessage,
156
- fetchNextPage,
157
- hasNextPage,
158
- isFetchingNextPage,
159
- features: featuresProp,
160
- size = "md",
161
- infiniteScroll,
162
- columnPinning,
163
- slots,
164
- rowActions,
165
- initialSorting,
166
- initialPagination,
167
- initialColumnFilters,
168
- initialGlobalFilter,
169
- initialColumnVisibility,
170
- initialRowSelection,
171
- callbacks,
172
- tableOptions,
173
- className,
174
- style,
175
- }: DataTableProps<TData>) {
176
- const features = useMemo(() => ({ ...DEFAULT_FEATURES, ...featuresProp }), [featuresProp]);
177
-
178
- const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
179
- initialColumnVisibility ?? {},
180
- );
181
- const {
182
- sorting,
183
- handleSortingChange,
184
- pagination,
185
- handlePaginationChange,
186
- columnFilters,
187
- handleColumnFiltersChange,
188
- globalFilter,
189
- handleGlobalFilterChange,
190
- rowSelection,
191
- handleRowSelectionChange,
192
- } = useDataTableStates({
193
- initialPagination,
194
- initialSorting,
195
- initialColumnFilters,
196
- initialGlobalFilter,
197
- initialColumnVisibility,
198
- initialRowSelection,
199
- callbacks,
200
- mode,
201
- });
202
-
203
- const stableColumns = useMemo(() => {
204
- let result: ColumnDef<TData, unknown>[] = columns;
205
-
206
- if (features.rowSelection) {
207
- result = [
208
- {
209
- id: "select",
210
- header: ({ table }) => (
211
- <IndeterminateCheckbox
212
- checked={table.getIsAllPageRowsSelected()}
213
- indeterminate={table.getIsSomePageRowsSelected()}
214
- onChange={table.getToggleAllPageRowsSelectedHandler()}
215
- label="Select all rows"
216
- />
217
- ),
218
- cell: ({ row }) => (
219
- <IndeterminateCheckbox
220
- checked={row.getIsSelected()}
221
- indeterminate={row.getIsSomeSelected()}
222
- onChange={row.getToggleSelectedHandler()}
223
- label={`Select row ${row.id}`}
224
- />
225
- ),
226
- enableSorting: false,
227
- enableHiding: false,
228
- size: 40,
229
- } satisfies ColumnDef<TData, unknown>,
230
- ...result,
231
- ];
232
- }
233
-
234
- if (features.rowActions && rowActions && rowActions.length > 0) {
235
- result = [
236
- ...result,
237
- {
238
- id: "actions",
239
- header: "Actions",
240
- cell: ({ row }) => <RowActionsCell row={row} actions={rowActions} />,
241
- enableSorting: false,
242
- enableHiding: false,
243
- size: 80,
244
- } satisfies ColumnDef<TData, unknown>,
245
- ];
246
- }
247
-
248
- return result;
249
- }, [columns, features.rowSelection, features.rowActions, rowActions]);
250
-
251
- const displayedData = useMemo(() => data ?? [], [data]);
252
- const enablePagination = features.pagination && mode !== "infinite";
253
-
254
- const table = useReactTable<TData>({
255
- data: displayedData,
256
- columns: stableColumns,
257
- state: {
258
- sorting,
259
- pagination: enablePagination ? pagination : undefined,
260
- columnFilters,
261
- globalFilter,
262
- rowSelection,
263
- columnVisibility,
264
- columnPinning: {
265
- left: columnPinning?.left ?? [],
266
- right: columnPinning?.right ?? [],
267
- },
268
- },
269
- onSortingChange: handleSortingChange,
270
- onPaginationChange: handlePaginationChange,
271
- onColumnFiltersChange: handleColumnFiltersChange,
272
- onGlobalFilterChange: handleGlobalFilterChange,
273
- onRowSelectionChange: handleRowSelectionChange,
274
- onColumnVisibilityChange: setColumnVisibility,
275
- enableRowSelection: features.rowSelection,
276
- enableSorting: features.sorting,
277
- enableColumnResizing: features.columnResizing,
278
- enableColumnPinning: features.columnPinning,
279
- getCoreRowModel: getCoreRowModel(),
280
- getSortedRowModel: features.sorting ? getSortedRowModel() : undefined,
281
- getFilteredRowModel: getFilteredRowModel(),
282
- getPaginationRowModel:
283
- enablePagination && mode === "client" ? getPaginationRowModel() : undefined,
284
- getExpandedRowModel: getExpandedRowModel(),
285
- getFacetedRowModel: getFacetedRowModel(),
286
- getFacetedUniqueValues: getFacetedUniqueValues(),
287
- manualPagination: mode === "server",
288
- manualSorting: mode === "server",
289
- manualFiltering: mode === "server",
290
- pageCount: mode === "server" ? (pageCount ?? 0) : undefined,
291
- autoResetPageIndex: false,
292
- ...tableOptions,
293
- } as Parameters<typeof useReactTable<TData>>[0]);
294
-
295
- const selectedRowModel = table.getSelectedRowModel();
296
- const selectedRows = useMemo(
297
- () => selectedRowModel.rows.map((r) => r.original),
298
- [selectedRowModel.rows],
299
- );
300
- const { rows } = table.getRowModel();
301
- const headerGroups = table.getHeaderGroups();
302
-
303
- const showPagination = enablePagination && mode === "client" && rows.length > 0;
304
- const showInfiniteLoader = mode === "infinite" && fetchNextPage;
305
- const showToolbar =
306
- features.globalSearch ||
307
- features.columnVisibility ||
308
- slots?.toolbar ||
309
- (features.bulkActions && selectedRows.length > 0);
310
-
311
- const tableContainerRef = useRef<HTMLDivElement>(null);
312
- const [isScrolled, setIsScrolled] = useState(false);
313
- const handleScroll = useCallback((e: UIEvent<HTMLDivElement>) => {
314
- setIsScrolled((e.target as HTMLDivElement).scrollTop > 0);
315
- }, []);
316
-
317
- const emptyState = useMemo(() => slots?.empty ?? <DataTableEmpty />, [slots?.empty]);
318
- const loadingState = useMemo(
319
- () => slots?.loading ?? <DataTableLoading columnCount={stableColumns.length} size={size} />,
320
- [slots?.loading, stableColumns.length, size],
321
- );
322
- const errorState = useMemo(
323
- () => slots?.error ?? <DataTableError message={errorMessage} />,
324
- [slots?.error, errorMessage],
325
- );
326
-
327
- if (isError) return <div className={className}>{errorState}</div>;
328
- if (isLoading) return <div className={className}>{loadingState}</div>;
329
-
330
- return (
331
- <div className={className} style={style}>
332
- {showToolbar && (
333
- <DataTableToolbar
334
- table={table}
335
- features={features}
336
- globalSearch={globalFilter}
337
- onGlobalSearchChange={handleGlobalFilterChange}
338
- customToolbar={slots?.toolbar}
339
- bulkActions={
340
- features.bulkActions
341
- ? ((
342
- tableOptions as {
343
- bulkActions?: {
344
- label: string;
345
- onClick: (rows: TData[]) => void;
346
- }[];
347
- }
348
- )?.bulkActions ?? undefined)
349
- : undefined
350
- }
351
- selectedRows={selectedRows}
352
- />
353
- )}
354
-
355
- <div ref={tableContainerRef} onScroll={handleScroll} className={styles.tableContainer}>
356
- <table className={styles.table} aria-label="Data table">
357
- <thead
358
- className={
359
- features.stickyHeader
360
- ? isScrolled
361
- ? styles.theadStickyScrolled
362
- : styles.theadSticky
363
- : styles.theadStatic
364
- }
365
- >
366
- {headerGroups.map((hg) => (
367
- <tr key={hg.id} className={styles.headRow}>
368
- {hg.headers.map((header) => {
369
- const isSorted = header.column.getIsSorted();
370
- const isSortable = header.column.getCanSort();
371
- return (
372
- <th
373
- key={header.id}
374
- colSpan={header.colSpan}
375
- className={clsx(
376
- styles.th,
377
- size === "sm" && styles.thSm,
378
- size === "lg" && styles.thLg,
379
- isSortable && styles.thSortable,
380
- )}
381
- onClick={isSortable ? header.column.getToggleSortingHandler() : undefined}
382
- aria-sort={
383
- isSorted === "asc"
384
- ? "ascending"
385
- : isSorted === "desc"
386
- ? "descending"
387
- : undefined
388
- }
389
- aria-label={
390
- isSortable
391
- ? `Sort by ${header.column.columnDef.header?.toString() ?? header.id}`
392
- : undefined
393
- }
394
- >
395
- <div className={styles.thContent}>
396
- {header.isPlaceholder
397
- ? null
398
- : flexRender(header.column.columnDef.header, header.getContext())}
399
- {isSorted === "asc" && <CaretUpIcon size={10} aria-hidden="true" />}
400
- {isSorted === "desc" && <CaretDownIcon size={10} aria-hidden="true" />}
401
- </div>
402
- </th>
403
- );
404
- })}
405
- </tr>
406
- ))}
407
- </thead>
408
- <tbody>
409
- {rows.length === 0 ? (
410
- <tr>
411
- <td colSpan={stableColumns.length} className={styles.emptyTd}>
412
- {emptyState}
413
- </td>
414
- </tr>
415
- ) : (
416
- rows.map((row) => {
417
- const selected = row.getIsSelected();
418
- return (
419
- <tr
420
- key={row.id}
421
- onClick={() => callbacks?.onRowClick?.(row.original)}
422
- onKeyDown={(e) => {
423
- if (e.key === "Enter" || e.key === " ") callbacks?.onRowClick?.(row.original);
424
- }}
425
- tabIndex={callbacks?.onRowClick ? 0 : undefined}
426
- role={callbacks?.onRowClick ? "button" : undefined}
427
- className={clsx(
428
- styles.tr,
429
- size === "sm" && styles.trSm,
430
- size === "lg" && styles.trLg,
431
- selected && styles.trSelected,
432
- )}
433
- aria-selected={selected}
434
- >
435
- {row.getVisibleCells().map((cell) => (
436
- <td
437
- key={cell.id}
438
- className={clsx(
439
- styles.td,
440
- size === "sm" && styles.tdSm,
441
- size === "lg" && styles.tdLg,
442
- )}
443
- >
444
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
445
- </td>
446
- ))}
447
- </tr>
448
- );
449
- })
450
- )}
451
- </tbody>
452
- </table>
453
- </div>
454
-
455
- {isFetching && !isFetchingNextPage && mode !== "client" && (
456
- <div className={styles.fetchingBar}>
457
- <Spinner />
458
- <span className={styles.fetchingText}>Updating...</span>
459
- </div>
460
- )}
461
-
462
- {showPagination && <DataTablePagination table={table} />}
463
-
464
- {showInfiniteLoader && (
465
- <DataTableInfiniteLoader
466
- fetchNextPage={fetchNextPage}
467
- hasNextPage={Boolean(hasNextPage)}
468
- isFetchingNextPage={Boolean(isFetchingNextPage)}
469
- isFetching={isFetching}
470
- config={infiniteScroll}
471
- />
472
- )}
473
- </div>
474
- );
475
- }
476
-
477
- interface RowActionsCellProps<TData> {
478
- row: Row<TData>;
479
- actions: RowAction<TData>[];
480
- }
481
-
482
- function RowActionsCell<TData>({ row, actions }: RowActionsCellProps<TData>) {
483
- return (
484
- <div className={styles.actionsCell}>
485
- {actions.map((action, i) => {
486
- const isDisabled =
487
- typeof action.disabled === "function" ? action.disabled(row.original) : action.disabled;
488
- return (
489
- <Button
490
- key={`row-action-${i.toString()}`}
491
- variant="ghost"
492
- size="sm"
493
- disabled={isDisabled}
494
- onClick={(e) => {
495
- e.stopPropagation();
496
- action.onClick(row.original);
497
- }}
498
- aria-label={action.label}
499
- >
500
- {action.icon && <span>{action.icon}</span>}
501
- {action.label}
502
- </Button>
503
- );
504
- })}
505
- </div>
506
- );
507
- }
@@ -1,24 +0,0 @@
1
- export { DataTable } from "./data-table";
2
- export { DataTableColumnToggle } from "./data-table-column-toggle";
3
- export { DataTableEmpty } from "./data-table-empty";
4
- export { DataTableError } from "./data-table-error";
5
- export { DataTableInfiniteLoader } from "./data-table-infinite-loader";
6
- export { DataTableLoading } from "./data-table-loading";
7
- export { DataTablePagination } from "./data-table-pagination";
8
- export { DataTableToolbar } from "./data-table-toolbar";
9
- export type {
10
- BulkAction,
11
- ColumnPinningOptions,
12
- DataTableProps,
13
- InfiniteScrollConfig,
14
- InfiniteScrollMode,
15
- RowAction,
16
- ServerTableResponse,
17
- ServerTableState,
18
- TableCallbacks,
19
- TableFeatures,
20
- TableMode,
21
- TableSize,
22
- TableSlots,
23
- } from "./types";
24
- export { useInfiniteScroll } from "./use-infinite-scroll";
@@ -1,169 +0,0 @@
1
- import type {
2
- ColumnDef,
3
- ColumnFiltersState,
4
- OnChangeFn,
5
- PaginationState,
6
- RowSelectionState,
7
- SortingState,
8
- TableOptions,
9
- VisibilityState,
10
- } from "@tanstack/react-table";
11
- import type { ReactNode } from "react";
12
-
13
- // ─── Data loading modes ──────────────────────────────────────────────────────
14
-
15
- export type TableMode = "client" | "server" | "infinite";
16
-
17
- // ─── Server-state metadata ───────────────────────────────────────────────────
18
-
19
- export interface ServerTableState {
20
- pagination: PaginationState;
21
- sorting: SortingState;
22
- columnFilters: ColumnFiltersState;
23
- globalFilter: string;
24
- }
25
-
26
- export interface ServerTableResponse<TData> {
27
- rows: TData[];
28
- totalCount: number;
29
- pageCount: number;
30
- }
31
-
32
- // ─── Feature configuration ───────────────────────────────────────────────────
33
-
34
- export interface TableFeatures {
35
- sorting?: boolean;
36
- globalSearch?: boolean;
37
- pagination?: boolean;
38
- columnVisibility?: boolean;
39
- rowSelection?: boolean;
40
- columnResizing?: boolean;
41
- columnPinning?: boolean;
42
- stickyHeader?: boolean;
43
- rowActions?: boolean;
44
- bulkActions?: boolean;
45
- }
46
-
47
- // ─── Infinite scroll configuration ───────────────────────────────────────────
48
-
49
- export type InfiniteScrollMode = "auto" | "manual";
50
-
51
- export interface InfiniteScrollConfig {
52
- mode: InfiniteScrollMode;
53
- /** Root margin for IntersectionObserver. Default: "200px" */
54
- rootMargin?: string;
55
- /** IntersectionObserver threshold. Default: 0 */
56
- threshold?: number;
57
- /** Load-more button text for manual mode. Default: "Load more" */
58
- loadMoreText?: string;
59
- /** Loading text. Default: "Loading..." */
60
- loadingText?: string;
61
- /** No more data text. Default: "No more results" */
62
- noMoreText?: string;
63
- }
64
-
65
- // ─── Column pinning configuration ────────────────────────────────────────────
66
-
67
- export interface ColumnPinningOptions {
68
- left?: string[];
69
- right?: string[];
70
- }
71
-
72
- // ─── Row action ──────────────────────────────────────────────────────────────
73
-
74
- export interface RowAction<TData> {
75
- label: string;
76
- icon?: ReactNode;
77
- onClick: (row: TData) => void;
78
- disabled?: boolean | ((row: TData) => boolean);
79
- variant?: "default" | "destructive";
80
- }
81
-
82
- // ─── Bulk action ─────────────────────────────────────────────────────────────
83
-
84
- export interface BulkAction<TData> {
85
- label: string;
86
- icon?: ReactNode;
87
- onClick: (selectedRows: TData[]) => void;
88
- disabled?: boolean;
89
- variant?: "default" | "destructive";
90
- }
91
-
92
- // ─── Component slots ─────────────────────────────────────────────────────────
93
-
94
- export interface TableSlots<TData> {
95
- toolbar?: ReactNode;
96
- pagination?: ReactNode;
97
- empty?: ReactNode;
98
- loading?: ReactNode;
99
- error?: ReactNode;
100
- rowActions?: (row: TData) => ReactNode;
101
- bulkActions?: (selectedRows: TData[]) => ReactNode;
102
- }
103
-
104
- // ─── State callbacks ─────────────────────────────────────────────────────────
105
-
106
- export interface TableCallbacks<TData> {
107
- onSortingChange?: OnChangeFn<SortingState>;
108
- onPaginationChange?: OnChangeFn<PaginationState>;
109
- onGlobalFilterChange?: OnChangeFn<string>;
110
- onColumnFiltersChange?: OnChangeFn<ColumnFiltersState>;
111
- onRowSelectionChange?: OnChangeFn<RowSelectionState>;
112
- onRowClick?: (row: TData) => void;
113
- }
114
-
115
- // ─── Table size ──────────────────────────────────────────────────────────────
116
-
117
- export type TableSize = "sm" | "md" | "lg";
118
-
119
- // ─── DataTable unified props ─────────────────────────────────────────────────
120
-
121
- export interface DataTableProps<TData extends Record<string, unknown>> {
122
- // ── Data ──
123
- columns: ColumnDef<TData, unknown>[];
124
- data: TData[] | undefined;
125
- totalCount?: number;
126
- pageCount?: number;
127
-
128
- // ── Mode ──
129
- mode?: TableMode;
130
-
131
- // ── States ──
132
- isLoading?: boolean;
133
- isFetching?: boolean;
134
- isError?: boolean;
135
- errorMessage?: string;
136
-
137
- // ── TanStack Query integration (server + infinite) ──
138
- fetchNextPage?: () => void;
139
- hasNextPage?: boolean;
140
- isFetchingNextPage?: boolean;
141
-
142
- // ── Configuration ──
143
- features?: TableFeatures;
144
- size?: TableSize;
145
- infiniteScroll?: InfiniteScrollConfig;
146
- columnPinning?: ColumnPinningOptions;
147
-
148
- // ── Slots ──
149
- slots?: TableSlots<TData>;
150
- rowActions?: RowAction<TData>[];
151
-
152
- // ── State ──
153
- initialSorting?: SortingState;
154
- initialPagination?: PaginationState;
155
- initialColumnFilters?: ColumnFiltersState;
156
- initialGlobalFilter?: string;
157
- initialColumnVisibility?: VisibilityState;
158
- initialRowSelection?: RowSelectionState;
159
-
160
- // ── Callbacks ──
161
- callbacks?: TableCallbacks<TData>;
162
-
163
- // ── Table options override ──
164
- tableOptions?: Partial<TableOptions<TData>>;
165
-
166
- // ── Styles ──
167
- className?: string;
168
- style?: React.CSSProperties;
169
- }