@comitor/ui 3.0.1 → 3.1.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 (76) hide show
  1. package/dist/components/composite/confirm-dialog.js +56 -35
  2. package/dist/components/composite/confirm-dialog.js.map +1 -1
  3. package/dist/components/composite/search-filter-bar-constants.d.ts +3 -0
  4. package/dist/components/composite/search-filter-bar-constants.js.map +1 -1
  5. package/dist/components/composite/table-pagination-constants.d.ts +3 -1
  6. package/dist/components/composite/table-pagination-constants.js.map +1 -1
  7. package/dist/components/composite/table-pagination.js +10 -2
  8. package/dist/components/composite/table-pagination.js.map +1 -1
  9. package/dist/components/composite/text-utils.d.ts +4 -23
  10. package/dist/components/composite/text-utils.js +1 -11
  11. package/dist/components/composite/text-utils.js.map +1 -1
  12. package/dist/components/ui/banner.d.ts +8 -0
  13. package/dist/components/ui/banner.js +149 -35
  14. package/dist/components/ui/banner.js.map +1 -1
  15. package/dist/components/ui/mask-input-constants.d.ts +34 -2
  16. package/dist/components/ui/mask-input-constants.js +56 -5
  17. package/dist/components/ui/mask-input-constants.js.map +1 -1
  18. package/dist/components/ui/mask-input.d.ts +6 -5
  19. package/dist/components/ui/mask-input.js +5 -1
  20. package/dist/components/ui/mask-input.js.map +1 -1
  21. package/dist/components/ui/mention.d.ts +14 -2
  22. package/dist/components/ui/mention.js +33 -6
  23. package/dist/components/ui/mention.js.map +1 -1
  24. package/dist/components/ui/phone-input.d.ts +18 -8
  25. package/dist/components/ui/phone-input.js +120 -33
  26. package/dist/components/ui/phone-input.js.map +1 -1
  27. package/dist/components/ui/scroll-spy.js +46 -31
  28. package/dist/components/ui/scroll-spy.js.map +1 -1
  29. package/dist/components/ui/stepper.js +26 -3
  30. package/dist/components/ui/stepper.js.map +1 -1
  31. package/dist/components/ui/time-picker.d.ts +24 -4
  32. package/dist/components/ui/time-picker.js +425 -162
  33. package/dist/components/ui/time-picker.js.map +1 -1
  34. package/dist/components/ui/timeline.js +45 -9
  35. package/dist/components/ui/timeline.js.map +1 -1
  36. package/dist/data-grid/data-grid-cell-variants.js +4 -4
  37. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  38. package/dist/data-grid/data-grid-cell-wrapper.d.ts +1 -1
  39. package/dist/data-grid/data-grid-cell-wrapper.js +6 -0
  40. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
  41. package/dist/data-grid/data-grid-row.js +1 -1
  42. package/dist/data-grid/data-grid-row.js.map +1 -1
  43. package/dist/data-grid/data-grid-select-column.js +6 -2
  44. package/dist/data-grid/data-grid-select-column.js.map +1 -1
  45. package/dist/data-grid/data-grid-skeleton.d.ts +5 -0
  46. package/dist/data-grid/data-grid-skeleton.js +1 -4
  47. package/dist/data-grid/data-grid-skeleton.js.map +1 -1
  48. package/dist/data-grid/data-grid.d.ts +1 -1
  49. package/dist/data-grid/data-grid.js +3 -2
  50. package/dist/data-grid/data-grid.js.map +1 -1
  51. package/dist/data-grid/filters.js +3 -2
  52. package/dist/data-grid/filters.js.map +1 -1
  53. package/dist/data-grid/types.d.ts +7 -0
  54. package/dist/data-grid/use-data-grid.d.ts +3 -2
  55. package/dist/data-grid/use-data-grid.js +70 -24
  56. package/dist/data-grid/use-data-grid.js.map +1 -1
  57. package/dist/data-grid/utils.d.ts +7 -0
  58. package/dist/data-grid/utils.js +3 -1
  59. package/dist/data-grid/utils.js.map +1 -1
  60. package/dist/i18n/namespaces/search-filter-bar.js +8 -6
  61. package/dist/i18n/namespaces/search-filter-bar.js.map +1 -1
  62. package/dist/i18n/namespaces/table-pagination.js +6 -4
  63. package/dist/i18n/namespaces/table-pagination.js.map +1 -1
  64. package/dist/index.d.ts +2 -1
  65. package/dist/lib/collect-item-elements.d.ts +47 -0
  66. package/dist/lib/collect-item-elements.js +22 -0
  67. package/dist/lib/collect-item-elements.js.map +1 -0
  68. package/dist/lib/text-search.d.ts +32 -0
  69. package/dist/lib/text-search.js +17 -0
  70. package/dist/lib/text-search.js.map +1 -0
  71. package/dist/shell/app-shell.d.ts +1 -0
  72. package/dist/shell/display-axis-provider.d.ts +1 -1
  73. package/dist/shell/index.d.ts +2 -1
  74. package/dist/shell/shell-context.d.ts +1 -0
  75. package/dist/shell/utils.d.ts +2 -1
  76. package/package.json +1 -1
@@ -189,7 +189,7 @@ function DataGridRowImpl({
189
189
  isSelected: isCellSelected,
190
190
  isSearchMatch,
191
191
  isActiveSearchMatch,
192
- readOnly
192
+ readOnly: readOnly || cell.column.columnDef.meta?.readOnly === true
193
193
  }
194
194
  )
195
195
  },
@@ -1 +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 \"../lib/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":[]}
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 \"../lib/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 // `meta.readOnly` (3.1.0) khoá riêng cột này — kiểu ô nào cũng đã biết vẽ trạng thái chỉ đọc.\n readOnly={readOnly || cell.column.columnDef.meta?.readOnly === true}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -50,7 +50,7 @@ function DataGridSelectCheckbox({
50
50
  // `lint:a11y` nhân opacity vào màu chữ nên `opacity-0` đọc ra 1,00:1 — một chỗ
51
51
  // "chữ không đọc được" mà cổng không thể biết là cố ý. `invisible` cũng đúng hơn
52
52
  // về ngữ nghĩa: nó rút phần tử khỏi cây trợ năng thay vì để nó vẫn được đọc lên.
53
- "pointer-events-none absolute start-3 top-1.5 flex size-4 items-center justify-center text-muted-foreground text-xs tabular-nums group-hover:invisible",
53
+ "pointer-events-none absolute start-3 top-1.5 flex size-4 items-center justify-center text-muted-foreground text-xs tabular-nums group-hover:invisible group-has-[:focus-visible]:invisible",
54
54
  checked && "invisible"
55
55
  ),
56
56
  children: rowNumber
@@ -67,7 +67,11 @@ function DataGridSelectCheckbox({
67
67
  "relative transition-[shadow,border,opacity]",
68
68
  // Ở đây PHẢI là `opacity`, không phải `invisible` như số thứ tự bên trên: đây là
69
69
  // checkbox thật, `visibility:hidden` sẽ làm nó mất luôn khả năng nhận Tab.
70
- "opacity-0 group-hover:opacity-100 data-[state=checked]:opacity-100",
70
+ // ⚠ Khác bản gốc (3.1.0): `focus-visible:opacity-100` (và số thứ tự nhường chỗ qua
71
+ // `group-has-[:focus-visible]`) — bản gốc chỉ hiện checkbox khi hover hay đã tick, nên
72
+ // người dùng bàn phím Tab tới một dòng CHƯA tick thì vòng focus nằm trên một phần tử
73
+ // trong suốt: thấy mỗi số thứ tự, không biết Space sẽ tick gì.
74
+ "opacity-0 group-hover:opacity-100 focus-visible:opacity-100 data-[state=checked]:opacity-100",
71
75
  className
72
76
  ),
73
77
  checked,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-select-column.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CellContext, ColumnDef, HeaderContext } from \"@tanstack/react-table\";\nimport * as React from \"react\";\nimport { Checkbox } from \"../components/ui/checkbox.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\n\ntype HitboxSize = \"default\" | \"sm\" | \"lg\";\n\ninterface DataGridSelectHitboxProps {\n htmlFor: string;\n children: React.ReactNode;\n size?: HitboxSize;\n debug?: boolean;\n}\n\nfunction DataGridSelectHitbox({ htmlFor, children, size, debug }: DataGridSelectHitboxProps) {\n return (\n <div\n className={cn(\n \"group relative -my-1.5 h-[calc(100%+0.75rem)] py-1.5\",\n size === \"default\" && \"-ms-3 -me-2 ps-3 pe-2\",\n size === \"sm\" && \"-ms-3 -me-1.5 ps-3 pe-1.5\",\n size === \"lg\" && \"-mx-3 px-3\"\n )}\n >\n {children}\n {/* biome-ignore lint/a11y/noLabelWithoutControl: htmlFor trỏ tới Checkbox ngay bên trên (vùng bấm mở rộng) */}\n <label\n htmlFor={htmlFor}\n className={cn(\n \"absolute inset-0 cursor-pointer\",\n debug && \"border border-destructive-ink border-dashed bg-destructive/20\"\n )}\n />\n </div>\n );\n}\n\ninterface DataGridSelectCheckboxProps extends Omit<React.ComponentProps<typeof Checkbox>, \"id\"> {\n rowNumber?: number;\n hitboxSize?: HitboxSize;\n debug?: boolean;\n}\n\nfunction DataGridSelectCheckbox({\n rowNumber,\n hitboxSize,\n debug,\n checked,\n className,\n ...props\n}: DataGridSelectCheckboxProps) {\n const id = React.useId();\n\n if (rowNumber !== undefined) {\n return (\n <DataGridSelectHitbox htmlFor={id} size={hitboxSize} debug={debug}>\n <div\n aria-hidden=\"true\"\n className={cn(\n // Ẩn bằng `visibility`, KHÔNG bằng `opacity-0`: số thứ tự là chữ, và cổng\n // `lint:a11y` nhân opacity vào màu chữ nên `opacity-0` đọc ra 1,00:1 — một chỗ\n // \"chữ không đọc được\" mà cổng không thể biết là cố ý. `invisible` cũng đúng hơn\n // về ngữ nghĩa: nó rút phần tử khỏi cây trợ năng thay vì để nó vẫn được đọc lên.\n \"pointer-events-none absolute start-3 top-1.5 flex size-4 items-center justify-center text-muted-foreground text-xs tabular-nums group-hover:invisible\",\n checked && \"invisible\"\n )}\n >\n {rowNumber}\n </div>\n <Checkbox\n id={id}\n className={cn(\n // Không ghi đè viền hover: `Checkbox` của gói đã đi qua `--choice-edge` /\n // `--choice-edge-strong`. `border-primary/40` vừa sai vai trò (bậc tô NỀN đem vẽ\n // NÉT) vừa chỉ 1,25:1 ở light.\n \"relative transition-[shadow,border,opacity]\",\n // Ở đây PHẢI là `opacity`, không phải `invisible` như số thứ tự bên trên: đây là\n // checkbox thật, `visibility:hidden` sẽ làm nó mất luôn khả năng nhận Tab.\n \"opacity-0 group-hover:opacity-100 data-[state=checked]:opacity-100\",\n className\n )}\n checked={checked}\n {...props}\n />\n </DataGridSelectHitbox>\n );\n }\n\n return (\n <DataGridSelectHitbox htmlFor={id} size={hitboxSize} debug={debug}>\n <Checkbox id={id} className={cn(\"relative transition-[shadow,border]\", className)} checked={checked} {...props} />\n </DataGridSelectHitbox>\n );\n}\n\ninterface DataGridSelectHeaderProps<TData> extends Pick<HeaderContext<TData, unknown>, \"table\"> {\n hitboxSize?: HitboxSize;\n readOnly?: boolean;\n debug?: boolean;\n}\n\nfunction DataGridSelectHeader<TData>({ table, hitboxSize, readOnly, debug }: DataGridSelectHeaderProps<TData>) {\n const msg = useDataGridMessages();\n const onCheckedChange = React.useCallback((value: boolean) => table.toggleAllPageRowsSelected(value), [table]);\n\n if (readOnly) {\n return <div className=\"mt-1 flex items-center ps-1 text-muted-foreground text-sm\">#</div>;\n }\n\n return (\n <DataGridSelectCheckbox\n aria-label={msg.select.all}\n checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && \"indeterminate\")}\n onCheckedChange={onCheckedChange}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n );\n}\n\ninterface DataGridSelectCellProps<TData> extends Pick<CellContext<TData, unknown>, \"row\" | \"table\"> {\n hitboxSize?: HitboxSize;\n enableRowMarkers?: boolean;\n readOnly?: boolean;\n debug?: boolean;\n}\n\nfunction DataGridSelectCell<TData>({\n row,\n table,\n hitboxSize,\n enableRowMarkers,\n readOnly,\n debug\n}: DataGridSelectCellProps<TData>) {\n const msg = useDataGridMessages();\n const meta = table.options.meta;\n const rowNumber = enableRowMarkers ? (meta?.getVisualRowIndex?.(row.id) ?? row.index + 1) : undefined;\n\n const onCheckedChange = React.useCallback(\n (value: boolean) => {\n if (meta?.onRowSelect) {\n meta.onRowSelect(row.id, value, false);\n } else {\n row.toggleSelected(value);\n }\n },\n [meta, row]\n );\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n if (event.shiftKey) {\n event.preventDefault();\n meta?.onRowSelect?.(row.id, !row.getIsSelected(), true);\n }\n },\n [meta, row]\n );\n\n if (readOnly) {\n return (\n <div className=\"flex items-center ps-1 text-muted-foreground text-xs tabular-nums\">\n {rowNumber ?? row.index + 1}\n </div>\n );\n }\n\n return (\n <DataGridSelectCheckbox\n aria-label={rowNumber ? msg.select.rowNumber(rowNumber) : msg.select.row}\n checked={row.getIsSelected()}\n onCheckedChange={onCheckedChange}\n onClick={onClick}\n rowNumber={rowNumber}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n );\n}\n\ninterface GetDataGridSelectColumnOptions<TData> extends Omit<Partial<ColumnDef<TData>>, \"id\" | \"header\" | \"cell\"> {\n enableRowMarkers?: boolean;\n readOnly?: boolean;\n hitboxSize?: HitboxSize;\n debug?: boolean;\n}\n\nexport function getDataGridSelectColumn<TData>({\n size = 40,\n hitboxSize = \"default\",\n enableHiding = false,\n enableResizing = false,\n enableSorting = false,\n enableRowMarkers = false,\n readOnly = false,\n debug = false,\n ...props\n}: GetDataGridSelectColumnOptions<TData> = {}): ColumnDef<TData> {\n return {\n id: \"select\",\n header: ({ table }) => (\n <DataGridSelectHeader table={table} hitboxSize={hitboxSize} readOnly={readOnly} debug={debug} />\n ),\n cell: ({ row, table }) => (\n <DataGridSelectCell\n row={row}\n table={table}\n enableRowMarkers={enableRowMarkers}\n readOnly={readOnly}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n ),\n size,\n enableHiding,\n enableResizing,\n enableSorting,\n ...props\n };\n}\n"],"mappings":";AAmBI,SAUE,KAVF;AAhBJ,YAAY,WAAW;AACvB,SAAS,gBAAgB;AACzB,SAAS,UAAU;AACnB,SAAS,2BAA2B;AAWpC,SAAS,qBAAqB,EAAE,SAAS,UAAU,MAAM,MAAM,GAA8B;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,aAAa;AAAA,QACtB,SAAS,QAAQ;AAAA,QACjB,SAAS,QAAQ;AAAA,MACnB;AAAA,MAEC;AAAA;AAAA,QAED;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,WAAW;AAAA,cACT;AAAA,cACA,SAAS;AAAA,YACX;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAQA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgC;AAC9B,QAAM,KAAK,MAAM,MAAM;AAEvB,MAAI,cAAc,QAAW;AAC3B,WACE,qBAAC,wBAAqB,SAAS,IAAI,MAAM,YAAY,OACnD;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,eAAY;AAAA,UACZ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,YAKT;AAAA,YACA,WAAW;AAAA,UACb;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,WAAW;AAAA;AAAA;AAAA;AAAA,YAIT;AAAA;AAAA;AAAA,YAGA;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACC,GAAG;AAAA;AAAA,MACN;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,oBAAC,wBAAqB,SAAS,IAAI,MAAM,YAAY,OACnD,8BAAC,YAAS,IAAQ,WAAW,GAAG,uCAAuC,SAAS,GAAG,SAAmB,GAAG,OAAO,GAClH;AAEJ;AAQA,SAAS,qBAA4B,EAAE,OAAO,YAAY,UAAU,MAAM,GAAqC;AAC7G,QAAM,MAAM,oBAAoB;AAChC,QAAM,kBAAkB,MAAM,YAAY,CAAC,UAAmB,MAAM,0BAA0B,KAAK,GAAG,CAAC,KAAK,CAAC;AAE7G,MAAI,UAAU;AACZ,WAAO,oBAAC,SAAI,WAAU,6DAA4D,eAAC;AAAA,EACrF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,cAAY,IAAI,OAAO;AAAA,MACvB,SAAS,MAAM,yBAAyB,KAAM,MAAM,0BAA0B,KAAK;AAAA,MACnF;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AASA,SAAS,mBAA0B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,MAAM,oBAAoB;AAChC,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,YAAY,mBAAoB,MAAM,oBAAoB,IAAI,EAAE,KAAK,IAAI,QAAQ,IAAK;AAE5F,QAAM,kBAAkB,MAAM;AAAA,IAC5B,CAAC,UAAmB;AAClB,UAAI,MAAM,aAAa;AACrB,aAAK,YAAY,IAAI,IAAI,OAAO,KAAK;AAAA,MACvC,OAAO;AACL,YAAI,eAAe,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,IACA,CAAC,MAAM,GAAG;AAAA,EACZ;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,UAA+C;AAC9C,UAAI,MAAM,UAAU;AAClB,cAAM,eAAe;AACrB,cAAM,cAAc,IAAI,IAAI,CAAC,IAAI,cAAc,GAAG,IAAI;AAAA,MACxD;AAAA,IACF;AAAA,IACA,CAAC,MAAM,GAAG;AAAA,EACZ;AAEA,MAAI,UAAU;AACZ,WACE,oBAAC,SAAI,WAAU,qEACZ,uBAAa,IAAI,QAAQ,GAC5B;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,cAAY,YAAY,IAAI,OAAO,UAAU,SAAS,IAAI,IAAI,OAAO;AAAA,MACrE,SAAS,IAAI,cAAc;AAAA,MAC3B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AASO,SAAS,wBAA+B;AAAA,EAC7C,OAAO;AAAA,EACP,aAAa;AAAA,EACb,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,GAAG;AACL,IAA2C,CAAC,GAAqB;AAC/D,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QAAQ,CAAC,EAAE,MAAM,MACf,oBAAC,wBAAqB,OAAc,YAAwB,UAAoB,OAAc;AAAA,IAEhG,MAAM,CAAC,EAAE,KAAK,MAAM,MAClB;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-select-column.tsx"],"sourcesContent":["\"use client\";\n\nimport type { CellContext, ColumnDef, HeaderContext } from \"@tanstack/react-table\";\nimport * as React from \"react\";\nimport { Checkbox } from \"../components/ui/checkbox.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useDataGridMessages } from \"./data-grid-locale.js\";\n\ntype HitboxSize = \"default\" | \"sm\" | \"lg\";\n\ninterface DataGridSelectHitboxProps {\n htmlFor: string;\n children: React.ReactNode;\n size?: HitboxSize;\n debug?: boolean;\n}\n\nfunction DataGridSelectHitbox({ htmlFor, children, size, debug }: DataGridSelectHitboxProps) {\n return (\n <div\n className={cn(\n \"group relative -my-1.5 h-[calc(100%+0.75rem)] py-1.5\",\n size === \"default\" && \"-ms-3 -me-2 ps-3 pe-2\",\n size === \"sm\" && \"-ms-3 -me-1.5 ps-3 pe-1.5\",\n size === \"lg\" && \"-mx-3 px-3\"\n )}\n >\n {children}\n {/* biome-ignore lint/a11y/noLabelWithoutControl: htmlFor trỏ tới Checkbox ngay bên trên (vùng bấm mở rộng) */}\n <label\n htmlFor={htmlFor}\n className={cn(\n \"absolute inset-0 cursor-pointer\",\n debug && \"border border-destructive-ink border-dashed bg-destructive/20\"\n )}\n />\n </div>\n );\n}\n\ninterface DataGridSelectCheckboxProps extends Omit<React.ComponentProps<typeof Checkbox>, \"id\"> {\n rowNumber?: number;\n hitboxSize?: HitboxSize;\n debug?: boolean;\n}\n\nfunction DataGridSelectCheckbox({\n rowNumber,\n hitboxSize,\n debug,\n checked,\n className,\n ...props\n}: DataGridSelectCheckboxProps) {\n const id = React.useId();\n\n if (rowNumber !== undefined) {\n return (\n <DataGridSelectHitbox htmlFor={id} size={hitboxSize} debug={debug}>\n <div\n aria-hidden=\"true\"\n className={cn(\n // Ẩn bằng `visibility`, KHÔNG bằng `opacity-0`: số thứ tự là chữ, và cổng\n // `lint:a11y` nhân opacity vào màu chữ nên `opacity-0` đọc ra 1,00:1 — một chỗ\n // \"chữ không đọc được\" mà cổng không thể biết là cố ý. `invisible` cũng đúng hơn\n // về ngữ nghĩa: nó rút phần tử khỏi cây trợ năng thay vì để nó vẫn được đọc lên.\n \"pointer-events-none absolute start-3 top-1.5 flex size-4 items-center justify-center text-muted-foreground text-xs tabular-nums group-hover:invisible group-has-[:focus-visible]:invisible\",\n checked && \"invisible\"\n )}\n >\n {rowNumber}\n </div>\n <Checkbox\n id={id}\n className={cn(\n // Không ghi đè viền hover: `Checkbox` của gói đã đi qua `--choice-edge` /\n // `--choice-edge-strong`. `border-primary/40` vừa sai vai trò (bậc tô NỀN đem vẽ\n // NÉT) vừa chỉ 1,25:1 ở light.\n \"relative transition-[shadow,border,opacity]\",\n // Ở đây PHẢI là `opacity`, không phải `invisible` như số thứ tự bên trên: đây là\n // checkbox thật, `visibility:hidden` sẽ làm nó mất luôn khả năng nhận Tab.\n // ⚠ Khác bản gốc (3.1.0): `focus-visible:opacity-100` (và số thứ tự nhường chỗ qua\n // `group-has-[:focus-visible]`) — bản gốc chỉ hiện checkbox khi hover hay đã tick, nên\n // người dùng bàn phím Tab tới một dòng CHƯA tick thì vòng focus nằm trên một phần tử\n // trong suốt: thấy mỗi số thứ tự, không biết Space sẽ tick gì.\n \"opacity-0 group-hover:opacity-100 focus-visible:opacity-100 data-[state=checked]:opacity-100\",\n className\n )}\n checked={checked}\n {...props}\n />\n </DataGridSelectHitbox>\n );\n }\n\n return (\n <DataGridSelectHitbox htmlFor={id} size={hitboxSize} debug={debug}>\n <Checkbox id={id} className={cn(\"relative transition-[shadow,border]\", className)} checked={checked} {...props} />\n </DataGridSelectHitbox>\n );\n}\n\ninterface DataGridSelectHeaderProps<TData> extends Pick<HeaderContext<TData, unknown>, \"table\"> {\n hitboxSize?: HitboxSize;\n readOnly?: boolean;\n debug?: boolean;\n}\n\nfunction DataGridSelectHeader<TData>({ table, hitboxSize, readOnly, debug }: DataGridSelectHeaderProps<TData>) {\n const msg = useDataGridMessages();\n const onCheckedChange = React.useCallback((value: boolean) => table.toggleAllPageRowsSelected(value), [table]);\n\n if (readOnly) {\n return <div className=\"mt-1 flex items-center ps-1 text-muted-foreground text-sm\">#</div>;\n }\n\n return (\n <DataGridSelectCheckbox\n aria-label={msg.select.all}\n checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && \"indeterminate\")}\n onCheckedChange={onCheckedChange}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n );\n}\n\ninterface DataGridSelectCellProps<TData> extends Pick<CellContext<TData, unknown>, \"row\" | \"table\"> {\n hitboxSize?: HitboxSize;\n enableRowMarkers?: boolean;\n readOnly?: boolean;\n debug?: boolean;\n}\n\nfunction DataGridSelectCell<TData>({\n row,\n table,\n hitboxSize,\n enableRowMarkers,\n readOnly,\n debug\n}: DataGridSelectCellProps<TData>) {\n const msg = useDataGridMessages();\n const meta = table.options.meta;\n const rowNumber = enableRowMarkers ? (meta?.getVisualRowIndex?.(row.id) ?? row.index + 1) : undefined;\n\n const onCheckedChange = React.useCallback(\n (value: boolean) => {\n if (meta?.onRowSelect) {\n meta.onRowSelect(row.id, value, false);\n } else {\n row.toggleSelected(value);\n }\n },\n [meta, row]\n );\n\n const onClick = React.useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n if (event.shiftKey) {\n event.preventDefault();\n meta?.onRowSelect?.(row.id, !row.getIsSelected(), true);\n }\n },\n [meta, row]\n );\n\n if (readOnly) {\n return (\n <div className=\"flex items-center ps-1 text-muted-foreground text-xs tabular-nums\">\n {rowNumber ?? row.index + 1}\n </div>\n );\n }\n\n return (\n <DataGridSelectCheckbox\n aria-label={rowNumber ? msg.select.rowNumber(rowNumber) : msg.select.row}\n checked={row.getIsSelected()}\n onCheckedChange={onCheckedChange}\n onClick={onClick}\n rowNumber={rowNumber}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n );\n}\n\ninterface GetDataGridSelectColumnOptions<TData> extends Omit<Partial<ColumnDef<TData>>, \"id\" | \"header\" | \"cell\"> {\n enableRowMarkers?: boolean;\n readOnly?: boolean;\n hitboxSize?: HitboxSize;\n debug?: boolean;\n}\n\nexport function getDataGridSelectColumn<TData>({\n size = 40,\n hitboxSize = \"default\",\n enableHiding = false,\n enableResizing = false,\n enableSorting = false,\n enableRowMarkers = false,\n readOnly = false,\n debug = false,\n ...props\n}: GetDataGridSelectColumnOptions<TData> = {}): ColumnDef<TData> {\n return {\n id: \"select\",\n header: ({ table }) => (\n <DataGridSelectHeader table={table} hitboxSize={hitboxSize} readOnly={readOnly} debug={debug} />\n ),\n cell: ({ row, table }) => (\n <DataGridSelectCell\n row={row}\n table={table}\n enableRowMarkers={enableRowMarkers}\n readOnly={readOnly}\n hitboxSize={hitboxSize}\n debug={debug}\n />\n ),\n size,\n enableHiding,\n enableResizing,\n enableSorting,\n ...props\n };\n}\n"],"mappings":"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","names":[]}
@@ -2,6 +2,11 @@ import * as React from 'react';
2
2
 
3
3
  interface DivProps extends React.ComponentProps<"div"> {
4
4
  }
5
+ /**
6
+ * Chỉ là KHUNG: không có con thì nó là một ô trống cao gần hết màn hình. Ghép
7
+ * `DataGridSkeletonToolbar` và `DataGridSkeletonGrid` vào trong. Mặc định cao `100dvh − 4rem`
8
+ * (lưới chiếm cả trang); lưới cao cố định thì truyền `className="h-[352px]"` cho khớp.
9
+ */
5
10
  declare function DataGridSkeleton({ className, ...props }: DivProps): React.JSX.Element;
6
11
  interface DataGridSkeletonToolbarProps extends DivProps {
7
12
  align?: "start" | "center" | "end";
@@ -6,10 +6,7 @@ function DataGridSkeleton({ className, ...props }) {
6
6
  "div",
7
7
  {
8
8
  "data-slot": "grid-skeleton",
9
- className: cn(
10
- "flex h-[calc(100dvh-4rem)] w-full flex-col gap-4 has-[>[data-slot=grid-skeleton-toolbar]]:h-[calc(100dvh-5rem)]",
11
- className
12
- ),
9
+ className: cn("flex h-[calc(100dvh-4rem)] w-full flex-col gap-4", className),
13
10
  ...props
14
11
  }
15
12
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/data-grid-skeleton.tsx"],"sourcesContent":["import type * as React from \"react\";\nimport { Skeleton } from \"../components/ui/skeleton.js\";\nimport { cn } from \"../lib/cn.js\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {}\n\nfunction DataGridSkeleton({ className, ...props }: DivProps) {\n return (\n <div\n data-slot=\"grid-skeleton\"\n className={cn(\n \"flex h-[calc(100dvh-4rem)] w-full flex-col gap-4 has-[>[data-slot=grid-skeleton-toolbar]]:h-[calc(100dvh-5rem)]\",\n className\n )}\n {...props}\n />\n );\n}\n\ninterface DataGridSkeletonToolbarProps extends DivProps {\n align?: \"start\" | \"center\" | \"end\";\n actionCount?: number;\n}\n\nfunction DataGridSkeletonToolbar({\n align = \"end\",\n actionCount = 4,\n className,\n ...props\n}: DataGridSkeletonToolbarProps) {\n return (\n <div\n data-slot=\"grid-skeleton-toolbar\"\n className={cn(\n \"flex items-center gap-2\",\n {\n \"justify-start\": align === \"start\",\n \"justify-center\": align === \"center\",\n \"justify-end\": align === \"end\"\n },\n className\n )}\n {...props}\n >\n {Array.from({ length: actionCount }).map((_, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: danh sách placeholder tĩnh, index là key đúng\n <Skeleton key={i} className=\"h-7 w-20 shrink-0\" />\n ))}\n </div>\n );\n}\n\nfunction DataGridSkeletonGrid({ className, ...props }: DivProps) {\n return <Skeleton data-slot=\"grid-skeleton-grid\" className={cn(\"flex-1\", className)} {...props} />;\n}\n\nexport { DataGridSkeleton, DataGridSkeletonGrid, DataGridSkeletonToolbar };\n"],"mappings":"AAQI;AAPJ,SAAS,gBAAgB;AACzB,SAAS,UAAU;AAInB,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAAa;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAOA,SAAS,wBAAwB;AAAA,EAC/B,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAiC;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,UACE,iBAAiB,UAAU;AAAA,UAC3B,kBAAkB,UAAU;AAAA,UAC5B,eAAe,UAAU;AAAA,QAC3B;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH,gBAAM,KAAK,EAAE,QAAQ,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG;AAAA;AAAA,QAE3C,oBAAC,YAAiB,WAAU,uBAAb,CAAiC;AAAA,OACjD;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,qBAAqB,EAAE,WAAW,GAAG,MAAM,GAAa;AAC/D,SAAO,oBAAC,YAAS,aAAU,sBAAqB,WAAW,GAAG,UAAU,SAAS,GAAI,GAAG,OAAO;AACjG;","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/data-grid-skeleton.tsx"],"sourcesContent":["import type * as React from \"react\";\nimport { Skeleton } from \"../components/ui/skeleton.js\";\nimport { cn } from \"../lib/cn.js\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {}\n\n/**\n * Chỉ là KHUNG: không có con thì nó là một ô trống cao gần hết màn hình. Ghép\n * `DataGridSkeletonToolbar` và `DataGridSkeletonGrid` vào trong. Mặc định cao `100dvh − 4rem`\n * (lưới chiếm cả trang); lưới cao cố định thì truyền `className=\"h-[352px]\"` cho khớp.\n */\nfunction DataGridSkeleton({ className, ...props }: DivProps) {\n return (\n <div\n data-slot=\"grid-skeleton\"\n // ⚠ Khác bản gốc (3.1.0): bỏ `has-[>[data-slot=grid-skeleton-toolbar]]:h-[calc(100dvh-5rem)]`.\n // Biến thể `:has()` nặng specificity hơn một class `h-*` thường, nên hễ có toolbar bên trong\n // là chiều cao app truyền vào (`className=\"h-80\"`) thua — khung vẫn cao gần hết màn hình, và\n // tailwind-merge không gỡ được vì hai class khác biến thể. Giờ chiều cao mặc định chỉ còn một\n // class, `h-*` của app luôn thay được. Cái giá: khung có toolbar cao hơn bản gốc 1rem.\n className={cn(\"flex h-[calc(100dvh-4rem)] w-full flex-col gap-4\", className)}\n {...props}\n />\n );\n}\n\ninterface DataGridSkeletonToolbarProps extends DivProps {\n align?: \"start\" | \"center\" | \"end\";\n actionCount?: number;\n}\n\nfunction DataGridSkeletonToolbar({\n align = \"end\",\n actionCount = 4,\n className,\n ...props\n}: DataGridSkeletonToolbarProps) {\n return (\n <div\n data-slot=\"grid-skeleton-toolbar\"\n className={cn(\n \"flex items-center gap-2\",\n {\n \"justify-start\": align === \"start\",\n \"justify-center\": align === \"center\",\n \"justify-end\": align === \"end\"\n },\n className\n )}\n {...props}\n >\n {Array.from({ length: actionCount }).map((_, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: danh sách placeholder tĩnh, index là key đúng\n <Skeleton key={i} className=\"h-7 w-20 shrink-0\" />\n ))}\n </div>\n );\n}\n\nfunction DataGridSkeletonGrid({ className, ...props }: DivProps) {\n return <Skeleton data-slot=\"grid-skeleton-grid\" className={cn(\"flex-1\", className)} {...props} />;\n}\n\nexport { DataGridSkeleton, DataGridSkeletonGrid, DataGridSkeletonToolbar };\n"],"mappings":"AAaI;AAZJ,SAAS,gBAAgB;AACzB,SAAS,UAAU;AASnB,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAAa;AAC3D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MAMV,WAAW,GAAG,oDAAoD,SAAS;AAAA,MAC1E,GAAG;AAAA;AAAA,EACN;AAEJ;AAOA,SAAS,wBAAwB;AAAA,EAC/B,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAiC;AAC/B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,UACE,iBAAiB,UAAU;AAAA,UAC3B,kBAAkB,UAAU;AAAA,UAC5B,eAAe,UAAU;AAAA,QAC3B;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH,gBAAM,KAAK,EAAE,QAAQ,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG;AAAA;AAAA,QAE3C,oBAAC,YAAiB,WAAU,uBAAb,CAAiC;AAAA,OACjD;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,qBAAqB,EAAE,WAAW,GAAG,MAAM,GAAa;AAC/D,SAAO,oBAAC,YAAS,aAAU,sBAAqB,WAAW,GAAG,UAAU,SAAS,GAAI,GAAG,OAAO;AACjG;","names":[]}
@@ -40,6 +40,6 @@ interface DataGridProps<TData> extends Omit<ReturnType<typeof useDataGrid<TData>
40
40
  * `aria-label` / `aria-labelledby` / `aria-describedby` được đặt lên đúng thẻ mang
41
41
  * `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).
42
42
  */
43
- 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
+ declare function DataGrid<TData>({ dataGridRef: _dataGridRef, dataGridElementRef, 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;
44
44
 
45
45
  export { DataGrid };
@@ -14,7 +14,8 @@ import { DataGridSearch } from "./data-grid-search.js";
14
14
  import { flexRender, getColumnBorderVisibility, getColumnPinningStyle } from "./utils.js";
15
15
  const EMPTY_CELL_SELECTION_SET = /* @__PURE__ */ new Set();
16
16
  function DataGrid({
17
- dataGridRef,
17
+ dataGridRef: _dataGridRef,
18
+ dataGridElementRef,
18
19
  headerRef,
19
20
  rowMapRef,
20
21
  footerRef,
@@ -86,7 +87,7 @@ function DataGrid({
86
87
  "aria-colcount": columns.length,
87
88
  "data-slot": "grid",
88
89
  tabIndex: 0,
89
- ref: dataGridRef,
90
+ ref: dataGridElementRef,
90
91
  className: "relative grid select-none overflow-auto rounded-lg border bg-card focus:outline-none focus-visible:outline-none",
91
92
  style: {
92
93
  ...columnSizeVars,
@@ -1 +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 { useAsRef } from \"../lib/use-as-ref.js\";\nimport { DataGridColumnHeader } from \"./data-grid-column-header.js\";\nimport { DataGridContextMenu } from \"./data-grid-context-menu.js\";\nimport { DataGridLocaleProvider, useResolvedDataGridMessages } 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, DataGridMessages } from \"./messages.js\";\nimport type { Direction } from \"./types.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\"` · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn. Bỏ trống thì theo\n * `DataGridLocaleProvider` bọc ngoài, rồi ngôn ngữ của `ComitorLocaleProvider`, rồi `\"en\"`.\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: chúng theo provider,\n * hoặc nhận `locale` riêng 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 // Prop → `DataGridLocaleProvider` bọc ngoài → `ComitorLocaleProvider` → `\"en\"`. Provider bên\n // dưới giải lại với CÙNG đầu vào nên cây con thấy đúng bảng này (kèm locale ngày tương ứng).\n const msg = useResolvedDataGridMessages(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":[]}
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 { useAsRef } from \"../lib/use-as-ref.js\";\nimport { DataGridColumnHeader } from \"./data-grid-column-header.js\";\nimport { DataGridContextMenu } from \"./data-grid-context-menu.js\";\nimport { DataGridLocaleProvider, useResolvedDataGridMessages } 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, DataGridMessages } from \"./messages.js\";\nimport type { Direction } from \"./types.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\"` · `\"vi\"`, hoặc cả một bảng chuỗi tự soạn. Bỏ trống thì theo\n * `DataGridLocaleProvider` bọc ngoài, rồi ngôn ngữ của `ComitorLocaleProvider`, rồi `\"en\"`.\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: chúng theo provider,\n * hoặc nhận `locale` riêng 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: _dataGridRef,\n dataGridElementRef,\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 // Prop → `DataGridLocaleProvider` bọc ngoài → `ComitorLocaleProvider` → `\"en\"`. Provider bên\n // dưới giải lại với CÙNG đầu vào nên cây con thấy đúng bảng này (kèm locale ngày tương ứng).\n const msg = useResolvedDataGridMessages(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 // Callback ref (3.1.0): hook biết khi phần tử lưới đổi — xem `dataGridElementRef` trong `useDataGrid`.\n ref={dataGridElementRef}\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":[]}
@@ -1,3 +1,4 @@
1
+ import { normalizeVi } from "../lib/text-search.js";
1
2
  import { getDataGridMessages } from "./messages.js";
2
3
  function buildTextOperators(m) {
3
4
  const op = m.filter.operators;
@@ -118,8 +119,8 @@ function getFilterFn() {
118
119
  if (value === void 0 || value === null || value === "") {
119
120
  return true;
120
121
  }
121
- const cellValueStr = String(cellValue ?? "").toLowerCase();
122
- const filterValueStr = typeof value === "string" ? value.toLowerCase() : String(value);
122
+ const cellValueStr = normalizeVi(String(cellValue ?? ""));
123
+ const filterValueStr = typeof value === "string" ? normalizeVi(value) : String(value);
123
124
  if (operator === "contains") {
124
125
  return cellValueStr.includes(filterValueStr);
125
126
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/filters.ts"],"sourcesContent":["import type { FilterFn, Row } from \"@tanstack/react-table\";\nimport { type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport type {\n BooleanFilterOperator,\n DateFilterOperator,\n FilterOperator,\n FilterValue,\n NumberFilterOperator,\n SelectFilterOperator,\n TextFilterOperator\n} from \"./types.js\";\n\ninterface OperatorOption<TOperator extends FilterOperator> {\n label: string;\n value: TOperator;\n}\n\n/*\n * ⚠ Nhãn toán tử phụ thuộc NGÔN NGỮ, nên chúng phải dựng theo bảng chuỗi chứ không đóng băng vào\n * một mảng hằng ở đầu module. Sáu `build*` bên dưới là khuôn; sáu hằng `*_FILTER_OPERATORS` là\n * khuôn ấy áp cho bảng MẶC ĐỊNH (tiếng Anh), giữ lại vì chúng là API công khai từ `1.11.0` và vì\n * phần lớn app chỉ cần đúng bản đó.\n * Chỗ nào dựng giao diện theo `locale` thì gọi `getOperatorsForVariant(variant, messages)` —\n * đọc hằng ở trên là khoá cứng tiếng Anh.\n */\nfunction buildTextOperators(m: DataGridMessages): OperatorOption<TextFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.contains, value: \"contains\" },\n { label: op.notContains, value: \"notContains\" },\n { label: op.equals, value: \"equals\" },\n { label: op.notEquals, value: \"notEquals\" },\n { label: op.startsWith, value: \"startsWith\" },\n { label: op.endsWith, value: \"endsWith\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildNumberOperators(m: DataGridMessages): OperatorOption<NumberFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.equals, value: \"equals\" },\n { label: op.notEquals, value: \"notEquals\" },\n { label: op.lessThan, value: \"lessThan\" },\n { label: op.lessThanOrEqual, value: \"lessThanOrEqual\" },\n { label: op.greaterThan, value: \"greaterThan\" },\n { label: op.greaterThanOrEqual, value: \"greaterThanOrEqual\" },\n { label: op.isBetween, value: \"isBetween\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildDateOperators(m: DataGridMessages): OperatorOption<DateFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.dateEquals, value: \"equals\" },\n { label: op.dateNotEquals, value: \"notEquals\" },\n { label: op.before, value: \"before\" },\n { label: op.after, value: \"after\" },\n { label: op.onOrBefore, value: \"onOrBefore\" },\n { label: op.onOrAfter, value: \"onOrAfter\" },\n { label: op.isBetween, value: \"isBetween\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildSelectOperators(m: DataGridMessages): OperatorOption<SelectFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.is, value: \"is\" },\n { label: op.isNot, value: \"isNot\" },\n { label: op.isAnyOf, value: \"isAnyOf\" },\n { label: op.isNoneOf, value: \"isNoneOf\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildBooleanOperators(m: DataGridMessages): OperatorOption<BooleanFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.isTrue, value: \"isTrue\" },\n { label: op.isFalse, value: \"isFalse\" }\n ];\n}\n\nconst DEFAULT_MESSAGES = getDataGridMessages();\n\nexport const TEXT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<TextFilterOperator>> =\n buildTextOperators(DEFAULT_MESSAGES);\nexport const NUMBER_FILTER_OPERATORS: ReadonlyArray<OperatorOption<NumberFilterOperator>> =\n buildNumberOperators(DEFAULT_MESSAGES);\nexport const DATE_FILTER_OPERATORS: ReadonlyArray<OperatorOption<DateFilterOperator>> =\n buildDateOperators(DEFAULT_MESSAGES);\nexport const SELECT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<SelectFilterOperator>> =\n buildSelectOperators(DEFAULT_MESSAGES);\nexport const BOOLEAN_FILTER_OPERATORS: ReadonlyArray<OperatorOption<BooleanFilterOperator>> =\n buildBooleanOperators(DEFAULT_MESSAGES);\n\nexport function getDefaultOperator(variant: string): FilterOperator {\n switch (variant) {\n case \"number\":\n return \"equals\";\n case \"date\":\n return \"equals\";\n case \"select\":\n case \"multi-select\":\n return \"is\";\n case \"checkbox\":\n return \"isTrue\";\n default:\n return \"contains\";\n }\n}\n\n/**\n * Toán tử hợp lệ cho một kiểu ô, kèm nhãn đã dịch.\n *\n * `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ũ\n * không phải sửa.\n */\nexport function getOperatorsForVariant(\n variant: string,\n messages?: DataGridMessages\n): ReadonlyArray<{\n label: string;\n value: FilterOperator;\n}> {\n const m = messages ?? DEFAULT_MESSAGES;\n switch (variant) {\n case \"number\":\n return buildNumberOperators(m);\n case \"date\":\n return buildDateOperators(m);\n case \"select\":\n case \"multi-select\":\n return buildSelectOperators(m);\n case \"checkbox\":\n return buildBooleanOperators(m);\n default:\n return buildTextOperators(m);\n }\n}\n\nexport function getFilterFn<TData>(): FilterFn<TData> {\n return (row: Row<TData>, columnId: string, filterValue: unknown): boolean => {\n if (!filterValue || typeof filterValue !== \"object\") {\n return true;\n }\n\n const filter = filterValue as FilterValue;\n const { operator, value, endValue } = filter;\n\n const cellValue = row.getValue(columnId);\n\n if (operator === \"isEmpty\") {\n return (\n cellValue === null ||\n cellValue === undefined ||\n cellValue === \"\" ||\n (Array.isArray(cellValue) && cellValue.length === 0)\n );\n }\n\n if (operator === \"isNotEmpty\") {\n return !(\n cellValue === null ||\n cellValue === undefined ||\n cellValue === \"\" ||\n (Array.isArray(cellValue) && cellValue.length === 0)\n );\n }\n\n if (operator === \"isTrue\") {\n return cellValue === true;\n }\n\n if (operator === \"isFalse\") {\n return cellValue === false || !cellValue;\n }\n\n if (value === undefined || value === null || value === \"\") {\n return true;\n }\n\n const cellValueStr = String(cellValue ?? \"\").toLowerCase();\n const filterValueStr = typeof value === \"string\" ? value.toLowerCase() : String(value);\n\n if (operator === \"contains\") {\n return cellValueStr.includes(filterValueStr);\n }\n\n if (operator === \"notContains\") {\n return !cellValueStr.includes(filterValueStr);\n }\n\n if (operator === \"equals\") {\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n return cellValue === value;\n }\n if (cellValue instanceof Date && typeof value === \"string\") {\n const cellDate = new Date(cellValue);\n const filterDate = new Date(value);\n return cellDate.toDateString() === filterDate.toDateString();\n }\n return cellValueStr === filterValueStr;\n }\n\n if (operator === \"notEquals\") {\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n return cellValue !== value;\n }\n if (cellValue instanceof Date && typeof value === \"string\") {\n const cellDate = new Date(cellValue);\n const filterDate = new Date(value);\n return cellDate.toDateString() !== filterDate.toDateString();\n }\n return cellValueStr !== filterValueStr;\n }\n\n if (operator === \"startsWith\") {\n return cellValueStr.startsWith(filterValueStr);\n }\n\n if (operator === \"endsWith\") {\n return cellValueStr.endsWith(filterValueStr);\n }\n\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n if (operator === \"greaterThan\") {\n return cellValue > value;\n }\n\n if (operator === \"greaterThanOrEqual\") {\n return cellValue >= value;\n }\n\n if (operator === \"lessThan\") {\n return cellValue < value;\n }\n\n if (operator === \"lessThanOrEqual\") {\n return cellValue <= value;\n }\n\n if (operator === \"isBetween\" && typeof endValue === \"number\") {\n return cellValue >= value && cellValue <= endValue;\n }\n }\n\n if (cellValue instanceof Date || typeof cellValue === \"string\") {\n const cellDate = new Date(cellValue);\n if (!Number.isNaN(cellDate.getTime()) && typeof value === \"string\") {\n const filterDate = new Date(value);\n\n if (operator === \"before\") {\n return cellDate < filterDate;\n }\n\n if (operator === \"after\") {\n return cellDate > filterDate;\n }\n\n if (operator === \"onOrBefore\") {\n return cellDate <= filterDate;\n }\n\n if (operator === \"onOrAfter\") {\n return cellDate >= filterDate;\n }\n\n if (operator === \"isBetween\" && typeof endValue === \"string\") {\n const filterDate2 = new Date(endValue);\n return cellDate >= filterDate && cellDate <= filterDate2;\n }\n }\n }\n\n if (operator === \"is\") {\n if (Array.isArray(cellValue)) {\n return cellValue.some((v) => String(v) === String(value));\n }\n return String(cellValue) === String(value);\n }\n\n if (operator === \"isNot\") {\n if (Array.isArray(cellValue)) {\n return !cellValue.some((v) => String(v) === String(value));\n }\n return String(cellValue) !== String(value);\n }\n\n if (operator === \"isAnyOf\" && Array.isArray(value)) {\n if (Array.isArray(cellValue)) {\n return cellValue.some((v) => value.some((fv) => String(v) === String(fv)));\n }\n return value.some((fv) => String(cellValue) === String(fv));\n }\n\n if (operator === \"isNoneOf\" && Array.isArray(value)) {\n if (Array.isArray(cellValue)) {\n return !cellValue.some((v) => value.some((fv) => String(v) === String(fv)));\n }\n return !value.some((fv) => String(cellValue) === String(fv));\n }\n\n return true;\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/data-grid/filters.ts"],"sourcesContent":["import type { FilterFn, Row } from \"@tanstack/react-table\";\nimport { normalizeVi } from \"../lib/text-search.js\";\nimport { type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport type {\n BooleanFilterOperator,\n DateFilterOperator,\n FilterOperator,\n FilterValue,\n NumberFilterOperator,\n SelectFilterOperator,\n TextFilterOperator\n} from \"./types.js\";\n\ninterface OperatorOption<TOperator extends FilterOperator> {\n label: string;\n value: TOperator;\n}\n\n/*\n * ⚠ Nhãn toán tử phụ thuộc NGÔN NGỮ, nên chúng phải dựng theo bảng chuỗi chứ không đóng băng vào\n * một mảng hằng ở đầu module. Sáu `build*` bên dưới là khuôn; sáu hằng `*_FILTER_OPERATORS` là\n * khuôn ấy áp cho bảng MẶC ĐỊNH (tiếng Anh), giữ lại vì chúng là API công khai từ `1.11.0` và vì\n * phần lớn app chỉ cần đúng bản đó.\n * Chỗ nào dựng giao diện theo `locale` thì gọi `getOperatorsForVariant(variant, messages)` —\n * đọc hằng ở trên là khoá cứng tiếng Anh.\n */\nfunction buildTextOperators(m: DataGridMessages): OperatorOption<TextFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.contains, value: \"contains\" },\n { label: op.notContains, value: \"notContains\" },\n { label: op.equals, value: \"equals\" },\n { label: op.notEquals, value: \"notEquals\" },\n { label: op.startsWith, value: \"startsWith\" },\n { label: op.endsWith, value: \"endsWith\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildNumberOperators(m: DataGridMessages): OperatorOption<NumberFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.equals, value: \"equals\" },\n { label: op.notEquals, value: \"notEquals\" },\n { label: op.lessThan, value: \"lessThan\" },\n { label: op.lessThanOrEqual, value: \"lessThanOrEqual\" },\n { label: op.greaterThan, value: \"greaterThan\" },\n { label: op.greaterThanOrEqual, value: \"greaterThanOrEqual\" },\n { label: op.isBetween, value: \"isBetween\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildDateOperators(m: DataGridMessages): OperatorOption<DateFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.dateEquals, value: \"equals\" },\n { label: op.dateNotEquals, value: \"notEquals\" },\n { label: op.before, value: \"before\" },\n { label: op.after, value: \"after\" },\n { label: op.onOrBefore, value: \"onOrBefore\" },\n { label: op.onOrAfter, value: \"onOrAfter\" },\n { label: op.isBetween, value: \"isBetween\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildSelectOperators(m: DataGridMessages): OperatorOption<SelectFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.is, value: \"is\" },\n { label: op.isNot, value: \"isNot\" },\n { label: op.isAnyOf, value: \"isAnyOf\" },\n { label: op.isNoneOf, value: \"isNoneOf\" },\n { label: op.isEmpty, value: \"isEmpty\" },\n { label: op.isNotEmpty, value: \"isNotEmpty\" }\n ];\n}\n\nfunction buildBooleanOperators(m: DataGridMessages): OperatorOption<BooleanFilterOperator>[] {\n const op = m.filter.operators;\n return [\n { label: op.isTrue, value: \"isTrue\" },\n { label: op.isFalse, value: \"isFalse\" }\n ];\n}\n\nconst DEFAULT_MESSAGES = getDataGridMessages();\n\nexport const TEXT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<TextFilterOperator>> =\n buildTextOperators(DEFAULT_MESSAGES);\nexport const NUMBER_FILTER_OPERATORS: ReadonlyArray<OperatorOption<NumberFilterOperator>> =\n buildNumberOperators(DEFAULT_MESSAGES);\nexport const DATE_FILTER_OPERATORS: ReadonlyArray<OperatorOption<DateFilterOperator>> =\n buildDateOperators(DEFAULT_MESSAGES);\nexport const SELECT_FILTER_OPERATORS: ReadonlyArray<OperatorOption<SelectFilterOperator>> =\n buildSelectOperators(DEFAULT_MESSAGES);\nexport const BOOLEAN_FILTER_OPERATORS: ReadonlyArray<OperatorOption<BooleanFilterOperator>> =\n buildBooleanOperators(DEFAULT_MESSAGES);\n\nexport function getDefaultOperator(variant: string): FilterOperator {\n switch (variant) {\n case \"number\":\n return \"equals\";\n case \"date\":\n return \"equals\";\n case \"select\":\n case \"multi-select\":\n return \"is\";\n case \"checkbox\":\n return \"isTrue\";\n default:\n return \"contains\";\n }\n}\n\n/**\n * Toán tử hợp lệ cho một kiểu ô, kèm nhãn đã dịch.\n *\n * `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ũ\n * không phải sửa.\n */\nexport function getOperatorsForVariant(\n variant: string,\n messages?: DataGridMessages\n): ReadonlyArray<{\n label: string;\n value: FilterOperator;\n}> {\n const m = messages ?? DEFAULT_MESSAGES;\n switch (variant) {\n case \"number\":\n return buildNumberOperators(m);\n case \"date\":\n return buildDateOperators(m);\n case \"select\":\n case \"multi-select\":\n return buildSelectOperators(m);\n case \"checkbox\":\n return buildBooleanOperators(m);\n default:\n return buildTextOperators(m);\n }\n}\n\nexport function getFilterFn<TData>(): FilterFn<TData> {\n return (row: Row<TData>, columnId: string, filterValue: unknown): boolean => {\n if (!filterValue || typeof filterValue !== \"object\") {\n return true;\n }\n\n const filter = filterValue as FilterValue;\n const { operator, value, endValue } = filter;\n\n const cellValue = row.getValue(columnId);\n\n if (operator === \"isEmpty\") {\n return (\n cellValue === null ||\n cellValue === undefined ||\n cellValue === \"\" ||\n (Array.isArray(cellValue) && cellValue.length === 0)\n );\n }\n\n if (operator === \"isNotEmpty\") {\n return !(\n cellValue === null ||\n cellValue === undefined ||\n cellValue === \"\" ||\n (Array.isArray(cellValue) && cellValue.length === 0)\n );\n }\n\n if (operator === \"isTrue\") {\n return cellValue === true;\n }\n\n if (operator === \"isFalse\") {\n return cellValue === false || !cellValue;\n }\n\n if (value === undefined || value === null || value === \"\") {\n return true;\n }\n\n /*\n * ⚠ Khác bản gốc (3.1.0): hai vế đi qua `normalizeVi` — bỏ dấu, hạ chữ thường, `trim` — thay vì\n * chỉ `toLowerCase()`. Bản gốc phân biệt dấu, nên \"Chứa: duc thanh\" không ra \"Nguyễn Đức Thành\"\n * trong khi `Combobox` / `SearchFilterBar` / `Mention` của gói đều ra — cùng một người dùng, hai\n * luật tìm. Chỉ các toán tử CHỮ đọc hai chuỗi này; số và ngày có nhánh riêng bên dưới.\n */\n const cellValueStr = normalizeVi(String(cellValue ?? \"\"));\n const filterValueStr = typeof value === \"string\" ? normalizeVi(value) : String(value);\n\n if (operator === \"contains\") {\n return cellValueStr.includes(filterValueStr);\n }\n\n if (operator === \"notContains\") {\n return !cellValueStr.includes(filterValueStr);\n }\n\n if (operator === \"equals\") {\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n return cellValue === value;\n }\n if (cellValue instanceof Date && typeof value === \"string\") {\n const cellDate = new Date(cellValue);\n const filterDate = new Date(value);\n return cellDate.toDateString() === filterDate.toDateString();\n }\n return cellValueStr === filterValueStr;\n }\n\n if (operator === \"notEquals\") {\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n return cellValue !== value;\n }\n if (cellValue instanceof Date && typeof value === \"string\") {\n const cellDate = new Date(cellValue);\n const filterDate = new Date(value);\n return cellDate.toDateString() !== filterDate.toDateString();\n }\n return cellValueStr !== filterValueStr;\n }\n\n if (operator === \"startsWith\") {\n return cellValueStr.startsWith(filterValueStr);\n }\n\n if (operator === \"endsWith\") {\n return cellValueStr.endsWith(filterValueStr);\n }\n\n if (typeof cellValue === \"number\" && typeof value === \"number\") {\n if (operator === \"greaterThan\") {\n return cellValue > value;\n }\n\n if (operator === \"greaterThanOrEqual\") {\n return cellValue >= value;\n }\n\n if (operator === \"lessThan\") {\n return cellValue < value;\n }\n\n if (operator === \"lessThanOrEqual\") {\n return cellValue <= value;\n }\n\n if (operator === \"isBetween\" && typeof endValue === \"number\") {\n return cellValue >= value && cellValue <= endValue;\n }\n }\n\n if (cellValue instanceof Date || typeof cellValue === \"string\") {\n const cellDate = new Date(cellValue);\n if (!Number.isNaN(cellDate.getTime()) && typeof value === \"string\") {\n const filterDate = new Date(value);\n\n if (operator === \"before\") {\n return cellDate < filterDate;\n }\n\n if (operator === \"after\") {\n return cellDate > filterDate;\n }\n\n if (operator === \"onOrBefore\") {\n return cellDate <= filterDate;\n }\n\n if (operator === \"onOrAfter\") {\n return cellDate >= filterDate;\n }\n\n if (operator === \"isBetween\" && typeof endValue === \"string\") {\n const filterDate2 = new Date(endValue);\n return cellDate >= filterDate && cellDate <= filterDate2;\n }\n }\n }\n\n if (operator === \"is\") {\n if (Array.isArray(cellValue)) {\n return cellValue.some((v) => String(v) === String(value));\n }\n return String(cellValue) === String(value);\n }\n\n if (operator === \"isNot\") {\n if (Array.isArray(cellValue)) {\n return !cellValue.some((v) => String(v) === String(value));\n }\n return String(cellValue) !== String(value);\n }\n\n if (operator === \"isAnyOf\" && Array.isArray(value)) {\n if (Array.isArray(cellValue)) {\n return cellValue.some((v) => value.some((fv) => String(v) === String(fv)));\n }\n return value.some((fv) => String(cellValue) === String(fv));\n }\n\n if (operator === \"isNoneOf\" && Array.isArray(value)) {\n if (Array.isArray(cellValue)) {\n return !cellValue.some((v) => value.some((fv) => String(v) === String(fv)));\n }\n return !value.some((fv) => String(cellValue) === String(fv));\n }\n\n return true;\n };\n}\n"],"mappings":"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","names":[]}
@@ -59,6 +59,13 @@ declare module "@tanstack/react-table" {
59
59
  label?: string;
60
60
  /** Kiểu ô của cột — xem {@link CellOpts} */
61
61
  cell?: CellOpts;
62
+ /**
63
+ * Khoá sửa RIÊNG cột này (thêm ở `3.1.0`): ô vẫn hiển thị theo kiểu của nó, lọc/sắp xếp/sao chép
64
+ * như thường, nhưng không mở trình sửa, và mọi đường GHI — gõ, dán, xoá nội dung, cắt, bấm ô
65
+ * checkbox — bỏ qua nó. Dùng cho cột định danh hay cột tính ra (mã đơn, SKU, "liên hệ lần cuối")
66
+ * trong một lưới mà các cột khác vẫn sửa được. Khoá cả lưới thì dùng `readOnly` của `useDataGrid`.
67
+ */
68
+ readOnly?: boolean;
62
69
  }
63
70
  interface TableMeta<TData extends RowData> {
64
71
  dataGridRef?: React.RefObject<HTMLElement | null>;
@@ -82,6 +82,7 @@ interface UseDataGridProps<TData> extends Omit<TableOptions<TData>, "pageCount"
82
82
  }
83
83
  declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, overscan, dir: dirProp, initialState, locale, ...props }: UseDataGridProps<TData>): {
84
84
  dataGridRef: React.RefObject<HTMLDivElement | null>;
85
+ dataGridElementRef: (node: HTMLDivElement | null) => void;
85
86
  headerRef: React.RefObject<HTMLDivElement | null>;
86
87
  rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;
87
88
  footerRef: React.RefObject<HTMLDivElement | null>;
@@ -106,7 +107,7 @@ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, o
106
107
  pasteDialog: PasteDialogState;
107
108
  onRowAdd: ((event?: React.MouseEvent<HTMLDivElement>) => Promise<void>) | undefined;
108
109
  adjustLayout: boolean;
109
- locale: "en" | "vi" | {
110
+ locale: {
110
111
  grid: {
111
112
  label: string;
112
113
  addRow: string;
@@ -381,7 +382,7 @@ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, o
381
382
  shortcuts: string;
382
383
  };
383
384
  };
384
- } | undefined;
385
+ } | "en" | "vi" | undefined;
385
386
  };
386
387
 
387
388
  export { type UseDataGridProps, useDataGrid };