@comitor/ui 1.11.0 → 2.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 (176) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +23 -7
  3. package/dist/components/ui/action-bar-constants.d.ts +31 -0
  4. package/dist/components/ui/action-bar-constants.js +9 -0
  5. package/dist/components/ui/action-bar-constants.js.map +1 -0
  6. package/dist/components/ui/action-bar.d.ts +67 -0
  7. package/dist/components/ui/action-bar.js +522 -0
  8. package/dist/components/ui/action-bar.js.map +1 -0
  9. package/dist/components/ui/badge-overflow.d.ts +30 -0
  10. package/dist/components/ui/badge-overflow.js +179 -0
  11. package/dist/components/ui/badge-overflow.js.map +1 -0
  12. package/dist/components/ui/banner.d.ts +79 -0
  13. package/dist/components/ui/banner.js +518 -0
  14. package/dist/components/ui/banner.js.map +1 -0
  15. package/dist/components/ui/circular-progress.d.ts +50 -0
  16. package/dist/components/ui/circular-progress.js +266 -0
  17. package/dist/components/ui/circular-progress.js.map +1 -0
  18. package/dist/components/ui/cropper-constants.d.ts +34 -0
  19. package/dist/components/ui/cropper-constants.js +10 -0
  20. package/dist/components/ui/cropper-constants.js.map +1 -0
  21. package/dist/components/ui/cropper.d.ts +106 -0
  22. package/dist/components/ui/cropper.js +1269 -0
  23. package/dist/components/ui/cropper.js.map +1 -0
  24. package/dist/components/ui/drawer.d.ts +1 -1
  25. package/dist/components/ui/drawer.js +2 -2
  26. package/dist/components/ui/drawer.js.map +1 -1
  27. package/dist/components/ui/kanban-constants.d.ts +61 -0
  28. package/dist/components/ui/kanban-constants.js +21 -0
  29. package/dist/components/ui/kanban-constants.js.map +1 -0
  30. package/dist/components/ui/kanban.d.ts +72 -0
  31. package/dist/components/ui/kanban.js +787 -0
  32. package/dist/components/ui/kanban.js.map +1 -0
  33. package/dist/components/ui/key-value-constants.d.ts +33 -0
  34. package/dist/components/ui/key-value-constants.js +11 -0
  35. package/dist/components/ui/key-value-constants.js.map +1 -0
  36. package/dist/components/ui/key-value.d.ts +96 -0
  37. package/dist/components/ui/key-value.js +641 -0
  38. package/dist/components/ui/key-value.js.map +1 -0
  39. package/dist/components/ui/mask-input-constants.d.ts +99 -0
  40. package/dist/components/ui/mask-input-constants.js +601 -0
  41. package/dist/components/ui/mask-input-constants.js.map +1 -0
  42. package/dist/components/ui/mask-input.d.ts +36 -0
  43. package/dist/components/ui/mask-input.js +558 -0
  44. package/dist/components/ui/mask-input.js.map +1 -0
  45. package/dist/components/ui/mention.d.ts +31 -0
  46. package/dist/components/ui/mention.js +128 -0
  47. package/dist/components/ui/mention.js.map +1 -0
  48. package/dist/components/ui/phone-input-constants.d.ts +54 -0
  49. package/dist/components/ui/phone-input-constants.js +9 -0
  50. package/dist/components/ui/phone-input-constants.js.map +1 -0
  51. package/dist/components/ui/phone-input.d.ts +85 -0
  52. package/dist/components/ui/phone-input.js +743 -0
  53. package/dist/components/ui/phone-input.js.map +1 -0
  54. package/dist/components/ui/rating.d.ts +56 -0
  55. package/dist/components/ui/rating.js +646 -0
  56. package/dist/components/ui/rating.js.map +1 -0
  57. package/dist/components/ui/responsive-dialog.d.ts +35 -0
  58. package/dist/components/ui/responsive-dialog.js +193 -0
  59. package/dist/components/ui/responsive-dialog.js.map +1 -0
  60. package/dist/components/ui/scroll-spy.d.ts +42 -0
  61. package/dist/components/ui/scroll-spy.js +334 -0
  62. package/dist/components/ui/scroll-spy.js.map +1 -0
  63. package/dist/components/ui/stepper-constants.d.ts +29 -0
  64. package/dist/components/ui/stepper-constants.js +9 -0
  65. package/dist/components/ui/stepper-constants.js.map +1 -0
  66. package/dist/components/ui/stepper.d.ts +80 -0
  67. package/dist/components/ui/stepper.js +902 -0
  68. package/dist/components/ui/stepper.js.map +1 -0
  69. package/dist/components/ui/tags-input.d.ts +41 -0
  70. package/dist/components/ui/tags-input.js +127 -0
  71. package/dist/components/ui/tags-input.js.map +1 -0
  72. package/dist/components/ui/time-picker-constants.d.ts +48 -0
  73. package/dist/components/ui/time-picker-constants.js +14 -0
  74. package/dist/components/ui/time-picker-constants.js.map +1 -0
  75. package/dist/components/ui/time-picker.d.ts +115 -0
  76. package/dist/components/ui/time-picker.js +1620 -0
  77. package/dist/components/ui/time-picker.js.map +1 -0
  78. package/dist/components/ui/timeline.d.ts +32 -0
  79. package/dist/components/ui/timeline.js +559 -0
  80. package/dist/components/ui/timeline.js.map +1 -0
  81. package/dist/components/ui/tour-constants.d.ts +39 -0
  82. package/dist/components/ui/tour-constants.js +12 -0
  83. package/dist/components/ui/tour-constants.js.map +1 -0
  84. package/dist/components/ui/tour.d.ts +124 -0
  85. package/dist/components/ui/tour.js +1207 -0
  86. package/dist/components/ui/tour.js.map +1 -0
  87. package/dist/data-grid/data-grid-cell-variants.js +10 -6
  88. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  89. package/dist/data-grid/data-grid-cell-wrapper.js +1 -1
  90. package/dist/data-grid/data-grid-cell-wrapper.js.map +1 -1
  91. package/dist/data-grid/data-grid-column-header.js +4 -2
  92. package/dist/data-grid/data-grid-column-header.js.map +1 -1
  93. package/dist/data-grid/data-grid-context-menu.js +5 -4
  94. package/dist/data-grid/data-grid-context-menu.js.map +1 -1
  95. package/dist/data-grid/data-grid-filter-menu.d.ts +12 -1
  96. package/dist/data-grid/data-grid-filter-menu.js +28 -17
  97. package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
  98. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +12 -1
  99. package/dist/data-grid/data-grid-keyboard-shortcuts.js +7 -5
  100. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
  101. package/dist/data-grid/data-grid-locale.d.ts +24 -0
  102. package/dist/data-grid/data-grid-locale.js +25 -0
  103. package/dist/data-grid/data-grid-locale.js.map +1 -0
  104. package/dist/data-grid/data-grid-paste-dialog.js +3 -2
  105. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
  106. package/dist/data-grid/data-grid-row-height-menu.d.ts +11 -1
  107. package/dist/data-grid/data-grid-row-height-menu.js +25 -28
  108. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
  109. package/dist/data-grid/data-grid-row.js +1 -1
  110. package/dist/data-grid/data-grid-row.js.map +1 -1
  111. package/dist/data-grid/data-grid-search.js +6 -5
  112. package/dist/data-grid/data-grid-search.js.map +1 -1
  113. package/dist/data-grid/data-grid-select-column.js +3 -1
  114. package/dist/data-grid/data-grid-select-column.js.map +1 -1
  115. package/dist/data-grid/data-grid-sort-menu.d.ts +12 -1
  116. package/dist/data-grid/data-grid-sort-menu.js +25 -13
  117. package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
  118. package/dist/data-grid/data-grid-view-menu.d.ts +12 -1
  119. package/dist/data-grid/data-grid-view-menu.js +12 -4
  120. package/dist/data-grid/data-grid-view-menu.js.map +1 -1
  121. package/dist/data-grid/data-grid.d.ts +14 -2
  122. package/dist/data-grid/data-grid.js +7 -4
  123. package/dist/data-grid/data-grid.js.map +1 -1
  124. package/dist/data-grid/filters.d.ts +17 -21
  125. package/dist/data-grid/filters.js +73 -52
  126. package/dist/data-grid/filters.js.map +1 -1
  127. package/dist/data-grid/index.d.ts +4 -1
  128. package/dist/data-grid/index.js +20 -2
  129. package/dist/data-grid/index.js.map +1 -1
  130. package/dist/data-grid/messages-en.d.ts +294 -0
  131. package/dist/data-grid/messages-en.js +279 -0
  132. package/dist/data-grid/messages-en.js.map +1 -0
  133. package/dist/data-grid/messages-vi.d.ts +12 -0
  134. package/dist/data-grid/messages-vi.js +279 -0
  135. package/dist/data-grid/messages-vi.js.map +1 -0
  136. package/dist/data-grid/messages.d.ts +549 -262
  137. package/dist/data-grid/messages.js +16 -275
  138. package/dist/data-grid/messages.js.map +1 -1
  139. package/dist/data-grid/sortable.js +1 -1
  140. package/dist/data-grid/sortable.js.map +1 -1
  141. package/dist/data-grid/use-data-grid-undo-redo.d.ts +13 -1
  142. package/dist/data-grid/use-data-grid-undo-redo.js +13 -10
  143. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  144. package/dist/data-grid/use-data-grid.d.ts +281 -1
  145. package/dist/data-grid/use-data-grid.js +24 -19
  146. package/dist/data-grid/use-data-grid.js.map +1 -1
  147. package/dist/data-grid/utils.d.ts +9 -1
  148. package/dist/data-grid/utils.js +3 -2
  149. package/dist/data-grid/utils.js.map +1 -1
  150. package/dist/index.d.ts +31 -0
  151. package/dist/index.js +42 -0
  152. package/dist/index.js.map +1 -1
  153. package/dist/lib/compose-refs.js.map +1 -0
  154. package/dist/lib/use-as-ref.js.map +1 -0
  155. package/dist/lib/use-is-mobile.d.ts +10 -0
  156. package/dist/lib/use-is-mobile.js +21 -0
  157. package/dist/lib/use-is-mobile.js.map +1 -0
  158. package/dist/lib/use-isomorphic-layout-effect.js.map +1 -0
  159. package/dist/lib/use-lazy-ref.js.map +1 -0
  160. package/dist/lib/visually-hidden-input.d.ts +12 -0
  161. package/dist/lib/visually-hidden-input.js +105 -0
  162. package/dist/lib/visually-hidden-input.js.map +1 -0
  163. package/package.json +5 -1
  164. package/styles.css +19 -0
  165. package/dist/data-grid/compose-refs.js.map +0 -1
  166. package/dist/data-grid/use-as-ref.js.map +0 -1
  167. package/dist/data-grid/use-isomorphic-layout-effect.js.map +0 -1
  168. package/dist/data-grid/use-lazy-ref.js.map +0 -1
  169. /package/dist/{data-grid → lib}/compose-refs.d.ts +0 -0
  170. /package/dist/{data-grid → lib}/compose-refs.js +0 -0
  171. /package/dist/{data-grid → lib}/use-as-ref.d.ts +0 -0
  172. /package/dist/{data-grid → lib}/use-as-ref.js +0 -0
  173. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.d.ts +0 -0
  174. /package/dist/{data-grid → lib}/use-isomorphic-layout-effect.js +0 -0
  175. /package/dist/{data-grid → lib}/use-lazy-ref.d.ts +0 -0
  176. /package/dist/{data-grid → lib}/use-lazy-ref.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-grid/use-data-grid-undo-redo.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { msg } from \"./messages.js\";\nimport { toastInfo, toastSuccess } from \"./toast.js\";\nimport { useAsRef } from \"./use-as-ref.js\";\nimport { useLazyRef } from \"./use-lazy-ref.js\";\nimport { getIsInPopover } from \"./utils.js\";\n\nconst DEFAULT_MAX_HISTORY = 100;\nconst BATCH_TIMEOUT = 300;\n\ninterface HistoryEntry<TData> {\n variant: \"cells_update\" | \"rows_add\" | \"rows_delete\";\n count: number;\n timestamp: number;\n undo: (currentData: TData[]) => TData[];\n redo: (currentData: TData[]) => TData[];\n}\n\ninterface UndoRedoCellUpdate {\n rowId: string;\n columnId: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\ninterface StoreState<TData> {\n undoStack: HistoryEntry<TData>[];\n redoStack: HistoryEntry<TData>[];\n hasPendingChanges: boolean;\n}\n\ninterface Store<TData> {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState<TData>;\n push: (entry: HistoryEntry<TData>) => void;\n undo: () => HistoryEntry<TData> | null;\n redo: () => HistoryEntry<TData> | null;\n clear: () => void;\n setPendingChanges: (value: boolean) => void;\n notify: () => void;\n}\n\nfunction useStore<T>(store: Store<T>, selector: (state: StoreState<T>) => boolean): boolean {\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction buildIndexById<TData>(data: TData[], getRowId: (row: TData) => string): Map<string, number> {\n const map = new Map<string, number>();\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n if (row) {\n map.set(getRowId(row), i);\n }\n }\n return map;\n}\n\nfunction getPendingKey(rowId: string, columnId: string): string {\n return `${rowId}\\0${columnId}`;\n}\n\n/**\n * Tuỳ chọn của {@link useDataGridUndoRedo}. Lịch sử nằm hoàn toàn ở phía trình duyệt:\n * hook tính ra mảng dữ liệu trước/sau rồi trả về qua `onDataChange` — ứng dụng tự\n * đồng bộ lên máy chủ (hoặc bỏ qua nếu chỉ làm việc với state cục bộ).\n */\ninterface UseDataGridUndoRedoProps<TData> {\n data: TData[];\n onDataChange: (data: TData[]) => void;\n /** Khoá định danh bản ghi — phải khớp với `getRowId` của lưới */\n getRowId: (row: TData) => string;\n /** Số thao tác giữ trong lịch sử (mặc định 100) */\n maxHistory?: number;\n enabled?: boolean;\n}\n\ninterface UseDataGridUndoRedoReturn<TData> {\n canUndo: boolean;\n canRedo: boolean;\n onUndo: () => void;\n onRedo: () => void;\n onClear: () => void;\n trackCellsUpdate: (updates: UndoRedoCellUpdate[]) => void;\n trackRowsAdd: (rows: TData[]) => void;\n trackRowsDelete: (rows: TData[]) => void;\n}\n\nfunction useDataGridUndoRedo<TData>({\n data,\n onDataChange,\n getRowId,\n maxHistory = DEFAULT_MAX_HISTORY,\n enabled = true\n}: UseDataGridUndoRedoProps<TData>): UseDataGridUndoRedoReturn<TData> {\n const propsRef = useAsRef({\n data,\n onDataChange,\n getRowId,\n maxHistory,\n enabled\n });\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n\n const stateRef = useLazyRef<StoreState<TData>>(() => ({\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n }));\n\n const pendingBatchRef = React.useRef<{\n byKey: Map<string, UndoRedoCellUpdate>;\n timeoutId: ReturnType<typeof setTimeout> | null;\n }>({\n byKey: new Map(),\n timeoutId: null\n });\n\n const pendingNotifyRef = React.useRef(false);\n\n const store = React.useMemo<Store<TData>>(() => {\n return {\n subscribe: (callback) => {\n listenersRef.current.add(callback);\n return () => listenersRef.current.delete(callback);\n },\n getState: () => stateRef.current,\n push: (entry) => {\n const state = stateRef.current;\n const newUndoStack = [...state.undoStack, entry];\n\n if (newUndoStack.length > propsRef.current.maxHistory) {\n newUndoStack.shift();\n }\n\n stateRef.current = {\n undoStack: newUndoStack,\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n undo: () => {\n const state = stateRef.current;\n if (state.undoStack.length === 0) return null;\n\n const entry = state.undoStack[state.undoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: state.undoStack.slice(0, -1),\n redoStack: [...state.redoStack, entry],\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n redo: () => {\n const state = stateRef.current;\n if (state.redoStack.length === 0) return null;\n\n const entry = state.redoStack[state.redoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: [...state.undoStack, entry],\n redoStack: state.redoStack.slice(0, -1),\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n clear: () => {\n stateRef.current = {\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n setPendingChanges: (value) => {\n if (stateRef.current.hasPendingChanges === value) return;\n stateRef.current = {\n ...stateRef.current,\n hasPendingChanges: value\n };\n\n if (!pendingNotifyRef.current) {\n pendingNotifyRef.current = true;\n queueMicrotask(() => {\n pendingNotifyRef.current = false;\n store.notify();\n });\n }\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef]);\n\n const canUndo = useStore(store, (state) => state.undoStack.length > 0 || state.hasPendingChanges);\n const canRedo = useStore(store, (state) => state.redoStack.length > 0);\n\n const onCommit = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.byKey.size === 0) return;\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n\n const updates = Array.from(pending.byKey.values());\n pending.byKey.clear();\n\n const { getRowId } = propsRef.current;\n\n const entry: HistoryEntry<TData> = {\n variant: \"cells_update\",\n count: updates.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = {\n ...row,\n [update.columnId]: update.previousValue\n };\n }\n }\n }\n return newData;\n },\n redo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = { ...row, [update.columnId]: update.newValue };\n }\n }\n }\n return newData;\n }\n };\n\n store.push(entry);\n }, [store, propsRef]);\n\n const onUndo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.undo();\n if (!entry) {\n toastInfo(msg.toast.nothingToUndo);\n return;\n }\n\n const newData = entry.undo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msg.toast.undone(entry.count));\n }, [store, propsRef, onCommit]);\n\n const onRedo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.redo();\n if (!entry) {\n toastInfo(msg.toast.nothingToRedo);\n return;\n }\n\n const newData = entry.redo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msg.toast.redone(entry.count));\n }, [store, propsRef, onCommit]);\n\n const onClear = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n pending.byKey.clear();\n\n store.clear();\n }, [store]);\n\n const trackCellsUpdate = React.useCallback(\n (updates: UndoRedoCellUpdate[]) => {\n if (!propsRef.current.enabled || updates.length === 0) return;\n\n const filteredUpdates = updates.filter((u) => !Object.is(u.previousValue, u.newValue));\n if (filteredUpdates.length === 0) return;\n\n const pending = pendingBatchRef.current;\n\n for (const update of filteredUpdates) {\n const key = getPendingKey(update.rowId, update.columnId);\n const existing = pending.byKey.get(key);\n\n if (existing) {\n pending.byKey.set(key, { ...existing, newValue: update.newValue });\n } else {\n pending.byKey.set(key, update);\n }\n }\n\n store.setPendingChanges(true);\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n pending.timeoutId = setTimeout(onCommit, BATCH_TIMEOUT);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsAdd = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId } = propsRef.current;\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n const rowsCopy = rows.map((row) => ({ ...row }));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_add\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n },\n redo: (currentData) => {\n return [...currentData, ...rowsCopy.map((row) => ({ ...row }))];\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsDelete = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId, data: currentData } = propsRef.current;\n\n const indexById = buildIndexById(currentData, getRowId);\n\n const rowsWithPositions: Array<{ index: number; row: TData }> = [];\n for (const row of rows) {\n const rowId = getRowId(row);\n const currentIndex = indexById.get(rowId);\n if (currentIndex !== undefined) {\n rowsWithPositions.push({\n index: currentIndex,\n row: { ...row }\n });\n }\n }\n\n rowsWithPositions.sort((a, b) => a.index - b.index);\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_delete\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n for (const { index, row } of rowsWithPositions) {\n const insertIndex = Math.min(index, newData.length);\n newData.splice(insertIndex, 0, { ...row });\n }\n return newData;\n },\n redo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n React.useEffect(() => {\n const pending = pendingBatchRef.current;\n return () => {\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n };\n }, []);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n function onKeyDown(event: KeyboardEvent) {\n const isCtrlOrCmd = event.ctrlKey || event.metaKey;\n const key = event.key.toLowerCase();\n\n if (!isCtrlOrCmd || (key !== \"z\" && key !== \"y\")) return;\n\n const activeElement = document.activeElement;\n if (activeElement) {\n const isInput = activeElement.tagName === \"INPUT\" || activeElement.tagName === \"TEXTAREA\";\n const isContentEditable = activeElement.getAttribute(\"contenteditable\") === \"true\";\n const isInPopover = getIsInPopover(activeElement);\n\n if (isInput || isContentEditable || isInPopover) return;\n }\n\n if (key === \"z\" && !event.shiftKey) {\n event.preventDefault();\n onUndo();\n return;\n }\n\n if ((key === \"z\" && event.shiftKey) || key === \"y\") {\n event.preventDefault();\n onRedo();\n }\n }\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, onUndo, onRedo]);\n\n return React.useMemo(\n () => ({\n canUndo,\n canRedo,\n onUndo,\n onRedo,\n onClear,\n trackCellsUpdate,\n trackRowsAdd,\n trackRowsDelete\n }),\n [canUndo, canRedo, onUndo, onRedo, onClear, trackCellsUpdate, trackRowsAdd, trackRowsDelete]\n );\n}\n\nexport {\n //\n type UndoRedoCellUpdate,\n useDataGridUndoRedo\n};\n"],"mappings":"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","names":["getRowId","currentData"]}
1
+ {"version":3,"sources":["../../src/data-grid/use-data-grid-undo-redo.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useAsRef } from \"../lib/use-as-ref.js\";\nimport { useLazyRef } from \"../lib/use-lazy-ref.js\";\nimport { type DataGridLocale, type DataGridMessages, getDataGridMessages } from \"./messages.js\";\nimport { toastInfo, toastSuccess } from \"./toast.js\";\nimport { getIsInPopover } from \"./utils.js\";\n\nconst DEFAULT_MAX_HISTORY = 100;\nconst BATCH_TIMEOUT = 300;\n\ninterface HistoryEntry<TData> {\n variant: \"cells_update\" | \"rows_add\" | \"rows_delete\";\n count: number;\n timestamp: number;\n undo: (currentData: TData[]) => TData[];\n redo: (currentData: TData[]) => TData[];\n}\n\ninterface UndoRedoCellUpdate {\n rowId: string;\n columnId: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\ninterface StoreState<TData> {\n undoStack: HistoryEntry<TData>[];\n redoStack: HistoryEntry<TData>[];\n hasPendingChanges: boolean;\n}\n\ninterface Store<TData> {\n subscribe: (callback: () => void) => () => void;\n getState: () => StoreState<TData>;\n push: (entry: HistoryEntry<TData>) => void;\n undo: () => HistoryEntry<TData> | null;\n redo: () => HistoryEntry<TData> | null;\n clear: () => void;\n setPendingChanges: (value: boolean) => void;\n notify: () => void;\n}\n\nfunction useStore<T>(store: Store<T>, selector: (state: StoreState<T>) => boolean): boolean {\n const getSnapshot = React.useCallback(() => selector(store.getState()), [store, selector]);\n\n return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\nfunction buildIndexById<TData>(data: TData[], getRowId: (row: TData) => string): Map<string, number> {\n const map = new Map<string, number>();\n for (let i = 0; i < data.length; i++) {\n const row = data[i];\n if (row) {\n map.set(getRowId(row), i);\n }\n }\n return map;\n}\n\nfunction getPendingKey(rowId: string, columnId: string): string {\n return `${rowId}\\0${columnId}`;\n}\n\n/**\n * Tuỳ chọn của {@link useDataGridUndoRedo}. Lịch sử nằm hoàn toàn ở phía trình duyệt:\n * hook tính ra mảng dữ liệu trước/sau rồi trả về qua `onDataChange` — ứng dụng tự\n * đồng bộ lên máy chủ (hoặc bỏ qua nếu chỉ làm việc với state cục bộ).\n */\ninterface UseDataGridUndoRedoProps<TData> {\n data: TData[];\n onDataChange: (data: TData[]) => void;\n /** Khoá định danh bản ghi — phải khớp với `getRowId` của lưới */\n getRowId: (row: TData) => string;\n /** Số thao tác giữ trong lịch sử (mặc định 100) */\n maxHistory?: number;\n enabled?: boolean;\n /**\n * Ngôn ngữ của toast mà hook bắn ra (`\"en\"` mặc định · `\"vi\"`, hoặc cả bảng chuỗi tự soạn).\n *\n * Hook giữ bảng chuỗi của RIÊNG nó chứ không đọc `DataGridLocaleProvider`: toast render ở gốc\n * app qua `<Toaster />`, ngoài hẳn cây DOM của lưới, nên không có context nào để đọc. Truyền\n * cùng giá trị đã truyền cho `useDataGrid`.\n */\n locale?: DataGridLocale | DataGridMessages;\n}\n\ninterface UseDataGridUndoRedoReturn<TData> {\n canUndo: boolean;\n canRedo: boolean;\n onUndo: () => void;\n onRedo: () => void;\n onClear: () => void;\n trackCellsUpdate: (updates: UndoRedoCellUpdate[]) => void;\n trackRowsAdd: (rows: TData[]) => void;\n trackRowsDelete: (rows: TData[]) => void;\n}\n\nfunction useDataGridUndoRedo<TData>({\n data,\n onDataChange,\n getRowId,\n maxHistory = DEFAULT_MAX_HISTORY,\n enabled = true,\n locale\n}: UseDataGridUndoRedoProps<TData>): UseDataGridUndoRedoReturn<TData> {\n const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);\n const msgRef = useAsRef(msg);\n const propsRef = useAsRef({\n data,\n onDataChange,\n getRowId,\n maxHistory,\n enabled\n });\n\n const listenersRef = useLazyRef(() => new Set<() => void>());\n\n const stateRef = useLazyRef<StoreState<TData>>(() => ({\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n }));\n\n const pendingBatchRef = React.useRef<{\n byKey: Map<string, UndoRedoCellUpdate>;\n timeoutId: ReturnType<typeof setTimeout> | null;\n }>({\n byKey: new Map(),\n timeoutId: null\n });\n\n const pendingNotifyRef = React.useRef(false);\n\n const store = React.useMemo<Store<TData>>(() => {\n return {\n subscribe: (callback) => {\n listenersRef.current.add(callback);\n return () => listenersRef.current.delete(callback);\n },\n getState: () => stateRef.current,\n push: (entry) => {\n const state = stateRef.current;\n const newUndoStack = [...state.undoStack, entry];\n\n if (newUndoStack.length > propsRef.current.maxHistory) {\n newUndoStack.shift();\n }\n\n stateRef.current = {\n undoStack: newUndoStack,\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n undo: () => {\n const state = stateRef.current;\n if (state.undoStack.length === 0) return null;\n\n const entry = state.undoStack[state.undoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: state.undoStack.slice(0, -1),\n redoStack: [...state.redoStack, entry],\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n redo: () => {\n const state = stateRef.current;\n if (state.redoStack.length === 0) return null;\n\n const entry = state.redoStack[state.redoStack.length - 1];\n if (!entry) return null;\n\n stateRef.current = {\n undoStack: [...state.undoStack, entry],\n redoStack: state.redoStack.slice(0, -1),\n hasPendingChanges: false\n };\n store.notify();\n return entry;\n },\n clear: () => {\n stateRef.current = {\n undoStack: [],\n redoStack: [],\n hasPendingChanges: false\n };\n store.notify();\n },\n setPendingChanges: (value) => {\n if (stateRef.current.hasPendingChanges === value) return;\n stateRef.current = {\n ...stateRef.current,\n hasPendingChanges: value\n };\n\n if (!pendingNotifyRef.current) {\n pendingNotifyRef.current = true;\n queueMicrotask(() => {\n pendingNotifyRef.current = false;\n store.notify();\n });\n }\n },\n notify: () => {\n for (const listener of listenersRef.current) {\n listener();\n }\n }\n };\n }, [listenersRef, stateRef, propsRef]);\n\n const canUndo = useStore(store, (state) => state.undoStack.length > 0 || state.hasPendingChanges);\n const canRedo = useStore(store, (state) => state.redoStack.length > 0);\n\n const onCommit = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.byKey.size === 0) return;\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n\n const updates = Array.from(pending.byKey.values());\n pending.byKey.clear();\n\n const { getRowId } = propsRef.current;\n\n const entry: HistoryEntry<TData> = {\n variant: \"cells_update\",\n count: updates.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = {\n ...row,\n [update.columnId]: update.previousValue\n };\n }\n }\n }\n return newData;\n },\n redo: (currentData) => {\n const newData = [...currentData];\n const indexById = buildIndexById(newData, getRowId);\n\n for (const update of updates) {\n const index = indexById.get(update.rowId);\n if (index !== undefined) {\n const row = newData[index];\n if (row) {\n newData[index] = { ...row, [update.columnId]: update.newValue };\n }\n }\n }\n return newData;\n }\n };\n\n store.push(entry);\n }, [store, propsRef]);\n\n const onUndo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.undo();\n if (!entry) {\n toastInfo(msgRef.current.toast.nothingToUndo);\n return;\n }\n\n const newData = entry.undo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msgRef.current.toast.undone(entry.count));\n }, [store, propsRef, onCommit, msgRef]);\n\n const onRedo = React.useCallback(() => {\n if (!propsRef.current.enabled) return;\n\n onCommit();\n\n const entry = store.redo();\n if (!entry) {\n toastInfo(msgRef.current.toast.nothingToRedo);\n return;\n }\n\n const newData = entry.redo(propsRef.current.data);\n propsRef.current.onDataChange(newData);\n\n toastSuccess(msgRef.current.toast.redone(entry.count));\n }, [store, propsRef, onCommit, msgRef]);\n\n const onClear = React.useCallback(() => {\n const pending = pendingBatchRef.current;\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n pending.timeoutId = null;\n }\n pending.byKey.clear();\n\n store.clear();\n }, [store]);\n\n const trackCellsUpdate = React.useCallback(\n (updates: UndoRedoCellUpdate[]) => {\n if (!propsRef.current.enabled || updates.length === 0) return;\n\n const filteredUpdates = updates.filter((u) => !Object.is(u.previousValue, u.newValue));\n if (filteredUpdates.length === 0) return;\n\n const pending = pendingBatchRef.current;\n\n for (const update of filteredUpdates) {\n const key = getPendingKey(update.rowId, update.columnId);\n const existing = pending.byKey.get(key);\n\n if (existing) {\n pending.byKey.set(key, { ...existing, newValue: update.newValue });\n } else {\n pending.byKey.set(key, update);\n }\n }\n\n store.setPendingChanges(true);\n\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n pending.timeoutId = setTimeout(onCommit, BATCH_TIMEOUT);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsAdd = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId } = propsRef.current;\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n const rowsCopy = rows.map((row) => ({ ...row }));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_add\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n },\n redo: (currentData) => {\n return [...currentData, ...rowsCopy.map((row) => ({ ...row }))];\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n const trackRowsDelete = React.useCallback(\n (rows: TData[]) => {\n if (!propsRef.current.enabled || rows.length === 0) return;\n\n onCommit();\n\n const { getRowId, data: currentData } = propsRef.current;\n\n const indexById = buildIndexById(currentData, getRowId);\n\n const rowsWithPositions: Array<{ index: number; row: TData }> = [];\n for (const row of rows) {\n const rowId = getRowId(row);\n const currentIndex = indexById.get(rowId);\n if (currentIndex !== undefined) {\n rowsWithPositions.push({\n index: currentIndex,\n row: { ...row }\n });\n }\n }\n\n rowsWithPositions.sort((a, b) => a.index - b.index);\n\n const rowIds = new Set(rows.map((row) => getRowId(row)));\n\n const entry: HistoryEntry<TData> = {\n variant: \"rows_delete\",\n count: rows.length,\n timestamp: Date.now(),\n undo: (currentData) => {\n const newData = [...currentData];\n for (const { index, row } of rowsWithPositions) {\n const insertIndex = Math.min(index, newData.length);\n newData.splice(insertIndex, 0, { ...row });\n }\n return newData;\n },\n redo: (currentData) => {\n return currentData.filter((row) => !rowIds.has(getRowId(row)));\n }\n };\n\n store.push(entry);\n },\n [store, propsRef, onCommit]\n );\n\n React.useEffect(() => {\n const pending = pendingBatchRef.current;\n return () => {\n if (pending.timeoutId) {\n clearTimeout(pending.timeoutId);\n }\n };\n }, []);\n\n React.useEffect(() => {\n if (!enabled) return;\n\n function onKeyDown(event: KeyboardEvent) {\n const isCtrlOrCmd = event.ctrlKey || event.metaKey;\n const key = event.key.toLowerCase();\n\n if (!isCtrlOrCmd || (key !== \"z\" && key !== \"y\")) return;\n\n const activeElement = document.activeElement;\n if (activeElement) {\n const isInput = activeElement.tagName === \"INPUT\" || activeElement.tagName === \"TEXTAREA\";\n const isContentEditable = activeElement.getAttribute(\"contenteditable\") === \"true\";\n const isInPopover = getIsInPopover(activeElement);\n\n if (isInput || isContentEditable || isInPopover) return;\n }\n\n if (key === \"z\" && !event.shiftKey) {\n event.preventDefault();\n onUndo();\n return;\n }\n\n if ((key === \"z\" && event.shiftKey) || key === \"y\") {\n event.preventDefault();\n onRedo();\n }\n }\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, onUndo, onRedo]);\n\n return React.useMemo(\n () => ({\n canUndo,\n canRedo,\n onUndo,\n onRedo,\n onClear,\n trackCellsUpdate,\n trackRowsAdd,\n trackRowsDelete\n }),\n [canUndo, canRedo, onUndo, onRedo, onClear, trackCellsUpdate, trackRowsAdd, trackRowsDelete]\n );\n}\n\nexport {\n //\n type UndoRedoCellUpdate,\n useDataGridUndoRedo\n};\n"],"mappings":"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","names":["getRowId","currentData"]}
@@ -2,7 +2,10 @@ import * as _tanstack_react_virtual from '@tanstack/react-virtual';
2
2
  import * as _tanstack_react_table from '@tanstack/react-table';
3
3
  import { TableOptions, TableMeta, ColumnDef } from '@tanstack/react-table';
4
4
  import * as React from 'react';
5
+ import { DataGridLocale } from './messages.js';
5
6
  import { CellPosition, CellUpdate, FileCellData, RowHeightValue, Direction, SearchState, ContextMenuState, PasteDialogState } from './types.js';
7
+ import { DataGridMessages } from './messages-en.js';
8
+ import './messages-vi.js';
6
9
 
7
10
  /**
8
11
  * Tuỳ chọn của {@link useDataGrid} — mở rộng `TableOptions` của TanStack Table v8.
@@ -60,8 +63,19 @@ interface UseDataGridProps<TData> extends Omit<TableOptions<TData>, "pageCount"
60
63
  enablePaste?: boolean;
61
64
  /** Chỉ xem: khoá mọi thao tác sửa/xoá/dán */
62
65
  readOnly?: boolean;
66
+ /**
67
+ * Ngôn ngữ của lưới: `"en"` (mặc định) · `"vi"`, hoặc cả một bảng chuỗi tự soạn.
68
+ *
69
+ * Giá trị này đi ra theo kết quả của hook, nên `<DataGrid {...gridProps}>` tự nhận được và tự
70
+ * phát xuống cây con. Hook cũng giữ một bản cho RIÊNG nó, vì toast render ở gốc app
71
+ * (`<Toaster />`) chứ không nằm trong cây của lưới — không có context nào ở đó để đọc.
72
+ *
73
+ * ⚠ Thanh công cụ nằm ngoài `<DataGrid>` nên KHÔNG nhận được giá trị này: bọc cả màn bằng
74
+ * `DataGridLocaleProvider`, hoặc truyền `locale` cho từng menu.
75
+ */
76
+ locale?: DataGridLocale | DataGridMessages;
63
77
  }
64
- declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, overscan, dir: dirProp, initialState, ...props }: UseDataGridProps<TData>): {
78
+ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, overscan, dir: dirProp, initialState, locale, ...props }: UseDataGridProps<TData>): {
65
79
  dataGridRef: React.RefObject<HTMLDivElement | null>;
66
80
  headerRef: React.RefObject<HTMLDivElement | null>;
67
81
  rowMapRef: React.RefObject<Map<number, HTMLDivElement>>;
@@ -87,6 +101,272 @@ declare function useDataGrid<TData>({ data, columns, rowHeight: rowHeightProp, o
87
101
  pasteDialog: PasteDialogState;
88
102
  onRowAdd: ((event?: React.MouseEvent<HTMLDivElement>) => Promise<void>) | undefined;
89
103
  adjustLayout: boolean;
104
+ locale: {
105
+ grid: {
106
+ label: string;
107
+ addRow: string;
108
+ };
109
+ search: {
110
+ placeholder: string;
111
+ label: string;
112
+ prevMatch: string;
113
+ nextMatch: string;
114
+ close: string;
115
+ matchPosition: (index: number, total: number) => string;
116
+ noResults: string;
117
+ typeToSearch: string;
118
+ };
119
+ column: {
120
+ sortAsc: string;
121
+ sortDesc: string;
122
+ removeSort: string;
123
+ pinLeft: string;
124
+ unpinLeft: string;
125
+ pinRight: string;
126
+ unpinRight: string;
127
+ hide: string;
128
+ resize: (label: string) => string;
129
+ variant: {
130
+ shortText: string;
131
+ longText: string;
132
+ number: string;
133
+ url: string;
134
+ checkbox: string;
135
+ select: string;
136
+ multiSelect: string;
137
+ date: string;
138
+ file: string;
139
+ };
140
+ };
141
+ select: {
142
+ all: string;
143
+ row: string;
144
+ rowNumber: (rowNumber: number) => string;
145
+ };
146
+ contextMenu: {
147
+ copy: string;
148
+ cut: string;
149
+ clear: string;
150
+ deleteRows: string;
151
+ };
152
+ pasteDialog: {
153
+ title: string;
154
+ description: (rowsNeeded: number) => string;
155
+ expandTitle: string;
156
+ expandDescription: (rowsNeeded: number) => string;
157
+ keepTitle: string;
158
+ keepDescription: string;
159
+ cancel: string;
160
+ confirm: string;
161
+ };
162
+ rowHeight: {
163
+ label: string;
164
+ short: string;
165
+ medium: string;
166
+ tall: string;
167
+ extraTall: string;
168
+ };
169
+ view: {
170
+ trigger: string;
171
+ toggleColumns: string;
172
+ searchPlaceholder: string;
173
+ empty: string;
174
+ };
175
+ sort: {
176
+ trigger: string;
177
+ title: string;
178
+ emptyTitle: string;
179
+ description: string;
180
+ emptyDescription: string;
181
+ add: string;
182
+ reset: string;
183
+ remove: string;
184
+ drag: string;
185
+ fieldPlaceholder: string;
186
+ fieldEmpty: string;
187
+ asc: string;
188
+ desc: string;
189
+ };
190
+ filter: {
191
+ trigger: string;
192
+ title: string;
193
+ emptyTitle: string;
194
+ description: string;
195
+ emptyDescription: string;
196
+ add: string;
197
+ reset: string;
198
+ remove: string;
199
+ drag: string;
200
+ where: string;
201
+ and: string;
202
+ fieldPlaceholder: string;
203
+ fieldEmpty: string;
204
+ valuePlaceholder: string;
205
+ rangeStart: string;
206
+ rangeEnd: string;
207
+ pickDate: string;
208
+ pickRange: string;
209
+ optionPlaceholder: string;
210
+ optionEmpty: string;
211
+ selectedCount: (count: number) => string;
212
+ emptyValue: (label: string) => string;
213
+ operators: {
214
+ contains: string;
215
+ notContains: string;
216
+ equals: string;
217
+ notEquals: string;
218
+ startsWith: string;
219
+ endsWith: string;
220
+ isEmpty: string;
221
+ isNotEmpty: string;
222
+ lessThan: string;
223
+ lessThanOrEqual: string;
224
+ greaterThan: string;
225
+ greaterThanOrEqual: string;
226
+ isBetween: string;
227
+ dateEquals: string;
228
+ dateNotEquals: string;
229
+ before: string;
230
+ after: string;
231
+ onOrBefore: string;
232
+ onOrAfter: string;
233
+ is: string;
234
+ isNot: string;
235
+ isAnyOf: string;
236
+ isNoneOf: string;
237
+ isTrue: string;
238
+ isFalse: string;
239
+ };
240
+ };
241
+ cell: {
242
+ longTextPlaceholder: string;
243
+ optionSearchPlaceholder: string;
244
+ optionEmpty: string;
245
+ clearAll: string;
246
+ invalidUrlTitle: string;
247
+ invalidUrlDescription: string;
248
+ };
249
+ file: {
250
+ uploadLabel: string;
251
+ dropHere: string;
252
+ dragHere: string;
253
+ browse: string;
254
+ limits: (maxSize?: string, maxFiles?: number) => string;
255
+ count: (count: number) => string;
256
+ clearAll: string;
257
+ remove: string;
258
+ uploading: string;
259
+ deleting: string;
260
+ tooMany: (maxFiles: number) => string;
261
+ tooLarge: (maxSize: string) => string;
262
+ wrongType: string;
263
+ rejectedOne: (name: string) => string;
264
+ rejectedMany: (name: string, others: number) => string;
265
+ uploadFailed: (count: number) => string;
266
+ deleteFailed: (name: string) => string;
267
+ deleteFailedMany: string;
268
+ };
269
+ toast: {
270
+ cellsCopied: (count: number) => string;
271
+ copyFailed: string;
272
+ cellsCut: (count: number) => string;
273
+ cutFailed: string;
274
+ cellsPasted: (count: number) => string;
275
+ cellsPastedWithSkipped: (count: number, skipped: number) => string;
276
+ pasteSkipped: (skipped: number) => string;
277
+ pasteFailed: string;
278
+ cellsCleared: (count: number) => string;
279
+ rowsDeleted: (count: number) => string;
280
+ nothingToUndo: string;
281
+ nothingToRedo: string;
282
+ undone: (count: number) => string;
283
+ redone: (count: number) => string;
284
+ };
285
+ booleanText: {
286
+ checked: string;
287
+ unchecked: string;
288
+ };
289
+ shortcuts: {
290
+ trigger: string;
291
+ title: string;
292
+ description: string;
293
+ searchPlaceholder: string;
294
+ emptyTitle: string;
295
+ emptyDescription: string;
296
+ close: string;
297
+ groups: {
298
+ navigation: string;
299
+ selection: string;
300
+ editing: string;
301
+ search: string;
302
+ filtering: string;
303
+ sorting: string;
304
+ general: string;
305
+ };
306
+ keys: {
307
+ click: string;
308
+ doubleClick: string;
309
+ };
310
+ navigation: {
311
+ move: string;
312
+ nextCell: string;
313
+ prevCell: string;
314
+ firstColumn: string;
315
+ lastColumn: string;
316
+ firstRowSameColumn: string;
317
+ lastRowSameColumn: string;
318
+ firstColumnSameRow: string;
319
+ lastColumnSameRow: string;
320
+ firstCell: string;
321
+ lastCell: string;
322
+ pageUp: string;
323
+ pageDown: string;
324
+ scrollUp: string;
325
+ scrollDown: string;
326
+ scrollLeft: string;
327
+ scrollRight: string;
328
+ };
329
+ selection: {
330
+ extend: string;
331
+ toTop: string;
332
+ toBottom: string;
333
+ toFirstColumn: string;
334
+ toLastColumn: string;
335
+ all: string;
336
+ toggle: string;
337
+ range: string;
338
+ clear: string;
339
+ };
340
+ editing: {
341
+ start: string;
342
+ insertRow: string;
343
+ copy: string;
344
+ cut: string;
345
+ paste: string;
346
+ clear: string;
347
+ deleteRows: string;
348
+ undo: string;
349
+ redo: string;
350
+ };
351
+ search: {
352
+ open: string;
353
+ next: string;
354
+ prev: string;
355
+ close: string;
356
+ };
357
+ filtering: {
358
+ toggle: string;
359
+ remove: string;
360
+ };
361
+ sorting: {
362
+ toggle: string;
363
+ remove: string;
364
+ };
365
+ general: {
366
+ shortcuts: string;
367
+ };
368
+ };
369
+ } | "en" | "vi" | undefined;
90
370
  };
91
371
 
92
372
  export { type UseDataGridProps, useDataGrid };
@@ -8,11 +8,11 @@ import {
8
8
  } from "@tanstack/react-table";
9
9
  import { useVirtualizer } from "@tanstack/react-virtual";
10
10
  import * as React from "react";
11
- import { msg } from "./messages.js";
11
+ import { useAsRef } from "../lib/use-as-ref.js";
12
+ import { useIsomorphicLayoutEffect } from "../lib/use-isomorphic-layout-effect.js";
13
+ import { useLazyRef } from "../lib/use-lazy-ref.js";
14
+ import { getDataGridMessages } from "./messages.js";
12
15
  import { toastError, toastSuccess } from "./toast.js";
13
- import { useAsRef } from "./use-as-ref.js";
14
- import { useIsomorphicLayoutEffect } from "./use-isomorphic-layout-effect.js";
15
- import { useLazyRef } from "./use-lazy-ref.js";
16
16
  import {
17
17
  formatDate,
18
18
  getCellKey,
@@ -57,8 +57,11 @@ function useDataGrid({
57
57
  overscan = OVERSCAN,
58
58
  dir: dirProp,
59
59
  initialState,
60
+ locale,
60
61
  ...props
61
62
  }) {
63
+ const msg = React.useMemo(() => getDataGridMessages(locale), [locale]);
64
+ const msgRef = useAsRef(msg);
62
65
  const dir = useDirection(dirProp);
63
66
  const dataGridRef = React.useRef(null);
64
67
  const tableRef = React.useRef(null);
@@ -456,11 +459,11 @@ function useDataGrid({
456
459
  if (currentState.cutCells.size > 0) {
457
460
  store.setState("cutCells", /* @__PURE__ */ new Set());
458
461
  }
459
- toastSuccess(msg.toast.cellsCopied(selectedCellsArray.length));
462
+ toastSuccess(msgRef.current.toast.cellsCopied(selectedCellsArray.length));
460
463
  } catch {
461
- toastError(msg.toast.copyFailed);
464
+ toastError(msgRef.current.toast.copyFailed);
462
465
  }
463
- }, [store, serializeCellsToTsv]);
466
+ }, [store, serializeCellsToTsv, msgRef]);
464
467
  const onCellsCut = React.useCallback(async () => {
465
468
  if (propsRef.current.readOnly) return;
466
469
  const result = serializeCellsToTsv();
@@ -469,11 +472,11 @@ function useDataGrid({
469
472
  try {
470
473
  await writeClipboardText(tsvData);
471
474
  store.setState("cutCells", new Set(selectedCellsArray));
472
- toastSuccess(msg.toast.cellsCut(selectedCellsArray.length));
475
+ toastSuccess(msgRef.current.toast.cellsCut(selectedCellsArray.length));
473
476
  } catch {
474
- toastError(msg.toast.cutFailed);
477
+ toastError(msgRef.current.toast.cutFailed);
475
478
  }
476
- }, [store, propsRef, serializeCellsToTsv]);
479
+ }, [store, propsRef, serializeCellsToTsv, msgRef]);
477
480
  const restoreFocus = React.useCallback((element) => {
478
481
  if (element && document.activeElement !== element) {
479
482
  requestAnimationFrame(() => {
@@ -710,12 +713,12 @@ function useDataGrid({
710
713
  processedValue = parsed.join(", ");
711
714
  }
712
715
  } else if (typeof parsed === "boolean") {
713
- processedValue = parsed ? msg.booleanText.checked : msg.booleanText.unchecked;
716
+ processedValue = parsed ? msgRef.current.booleanText.checked : msgRef.current.booleanText.unchecked;
714
717
  }
715
718
  } catch {
716
719
  const lower = pastedValue.toLowerCase();
717
720
  if (lower === "true" || lower === "false") {
718
- processedValue = lower === "true" ? msg.booleanText.checked : msg.booleanText.unchecked;
721
+ processedValue = lower === "true" ? msgRef.current.booleanText.checked : msgRef.current.booleanText.unchecked;
719
722
  }
720
723
  }
721
724
  }
@@ -755,9 +758,9 @@ function useDataGrid({
755
758
  }
756
759
  onDataUpdate(allUpdates);
757
760
  if (cellsSkipped > 0) {
758
- toastSuccess(msg.toast.cellsPastedWithSkipped(cellsUpdated, cellsSkipped));
761
+ toastSuccess(msgRef.current.toast.cellsPastedWithSkipped(cellsUpdated, cellsSkipped));
759
762
  } else {
760
- toastSuccess(msg.toast.cellsPasted(cellsUpdated));
763
+ toastSuccess(msgRef.current.toast.cellsPasted(cellsUpdated));
761
764
  }
762
765
  const endColumnId = navigableColumnIds[endColIndex];
763
766
  if (endColumnId) {
@@ -771,7 +774,7 @@ function useDataGrid({
771
774
  }
772
775
  restoreFocus(dataGridRef.current);
773
776
  } else if (cellsSkipped > 0) {
774
- toastError(msg.toast.pasteSkipped(cellsSkipped));
777
+ toastError(msgRef.current.toast.pasteSkipped(cellsSkipped));
775
778
  }
776
779
  if (currentState.pasteDialog.open) {
777
780
  store.setState("pasteDialog", {
@@ -781,10 +784,10 @@ function useDataGrid({
781
784
  });
782
785
  }
783
786
  } catch {
784
- toastError(msg.toast.pasteFailed);
787
+ toastError(msgRef.current.toast.pasteFailed);
785
788
  }
786
789
  },
787
- [store, navigableColumnIds, propsRef, onDataUpdate, selectRange, restoreFocus]
790
+ [store, navigableColumnIds, propsRef, onDataUpdate, selectRange, restoreFocus, msgRef]
788
791
  );
789
792
  const releaseFocusGuard = React.useCallback((immediate = false) => {
790
793
  if (immediate) {
@@ -2660,7 +2663,8 @@ function useDataGrid({
2660
2663
  contextMenu,
2661
2664
  pasteDialog,
2662
2665
  onRowAdd: hasRowAdd ? onRowAdd : void 0,
2663
- adjustLayout
2666
+ adjustLayout,
2667
+ locale
2664
2668
  }),
2665
2669
  [
2666
2670
  hasRowAdd,
@@ -2682,7 +2686,8 @@ function useDataGrid({
2682
2686
  contextMenu,
2683
2687
  pasteDialog,
2684
2688
  onRowAdd,
2685
- adjustLayout
2689
+ adjustLayout,
2690
+ locale
2686
2691
  ]
2687
2692
  );
2688
2693
  }