@comitor/ui 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +10 -2
  2. package/dist/data-grid/data-grid-cell-variants.js +10 -6
  3. package/dist/data-grid/data-grid-cell-variants.js.map +1 -1
  4. package/dist/data-grid/data-grid-column-header.js +4 -2
  5. package/dist/data-grid/data-grid-column-header.js.map +1 -1
  6. package/dist/data-grid/data-grid-context-menu.js +4 -3
  7. package/dist/data-grid/data-grid-context-menu.js.map +1 -1
  8. package/dist/data-grid/data-grid-filter-menu.d.ts +12 -1
  9. package/dist/data-grid/data-grid-filter-menu.js +28 -17
  10. package/dist/data-grid/data-grid-filter-menu.js.map +1 -1
  11. package/dist/data-grid/data-grid-keyboard-shortcuts.d.ts +12 -1
  12. package/dist/data-grid/data-grid-keyboard-shortcuts.js +7 -5
  13. package/dist/data-grid/data-grid-keyboard-shortcuts.js.map +1 -1
  14. package/dist/data-grid/data-grid-locale.d.ts +24 -0
  15. package/dist/data-grid/data-grid-locale.js +25 -0
  16. package/dist/data-grid/data-grid-locale.js.map +1 -0
  17. package/dist/data-grid/data-grid-paste-dialog.js +2 -1
  18. package/dist/data-grid/data-grid-paste-dialog.js.map +1 -1
  19. package/dist/data-grid/data-grid-row-height-menu.d.ts +11 -1
  20. package/dist/data-grid/data-grid-row-height-menu.js +25 -28
  21. package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
  22. package/dist/data-grid/data-grid-search.js +5 -4
  23. package/dist/data-grid/data-grid-search.js.map +1 -1
  24. package/dist/data-grid/data-grid-select-column.js +3 -1
  25. package/dist/data-grid/data-grid-select-column.js.map +1 -1
  26. package/dist/data-grid/data-grid-sort-menu.d.ts +12 -1
  27. package/dist/data-grid/data-grid-sort-menu.js +25 -13
  28. package/dist/data-grid/data-grid-sort-menu.js.map +1 -1
  29. package/dist/data-grid/data-grid-view-menu.d.ts +12 -1
  30. package/dist/data-grid/data-grid-view-menu.js +12 -4
  31. package/dist/data-grid/data-grid-view-menu.js.map +1 -1
  32. package/dist/data-grid/data-grid.d.ts +14 -2
  33. package/dist/data-grid/data-grid.js +6 -3
  34. package/dist/data-grid/data-grid.js.map +1 -1
  35. package/dist/data-grid/filters.d.ts +17 -21
  36. package/dist/data-grid/filters.js +73 -52
  37. package/dist/data-grid/filters.js.map +1 -1
  38. package/dist/data-grid/index.d.ts +4 -1
  39. package/dist/data-grid/index.js +20 -2
  40. package/dist/data-grid/index.js.map +1 -1
  41. package/dist/data-grid/messages-en.d.ts +294 -0
  42. package/dist/data-grid/messages-en.js +279 -0
  43. package/dist/data-grid/messages-en.js.map +1 -0
  44. package/dist/data-grid/messages-vi.d.ts +12 -0
  45. package/dist/data-grid/messages-vi.js +279 -0
  46. package/dist/data-grid/messages-vi.js.map +1 -0
  47. package/dist/data-grid/messages.d.ts +549 -262
  48. package/dist/data-grid/messages.js +16 -275
  49. package/dist/data-grid/messages.js.map +1 -1
  50. package/dist/data-grid/use-data-grid-undo-redo.d.ts +13 -1
  51. package/dist/data-grid/use-data-grid-undo-redo.js +11 -8
  52. package/dist/data-grid/use-data-grid-undo-redo.js.map +1 -1
  53. package/dist/data-grid/use-data-grid.d.ts +281 -1
  54. package/dist/data-grid/use-data-grid.js +21 -16
  55. package/dist/data-grid/use-data-grid.js.map +1 -1
  56. package/dist/data-grid/utils.d.ts +9 -1
  57. package/dist/data-grid/utils.js +3 -2
  58. package/dist/data-grid/utils.js.map +1 -1
  59. package/package.json +1 -1
@@ -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,7 +8,7 @@ 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 { getDataGridMessages } from "./messages.js";
12
12
  import { toastError, toastSuccess } from "./toast.js";
13
13
  import { useAsRef } from "./use-as-ref.js";
14
14
  import { useIsomorphicLayoutEffect } from "./use-isomorphic-layout-effect.js";
@@ -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
  }