@comitor/ui 1.10.0 → 2.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.
Files changed (110) hide show
  1. package/README.md +24 -5
  2. package/dist/data-grid/compose-refs.d.ts +19 -0
  3. package/dist/data-grid/compose-refs.js +41 -0
  4. package/dist/data-grid/compose-refs.js.map +1 -0
  5. package/dist/data-grid/data-grid-cell-variants.d.ts +15 -0
  6. package/dist/data-grid/data-grid-cell-variants.js +1738 -0
  7. package/dist/data-grid/data-grid-cell-variants.js.map +1 -0
  8. package/dist/data-grid/data-grid-cell-wrapper.d.ts +9 -0
  9. package/dist/data-grid/data-grid-cell-wrapper.js +179 -0
  10. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -0
  11. package/dist/data-grid/data-grid-cell.d.ts +8 -0
  12. package/dist/data-grid/data-grid-cell.js +101 -0
  13. package/dist/data-grid/data-grid-cell.js.map +1 -0
  14. package/dist/data-grid/data-grid-column-header.d.ts +12 -0
  15. package/dist/data-grid/data-grid-column-header.js +207 -0
  16. package/dist/data-grid/data-grid-column-header.js.map +1 -0
  17. package/dist/data-grid/data-grid-context-menu.d.ts +12 -0
  18. package/dist/data-grid/data-grid-context-menu.js +161 -0
  19. package/dist/data-grid/data-grid-context-menu.js.map +1 -0
  20. package/dist/data-grid/data-grid-filter-menu.d.ts +23 -0
  21. package/dist/data-grid/data-grid-filter-menu.js +725 -0
  22. package/dist/data-grid/data-grid-filter-menu.js.map +1 -0
  23. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +31 -0
  24. package/dist/data-grid/data-grid-keyboard-shortcuts.js +229 -0
  25. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -0
  26. package/dist/data-grid/data-grid-locale.d.ts +24 -0
  27. package/dist/data-grid/data-grid-locale.js +25 -0
  28. package/dist/data-grid/data-grid-locale.js.map +1 -0
  29. package/dist/data-grid/data-grid-paste-dialog.d.ts +11 -0
  30. package/dist/data-grid/data-grid-paste-dialog.js +104 -0
  31. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -0
  32. package/dist/data-grid/data-grid-presence.d.ts +14 -0
  33. package/dist/data-grid/data-grid-presence.js +16 -0
  34. package/dist/data-grid/data-grid-presence.js.map +1 -0
  35. package/dist/data-grid/data-grid-row-height-menu.d.ts +25 -0
  36. package/dist/data-grid/data-grid-row-height-menu.js +53 -0
  37. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -0
  38. package/dist/data-grid/data-grid-row.d.ts +28 -0
  39. package/dist/data-grid/data-grid-row.js +206 -0
  40. package/dist/data-grid/data-grid-row.js.map +1 -0
  41. package/dist/data-grid/data-grid-search.d.ts +10 -0
  42. package/dist/data-grid/data-grid-search.js +184 -0
  43. package/dist/data-grid/data-grid-search.js.map +1 -0
  44. package/dist/data-grid/data-grid-select-column.d.ts +12 -0
  45. package/dist/data-grid/data-grid-select-column.js +179 -0
  46. package/dist/data-grid/data-grid-select-column.js.map +1 -0
  47. package/dist/data-grid/data-grid-skeleton.d.ts +13 -0
  48. package/dist/data-grid/data-grid-skeleton.js +52 -0
  49. package/dist/data-grid/data-grid-skeleton.js.map +1 -0
  50. package/dist/data-grid/data-grid-sort-menu.d.ts +23 -0
  51. package/dist/data-grid/data-grid-sort-menu.js +283 -0
  52. package/dist/data-grid/data-grid-sort-menu.js.map +1 -0
  53. package/dist/data-grid/data-grid-view-menu.d.ts +24 -0
  54. package/dist/data-grid/data-grid-view-menu.js +74 -0
  55. package/dist/data-grid/data-grid-view-menu.js.map +1 -0
  56. package/dist/data-grid/data-grid.d.ts +44 -0
  57. package/dist/data-grid/data-grid.js +238 -0
  58. package/dist/data-grid/data-grid.js.map +1 -0
  59. package/dist/data-grid/filters.d.ts +27 -0
  60. package/dist/data-grid/filters.js +233 -0
  61. package/dist/data-grid/filters.js.map +1 -0
  62. package/dist/data-grid/index.d.ts +25 -0
  63. package/dist/data-grid/index.js +95 -0
  64. package/dist/data-grid/index.js.map +1 -0
  65. package/dist/data-grid/messages-en.d.ts +294 -0
  66. package/dist/data-grid/messages-en.js +279 -0
  67. package/dist/data-grid/messages-en.js.map +1 -0
  68. package/dist/data-grid/messages-vi.d.ts +12 -0
  69. package/dist/data-grid/messages-vi.js +279 -0
  70. package/dist/data-grid/messages-vi.js.map +1 -0
  71. package/dist/data-grid/messages.d.ts +567 -0
  72. package/dist/data-grid/messages.js +20 -0
  73. package/dist/data-grid/messages.js.map +1 -0
  74. package/dist/data-grid/sortable.d.ts +50 -0
  75. package/dist/data-grid/sortable.js +361 -0
  76. package/dist/data-grid/sortable.js.map +1 -0
  77. package/dist/data-grid/toast.d.ts +10 -0
  78. package/dist/data-grid/toast.js +21 -0
  79. package/dist/data-grid/toast.js.map +1 -0
  80. package/dist/data-grid/types.d.ts +189 -0
  81. package/dist/data-grid/types.js +1 -0
  82. package/dist/data-grid/types.js.map +1 -0
  83. package/dist/data-grid/use-as-ref.d.ts +5 -0
  84. package/dist/data-grid/use-as-ref.js +13 -0
  85. package/dist/data-grid/use-as-ref.js.map +1 -0
  86. package/dist/data-grid/use-badge-overflow.d.ts +24 -0
  87. package/dist/data-grid/use-badge-overflow.js +123 -0
  88. package/dist/data-grid/use-badge-overflow.js.map +1 -0
  89. package/dist/data-grid/use-callback-ref.d.ts +10 -0
  90. package/dist/data-grid/use-callback-ref.js +12 -0
  91. package/dist/data-grid/use-callback-ref.js.map +1 -0
  92. package/dist/data-grid/use-data-grid-undo-redo.d.ts +45 -0
  93. package/dist/data-grid/use-data-grid-undo-redo.js +348 -0
  94. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -0
  95. package/dist/data-grid/use-data-grid.d.ts +372 -0
  96. package/dist/data-grid/use-data-grid.js +2697 -0
  97. package/dist/data-grid/use-data-grid.js.map +1 -0
  98. package/dist/data-grid/use-debounced-callback.d.ts +6 -0
  99. package/dist/data-grid/use-debounced-callback.js +19 -0
  100. package/dist/data-grid/use-debounced-callback.js.map +1 -0
  101. package/dist/data-grid/use-isomorphic-layout-effect.d.ts +5 -0
  102. package/dist/data-grid/use-isomorphic-layout-effect.js +6 -0
  103. package/dist/data-grid/use-isomorphic-layout-effect.js.map +1 -0
  104. package/dist/data-grid/use-lazy-ref.d.ts +5 -0
  105. package/dist/data-grid/use-lazy-ref.js +12 -0
  106. package/dist/data-grid/use-lazy-ref.js.map +1 -0
  107. package/dist/data-grid/utils.d.ts +81 -0
  108. package/dist/data-grid/utils.js +409 -0
  109. package/dist/data-grid/utils.js.map +1 -0
  110. package/package.json +33 -15
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useDirection } from "@radix-ui/react-direction";
4
+ import { Check, Settings2 } from "lucide-react";
5
+ import * as React from "react";
6
+ import { Button } from "../components/ui/button.js";
7
+ import {
8
+ Command,
9
+ CommandEmpty,
10
+ CommandGroup,
11
+ CommandInput,
12
+ CommandItem,
13
+ CommandList
14
+ } from "../components/ui/command.js";
15
+ import { Popover, PopoverContent, PopoverTrigger } from "../components/ui/popover.js";
16
+ import { cn } from "../lib/cn.js";
17
+ import { useResolvedDataGridMessages } from "./data-grid-locale.js";
18
+ function DataGridViewMenu({
19
+ table,
20
+ disabled,
21
+ className,
22
+ locale,
23
+ ...props
24
+ }) {
25
+ const msg = useResolvedDataGridMessages(locale);
26
+ const dir = useDirection();
27
+ const columns = React.useMemo(
28
+ () => table.getAllColumns().filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide()),
29
+ [table]
30
+ );
31
+ return /* @__PURE__ */ jsxs(Popover, { children: [
32
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
33
+ Button,
34
+ {
35
+ "aria-label": msg.view.toggleColumns,
36
+ role: "combobox",
37
+ dir,
38
+ variant: "outline",
39
+ size: "sm",
40
+ className: "font-normal",
41
+ disabled,
42
+ children: [
43
+ /* @__PURE__ */ jsx(Settings2, { "aria-hidden": "true", className: "size-3.5 text-muted-foreground" }),
44
+ msg.view.trigger
45
+ ]
46
+ }
47
+ ) }),
48
+ /* @__PURE__ */ jsx(PopoverContent, { dir, align: "end", className: cn("w-52 p-0", className), ...props, children: /* @__PURE__ */ jsxs(Command, { children: [
49
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: msg.view.searchPlaceholder, "aria-label": msg.view.searchPlaceholder }),
50
+ /* @__PURE__ */ jsxs(CommandList, { children: [
51
+ /* @__PURE__ */ jsx(CommandEmpty, { children: msg.view.empty }),
52
+ /* @__PURE__ */ jsx(CommandGroup, { children: columns.map((column) => {
53
+ const isVisible = column.getIsVisible();
54
+ return /* @__PURE__ */ jsxs(
55
+ CommandItem,
56
+ {
57
+ value: column.columnDef.meta?.label ?? column.id,
58
+ onSelect: () => column.toggleVisibility(!isVisible),
59
+ children: [
60
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: column.columnDef.meta?.label ?? column.id }),
61
+ /* @__PURE__ */ jsx(Check, { className: cn("ms-auto size-4", isVisible ? "opacity-100" : "opacity-0") })
62
+ ]
63
+ },
64
+ column.id
65
+ );
66
+ }) })
67
+ ] })
68
+ ] }) })
69
+ ] });
70
+ }
71
+ export {
72
+ DataGridViewMenu
73
+ };
74
+ //# sourceMappingURL=data-grid-view-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-view-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { useDirection } from \"@radix-ui/react-direction\";\nimport type { Table } from \"@tanstack/react-table\";\nimport { Check, Settings2 } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList\n} from \"../components/ui/command.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../components/ui/popover.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useResolvedDataGridMessages } from \"./data-grid-locale.js\";\nimport type { DataGridLocale, DataGridMessages } from \"./messages.js\";\n\ninterface DataGridViewMenuProps<TData> extends React.ComponentProps<typeof PopoverContent> {\n table: Table<TData>;\n disabled?: boolean;\n /**\n * Ngôn ngữ của menu: `\"en\"` (mặc định) · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn.\n *\n * ⚠ Thanh công cụ nằm NGOÀI `<DataGrid>`, nên `locale` truyền cho lưới không với tới đây. Bọc\n * cả màn bằng `DataGridLocaleProvider` (một lần, đúng cho mọi mảnh), hoặc truyền prop này cho\n * từng menu. Prop thắng provider.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\n/** Ẩn/hiện cột của lưới. Cột muốn khoá luôn hiển thị thì đặt `enableHiding: false`. */\nexport function DataGridViewMenu<TData>({\n table,\n disabled,\n className,\n locale,\n ...props\n}: DataGridViewMenuProps<TData>) {\n const msg = useResolvedDataGridMessages(locale);\n const dir = useDirection();\n\n const columns = React.useMemo(\n () => table.getAllColumns().filter((column) => typeof column.accessorFn !== \"undefined\" && column.getCanHide()),\n [table]\n );\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n aria-label={msg.view.toggleColumns}\n role=\"combobox\"\n dir={dir}\n variant=\"outline\"\n size=\"sm\"\n className=\"font-normal\"\n disabled={disabled}\n >\n <Settings2 aria-hidden=\"true\" className=\"size-3.5 text-muted-foreground\" />\n {msg.view.trigger}\n </Button>\n </PopoverTrigger>\n <PopoverContent dir={dir} align=\"end\" className={cn(\"w-52 p-0\", className)} {...props}>\n <Command>\n <CommandInput placeholder={msg.view.searchPlaceholder} aria-label={msg.view.searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{msg.view.empty}</CommandEmpty>\n <CommandGroup>\n {columns.map((column) => {\n const isVisible = column.getIsVisible();\n\n return (\n <CommandItem\n key={column.id}\n value={column.columnDef.meta?.label ?? column.id}\n onSelect={() => column.toggleVisibility(!isVisible)}\n >\n <span className=\"truncate\">{column.columnDef.meta?.label ?? column.id}</span>\n <Check className={cn(\"ms-auto size-4\", isVisible ? \"opacity-100\" : \"opacity-0\")} />\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n"],"mappings":";AAoDQ,SASE,KATF;AAlDR,SAAS,oBAAoB;AAE7B,SAAS,OAAO,iBAAiB;AACjC,YAAY,WAAW;AACvB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,UAAU;AACnB,SAAS,mCAAmC;AAiBrC,SAAS,iBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiC;AAC/B,QAAM,MAAM,4BAA4B,MAAM;AAC9C,QAAM,MAAM,aAAa;AAEzB,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM,MAAM,cAAc,EAAE,OAAO,CAAC,WAAW,OAAO,OAAO,eAAe,eAAe,OAAO,WAAW,CAAC;AAAA,IAC9G,CAAC,KAAK;AAAA,EACR;AAEA,SACE,qBAAC,WACC;AAAA,wBAAC,kBAAe,SAAO,MACrB;AAAA,MAAC;AAAA;AAAA,QACC,cAAY,IAAI,KAAK;AAAA,QACrB,MAAK;AAAA,QACL;AAAA,QACA,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,WAAU;AAAA,QACV;AAAA,QAEA;AAAA,8BAAC,aAAU,eAAY,QAAO,WAAU,kCAAiC;AAAA,UACxE,IAAI,KAAK;AAAA;AAAA;AAAA,IACZ,GACF;AAAA,IACA,oBAAC,kBAAe,KAAU,OAAM,OAAM,WAAW,GAAG,YAAY,SAAS,GAAI,GAAG,OAC9E,+BAAC,WACC;AAAA,0BAAC,gBAAa,aAAa,IAAI,KAAK,mBAAmB,cAAY,IAAI,KAAK,mBAAmB;AAAA,MAC/F,qBAAC,eACC;AAAA,4BAAC,gBAAc,cAAI,KAAK,OAAM;AAAA,QAC9B,oBAAC,gBACE,kBAAQ,IAAI,CAAC,WAAW;AACvB,gBAAM,YAAY,OAAO,aAAa;AAEtC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,OAAO,OAAO,UAAU,MAAM,SAAS,OAAO;AAAA,cAC9C,UAAU,MAAM,OAAO,iBAAiB,CAAC,SAAS;AAAA,cAElD;AAAA,oCAAC,UAAK,WAAU,YAAY,iBAAO,UAAU,MAAM,SAAS,OAAO,IAAG;AAAA,gBACtE,oBAAC,SAAM,WAAW,GAAG,kBAAkB,YAAY,gBAAgB,WAAW,GAAG;AAAA;AAAA;AAAA,YAL5E,OAAO;AAAA,UAMd;AAAA,QAEJ,CAAC,GACH;AAAA,SACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { DataGridLocale } from './messages.js';
3
+ import { Direction } from './types.js';
4
+ import { useDataGrid } from './use-data-grid.js';
5
+ import { DataGridMessages } from './messages-en.js';
6
+ import './messages-vi.js';
7
+ import '@tanstack/react-table';
8
+ import '@tanstack/react-virtual';
9
+
10
+ interface DataGridProps<TData> extends Omit<ReturnType<typeof useDataGrid<TData>>, "dir" | "locale">, Omit<React.ComponentProps<"div">, "contextMenu"> {
11
+ dir?: Direction;
12
+ /**
13
+ * Chiều cao tối đa của hộp cuộn: số = px, chuỗi = giá trị CSS bất kỳ
14
+ * (vd `"100%"`, `"calc(100dvh - 220px)"` khi muốn lưới lấp đầy phần còn lại của trang).
15
+ */
16
+ height?: number | string;
17
+ /** Kéo giãn các cột cho vừa bề ngang khi tổng bề rộng cột nhỏ hơn khung */
18
+ stretchColumns?: boolean;
19
+ /**
20
+ * Ngôn ngữ của lưới: `"en"` (mặc định) · `"vi"`, hoặc cả một bảng chuỗi tự soạn.
21
+ *
22
+ * Thường nó đi theo kết quả của {@link useDataGrid} nên không phải truyền lại ở đây. Giá trị
23
+ * đặt thẳng lên `<DataGrid>` THẮNG giá trị từ hook — hữu ích khi một màn có hai lưới khác ngôn
24
+ * ngữ. Thanh công cụ nằm ngoài lưới nên nó KHÔNG nhận được giá trị này: bọc cả màn bằng
25
+ * `DataGridLocaleProvider`, hoặc truyền `locale` cho từng menu.
26
+ */
27
+ locale?: DataGridLocale | DataGridMessages;
28
+ }
29
+ /**
30
+ * Lưới dữ liệu sửa trực tiếp (như bảng tính): ảo hoá dòng, điều hướng bằng bàn phím,
31
+ * chọn vùng ô, sao chép/cắt/dán, ghim cột, đổi rộng cột.
32
+ *
33
+ * Nhận nguyên kết quả của {@link useDataGrid}:
34
+ * ```tsx
35
+ * const { table, ...dataGridProps } = useDataGrid({ data, columns, onDataChange });
36
+ * <DataGrid {...dataGridProps} table={table} aria-label="Danh sách bệnh nhân" />
37
+ * ```
38
+ *
39
+ * `aria-label` / `aria-labelledby` / `aria-describedby` được đặt lên đúng thẻ mang
40
+ * `role="grid"` (thẻ bọc ngoài chỉ là hộp bố cục, gắn nhãn lên đó trình đọc màn hình bỏ qua).
41
+ */
42
+ declare function DataGrid<TData>({ dataGridRef, headerRef, rowMapRef, footerRef, dir, table, tableMeta, virtualTotalSize, virtualItems, measureElement, columns, columnSizeVars, searchState, searchMatchesByRow, activeSearchMatch, cellSelectionMap, focusedCell, editingCell, rowHeight, contextMenu, pasteDialog, onRowAdd: onRowAddProp, height, stretchColumns, locale, adjustLayout, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, ...props }: DataGridProps<TData>): React.JSX.Element;
43
+
44
+ export { DataGrid };
@@ -0,0 +1,238 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Plus } from "lucide-react";
4
+ import * as React from "react";
5
+ import { TooltipProvider } from "../components/ui/tooltip.js";
6
+ import { cn } from "../lib/cn.js";
7
+ import { DataGridColumnHeader } from "./data-grid-column-header.js";
8
+ import { DataGridContextMenu } from "./data-grid-context-menu.js";
9
+ import { DataGridLocaleProvider } from "./data-grid-locale.js";
10
+ import { DataGridPasteDialog } from "./data-grid-paste-dialog.js";
11
+ import { DataGridRow } from "./data-grid-row.js";
12
+ import { DataGridSearch } from "./data-grid-search.js";
13
+ import { getDataGridMessages } from "./messages.js";
14
+ import { useAsRef } from "./use-as-ref.js";
15
+ import { flexRender, getColumnBorderVisibility, getColumnPinningStyle } from "./utils.js";
16
+ const EMPTY_CELL_SELECTION_SET = /* @__PURE__ */ new Set();
17
+ function DataGrid({
18
+ dataGridRef,
19
+ headerRef,
20
+ rowMapRef,
21
+ footerRef,
22
+ dir = "ltr",
23
+ table,
24
+ tableMeta,
25
+ virtualTotalSize,
26
+ virtualItems,
27
+ measureElement,
28
+ columns,
29
+ columnSizeVars,
30
+ searchState,
31
+ searchMatchesByRow,
32
+ activeSearchMatch,
33
+ cellSelectionMap,
34
+ focusedCell,
35
+ editingCell,
36
+ rowHeight,
37
+ contextMenu,
38
+ pasteDialog,
39
+ onRowAdd: onRowAddProp,
40
+ height = 600,
41
+ stretchColumns = false,
42
+ locale,
43
+ adjustLayout = false,
44
+ className,
45
+ "aria-label": ariaLabel,
46
+ "aria-labelledby": ariaLabelledby,
47
+ "aria-describedby": ariaDescribedby,
48
+ ...props
49
+ }) {
50
+ const rows = table.getRowModel().rows;
51
+ const readOnly = tableMeta?.readOnly ?? false;
52
+ const columnVisibility = table.getState().columnVisibility;
53
+ const columnPinning = table.getState().columnPinning;
54
+ const onRowAddRef = useAsRef(onRowAddProp);
55
+ const onRowAdd = React.useCallback(
56
+ (event) => {
57
+ onRowAddRef.current?.(event);
58
+ },
59
+ [onRowAddRef]
60
+ );
61
+ const onDataGridContextMenu = React.useCallback((event) => {
62
+ event.preventDefault();
63
+ }, []);
64
+ const onFooterCellKeyDown = React.useCallback(
65
+ (event) => {
66
+ if (!onRowAddRef.current) return;
67
+ if (event.key === "Enter" || event.key === " ") {
68
+ event.preventDefault();
69
+ onRowAddRef.current();
70
+ }
71
+ },
72
+ [onRowAddRef]
73
+ );
74
+ const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);
75
+ return /* @__PURE__ */ jsx(DataGridLocaleProvider, { locale, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs("div", { "data-slot": "grid-wrapper", dir, ...props, className: cn("relative flex w-full flex-col", className), children: [
76
+ searchState && /* @__PURE__ */ jsx(DataGridSearch, { ...searchState }),
77
+ /* @__PURE__ */ jsx(DataGridContextMenu, { tableMeta, columns, contextMenu }),
78
+ /* @__PURE__ */ jsx(DataGridPasteDialog, { tableMeta, pasteDialog }),
79
+ /* @__PURE__ */ jsxs(
80
+ "div",
81
+ {
82
+ role: "grid",
83
+ "aria-label": ariaLabelledby ? void 0 : ariaLabel ?? msg.grid.label,
84
+ "aria-labelledby": ariaLabelledby,
85
+ "aria-describedby": ariaDescribedby,
86
+ "aria-rowcount": rows.length + (onRowAddProp ? 1 : 0),
87
+ "aria-colcount": columns.length,
88
+ "data-slot": "grid",
89
+ tabIndex: 0,
90
+ ref: dataGridRef,
91
+ className: "relative grid select-none overflow-auto rounded-lg border bg-card focus:outline-none focus-visible:outline-none",
92
+ style: {
93
+ ...columnSizeVars,
94
+ maxHeight: typeof height === "number" ? `${height}px` : height
95
+ },
96
+ onContextMenu: onDataGridContextMenu,
97
+ children: [
98
+ /* @__PURE__ */ jsx(
99
+ "div",
100
+ {
101
+ role: "rowgroup",
102
+ "data-slot": "grid-header",
103
+ ref: headerRef,
104
+ className: "sticky top-0 z-10 grid border-b bg-card",
105
+ children: table.getHeaderGroups().map((headerGroup, rowIndex) => /* @__PURE__ */ jsx(
106
+ "div",
107
+ {
108
+ role: "row",
109
+ "aria-rowindex": rowIndex + 1,
110
+ "data-slot": "grid-header-row",
111
+ tabIndex: -1,
112
+ className: "flex w-full",
113
+ children: headerGroup.headers.map((header, colIndex) => {
114
+ const sorting = table.getState().sorting;
115
+ const currentSort = sorting.find((sort) => sort.id === header.column.id);
116
+ const isSortable = header.column.getCanSort();
117
+ const nextHeader = headerGroup.headers[colIndex + 1];
118
+ const isLastColumn = colIndex === headerGroup.headers.length - 1;
119
+ const { showEndBorder, showStartBorder } = getColumnBorderVisibility({
120
+ column: header.column,
121
+ nextColumn: nextHeader?.column,
122
+ isLastColumn
123
+ });
124
+ return /* @__PURE__ */ jsx(
125
+ "div",
126
+ {
127
+ role: "columnheader",
128
+ "aria-colindex": colIndex + 1,
129
+ "aria-sort": currentSort?.desc === false ? "ascending" : currentSort?.desc === true ? "descending" : isSortable ? "none" : void 0,
130
+ "data-slot": "grid-header-cell",
131
+ tabIndex: -1,
132
+ className: cn("relative", {
133
+ grow: stretchColumns && header.column.id !== "select",
134
+ "border-e": showEndBorder && header.column.id !== "select",
135
+ "border-s": showStartBorder && header.column.id !== "select"
136
+ }),
137
+ style: {
138
+ ...getColumnPinningStyle({ column: header.column, dir }),
139
+ width: `calc(var(--header-${header.id}-size) * 1px)`
140
+ },
141
+ children: header.isPlaceholder ? null : typeof header.column.columnDef.header === "function" ? /* @__PURE__ */ jsx("div", { className: "size-full px-3 py-1.5", children: flexRender(header.column.columnDef.header, header.getContext()) }) : /* @__PURE__ */ jsx(DataGridColumnHeader, { header, table })
142
+ },
143
+ header.id
144
+ );
145
+ })
146
+ },
147
+ headerGroup.id
148
+ ))
149
+ }
150
+ ),
151
+ /* @__PURE__ */ jsx(
152
+ "div",
153
+ {
154
+ role: "rowgroup",
155
+ "data-slot": "grid-body",
156
+ className: "relative grid",
157
+ style: {
158
+ height: `${virtualTotalSize}px`,
159
+ contain: adjustLayout ? "layout paint" : "strict"
160
+ },
161
+ children: virtualItems.map((virtualItem) => {
162
+ const row = rows[virtualItem.index];
163
+ if (!row) return null;
164
+ const cellSelectionKeys = cellSelectionMap?.get(virtualItem.index) ?? EMPTY_CELL_SELECTION_SET;
165
+ const searchMatchColumns = searchMatchesByRow?.get(virtualItem.index) ?? null;
166
+ const isActiveSearchRow = activeSearchMatch?.rowIndex === virtualItem.index;
167
+ return /* @__PURE__ */ jsx(
168
+ DataGridRow,
169
+ {
170
+ row,
171
+ tableMeta,
172
+ rowMapRef,
173
+ virtualItem,
174
+ measureElement,
175
+ rowHeight,
176
+ columnVisibility,
177
+ columnPinning,
178
+ focusedCell,
179
+ editingCell,
180
+ cellSelectionKeys,
181
+ searchMatchColumns,
182
+ activeSearchMatch: isActiveSearchRow ? activeSearchMatch : null,
183
+ dir,
184
+ adjustLayout,
185
+ stretchColumns,
186
+ readOnly
187
+ },
188
+ row.id
189
+ );
190
+ })
191
+ }
192
+ ),
193
+ !readOnly && onRowAddProp && /* @__PURE__ */ jsx(
194
+ "div",
195
+ {
196
+ role: "rowgroup",
197
+ "data-slot": "grid-footer",
198
+ ref: footerRef,
199
+ className: "sticky bottom-0 z-10 grid border-t bg-card",
200
+ children: /* @__PURE__ */ jsx(
201
+ "div",
202
+ {
203
+ role: "row",
204
+ "aria-rowindex": rows.length + 2,
205
+ "data-slot": "grid-add-row",
206
+ tabIndex: -1,
207
+ className: "flex w-full",
208
+ children: /* @__PURE__ */ jsx(
209
+ "div",
210
+ {
211
+ role: "gridcell",
212
+ tabIndex: 0,
213
+ className: "relative flex h-9 grow items-center bg-muted/30 transition-colors hover:bg-muted/50 focus:bg-muted/50 focus:outline-none focus-visible:outline-none",
214
+ style: {
215
+ width: table.getTotalSize(),
216
+ minWidth: table.getTotalSize()
217
+ },
218
+ onClick: onRowAdd,
219
+ onKeyDown: onFooterCellKeyDown,
220
+ children: /* @__PURE__ */ jsxs("div", { className: "sticky start-0 flex items-center gap-2 px-3 text-muted-foreground", children: [
221
+ /* @__PURE__ */ jsx(Plus, { className: "size-3.5" }),
222
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: msg.grid.addRow })
223
+ ] })
224
+ }
225
+ )
226
+ }
227
+ )
228
+ }
229
+ )
230
+ ]
231
+ }
232
+ )
233
+ ] }) }) });
234
+ }
235
+ export {
236
+ DataGrid
237
+ };
238
+ //# sourceMappingURL=data-grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-grid/data-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport { Plus } from \"lucide-react\";\nimport * as React from \"react\";\nimport { TooltipProvider } from \"../components/ui/tooltip.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { DataGridColumnHeader } from \"./data-grid-column-header.js\";\nimport { DataGridContextMenu } from \"./data-grid-context-menu.js\";\nimport { DataGridLocaleProvider } from \"./data-grid-locale.js\";\nimport { DataGridPasteDialog } from \"./data-grid-paste-dialog.js\";\nimport { DataGridRow } from \"./data-grid-row.js\";\nimport { DataGridSearch } from \"./data-grid-search.js\";\nimport { type DataGridLocale, type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport type { Direction } from \"./types.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport type { useDataGrid } from \"./use-data-grid.js\";\nimport { flexRender, getColumnBorderVisibility, getColumnPinningStyle } from \"./utils.js\";\n\nconst EMPTY_CELL_SELECTION_SET = new Set<string>();\n\ninterface DataGridProps<TData>\n extends Omit<ReturnType<typeof useDataGrid<TData>>, \"dir\" | \"locale\">,\n Omit<React.ComponentProps<\"div\">, \"contextMenu\"> {\n dir?: Direction;\n /**\n * Chiều cao tối đa của hộp cuộn: số = px, chuỗi = giá trị CSS bất kỳ\n * (vd `\"100%\"`, `\"calc(100dvh - 220px)\"` khi muốn lưới lấp đầy phần còn lại của trang).\n */\n height?: number | string;\n /** Kéo giãn các cột cho vừa bề ngang khi tổng bề rộng cột nhỏ hơn khung */\n stretchColumns?: boolean;\n /**\n * Ngôn ngữ của lưới: `\"en\"` (mặc định) · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn.\n *\n * Thường nó đi theo kết quả của {@link useDataGrid} nên không phải truyền lại ở đây. Giá trị\n * đặt thẳng lên `<DataGrid>` THẮNG giá trị từ hook — hữu ích khi một màn có hai lưới khác ngôn\n * ngữ. Thanh công cụ nằm ngoài lưới nên nó KHÔNG nhận được giá trị này: bọc cả màn bằng\n * `DataGridLocaleProvider`, hoặc truyền `locale` cho từng menu.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\n/**\n * Lưới dữ liệu sửa trực tiếp (như bảng tính): ảo hoá dòng, điều hướng bằng bàn phím,\n * chọn vùng ô, sao chép/cắt/dán, ghim cột, đổi rộng cột.\n *\n * Nhận nguyên kết quả của {@link useDataGrid}:\n * ```tsx\n * const { table, ...dataGridProps } = useDataGrid({ data, columns, onDataChange });\n * <DataGrid {...dataGridProps} table={table} aria-label=\"Danh sách bệnh nhân\" />\n * ```\n *\n * `aria-label` / `aria-labelledby` / `aria-describedby` được đặt lên đúng thẻ mang\n * `role=\"grid\"` (thẻ bọc ngoài chỉ là hộp bố cục, gắn nhãn lên đó trình đọc màn hình bỏ qua).\n */\nexport function DataGrid<TData>({\n dataGridRef,\n headerRef,\n rowMapRef,\n footerRef,\n dir = \"ltr\",\n table,\n tableMeta,\n virtualTotalSize,\n virtualItems,\n measureElement,\n columns,\n columnSizeVars,\n searchState,\n searchMatchesByRow,\n activeSearchMatch,\n cellSelectionMap,\n focusedCell,\n editingCell,\n rowHeight,\n contextMenu,\n pasteDialog,\n onRowAdd: onRowAddProp,\n height = 600,\n stretchColumns = false,\n locale,\n adjustLayout = false,\n className,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-describedby\": ariaDescribedby,\n ...props\n}: DataGridProps<TData>) {\n const rows = table.getRowModel().rows;\n const readOnly = tableMeta?.readOnly ?? false;\n const columnVisibility = table.getState().columnVisibility;\n const columnPinning = table.getState().columnPinning;\n\n const onRowAddRef = useAsRef(onRowAddProp);\n\n const onRowAdd = React.useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n onRowAddRef.current?.(event);\n },\n [onRowAddRef]\n );\n\n const onDataGridContextMenu = React.useCallback((event: React.MouseEvent<HTMLDivElement>) => {\n event.preventDefault();\n }, []);\n\n const onFooterCellKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (!onRowAddRef.current) return;\n\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onRowAddRef.current();\n }\n },\n [onRowAddRef]\n );\n\n const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);\n\n return (\n <DataGridLocaleProvider locale={locale}>\n <TooltipProvider delayDuration={200}>\n <div data-slot=\"grid-wrapper\" dir={dir} {...props} className={cn(\"relative flex w-full flex-col\", className)}>\n {searchState && <DataGridSearch {...searchState} />}\n <DataGridContextMenu tableMeta={tableMeta} columns={columns} contextMenu={contextMenu} />\n <DataGridPasteDialog tableMeta={tableMeta} pasteDialog={pasteDialog} />\n <div\n role=\"grid\"\n aria-label={ariaLabelledby ? undefined : (ariaLabel ?? msg.grid.label)}\n aria-labelledby={ariaLabelledby}\n aria-describedby={ariaDescribedby}\n aria-rowcount={rows.length + (onRowAddProp ? 1 : 0)}\n aria-colcount={columns.length}\n data-slot=\"grid\"\n tabIndex={0}\n ref={dataGridRef}\n className=\"relative grid select-none overflow-auto rounded-lg border bg-card focus:outline-none focus-visible:outline-none\"\n style={{\n ...columnSizeVars,\n maxHeight: typeof height === \"number\" ? `${height}px` : height\n }}\n onContextMenu={onDataGridContextMenu}\n >\n <div\n role=\"rowgroup\"\n data-slot=\"grid-header\"\n ref={headerRef}\n className=\"sticky top-0 z-10 grid border-b bg-card\"\n >\n {table.getHeaderGroups().map((headerGroup, rowIndex) => (\n <div\n key={headerGroup.id}\n role=\"row\"\n aria-rowindex={rowIndex + 1}\n data-slot=\"grid-header-row\"\n tabIndex={-1}\n className=\"flex w-full\"\n >\n {headerGroup.headers.map((header, colIndex) => {\n const sorting = table.getState().sorting;\n const currentSort = sorting.find((sort) => sort.id === header.column.id);\n const isSortable = header.column.getCanSort();\n\n const nextHeader = headerGroup.headers[colIndex + 1];\n const isLastColumn = colIndex === headerGroup.headers.length - 1;\n\n const { showEndBorder, showStartBorder } = getColumnBorderVisibility({\n column: header.column,\n nextColumn: nextHeader?.column,\n isLastColumn\n });\n\n return (\n <div\n key={header.id}\n role=\"columnheader\"\n aria-colindex={colIndex + 1}\n aria-sort={\n currentSort?.desc === false\n ? \"ascending\"\n : currentSort?.desc === true\n ? \"descending\"\n : isSortable\n ? \"none\"\n : undefined\n }\n data-slot=\"grid-header-cell\"\n tabIndex={-1}\n className={cn(\"relative\", {\n grow: stretchColumns && header.column.id !== \"select\",\n \"border-e\": showEndBorder && header.column.id !== \"select\",\n \"border-s\": showStartBorder && header.column.id !== \"select\"\n })}\n style={{\n ...getColumnPinningStyle({ column: header.column, dir }),\n width: `calc(var(--header-${header.id}-size) * 1px)`\n }}\n >\n {header.isPlaceholder ? null : typeof header.column.columnDef.header === \"function\" ? (\n <div className=\"size-full px-3 py-1.5\">\n {flexRender(header.column.columnDef.header, header.getContext())}\n </div>\n ) : (\n <DataGridColumnHeader header={header} table={table} />\n )}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n <div\n role=\"rowgroup\"\n data-slot=\"grid-body\"\n className=\"relative grid\"\n style={{\n height: `${virtualTotalSize}px`,\n contain: adjustLayout ? \"layout paint\" : \"strict\"\n }}\n >\n {virtualItems.map((virtualItem) => {\n const row = rows[virtualItem.index];\n if (!row) return null;\n\n const cellSelectionKeys = cellSelectionMap?.get(virtualItem.index) ?? EMPTY_CELL_SELECTION_SET;\n\n const searchMatchColumns = searchMatchesByRow?.get(virtualItem.index) ?? null;\n const isActiveSearchRow = activeSearchMatch?.rowIndex === virtualItem.index;\n\n return (\n <DataGridRow\n key={row.id}\n row={row}\n tableMeta={tableMeta}\n rowMapRef={rowMapRef}\n virtualItem={virtualItem}\n measureElement={measureElement}\n rowHeight={rowHeight}\n columnVisibility={columnVisibility}\n columnPinning={columnPinning}\n focusedCell={focusedCell}\n editingCell={editingCell}\n cellSelectionKeys={cellSelectionKeys}\n searchMatchColumns={searchMatchColumns}\n activeSearchMatch={isActiveSearchRow ? activeSearchMatch : null}\n dir={dir}\n adjustLayout={adjustLayout}\n stretchColumns={stretchColumns}\n readOnly={readOnly}\n />\n );\n })}\n </div>\n {/* Chỉ hiện dòng \"Thêm dòng\" khi ứng dụng thật sự truyền onRowAdd\n (biến `onRowAdd` bên dưới là callback bọc, luôn khác undefined). */}\n {!readOnly && onRowAddProp && (\n <div\n role=\"rowgroup\"\n data-slot=\"grid-footer\"\n ref={footerRef}\n className=\"sticky bottom-0 z-10 grid border-t bg-card\"\n >\n <div\n role=\"row\"\n aria-rowindex={rows.length + 2}\n data-slot=\"grid-add-row\"\n tabIndex={-1}\n className=\"flex w-full\"\n >\n <div\n role=\"gridcell\"\n tabIndex={0}\n className=\"relative flex h-9 grow items-center bg-muted/30 transition-colors hover:bg-muted/50 focus:bg-muted/50 focus:outline-none focus-visible:outline-none\"\n style={{\n width: table.getTotalSize(),\n minWidth: table.getTotalSize()\n }}\n onClick={onRowAdd}\n onKeyDown={onFooterCellKeyDown}\n >\n <div className=\"sticky start-0 flex items-center gap-2 px-3 text-muted-foreground\">\n <Plus className=\"size-3.5\" />\n <span className=\"text-sm\">{msg.grid.addRow}</span>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n </TooltipProvider>\n </DataGridLocaleProvider>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,27 @@
1
+ import { FilterFn } from '@tanstack/react-table';
2
+ import { DataGridMessages } from './messages-en.js';
3
+ import { FilterOperator, BooleanFilterOperator, DateFilterOperator, NumberFilterOperator, SelectFilterOperator, TextFilterOperator } from './types.js';
4
+
5
+ interface OperatorOption<TOperator extends FilterOperator> {
6
+ label: string;
7
+ value: TOperator;
8
+ }
9
+ declare const TEXT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<TextFilterOperator>>;
10
+ declare const NUMBER_FILTER_OPERATORS: ReadonlyArray<OperatorOption<NumberFilterOperator>>;
11
+ declare const DATE_FILTER_OPERATORS: ReadonlyArray<OperatorOption<DateFilterOperator>>;
12
+ declare const SELECT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<SelectFilterOperator>>;
13
+ declare const BOOLEAN_FILTER_OPERATORS: ReadonlyArray<OperatorOption<BooleanFilterOperator>>;
14
+ declare function getDefaultOperator(variant: string): FilterOperator;
15
+ /**
16
+ * Toán tử hợp lệ cho một kiểu ô, kèm nhãn đã dịch.
17
+ *
18
+ * `messages` bỏ trống = bảng mặc định (tiếng Anh) — giữ đúng chữ ký của `1.11.0` nên chỗ gọi cũ
19
+ * không phải sửa.
20
+ */
21
+ declare function getOperatorsForVariant(variant: string, messages?: DataGridMessages): ReadonlyArray<{
22
+ label: string;
23
+ value: FilterOperator;
24
+ }>;
25
+ declare function getFilterFn<TData>(): FilterFn<TData>;
26
+
27
+ export { BOOLEAN_FILTER_OPERATORS, DATE_FILTER_OPERATORS, NUMBER_FILTER_OPERATORS, SELECT_FILTER_OPERATORS, TEXT_FILTER_OPERATORS, getDefaultOperator, getFilterFn, getOperatorsForVariant };
@@ -0,0 +1,233 @@
1
+ import { getDataGridMessages } from "./messages.js";
2
+ function buildTextOperators(m) {
3
+ const op = m.filter.operators;
4
+ return [
5
+ { label: op.contains, value: "contains" },
6
+ { label: op.notContains, value: "notContains" },
7
+ { label: op.equals, value: "equals" },
8
+ { label: op.notEquals, value: "notEquals" },
9
+ { label: op.startsWith, value: "startsWith" },
10
+ { label: op.endsWith, value: "endsWith" },
11
+ { label: op.isEmpty, value: "isEmpty" },
12
+ { label: op.isNotEmpty, value: "isNotEmpty" }
13
+ ];
14
+ }
15
+ function buildNumberOperators(m) {
16
+ const op = m.filter.operators;
17
+ return [
18
+ { label: op.equals, value: "equals" },
19
+ { label: op.notEquals, value: "notEquals" },
20
+ { label: op.lessThan, value: "lessThan" },
21
+ { label: op.lessThanOrEqual, value: "lessThanOrEqual" },
22
+ { label: op.greaterThan, value: "greaterThan" },
23
+ { label: op.greaterThanOrEqual, value: "greaterThanOrEqual" },
24
+ { label: op.isBetween, value: "isBetween" },
25
+ { label: op.isEmpty, value: "isEmpty" },
26
+ { label: op.isNotEmpty, value: "isNotEmpty" }
27
+ ];
28
+ }
29
+ function buildDateOperators(m) {
30
+ const op = m.filter.operators;
31
+ return [
32
+ { label: op.dateEquals, value: "equals" },
33
+ { label: op.dateNotEquals, value: "notEquals" },
34
+ { label: op.before, value: "before" },
35
+ { label: op.after, value: "after" },
36
+ { label: op.onOrBefore, value: "onOrBefore" },
37
+ { label: op.onOrAfter, value: "onOrAfter" },
38
+ { label: op.isBetween, value: "isBetween" },
39
+ { label: op.isEmpty, value: "isEmpty" },
40
+ { label: op.isNotEmpty, value: "isNotEmpty" }
41
+ ];
42
+ }
43
+ function buildSelectOperators(m) {
44
+ const op = m.filter.operators;
45
+ return [
46
+ { label: op.is, value: "is" },
47
+ { label: op.isNot, value: "isNot" },
48
+ { label: op.isAnyOf, value: "isAnyOf" },
49
+ { label: op.isNoneOf, value: "isNoneOf" },
50
+ { label: op.isEmpty, value: "isEmpty" },
51
+ { label: op.isNotEmpty, value: "isNotEmpty" }
52
+ ];
53
+ }
54
+ function buildBooleanOperators(m) {
55
+ const op = m.filter.operators;
56
+ return [
57
+ { label: op.isTrue, value: "isTrue" },
58
+ { label: op.isFalse, value: "isFalse" }
59
+ ];
60
+ }
61
+ const DEFAULT_MESSAGES = getDataGridMessages();
62
+ const TEXT_FILTER_OPERATORS = buildTextOperators(DEFAULT_MESSAGES);
63
+ const NUMBER_FILTER_OPERATORS = buildNumberOperators(DEFAULT_MESSAGES);
64
+ const DATE_FILTER_OPERATORS = buildDateOperators(DEFAULT_MESSAGES);
65
+ const SELECT_FILTER_OPERATORS = buildSelectOperators(DEFAULT_MESSAGES);
66
+ const BOOLEAN_FILTER_OPERATORS = buildBooleanOperators(DEFAULT_MESSAGES);
67
+ function getDefaultOperator(variant) {
68
+ switch (variant) {
69
+ case "number":
70
+ return "equals";
71
+ case "date":
72
+ return "equals";
73
+ case "select":
74
+ case "multi-select":
75
+ return "is";
76
+ case "checkbox":
77
+ return "isTrue";
78
+ default:
79
+ return "contains";
80
+ }
81
+ }
82
+ function getOperatorsForVariant(variant, messages) {
83
+ const m = messages ?? DEFAULT_MESSAGES;
84
+ switch (variant) {
85
+ case "number":
86
+ return buildNumberOperators(m);
87
+ case "date":
88
+ return buildDateOperators(m);
89
+ case "select":
90
+ case "multi-select":
91
+ return buildSelectOperators(m);
92
+ case "checkbox":
93
+ return buildBooleanOperators(m);
94
+ default:
95
+ return buildTextOperators(m);
96
+ }
97
+ }
98
+ function getFilterFn() {
99
+ return (row, columnId, filterValue) => {
100
+ if (!filterValue || typeof filterValue !== "object") {
101
+ return true;
102
+ }
103
+ const filter = filterValue;
104
+ const { operator, value, endValue } = filter;
105
+ const cellValue = row.getValue(columnId);
106
+ if (operator === "isEmpty") {
107
+ return cellValue === null || cellValue === void 0 || cellValue === "" || Array.isArray(cellValue) && cellValue.length === 0;
108
+ }
109
+ if (operator === "isNotEmpty") {
110
+ return !(cellValue === null || cellValue === void 0 || cellValue === "" || Array.isArray(cellValue) && cellValue.length === 0);
111
+ }
112
+ if (operator === "isTrue") {
113
+ return cellValue === true;
114
+ }
115
+ if (operator === "isFalse") {
116
+ return cellValue === false || !cellValue;
117
+ }
118
+ if (value === void 0 || value === null || value === "") {
119
+ return true;
120
+ }
121
+ const cellValueStr = String(cellValue ?? "").toLowerCase();
122
+ const filterValueStr = typeof value === "string" ? value.toLowerCase() : String(value);
123
+ if (operator === "contains") {
124
+ return cellValueStr.includes(filterValueStr);
125
+ }
126
+ if (operator === "notContains") {
127
+ return !cellValueStr.includes(filterValueStr);
128
+ }
129
+ if (operator === "equals") {
130
+ if (typeof cellValue === "number" && typeof value === "number") {
131
+ return cellValue === value;
132
+ }
133
+ if (cellValue instanceof Date && typeof value === "string") {
134
+ const cellDate = new Date(cellValue);
135
+ const filterDate = new Date(value);
136
+ return cellDate.toDateString() === filterDate.toDateString();
137
+ }
138
+ return cellValueStr === filterValueStr;
139
+ }
140
+ if (operator === "notEquals") {
141
+ if (typeof cellValue === "number" && typeof value === "number") {
142
+ return cellValue !== value;
143
+ }
144
+ if (cellValue instanceof Date && typeof value === "string") {
145
+ const cellDate = new Date(cellValue);
146
+ const filterDate = new Date(value);
147
+ return cellDate.toDateString() !== filterDate.toDateString();
148
+ }
149
+ return cellValueStr !== filterValueStr;
150
+ }
151
+ if (operator === "startsWith") {
152
+ return cellValueStr.startsWith(filterValueStr);
153
+ }
154
+ if (operator === "endsWith") {
155
+ return cellValueStr.endsWith(filterValueStr);
156
+ }
157
+ if (typeof cellValue === "number" && typeof value === "number") {
158
+ if (operator === "greaterThan") {
159
+ return cellValue > value;
160
+ }
161
+ if (operator === "greaterThanOrEqual") {
162
+ return cellValue >= value;
163
+ }
164
+ if (operator === "lessThan") {
165
+ return cellValue < value;
166
+ }
167
+ if (operator === "lessThanOrEqual") {
168
+ return cellValue <= value;
169
+ }
170
+ if (operator === "isBetween" && typeof endValue === "number") {
171
+ return cellValue >= value && cellValue <= endValue;
172
+ }
173
+ }
174
+ if (cellValue instanceof Date || typeof cellValue === "string") {
175
+ const cellDate = new Date(cellValue);
176
+ if (!Number.isNaN(cellDate.getTime()) && typeof value === "string") {
177
+ const filterDate = new Date(value);
178
+ if (operator === "before") {
179
+ return cellDate < filterDate;
180
+ }
181
+ if (operator === "after") {
182
+ return cellDate > filterDate;
183
+ }
184
+ if (operator === "onOrBefore") {
185
+ return cellDate <= filterDate;
186
+ }
187
+ if (operator === "onOrAfter") {
188
+ return cellDate >= filterDate;
189
+ }
190
+ if (operator === "isBetween" && typeof endValue === "string") {
191
+ const filterDate2 = new Date(endValue);
192
+ return cellDate >= filterDate && cellDate <= filterDate2;
193
+ }
194
+ }
195
+ }
196
+ if (operator === "is") {
197
+ if (Array.isArray(cellValue)) {
198
+ return cellValue.some((v) => String(v) === String(value));
199
+ }
200
+ return String(cellValue) === String(value);
201
+ }
202
+ if (operator === "isNot") {
203
+ if (Array.isArray(cellValue)) {
204
+ return !cellValue.some((v) => String(v) === String(value));
205
+ }
206
+ return String(cellValue) !== String(value);
207
+ }
208
+ if (operator === "isAnyOf" && Array.isArray(value)) {
209
+ if (Array.isArray(cellValue)) {
210
+ return cellValue.some((v) => value.some((fv) => String(v) === String(fv)));
211
+ }
212
+ return value.some((fv) => String(cellValue) === String(fv));
213
+ }
214
+ if (operator === "isNoneOf" && Array.isArray(value)) {
215
+ if (Array.isArray(cellValue)) {
216
+ return !cellValue.some((v) => value.some((fv) => String(v) === String(fv)));
217
+ }
218
+ return !value.some((fv) => String(cellValue) === String(fv));
219
+ }
220
+ return true;
221
+ };
222
+ }
223
+ export {
224
+ BOOLEAN_FILTER_OPERATORS,
225
+ DATE_FILTER_OPERATORS,
226
+ NUMBER_FILTER_OPERATORS,
227
+ SELECT_FILTER_OPERATORS,
228
+ TEXT_FILTER_OPERATORS,
229
+ getDefaultOperator,
230
+ getFilterFn,
231
+ getOperatorsForVariant
232
+ };
233
+ //# sourceMappingURL=filters.js.map