@comitor/ui 1.10.0 → 1.11.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 (101) hide show
  1. package/README.md +15 -4
  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 +1734 -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 +205 -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 +160 -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 +12 -0
  21. package/dist/data-grid/data-grid-filter-menu.js +714 -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 +20 -0
  24. package/dist/data-grid/data-grid-keyboard-shortcuts.js +227 -0
  25. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -0
  26. package/dist/data-grid/data-grid-paste-dialog.d.ts +11 -0
  27. package/dist/data-grid/data-grid-paste-dialog.js +103 -0
  28. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -0
  29. package/dist/data-grid/data-grid-presence.d.ts +14 -0
  30. package/dist/data-grid/data-grid-presence.js +16 -0
  31. package/dist/data-grid/data-grid-presence.js.map +1 -0
  32. package/dist/data-grid/data-grid-row-height-menu.d.ts +15 -0
  33. package/dist/data-grid/data-grid-row-height-menu.js +56 -0
  34. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -0
  35. package/dist/data-grid/data-grid-row.d.ts +28 -0
  36. package/dist/data-grid/data-grid-row.js +206 -0
  37. package/dist/data-grid/data-grid-row.js.map +1 -0
  38. package/dist/data-grid/data-grid-search.d.ts +10 -0
  39. package/dist/data-grid/data-grid-search.js +183 -0
  40. package/dist/data-grid/data-grid-search.js.map +1 -0
  41. package/dist/data-grid/data-grid-select-column.d.ts +12 -0
  42. package/dist/data-grid/data-grid-select-column.js +177 -0
  43. package/dist/data-grid/data-grid-select-column.js.map +1 -0
  44. package/dist/data-grid/data-grid-skeleton.d.ts +13 -0
  45. package/dist/data-grid/data-grid-skeleton.js +52 -0
  46. package/dist/data-grid/data-grid-skeleton.js.map +1 -0
  47. package/dist/data-grid/data-grid-sort-menu.d.ts +12 -0
  48. package/dist/data-grid/data-grid-sort-menu.js +271 -0
  49. package/dist/data-grid/data-grid-sort-menu.js.map +1 -0
  50. package/dist/data-grid/data-grid-view-menu.d.ts +13 -0
  51. package/dist/data-grid/data-grid-view-menu.js +66 -0
  52. package/dist/data-grid/data-grid-view-menu.js.map +1 -0
  53. package/dist/data-grid/data-grid.d.ts +32 -0
  54. package/dist/data-grid/data-grid.js +235 -0
  55. package/dist/data-grid/data-grid.js.map +1 -0
  56. package/dist/data-grid/filters.d.ts +31 -0
  57. package/dist/data-grid/filters.js +212 -0
  58. package/dist/data-grid/filters.js.map +1 -0
  59. package/dist/data-grid/index.d.ts +22 -0
  60. package/dist/data-grid/index.js +77 -0
  61. package/dist/data-grid/index.js.map +1 -0
  62. package/dist/data-grid/messages.d.ts +280 -0
  63. package/dist/data-grid/messages.js +279 -0
  64. package/dist/data-grid/messages.js.map +1 -0
  65. package/dist/data-grid/sortable.d.ts +50 -0
  66. package/dist/data-grid/sortable.js +361 -0
  67. package/dist/data-grid/sortable.js.map +1 -0
  68. package/dist/data-grid/toast.d.ts +10 -0
  69. package/dist/data-grid/toast.js +21 -0
  70. package/dist/data-grid/toast.js.map +1 -0
  71. package/dist/data-grid/types.d.ts +189 -0
  72. package/dist/data-grid/types.js +1 -0
  73. package/dist/data-grid/types.js.map +1 -0
  74. package/dist/data-grid/use-as-ref.d.ts +5 -0
  75. package/dist/data-grid/use-as-ref.js +13 -0
  76. package/dist/data-grid/use-as-ref.js.map +1 -0
  77. package/dist/data-grid/use-badge-overflow.d.ts +24 -0
  78. package/dist/data-grid/use-badge-overflow.js +123 -0
  79. package/dist/data-grid/use-badge-overflow.js.map +1 -0
  80. package/dist/data-grid/use-callback-ref.d.ts +10 -0
  81. package/dist/data-grid/use-callback-ref.js +12 -0
  82. package/dist/data-grid/use-callback-ref.js.map +1 -0
  83. package/dist/data-grid/use-data-grid-undo-redo.d.ts +33 -0
  84. package/dist/data-grid/use-data-grid-undo-redo.js +345 -0
  85. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -0
  86. package/dist/data-grid/use-data-grid.d.ts +92 -0
  87. package/dist/data-grid/use-data-grid.js +2692 -0
  88. package/dist/data-grid/use-data-grid.js.map +1 -0
  89. package/dist/data-grid/use-debounced-callback.d.ts +6 -0
  90. package/dist/data-grid/use-debounced-callback.js +19 -0
  91. package/dist/data-grid/use-debounced-callback.js.map +1 -0
  92. package/dist/data-grid/use-isomorphic-layout-effect.d.ts +5 -0
  93. package/dist/data-grid/use-isomorphic-layout-effect.js +6 -0
  94. package/dist/data-grid/use-isomorphic-layout-effect.js.map +1 -0
  95. package/dist/data-grid/use-lazy-ref.d.ts +5 -0
  96. package/dist/data-grid/use-lazy-ref.js +12 -0
  97. package/dist/data-grid/use-lazy-ref.js.map +1 -0
  98. package/dist/data-grid/utils.d.ts +73 -0
  99. package/dist/data-grid/utils.js +408 -0
  100. package/dist/data-grid/utils.js.map +1 -0
  101. package/package.json +33 -15
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { AlignVerticalSpaceAroundIcon, ChevronsDownUpIcon, EqualIcon, MinusIcon } from "lucide-react";
4
+ import * as React from "react";
5
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select.js";
6
+ import { cn } from "../lib/cn.js";
7
+ import { msg } from "./messages.js";
8
+ const rowHeights = [
9
+ {
10
+ label: msg.rowHeight.short,
11
+ value: "short",
12
+ icon: MinusIcon
13
+ },
14
+ {
15
+ label: msg.rowHeight.medium,
16
+ value: "medium",
17
+ icon: EqualIcon
18
+ },
19
+ {
20
+ label: msg.rowHeight.tall,
21
+ value: "tall",
22
+ icon: AlignVerticalSpaceAroundIcon
23
+ },
24
+ {
25
+ label: msg.rowHeight.extraTall,
26
+ value: "extra-tall",
27
+ icon: ChevronsDownUpIcon
28
+ }
29
+ ];
30
+ function DataGridRowHeightMenu({
31
+ table,
32
+ disabled,
33
+ triggerClassName,
34
+ ...props
35
+ }) {
36
+ const rowHeight = table.options.meta?.rowHeight;
37
+ const onRowHeightChange = table.options.meta?.onRowHeightChange;
38
+ const selectedRowHeight = React.useMemo(
39
+ () => rowHeights.find((option) => option.value === rowHeight) ?? rowHeights[0],
40
+ [rowHeight]
41
+ );
42
+ return /* @__PURE__ */ jsxs(Select, { value: rowHeight, onValueChange: onRowHeightChange, disabled, children: [
43
+ /* @__PURE__ */ jsx(SelectTrigger, { "aria-label": msg.rowHeight.label, className: cn("h-8 w-auto gap-2 font-normal", triggerClassName), children: /* @__PURE__ */ jsx(SelectValue, { placeholder: msg.rowHeight.label, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
44
+ /* @__PURE__ */ jsx(selectedRowHeight.icon, { "aria-hidden": "true", className: "size-4 text-muted-foreground" }),
45
+ selectedRowHeight.label
46
+ ] }) }) }),
47
+ /* @__PURE__ */ jsx(SelectContent, { ...props, children: /* @__PURE__ */ jsx(SelectGroup, { children: rowHeights.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
48
+ /* @__PURE__ */ jsx(option.icon, { "aria-hidden": "true", className: "size-4 text-muted-foreground" }),
49
+ option.label
50
+ ] }) }, option.value)) }) })
51
+ ] });
52
+ }
53
+ export {
54
+ DataGridRowHeightMenu
55
+ };
56
+ //# sourceMappingURL=data-grid-row-height-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-row-height-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport type { Table } from \"@tanstack/react-table\";\nimport { AlignVerticalSpaceAroundIcon, ChevronsDownUpIcon, EqualIcon, MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from \"../components/ui/select.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { msg } from \"./messages.js\";\n\nconst rowHeights = [\n {\n label: msg.rowHeight.short,\n value: \"short\" as const,\n icon: MinusIcon\n },\n {\n label: msg.rowHeight.medium,\n value: \"medium\" as const,\n icon: EqualIcon\n },\n {\n label: msg.rowHeight.tall,\n value: \"tall\" as const,\n icon: AlignVerticalSpaceAroundIcon\n },\n {\n label: msg.rowHeight.extraTall,\n value: \"extra-tall\" as const,\n icon: ChevronsDownUpIcon\n }\n] as const;\n\ninterface DataGridRowHeightMenuProps<TData> extends React.ComponentProps<typeof SelectContent> {\n table: Table<TData>;\n disabled?: boolean;\n /** className cho nút mở menu (mặc định cao 8 như các nút khác trên thanh công cụ) */\n triggerClassName?: string;\n}\n\n/** Chọn chiều cao dòng của lưới (thấp/vừa/cao/rất cao) — đặt trên thanh công cụ. */\nexport function DataGridRowHeightMenu<TData>({\n table,\n disabled,\n triggerClassName,\n ...props\n}: DataGridRowHeightMenuProps<TData>) {\n const rowHeight = table.options.meta?.rowHeight;\n const onRowHeightChange = table.options.meta?.onRowHeightChange;\n\n const selectedRowHeight = React.useMemo(\n () => rowHeights.find((option) => option.value === rowHeight) ?? rowHeights[0],\n [rowHeight]\n );\n\n return (\n <Select value={rowHeight} onValueChange={onRowHeightChange} disabled={disabled}>\n <SelectTrigger aria-label={msg.rowHeight.label} className={cn(\"h-8 w-auto gap-2 font-normal\", triggerClassName)}>\n <SelectValue placeholder={msg.rowHeight.label}>\n <span className=\"flex items-center gap-2\">\n <selectedRowHeight.icon aria-hidden=\"true\" className=\"size-4 text-muted-foreground\" />\n {selectedRowHeight.label}\n </span>\n </SelectValue>\n </SelectTrigger>\n <SelectContent {...props}>\n <SelectGroup>\n {rowHeights.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <span className=\"flex items-center gap-2\">\n <option.icon aria-hidden=\"true\" className=\"size-4 text-muted-foreground\" />\n {option.label}\n </span>\n </SelectItem>\n ))}\n </SelectGroup>\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":";AA0DU,SACE,KADF;AAvDV,SAAS,8BAA8B,oBAAoB,WAAW,iBAAiB;AACvF,YAAY,WAAW;AACvB,SAAS,QAAQ,eAAe,aAAa,YAAY,eAAe,mBAAmB;AAC3F,SAAS,UAAU;AACnB,SAAS,WAAW;AAEpB,MAAM,aAAa;AAAA,EACjB;AAAA,IACE,OAAO,IAAI,UAAU;AAAA,IACrB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO,IAAI,UAAU;AAAA,IACrB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO,IAAI,UAAU;AAAA,IACrB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,OAAO,IAAI,UAAU;AAAA,IACrB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AACF;AAUO,SAAS,sBAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsC;AACpC,QAAM,YAAY,MAAM,QAAQ,MAAM;AACtC,QAAM,oBAAoB,MAAM,QAAQ,MAAM;AAE9C,QAAM,oBAAoB,MAAM;AAAA,IAC9B,MAAM,WAAW,KAAK,CAAC,WAAW,OAAO,UAAU,SAAS,KAAK,WAAW,CAAC;AAAA,IAC7E,CAAC,SAAS;AAAA,EACZ;AAEA,SACE,qBAAC,UAAO,OAAO,WAAW,eAAe,mBAAmB,UAC1D;AAAA,wBAAC,iBAAc,cAAY,IAAI,UAAU,OAAO,WAAW,GAAG,gCAAgC,gBAAgB,GAC5G,8BAAC,eAAY,aAAa,IAAI,UAAU,OACtC,+BAAC,UAAK,WAAU,2BACd;AAAA,0BAAC,kBAAkB,MAAlB,EAAuB,eAAY,QAAO,WAAU,gCAA+B;AAAA,MACnF,kBAAkB;AAAA,OACrB,GACF,GACF;AAAA,IACA,oBAAC,iBAAe,GAAG,OACjB,8BAAC,eACE,qBAAW,IAAI,CAAC,WACf,oBAAC,cAA8B,OAAO,OAAO,OAC3C,+BAAC,UAAK,WAAU,2BACd;AAAA,0BAAC,OAAO,MAAP,EAAY,eAAY,QAAO,WAAU,gCAA+B;AAAA,MACxE,OAAO;AAAA,OACV,KAJe,OAAO,KAKxB,CACD,GACH,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -0,0 +1,28 @@
1
+ import { Row, TableMeta, VisibilityState, ColumnPinningState } from '@tanstack/react-table';
2
+ import { VirtualItem } from '@tanstack/react-virtual';
3
+ import * as React from 'react';
4
+ import { RowHeightValue, CellPosition, Direction } from './types.js';
5
+
6
+ interface DataGridRowProps<TData> extends React.ComponentProps<"div"> {
7
+ row: Row<TData>;
8
+ tableMeta: TableMeta<TData>;
9
+ virtualItem: VirtualItem;
10
+ measureElement: (node: Element | null) => void;
11
+ rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;
12
+ rowHeight: RowHeightValue;
13
+ columnVisibility: VisibilityState;
14
+ columnPinning: ColumnPinningState;
15
+ focusedCell: CellPosition | null;
16
+ editingCell: CellPosition | null;
17
+ cellSelectionKeys: Set<string>;
18
+ searchMatchColumns: Set<string> | null;
19
+ activeSearchMatch: CellPosition | null;
20
+ dir: Direction;
21
+ readOnly: boolean;
22
+ stretchColumns: boolean;
23
+ adjustLayout: boolean;
24
+ }
25
+ declare const DataGridRow: typeof DataGridRowImpl;
26
+ declare function DataGridRowImpl<TData>({ row, tableMeta, virtualItem, measureElement, rowMapRef, rowHeight, columnVisibility, columnPinning, focusedCell, editingCell, cellSelectionKeys, searchMatchColumns, activeSearchMatch, dir, readOnly, stretchColumns, adjustLayout, className, style, ref, ...props }: DataGridRowProps<TData>): React.JSX.Element;
27
+
28
+ export { DataGridRow };
@@ -0,0 +1,206 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { useComposedRefs } from "./compose-refs.js";
6
+ import { DataGridCell } from "./data-grid-cell.js";
7
+ import {
8
+ flexRender,
9
+ getCellKey,
10
+ getColumnBorderVisibility,
11
+ getColumnPinningStyle,
12
+ getRowHeightValue
13
+ } from "./utils.js";
14
+ const DataGridRow = React.memo(DataGridRowImpl, (prev, next) => {
15
+ const prevRowIndex = prev.virtualItem.index;
16
+ const nextRowIndex = next.virtualItem.index;
17
+ if (prev.row.id !== next.row.id) {
18
+ return false;
19
+ }
20
+ if (prev.row.original !== next.row.original) {
21
+ return false;
22
+ }
23
+ if (prev.virtualItem.start !== next.virtualItem.start) {
24
+ return false;
25
+ }
26
+ const prevHasFocus = prev.focusedCell?.rowIndex === prevRowIndex;
27
+ const nextHasFocus = next.focusedCell?.rowIndex === nextRowIndex;
28
+ if (prevHasFocus !== nextHasFocus) {
29
+ return false;
30
+ }
31
+ if (nextHasFocus && prevHasFocus) {
32
+ if (prev.focusedCell?.columnId !== next.focusedCell?.columnId) {
33
+ return false;
34
+ }
35
+ }
36
+ const prevHasEditing = prev.editingCell?.rowIndex === prevRowIndex;
37
+ const nextHasEditing = next.editingCell?.rowIndex === nextRowIndex;
38
+ if (prevHasEditing !== nextHasEditing) {
39
+ return false;
40
+ }
41
+ if (nextHasEditing && prevHasEditing) {
42
+ if (prev.editingCell?.columnId !== next.editingCell?.columnId) {
43
+ return false;
44
+ }
45
+ }
46
+ if (prev.cellSelectionKeys !== next.cellSelectionKeys) {
47
+ return false;
48
+ }
49
+ if (prev.columnVisibility !== next.columnVisibility) {
50
+ return false;
51
+ }
52
+ if (prev.rowHeight !== next.rowHeight) {
53
+ return false;
54
+ }
55
+ if (prev.columnPinning !== next.columnPinning) {
56
+ return false;
57
+ }
58
+ if (prev.readOnly !== next.readOnly) {
59
+ return false;
60
+ }
61
+ if (prev.searchMatchColumns !== next.searchMatchColumns) {
62
+ return false;
63
+ }
64
+ if (prev.activeSearchMatch?.columnId !== next.activeSearchMatch?.columnId) {
65
+ return false;
66
+ }
67
+ if (prev.dir !== next.dir) {
68
+ return false;
69
+ }
70
+ if (prev.adjustLayout !== next.adjustLayout) {
71
+ return false;
72
+ }
73
+ if (prev.stretchColumns !== next.stretchColumns) {
74
+ return false;
75
+ }
76
+ return true;
77
+ });
78
+ function DataGridRowImpl({
79
+ row,
80
+ tableMeta,
81
+ virtualItem,
82
+ measureElement,
83
+ rowMapRef,
84
+ rowHeight,
85
+ columnVisibility,
86
+ columnPinning,
87
+ focusedCell,
88
+ editingCell,
89
+ cellSelectionKeys,
90
+ searchMatchColumns,
91
+ activeSearchMatch,
92
+ dir,
93
+ readOnly,
94
+ stretchColumns,
95
+ adjustLayout,
96
+ className,
97
+ style,
98
+ ref,
99
+ ...props
100
+ }) {
101
+ const virtualRowIndex = virtualItem.index;
102
+ const onRowChange = React.useCallback(
103
+ (node) => {
104
+ if (typeof virtualRowIndex === "undefined") return;
105
+ if (node) {
106
+ measureElement(node);
107
+ rowMapRef.current?.set(virtualRowIndex, node);
108
+ } else {
109
+ rowMapRef.current?.delete(virtualRowIndex);
110
+ }
111
+ },
112
+ [virtualRowIndex, measureElement, rowMapRef]
113
+ );
114
+ const rowRef = useComposedRefs(ref, onRowChange);
115
+ const isRowSelected = row.getIsSelected();
116
+ const visibleCells = React.useMemo(() => row.getVisibleCells(), [row, columnVisibility, columnPinning]);
117
+ return /* @__PURE__ */ jsx(
118
+ "div",
119
+ {
120
+ role: "row",
121
+ "aria-rowindex": virtualRowIndex + 2,
122
+ "aria-selected": isRowSelected,
123
+ "data-index": virtualRowIndex,
124
+ "data-slot": "grid-row",
125
+ tabIndex: -1,
126
+ ...props,
127
+ ref: rowRef,
128
+ className: cn(
129
+ "absolute flex w-full border-b [content-visibility:auto]",
130
+ !adjustLayout && "will-change-transform",
131
+ className
132
+ ),
133
+ style: {
134
+ height: `${getRowHeightValue(rowHeight)}px`,
135
+ ...adjustLayout ? { top: `${virtualItem.start}px` } : { transform: `translateY(${virtualItem.start}px)` },
136
+ ...style
137
+ },
138
+ children: visibleCells.map((cell, colIndex) => {
139
+ const columnId = cell.column.id;
140
+ const isCellFocused = focusedCell?.rowIndex === virtualRowIndex && focusedCell?.columnId === columnId;
141
+ const isCellEditing = editingCell?.rowIndex === virtualRowIndex && editingCell?.columnId === columnId;
142
+ const isCellSelected = cellSelectionKeys?.has(getCellKey(virtualRowIndex, columnId)) ?? false;
143
+ const isSearchMatch = searchMatchColumns?.has(columnId) ?? false;
144
+ const isActiveSearchMatch = activeSearchMatch?.columnId === columnId;
145
+ const nextCell = visibleCells[colIndex + 1];
146
+ const isLastColumn = colIndex === visibleCells.length - 1;
147
+ const { showEndBorder, showStartBorder } = getColumnBorderVisibility({
148
+ column: cell.column,
149
+ nextColumn: nextCell?.column,
150
+ isLastColumn
151
+ });
152
+ return /* @__PURE__ */ jsx(
153
+ "div",
154
+ {
155
+ role: "gridcell",
156
+ "aria-colindex": colIndex + 1,
157
+ "data-highlighted": isCellFocused ? "" : void 0,
158
+ "data-slot": "grid-cell",
159
+ tabIndex: -1,
160
+ className: cn({
161
+ grow: stretchColumns && columnId !== "select",
162
+ "border-e": showEndBorder && columnId !== "select",
163
+ "border-s": showStartBorder && columnId !== "select"
164
+ }),
165
+ style: {
166
+ ...getColumnPinningStyle({ column: cell.column, dir }),
167
+ width: `calc(var(--col-${columnId}-size) * 1px)`
168
+ },
169
+ children: typeof cell.column.columnDef.header === "function" ? /* @__PURE__ */ jsx(
170
+ "div",
171
+ {
172
+ className: cn("size-full px-3 py-1.5", {
173
+ // `--accent` là bậc tint TRẠNG THÁI của gói (Tabs/Switch/Progress dùng đúng nó
174
+ // để nói "đang bật"). `bg-primary/10` là gold pha 10% — 1,05:1, một dấu hiệu giả.
175
+ "bg-accent": isRowSelected
176
+ }),
177
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
178
+ }
179
+ ) : /* @__PURE__ */ jsx(
180
+ DataGridCell,
181
+ {
182
+ cell,
183
+ tableMeta,
184
+ rowIndex: virtualRowIndex,
185
+ columnId,
186
+ rowHeight,
187
+ isFocused: isCellFocused,
188
+ isEditing: isCellEditing,
189
+ isSelected: isCellSelected,
190
+ isSearchMatch,
191
+ isActiveSearchMatch,
192
+ readOnly
193
+ }
194
+ )
195
+ },
196
+ cell.id
197
+ );
198
+ })
199
+ },
200
+ row.id
201
+ );
202
+ }
203
+ export {
204
+ DataGridRow
205
+ };
206
+ //# sourceMappingURL=data-grid-row.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-row.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ColumnPinningState, Row, TableMeta, VisibilityState } from \"@tanstack/react-table\";\nimport type { VirtualItem } from \"@tanstack/react-virtual\";\nimport * as React from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { useComposedRefs } from \"./compose-refs.js\";\nimport { DataGridCell } from \"./data-grid-cell.js\";\nimport type { CellPosition, Direction, RowHeightValue } from \"./types.js\";\nimport {\n flexRender,\n getCellKey,\n getColumnBorderVisibility,\n getColumnPinningStyle,\n getRowHeightValue\n} from \"./utils.js\";\n\ninterface DataGridRowProps<TData> extends React.ComponentProps<\"div\"> {\n row: Row<TData>;\n tableMeta: TableMeta<TData>;\n virtualItem: VirtualItem;\n measureElement: (node: Element | null) => void;\n rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;\n rowHeight: RowHeightValue;\n columnVisibility: VisibilityState;\n columnPinning: ColumnPinningState;\n focusedCell: CellPosition | null;\n editingCell: CellPosition | null;\n cellSelectionKeys: Set<string>;\n searchMatchColumns: Set<string> | null;\n activeSearchMatch: CellPosition | null;\n dir: Direction;\n readOnly: boolean;\n stretchColumns: boolean;\n adjustLayout: boolean;\n}\n\nexport const DataGridRow = React.memo(DataGridRowImpl, (prev, next) => {\n const prevRowIndex = prev.virtualItem.index;\n const nextRowIndex = next.virtualItem.index;\n\n // Re-render if row identity changed\n if (prev.row.id !== next.row.id) {\n return false;\n }\n\n // Re-render if row data (original) reference changed\n if (prev.row.original !== next.row.original) {\n return false;\n }\n\n // Re-render if virtual position changed (handles transform updates)\n if (prev.virtualItem.start !== next.virtualItem.start) {\n return false;\n }\n\n // Re-render if focus state changed for this row\n const prevHasFocus = prev.focusedCell?.rowIndex === prevRowIndex;\n const nextHasFocus = next.focusedCell?.rowIndex === nextRowIndex;\n\n if (prevHasFocus !== nextHasFocus) {\n return false;\n }\n\n // Re-render if focused column changed within this row\n if (nextHasFocus && prevHasFocus) {\n if (prev.focusedCell?.columnId !== next.focusedCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if editing state changed for this row\n const prevHasEditing = prev.editingCell?.rowIndex === prevRowIndex;\n const nextHasEditing = next.editingCell?.rowIndex === nextRowIndex;\n\n if (prevHasEditing !== nextHasEditing) {\n return false;\n }\n\n // Re-render if editing column changed within this row\n if (nextHasEditing && prevHasEditing) {\n if (prev.editingCell?.columnId !== next.editingCell?.columnId) {\n return false;\n }\n }\n\n // Re-render if this row's selected cells changed\n // Using stable Set reference that only includes this row's cells\n if (prev.cellSelectionKeys !== next.cellSelectionKeys) {\n return false;\n }\n\n // Re-render if column visibility changed\n if (prev.columnVisibility !== next.columnVisibility) {\n return false;\n }\n\n // Re-render if row height changed\n if (prev.rowHeight !== next.rowHeight) {\n return false;\n }\n\n // Re-render if column pinning state changed\n if (prev.columnPinning !== next.columnPinning) {\n return false;\n }\n\n // Re-render if readOnly changed\n if (prev.readOnly !== next.readOnly) {\n return false;\n }\n\n // Re-render if search match columns changed for this row\n if (prev.searchMatchColumns !== next.searchMatchColumns) {\n return false;\n }\n\n // Re-render if active search match changed for this row\n if (prev.activeSearchMatch?.columnId !== next.activeSearchMatch?.columnId) {\n return false;\n }\n\n // Re-render if direction changed\n if (prev.dir !== next.dir) {\n return false;\n }\n\n // Re-render if adjustLayout state changed\n if (prev.adjustLayout !== next.adjustLayout) {\n return false;\n }\n\n // Re-render if stretchColumns changed\n if (prev.stretchColumns !== next.stretchColumns) {\n return false;\n }\n\n // Skip re-render - props are equal\n return true;\n}) as typeof DataGridRowImpl;\n\nfunction DataGridRowImpl<TData>({\n row,\n tableMeta,\n virtualItem,\n measureElement,\n rowMapRef,\n rowHeight,\n columnVisibility,\n columnPinning,\n focusedCell,\n editingCell,\n cellSelectionKeys,\n searchMatchColumns,\n activeSearchMatch,\n dir,\n readOnly,\n stretchColumns,\n adjustLayout,\n className,\n style,\n ref,\n ...props\n}: DataGridRowProps<TData>) {\n const virtualRowIndex = virtualItem.index;\n\n const onRowChange = React.useCallback(\n (node: HTMLDivElement | null) => {\n if (typeof virtualRowIndex === \"undefined\") return;\n\n if (node) {\n measureElement(node);\n rowMapRef.current?.set(virtualRowIndex, node);\n } else {\n rowMapRef.current?.delete(virtualRowIndex);\n }\n },\n [virtualRowIndex, measureElement, rowMapRef]\n );\n\n const rowRef = useComposedRefs(ref, onRowChange);\n\n const isRowSelected = row.getIsSelected();\n\n // Memoize visible cells to avoid recreating cell array on every render\n // Though TanStack returns new Cell wrappers, memoizing the array helps React's reconciliation\n // biome-ignore lint/correctness/useExhaustiveDependencies: columnVisibility and columnPinning are used for calculating the visible cells\n const visibleCells = React.useMemo(() => row.getVisibleCells(), [row, columnVisibility, columnPinning]);\n\n return (\n <div\n key={row.id}\n role=\"row\"\n aria-rowindex={virtualRowIndex + 2}\n aria-selected={isRowSelected}\n data-index={virtualRowIndex}\n data-slot=\"grid-row\"\n tabIndex={-1}\n {...props}\n ref={rowRef}\n className={cn(\n \"absolute flex w-full border-b [content-visibility:auto]\",\n !adjustLayout && \"will-change-transform\",\n className\n )}\n style={{\n height: `${getRowHeightValue(rowHeight)}px`,\n ...(adjustLayout ? { top: `${virtualItem.start}px` } : { transform: `translateY(${virtualItem.start}px)` }),\n ...style\n }}\n >\n {visibleCells.map((cell, colIndex) => {\n const columnId = cell.column.id;\n\n const isCellFocused = focusedCell?.rowIndex === virtualRowIndex && focusedCell?.columnId === columnId;\n const isCellEditing = editingCell?.rowIndex === virtualRowIndex && editingCell?.columnId === columnId;\n const isCellSelected = cellSelectionKeys?.has(getCellKey(virtualRowIndex, columnId)) ?? false;\n\n const isSearchMatch = searchMatchColumns?.has(columnId) ?? false;\n const isActiveSearchMatch = activeSearchMatch?.columnId === columnId;\n\n const nextCell = visibleCells[colIndex + 1];\n const isLastColumn = colIndex === visibleCells.length - 1;\n const { showEndBorder, showStartBorder } = getColumnBorderVisibility({\n column: cell.column,\n nextColumn: nextCell?.column,\n isLastColumn\n });\n\n return (\n <div\n key={cell.id}\n role=\"gridcell\"\n aria-colindex={colIndex + 1}\n data-highlighted={isCellFocused ? \"\" : undefined}\n data-slot=\"grid-cell\"\n tabIndex={-1}\n className={cn({\n grow: stretchColumns && columnId !== \"select\",\n \"border-e\": showEndBorder && columnId !== \"select\",\n \"border-s\": showStartBorder && columnId !== \"select\"\n })}\n style={{\n ...getColumnPinningStyle({ column: cell.column, dir }),\n width: `calc(var(--col-${columnId}-size) * 1px)`\n }}\n >\n {typeof cell.column.columnDef.header === \"function\" ? (\n <div\n className={cn(\"size-full px-3 py-1.5\", {\n // `--accent` là bậc tint TRẠNG THÁI của gói (Tabs/Switch/Progress dùng đúng nó\n // để nói \"đang bật\"). `bg-primary/10` là gold pha 10% — 1,05:1, một dấu hiệu giả.\n \"bg-accent\": isRowSelected\n })}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </div>\n ) : (\n <DataGridCell\n cell={cell}\n tableMeta={tableMeta}\n rowIndex={virtualRowIndex}\n columnId={columnId}\n rowHeight={rowHeight}\n isFocused={isCellFocused}\n isEditing={isCellEditing}\n isSelected={isCellSelected}\n isSearchMatch={isSearchMatch}\n isActiveSearchMatch={isActiveSearchMatch}\n readOnly={readOnly}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { SearchState } from './types.js';
3
+ import '@tanstack/react-table';
4
+
5
+ interface DataGridSearchProps extends SearchState {
6
+ }
7
+ declare const DataGridSearch: React.MemoExoticComponent<typeof DataGridSearchImpl>;
8
+ declare function DataGridSearchImpl({ searchMatches, matchIndex, searchOpen, onSearchOpenChange, searchQuery, onSearchQueryChange, onSearch, onNavigateToNextMatch, onNavigateToPrevMatch }: DataGridSearchProps): React.JSX.Element | null;
9
+
10
+ export { DataGridSearch };
@@ -0,0 +1,183 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { ChevronDown, ChevronUp, X } from "lucide-react";
4
+ import * as React from "react";
5
+ import { Button } from "../components/ui/button.js";
6
+ import { Input } from "../components/ui/input.js";
7
+ import { msg } from "./messages.js";
8
+ import { useAsRef } from "./use-as-ref.js";
9
+ import { useDebouncedCallback } from "./use-debounced-callback.js";
10
+ function onTriggerPointerDown(event) {
11
+ const target = event.target;
12
+ if (!(target instanceof HTMLElement)) return;
13
+ if (target.hasPointerCapture(event.pointerId)) {
14
+ target.releasePointerCapture(event.pointerId);
15
+ }
16
+ if (event.button === 0 && event.ctrlKey === false && event.pointerType === "mouse" && !(event.target instanceof HTMLInputElement)) {
17
+ event.preventDefault();
18
+ }
19
+ }
20
+ const DataGridSearch = React.memo(DataGridSearchImpl, (prev, next) => {
21
+ if (prev.searchOpen !== next.searchOpen) return false;
22
+ if (!next.searchOpen) return true;
23
+ if (prev.matchIndex !== next.matchIndex) return false;
24
+ if (prev.searchMatches.length !== next.searchMatches.length) return false;
25
+ for (let i = 0; i < prev.searchMatches.length; i++) {
26
+ const prevMatch = prev.searchMatches[i];
27
+ const nextMatch = next.searchMatches[i];
28
+ if (!prevMatch || !nextMatch) return false;
29
+ if (prevMatch.rowIndex !== nextMatch.rowIndex || prevMatch.columnId !== nextMatch.columnId) {
30
+ return false;
31
+ }
32
+ }
33
+ return true;
34
+ });
35
+ function DataGridSearchImpl({
36
+ searchMatches,
37
+ matchIndex,
38
+ searchOpen,
39
+ onSearchOpenChange,
40
+ searchQuery,
41
+ onSearchQueryChange,
42
+ onSearch,
43
+ onNavigateToNextMatch,
44
+ onNavigateToPrevMatch
45
+ }) {
46
+ const propsRef = useAsRef({
47
+ onSearchOpenChange,
48
+ onSearchQueryChange,
49
+ onSearch,
50
+ onNavigateToNextMatch,
51
+ onNavigateToPrevMatch
52
+ });
53
+ const inputRef = React.useRef(null);
54
+ const isComposingRef = React.useRef(false);
55
+ const [hasQuery, setHasQuery] = React.useState(searchQuery.length > 0);
56
+ React.useEffect(() => {
57
+ if (searchOpen) {
58
+ requestAnimationFrame(() => {
59
+ inputRef.current?.focus();
60
+ });
61
+ return;
62
+ }
63
+ isComposingRef.current = false;
64
+ setHasQuery(false);
65
+ }, [searchOpen]);
66
+ React.useEffect(() => {
67
+ if (!searchOpen) return;
68
+ function onEscape(event) {
69
+ if (event.key === "Escape") {
70
+ event.preventDefault();
71
+ propsRef.current.onSearchOpenChange(false);
72
+ }
73
+ }
74
+ document.addEventListener("keydown", onEscape);
75
+ return () => document.removeEventListener("keydown", onEscape);
76
+ }, [searchOpen, propsRef]);
77
+ const debouncedSearch = useDebouncedCallback((query) => {
78
+ propsRef.current.onSearch(query);
79
+ }, 150);
80
+ function onCompositionStart() {
81
+ isComposingRef.current = true;
82
+ }
83
+ function onCompositionEnd(event) {
84
+ isComposingRef.current = false;
85
+ const value = event.currentTarget.value;
86
+ setHasQuery(value.length > 0);
87
+ propsRef.current.onSearchQueryChange(value);
88
+ debouncedSearch(value);
89
+ }
90
+ function onKeyDown(event) {
91
+ event.stopPropagation();
92
+ if (event.key === "Enter") {
93
+ if (event.nativeEvent.isComposing) return;
94
+ event.preventDefault();
95
+ if (event.shiftKey) {
96
+ propsRef.current.onNavigateToPrevMatch();
97
+ } else {
98
+ propsRef.current.onNavigateToNextMatch();
99
+ }
100
+ }
101
+ }
102
+ function onChange(event) {
103
+ if (isComposingRef.current) return;
104
+ const value = event.target.value;
105
+ setHasQuery(value.length > 0);
106
+ propsRef.current.onSearchQueryChange(value);
107
+ debouncedSearch(value);
108
+ }
109
+ function onClose() {
110
+ propsRef.current.onSearchOpenChange(false);
111
+ }
112
+ function onPrevMatch() {
113
+ propsRef.current.onNavigateToPrevMatch();
114
+ }
115
+ function onNextMatch() {
116
+ propsRef.current.onNavigateToNextMatch();
117
+ }
118
+ if (!searchOpen) return null;
119
+ return /* @__PURE__ */ jsxs(
120
+ "div",
121
+ {
122
+ role: "search",
123
+ "data-slot": "grid-search",
124
+ className: "fade-in-0 slide-in-from-top-2 absolute end-4 top-4 z-50 flex animate-in flex-col gap-2 rounded-lg border bg-popover p-2 shadow-overlay",
125
+ children: [
126
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
127
+ /* @__PURE__ */ jsx(
128
+ Input,
129
+ {
130
+ autoComplete: "off",
131
+ autoCorrect: "off",
132
+ autoCapitalize: "off",
133
+ spellCheck: false,
134
+ placeholder: msg.search.placeholder,
135
+ "aria-label": msg.search.label,
136
+ className: "h-8 w-64",
137
+ ref: inputRef,
138
+ defaultValue: searchQuery,
139
+ onChange,
140
+ onKeyDown,
141
+ onCompositionStart,
142
+ onCompositionEnd
143
+ }
144
+ ),
145
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
146
+ /* @__PURE__ */ jsx(
147
+ Button,
148
+ {
149
+ "aria-label": msg.search.prevMatch,
150
+ variant: "ghost",
151
+ size: "icon",
152
+ className: "size-7",
153
+ onClick: onPrevMatch,
154
+ onPointerDown: onTriggerPointerDown,
155
+ disabled: searchMatches.length === 0,
156
+ children: /* @__PURE__ */ jsx(ChevronUp, {})
157
+ }
158
+ ),
159
+ /* @__PURE__ */ jsx(
160
+ Button,
161
+ {
162
+ "aria-label": msg.search.nextMatch,
163
+ variant: "ghost",
164
+ size: "icon",
165
+ className: "size-7",
166
+ onClick: onNextMatch,
167
+ onPointerDown: onTriggerPointerDown,
168
+ disabled: searchMatches.length === 0,
169
+ children: /* @__PURE__ */ jsx(ChevronDown, {})
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx(Button, { "aria-label": msg.search.close, variant: "ghost", size: "icon", className: "size-7", onClick: onClose, children: /* @__PURE__ */ jsx(X, {}) })
173
+ ] })
174
+ ] }),
175
+ /* @__PURE__ */ jsx("div", { role: "status", className: "flex items-center gap-1 whitespace-nowrap text-muted-foreground text-xs", children: searchMatches.length > 0 ? /* @__PURE__ */ jsx("span", { children: msg.search.matchPosition(matchIndex + 1, searchMatches.length) }) : hasQuery ? /* @__PURE__ */ jsx("span", { children: msg.search.noResults }) : /* @__PURE__ */ jsx("span", { children: msg.search.typeToSearch }) })
176
+ ]
177
+ }
178
+ );
179
+ }
180
+ export {
181
+ DataGridSearch
182
+ };
183
+ //# sourceMappingURL=data-grid-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-search.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDown, ChevronUp, X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport { Input } from \"../components/ui/input.js\";\nimport { msg } from \"./messages.js\";\nimport type { SearchState } from \"./types.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport { useDebouncedCallback } from \"./use-debounced-callback.js\";\n\nfunction onTriggerPointerDown(event: React.PointerEvent<HTMLButtonElement>) {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n if (target.hasPointerCapture(event.pointerId)) {\n target.releasePointerCapture(event.pointerId);\n }\n\n // Prevent the trigger from stealing focus away from the input\n if (\n event.button === 0 &&\n event.ctrlKey === false &&\n event.pointerType === \"mouse\" &&\n !(event.target instanceof HTMLInputElement)\n ) {\n event.preventDefault();\n }\n}\n\ninterface DataGridSearchProps extends SearchState {}\n\nexport const DataGridSearch = React.memo(DataGridSearchImpl, (prev, next) => {\n if (prev.searchOpen !== next.searchOpen) return false;\n\n if (!next.searchOpen) return true;\n\n // Exclude searchQuery because the input is uncontrolled, and hasQuery state handles the status text\n if (prev.matchIndex !== next.matchIndex) return false;\n\n if (prev.searchMatches.length !== next.searchMatches.length) return false;\n\n for (let i = 0; i < prev.searchMatches.length; i++) {\n const prevMatch = prev.searchMatches[i];\n const nextMatch = next.searchMatches[i];\n\n if (!prevMatch || !nextMatch) return false;\n\n if (prevMatch.rowIndex !== nextMatch.rowIndex || prevMatch.columnId !== nextMatch.columnId) {\n return false;\n }\n }\n\n return true;\n});\n\nfunction DataGridSearchImpl({\n searchMatches,\n matchIndex,\n searchOpen,\n onSearchOpenChange,\n searchQuery,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n}: DataGridSearchProps) {\n const propsRef = useAsRef({\n onSearchOpenChange,\n onSearchQueryChange,\n onSearch,\n onNavigateToNextMatch,\n onNavigateToPrevMatch\n });\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const isComposingRef = React.useRef(false);\n const [hasQuery, setHasQuery] = React.useState(searchQuery.length > 0);\n\n React.useEffect(() => {\n if (searchOpen) {\n requestAnimationFrame(() => {\n inputRef.current?.focus();\n });\n return;\n }\n\n isComposingRef.current = false;\n setHasQuery(false);\n }, [searchOpen]);\n\n React.useEffect(() => {\n if (!searchOpen) return;\n\n function onEscape(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n event.preventDefault();\n propsRef.current.onSearchOpenChange(false);\n }\n }\n\n document.addEventListener(\"keydown\", onEscape);\n return () => document.removeEventListener(\"keydown\", onEscape);\n }, [searchOpen, propsRef]);\n\n const debouncedSearch = useDebouncedCallback((query: string) => {\n propsRef.current.onSearch(query);\n }, 150);\n\n function onCompositionStart() {\n isComposingRef.current = true;\n }\n\n function onCompositionEnd(event: React.CompositionEvent<HTMLInputElement>) {\n isComposingRef.current = false;\n const value = event.currentTarget.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onKeyDown(event: React.KeyboardEvent) {\n event.stopPropagation();\n\n if (event.key === \"Enter\") {\n if (event.nativeEvent.isComposing) return;\n event.preventDefault();\n if (event.shiftKey) {\n propsRef.current.onNavigateToPrevMatch();\n } else {\n propsRef.current.onNavigateToNextMatch();\n }\n }\n }\n\n function onChange(event: React.ChangeEvent<HTMLInputElement>) {\n if (isComposingRef.current) return;\n const value = event.target.value;\n setHasQuery(value.length > 0);\n propsRef.current.onSearchQueryChange(value);\n debouncedSearch(value);\n }\n\n function onClose() {\n propsRef.current.onSearchOpenChange(false);\n }\n\n function onPrevMatch() {\n propsRef.current.onNavigateToPrevMatch();\n }\n\n function onNextMatch() {\n propsRef.current.onNavigateToNextMatch();\n }\n\n if (!searchOpen) return null;\n\n return (\n <div\n role=\"search\"\n data-slot=\"grid-search\"\n // `shadow-overlay`, KHÔNG `shadow-lg`: bóng của gói đi qua `--elevation-*` nên nó đổi\n // theo sáng/tối; `shadow-lg` của Tailwind là rgba đen cố định, ở dark gần như vô hình.\n className=\"fade-in-0 slide-in-from-top-2 absolute end-4 top-4 z-50 flex animate-in flex-col gap-2 rounded-lg border bg-popover p-2 shadow-overlay\"\n >\n <div className=\"flex items-center gap-2\">\n <Input\n autoComplete=\"off\"\n autoCorrect=\"off\"\n autoCapitalize=\"off\"\n spellCheck={false}\n placeholder={msg.search.placeholder}\n // placeholder biến mất ngay khi người dùng gõ → vẫn phải có nhãn riêng\n aria-label={msg.search.label}\n className=\"h-8 w-64\"\n ref={inputRef}\n defaultValue={searchQuery}\n onChange={onChange}\n onKeyDown={onKeyDown}\n onCompositionStart={onCompositionStart}\n onCompositionEnd={onCompositionEnd}\n />\n <div className=\"flex items-center gap-1\">\n <Button\n aria-label={msg.search.prevMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onPrevMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronUp />\n </Button>\n <Button\n aria-label={msg.search.nextMatch}\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-7\"\n onClick={onNextMatch}\n onPointerDown={onTriggerPointerDown}\n disabled={searchMatches.length === 0}\n >\n <ChevronDown />\n </Button>\n <Button aria-label={msg.search.close} variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={onClose}>\n <X />\n </Button>\n </div>\n </div>\n <div role=\"status\" className=\"flex items-center gap-1 whitespace-nowrap text-muted-foreground text-xs\">\n {searchMatches.length > 0 ? (\n <span>{msg.search.matchPosition(matchIndex + 1, searchMatches.length)}</span>\n ) : hasQuery ? (\n <span>{msg.search.noResults}</span>\n ) : (\n <span>{msg.search.typeToSearch}</span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,12 @@
1
+ import { ColumnDef } from '@tanstack/react-table';
2
+
3
+ type HitboxSize = "default" | "sm" | "lg";
4
+ interface GetDataGridSelectColumnOptions<TData> extends Omit<Partial<ColumnDef<TData>>, "id" | "header" | "cell"> {
5
+ enableRowMarkers?: boolean;
6
+ readOnly?: boolean;
7
+ hitboxSize?: HitboxSize;
8
+ debug?: boolean;
9
+ }
10
+ declare function getDataGridSelectColumn<TData>({ size, hitboxSize, enableHiding, enableResizing, enableSorting, enableRowMarkers, readOnly, debug, ...props }?: GetDataGridSelectColumnOptions<TData>): ColumnDef<TData>;
11
+
12
+ export { getDataGridSelectColumn };