es-grid-template 1.9.62 → 1.9.64

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 (47) hide show
  1. package/es/ali-table/Grid.js +6 -3
  2. package/es/ali-table/InternalTable.js +500 -49
  3. package/es/ali-table/base-table/calculations.js +1 -1
  4. package/es/ali-table/base-table/cell/EditCell.js +14 -6
  5. package/es/ali-table/base-table/renderFilter.js +1 -1
  6. package/es/ali-table/base-table/styles.js +1 -1
  7. package/es/ali-table/interfaces.d.ts +2 -2
  8. package/es/ali-table/pipeline/features/columnCustom.d.ts +5 -2
  9. package/es/ali-table/pipeline/features/columnCustom.js +172 -102
  10. package/es/ali-table/utils/constants.d.ts +1 -0
  11. package/es/ali-table/utils/constants.js +1 -0
  12. package/es/ali-table/utils/convertColumnsChoose.d.ts +2 -2
  13. package/es/ali-table/utils/convertColumnsChoose.js +1 -2
  14. package/es/ali-table/utils/utility.d.ts +16 -1
  15. package/es/ali-table/utils/utility.js +442 -2
  16. package/es/grid-component/type.d.ts +2 -2
  17. package/es/table-component/TableContainerEdit.js +148 -98
  18. package/es/table-component/body/EditableCell.js +9 -5
  19. package/es/table-component/body/TableBodyCellEdit.js +14 -8
  20. package/es/table-component/header/renderFilter.js +1 -1
  21. package/es/table-component/hook/utils.d.ts +1 -1
  22. package/es/table-component/hook/utils.js +2 -6
  23. package/es/table-component/useContext.d.ts +1 -1
  24. package/lib/ali-table/Grid.js +6 -3
  25. package/lib/ali-table/InternalTable.js +491 -45
  26. package/lib/ali-table/base-table/calculations.js +1 -1
  27. package/lib/ali-table/base-table/cell/EditCell.js +14 -6
  28. package/lib/ali-table/base-table/renderFilter.js +1 -1
  29. package/lib/ali-table/base-table/styles.js +1 -1
  30. package/lib/ali-table/interfaces.d.ts +2 -2
  31. package/lib/ali-table/pipeline/features/columnCustom.d.ts +5 -2
  32. package/lib/ali-table/pipeline/features/columnCustom.js +166 -100
  33. package/lib/ali-table/utils/constants.d.ts +1 -0
  34. package/lib/ali-table/utils/constants.js +2 -1
  35. package/lib/ali-table/utils/convertColumnsChoose.d.ts +2 -2
  36. package/lib/ali-table/utils/convertColumnsChoose.js +1 -2
  37. package/lib/ali-table/utils/utility.d.ts +16 -1
  38. package/lib/ali-table/utils/utility.js +454 -2
  39. package/lib/grid-component/type.d.ts +2 -2
  40. package/lib/table-component/TableContainerEdit.js +148 -98
  41. package/lib/table-component/body/EditableCell.js +9 -5
  42. package/lib/table-component/body/TableBodyCellEdit.js +11 -7
  43. package/lib/table-component/header/renderFilter.js +1 -1
  44. package/lib/table-component/hook/utils.d.ts +1 -1
  45. package/lib/table-component/hook/utils.js +2 -6
  46. package/lib/table-component/useContext.d.ts +1 -1
  47. package/package.json +1 -1
@@ -4,13 +4,19 @@ import * as fusion from '@alifd/next';
4
4
  import { useLocale } from "rc-master-ui/es/locale";
5
5
  import { faker } from "@faker-js/faker";
6
6
  import React, { Fragment, useRef } from "react";
7
+ import { useCopyToClipboard } from 'usehooks-ts';
7
8
  import "./base-table/styles.scss";
8
- import { addRowIdArray, findItemByKey, flatColumns2, flattenArray, flattenData,
9
+ import { addRowIdArray, checkDecimalSeparator, checkThousandSeparator, detectSeparators, findItemByKey, flatColumns2, flattenArray, flattenData, getColIdsBetween1,
9
10
  // getAllRowKey,
10
- getDefaultValue, getRowIdsBetween3, getRowIndexMap,
11
+ getDefaultValue, getRowIdsBetween3, getRowIndexMap, getSelectedCellMatrix1, getVisibleFields,
11
12
  // getInvisibleColumns,
12
- getVisibleFields, groupArrayByColumns, isTreeArray, newGuid, revertConvertFilters, sortByType, sumFields, unFlattenData, updateArrayByKey, updateOrInsert } from "../table-component/hook/utils";
13
- import { CustomProvider, ConfigProvider, Modal, Button, Dropdown } from 'rc-master-ui';
13
+ // getVisibleFields,
14
+ groupArrayByColumns, isDateValue, isEditable, isEmpty, isFormattedNumber, isTreeArray, newGuid, parseExcelClipboard, revertConvertFilters,
15
+ // sortByType,
16
+ sumFields, unFlattenData, updateArrayByKey
17
+ // updateOrInsert
18
+ } from "../table-component/hook/utils";
19
+ import { CustomProvider, ConfigProvider, Modal, Button, Dropdown, Typography } from 'rc-master-ui';
14
20
  import { getLocale, getLocales } from "../locale/useLocale";
15
21
  import { TableContext } from "./useContext";
16
22
  import Grid from "./Grid";
@@ -20,13 +26,20 @@ import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
20
26
  import { arrayMove } from "@dnd-kit/sortable";
21
27
  import { useForm } from "react-hook-form";
22
28
  import { yupResolver } from "@hookform/resolvers/yup";
23
- import { filterDataByColumns1 } from "./utils/utility";
29
+ import { filterDataByColumns1, sortByType, updateOrInsert } from "./utils/utility";
24
30
  import { stateKeyEndCell, stateKeyFocusedCell, stateKeyIsEditing, stateKeyStartCell } from "./utils/constants";
25
31
  import classNames from "classnames";
26
- import { getToolbarTemplate } from "../grid-component/hooks";
32
+ import { getEditType, getFormat, getToolbarTemplate } from "../grid-component/hooks";
27
33
  import withReactContent from "sweetalert2-react-content";
28
34
  import Swal from "sweetalert2";
35
+ import { collectNodes } from "./utils";
36
+ import { removeNumericFormat } from "react-numeric-component";
37
+ import moment from "moment";
29
38
  const MySwal = withReactContent(Swal);
39
+ const {
40
+ Paragraph,
41
+ Title
42
+ } = Typography;
30
43
  const InternalTable = props => {
31
44
  const {
32
45
  t,
@@ -69,6 +82,8 @@ const InternalTable = props => {
69
82
  defaultValue,
70
83
  commandSettings,
71
84
  headerData,
85
+ format,
86
+ onCellClick,
72
87
  ...rest
73
88
  } = props;
74
89
  const tableRef = useRef(null);
@@ -77,6 +92,7 @@ const InternalTable = props => {
77
92
  const childrenColumnName = 'children';
78
93
  const childrenKey = 'children';
79
94
  const [tableLocal] = useLocale('Table');
95
+ const [copiedText, copy] = useCopyToClipboard();
80
96
  const tableId = React.useMemo(() => {
81
97
  return faker.string.alpha(20);
82
98
  }, []);
@@ -86,6 +102,9 @@ const InternalTable = props => {
86
102
  const languages = React.useMemo(() => {
87
103
  return getLocales(lang);
88
104
  }, [lang]);
105
+
106
+ // const isCut = React.useRef<any>(false);
107
+
89
108
  const getWindowSize = () => {
90
109
  const {
91
110
  innerWidth,
@@ -119,18 +138,19 @@ const InternalTable = props => {
119
138
  return addRowIdArray(dataSource);
120
139
  }, [dataSource, groupAble, groupColumns, groupSetting]);
121
140
  const isDataTree = React.useMemo(() => {
122
- return isTreeArray(convertData);
123
- }, [convertData]);
141
+ return isTreeArray(dataSource);
142
+ }, [dataSource]);
124
143
  const mergedData = React.useMemo(() => {
125
144
  const shouldSkip = onFilter && onSorter;
126
145
  if (shouldSkip) {
127
146
  return convertData;
128
147
  }
129
-
130
- // const rs = filterDataByColumns(convertData, onFilter ? undefined : (filterState ?? defaultFilter), onSorter ? undefined : (sortState ?? defaultSorter), [], ignoreAccents)
131
148
  const rss = filterDataByColumns1(convertData, onFilter ? undefined : filterState ?? defaultFilter, onSorter ? undefined : sortState ?? defaultSorter, [], ignoreAccents);
132
149
  return rss;
133
150
  }, [convertData, filterState, ignoreAccents, sortState, onFilter, onSorter, defaultFilter, defaultSorter]);
151
+ const rowIndexMap = React.useMemo(() => {
152
+ return getRowIndexMap(convertData);
153
+ }, [convertData]);
134
154
  const footerData = React.useMemo(() => {
135
155
  if (!summary) {
136
156
  return undefined;
@@ -155,15 +175,15 @@ const InternalTable = props => {
155
175
  }
156
176
  return undefined;
157
177
  }, [defaultFilter]);
158
-
159
- // const columnInVisibility = React.useMemo(() => {
160
- // return getInvisibleColumns(columns)
161
- // }, [columns])
162
-
163
- const dataIds = React.useMemo(() => mergedData.map(it => it.rowId), [mergedData]);
164
178
  const columnVisibility = React.useMemo(() => {
165
179
  return getVisibleFields(columns);
166
180
  }, [columns]);
181
+ const dataIds = React.useMemo(() => convertData.map(it => it.rowId), [convertData]);
182
+
183
+ // const columnVisibility = React.useMemo(() => {
184
+ // return getVisibleFields(columns)
185
+ // }, [columns])
186
+
167
187
  const {
168
188
  control,
169
189
  handleSubmit,
@@ -194,7 +214,7 @@ const InternalTable = props => {
194
214
  }, [onDataChange]);
195
215
  const triggerPaste = React.useCallback((pastedRows, pastedColumnsArray, newData, copyRows) => {
196
216
  const handlePasteCallback = callbackData => {
197
- const rsFilterData = updateOrInsert(flattenArray([...convertData]), flattenArray([...callbackData]));
217
+ const rsFilterData = updateOrInsert(convertData, flattenArray(callbackData));
198
218
  const rs = unFlattenData(rsFilterData);
199
219
  triggerChangeData(rs, {
200
220
  type: 'OnPaste'
@@ -231,13 +251,10 @@ const InternalTable = props => {
231
251
  }, [onCellPaste, convertData, triggerChangeData]);
232
252
  const onSubmit = formData => {
233
253
  try {
234
- // const record = (await form.validateFields()) as RecordType;
235
254
  const row = {
236
255
  ...formData
237
256
  };
238
-
239
- // const newData = [...dataSource]
240
- const newData = [...convertData];
257
+ const newData = convertData;
241
258
  const index = flattenData(childrenColumnName, newData).findIndex(item => formData[rowKey] === item[rowKey]);
242
259
  const rs = updateArrayByKey(newData, row, rowKey);
243
260
  if (index > -1) {
@@ -280,8 +297,7 @@ const InternalTable = props => {
280
297
  indexRow,
281
298
  rowData: record,
282
299
  rowId: key,
283
- // rowsData: [...dataSource],
284
- rowsData: [...convertData],
300
+ rowsData: convertData,
285
301
  sumValue: []
286
302
  }, handleChangeCallback);
287
303
  } else {
@@ -294,8 +310,7 @@ const InternalTable = props => {
294
310
  indexRow,
295
311
  rowData: record,
296
312
  rowId: key,
297
- // rowsData: [...dataSource],
298
- rowsData: [...convertData],
313
+ rowsData: dataSource,
299
314
  sumValue: []
300
315
  }, handleChangeCallback);
301
316
  onSubmit(record);
@@ -401,6 +416,7 @@ const InternalTable = props => {
401
416
  columns,
402
417
  mode: sortMultiple !== false ? 'multiple' : 'single',
403
418
  dataSource: convertData,
419
+ rowIndexMap,
404
420
  columnResizeMode: 'onChange',
405
421
  disableUserSelectWhenResizing: true,
406
422
  allowFiltering,
@@ -443,9 +459,12 @@ const InternalTable = props => {
443
459
  handleSubmit,
444
460
  onSubmit,
445
461
  triggerPaste,
462
+ headerEditAble,
446
463
  handleCellChange,
447
464
  handleHeaderCellChange,
448
- headerData
465
+ headerData,
466
+ triggerChangeData,
467
+ onCellClick
449
468
  })).use(features.paginationFeature({
450
469
  pagination
451
470
  }));
@@ -456,14 +475,42 @@ const InternalTable = props => {
456
475
 
457
476
  // reorder rows after drag & drop
458
477
 
478
+ const isEditing = React.useMemo(() => {
479
+ return pipeline.getStateAtKey(stateKeyIsEditing);
480
+ }, [pipeline]);
481
+ const startCell = React.useMemo(() => {
482
+ return pipeline.getStateAtKey(stateKeyStartCell);
483
+ }, [pipeline]);
484
+ const endCell = React.useMemo(() => {
485
+ return pipeline.getStateAtKey(stateKeyEndCell);
486
+ }, [pipeline]);
487
+ const leafColumns = React.useMemo(() => {
488
+ return collectNodes(pipeline.getColumns(), 'leaf-only');
489
+ }, [pipeline]);
490
+ const rangeState = React.useMemo(() => {
491
+ return startCell && endCell ? getSelectedCellMatrix1(convertData, leafColumns, startCell, endCell) : undefined;
492
+ }, [convertData, endCell, leafColumns, startCell]);
493
+
494
+ // const rowIds = React.useMemo(() => {
495
+
496
+ // return getKeys(convertData, 'rowId')
497
+
498
+ // }, [convertData])
499
+
500
+ // const colIds = React.useMemo(() => {
501
+
502
+ // const leafColumns = collectNodes(pipeline.getColumns(), 'leaf-only')
503
+
504
+ // return getKeys(leafColumns, 'field')
505
+
506
+ // }, [pipeline])
507
+
459
508
  React.useEffect(() => {
460
509
  const handleClickOutside = event => {
461
510
  if (!editAble) {
462
511
  return;
463
512
  }
464
513
  const isEditting = pipeline.getStateAtKey(stateKeyIsEditing);
465
- const startCell = pipeline.getStateAtKey(stateKeyStartCell);
466
- const endCell = pipeline.getStateAtKey(stateKeyEndCell);
467
514
  const focusedCell = pipeline.getStateAtKey(stateKeyFocusedCell);
468
515
  const element = event.target;
469
516
  const container = document.querySelector(".be-popup-container");
@@ -528,15 +575,11 @@ const InternalTable = props => {
528
575
  return () => {
529
576
  document.removeEventListener('mousedown', handleClickOutside);
530
577
  };
531
- }, [tableId, pipeline, editAble, headerEditAble]);
578
+ }, [tableId, pipeline, editAble, headerEditAble, endCell, startCell]);
532
579
  const rowsFocus = React.useMemo(() => {
533
- const startCell = pipeline.getStateAtKey(stateKeyStartCell);
534
- const endCell = pipeline.getStateAtKey(stateKeyEndCell);
535
- const allRows = pipeline.getDataSource(); // flat data
536
- const rowIndexMap = getRowIndexMap(allRows);
537
580
  const selectRowIds = startCell && endCell ? getRowIdsBetween3(convertData, rowIndexMap, startCell.rowId, endCell.rowId) : [];
538
581
  return selectRowIds;
539
- }, [convertData, pipeline]);
582
+ }, [convertData, endCell, rowIndexMap, startCell]);
540
583
  function handleDragEnd(event) {
541
584
  const {
542
585
  active,
@@ -547,7 +590,7 @@ const InternalTable = props => {
547
590
 
548
591
  const oldIndex = dataIds.indexOf(active.id);
549
592
  const newIndex = dataIds.indexOf(over.id);
550
- const rs = arrayMove(mergedData, oldIndex, newIndex); // this is just a splice util
593
+ const rs = arrayMove(dataSource, oldIndex, newIndex); // this is just a splice util
551
594
 
552
595
  if (rowDnd?.onChange) {
553
596
  rowDnd?.onChange({
@@ -563,7 +606,7 @@ const InternalTable = props => {
563
606
  toolbar: item
564
607
  });
565
608
  } else {
566
- const filterData = flattenArray([...convertData]);
609
+ const filterData = flattenArray(convertData);
567
610
  const rs = filterData.filter(it => !rowsFocus.includes(it[rowKey]));
568
611
  const newDaa = unFlattenData(rs);
569
612
  if (commandSettings && commandSettings.confirmDialog) {
@@ -582,7 +625,7 @@ const InternalTable = props => {
582
625
  buttonsStyling: false
583
626
  }).then(async result => {
584
627
  if (result.value) {
585
- triggerChangeData?.([...newDaa], {
628
+ triggerChangeData?.(newDaa, {
586
629
  type: 'DELETE_ROWS',
587
630
  row: rowsFocus.length
588
631
  });
@@ -590,7 +633,7 @@ const InternalTable = props => {
590
633
  });
591
634
  } else {
592
635
  // không hiện dialog
593
- triggerChangeData?.([...newDaa], {
636
+ triggerChangeData?.(newDaa, {
594
637
  type: 'DELETE_ROWS',
595
638
  row: rowsFocus.length
596
639
  });
@@ -663,7 +706,7 @@ const InternalTable = props => {
663
706
  // không tính tree
664
707
 
665
708
  // Cập nhật data mới
666
- const newData = flattenArray([...convertData]);
709
+ const newData = flattenArray(convertData);
667
710
  const focusedCell = pipeline.getStateAtKey(stateKeyFocusedCell);
668
711
  const duplicatedItem = newData.find(it => it.rowId === focusedCell?.rowId);
669
712
  const duplicatedItems = [{
@@ -700,7 +743,7 @@ const InternalTable = props => {
700
743
  if (!record?.parentId) {
701
744
  // Cập nhật data mới
702
745
 
703
- const newData = [...convertData];
746
+ const newData = convertData;
704
747
  const newRows = Array.from({
705
748
  length: n
706
749
  }).map(() => defaultRowValue ? {
@@ -725,7 +768,7 @@ const InternalTable = props => {
725
768
  row: n
726
769
  });
727
770
  } else {
728
- const newData = [...convertData];
771
+ const newData = convertData;
729
772
  const parent = findItemByKey(newData, rowKey, record.parentId);
730
773
  const newRows = Array.from({
731
774
  length: n
@@ -741,7 +784,7 @@ const InternalTable = props => {
741
784
  });
742
785
 
743
786
  // Cập nhật childData mới
744
- const childData = parent?.children ? [...parent.children] : [];
787
+ const childData = parent?.children ? parent.children : [];
745
788
  const index = childData.findIndex(obj => obj[rowKey] === record[rowKey]);
746
789
  childData.splice(index, 0, ...newRows);
747
790
  const newRowData = {
@@ -764,7 +807,7 @@ const InternalTable = props => {
764
807
 
765
808
  // const insertAfter = Math.max(...rowsFocus)
766
809
 
767
- // const record: RecordType = flattenData(childrenColumnName, dataSource)[insertAfter]
810
+ // const record: RecordType = flattenData(childrenColumnName, convertData)[insertAfter]
768
811
 
769
812
  // const record: any = table.getRowModel().rows.find((it) => it.id === focusedCell?.rowId)?.original
770
813
  const record = convertData.find(it => it.rowId === focusedCell?.rowId);
@@ -775,7 +818,7 @@ const InternalTable = props => {
775
818
  } else {
776
819
  if (!record?.parentId) {
777
820
  // Cập nhật data mới
778
- const newData = [...convertData];
821
+ const newData = convertData;
779
822
  const newRows = Array.from({
780
823
  length: n
781
824
  }).map(() => defaultRowValue ? {
@@ -800,7 +843,7 @@ const InternalTable = props => {
800
843
  row: n
801
844
  });
802
845
  } else {
803
- const newData = [...convertData];
846
+ const newData = convertData;
804
847
  const parent = findItemByKey(newData, rowKey, record.parentId);
805
848
  const newRows = Array.from({
806
849
  length: n
@@ -822,7 +865,7 @@ const InternalTable = props => {
822
865
  // setMergedFilterKeys?.(rs)
823
866
 
824
867
  // Cập nhật childData mới
825
- const childData = parent?.children ? [...parent.children] : [];
868
+ const childData = parent?.children ? parent.children : [];
826
869
  const index = childData.findIndex(obj => obj[rowKey] === record[rowKey]);
827
870
  childData.splice(index + 1, 0, ...newRows);
828
871
  const newRowData = {
@@ -847,8 +890,7 @@ const InternalTable = props => {
847
890
  toolbar: item
848
891
  });
849
892
  } else {
850
- // const newData = [...dataSource]
851
- const newData = [...convertData];
893
+ const newData = convertData;
852
894
  let newElement;
853
895
  if (!record?.children || record?.children && record.children.length === 0) {
854
896
  newElement = {
@@ -900,6 +942,415 @@ const InternalTable = props => {
900
942
  // setInnerExpandedKeys(newExpandedKeys)
901
943
  // }
902
944
  }, [convertData, defaultValue, pipeline, triggerChangeData]);
945
+
946
+ // Copy
947
+
948
+ const copySelectionToClipboard = React.useCallback(() => {
949
+ if (!startCell || !endCell) {
950
+ return;
951
+ }
952
+ const allRows = dataSource;
953
+ const rowIds = getRowIdsBetween3(convertData, rowIndexMap, startCell?.rowId ?? '', endCell?.rowId ?? '');
954
+ const colIds = getColIdsBetween1(leafColumns, startCell?.colId ?? '', endCell?.colId ?? '');
955
+ const dataToCopy = [];
956
+ rowIds.forEach(rowId => {
957
+ const row = allRows.find(r => r.rowId === rowId);
958
+ if (!row) return;
959
+ const rowData = [];
960
+ colIds.forEach(colId => {
961
+ const value = row[colId];
962
+ rowData.push(value !== undefined ? String(value) : '');
963
+ });
964
+ dataToCopy.push(rowData);
965
+ });
966
+
967
+ // Convert to TSV string
968
+ const tsv = dataToCopy.map(row => row.join('\t')).join('\n');
969
+ copy(tsv).then(() => {}).catch(() => {});
970
+ }, [startCell, endCell, dataSource, convertData, rowIndexMap, leafColumns, copy]);
971
+ React.useEffect(() => {
972
+ const handleKeyDown = e => {
973
+ if (e.ctrlKey && e.key === 'c' && startCell && endCell && !isEditing) {
974
+ e.preventDefault();
975
+ copySelectionToClipboard();
976
+ }
977
+ if (e.ctrlKey && e.key === 'x' && startCell && endCell && !isEditing) {
978
+ e.preventDefault();
979
+ if (startCell && endCell) {
980
+ const tmpData = flattenArray(convertData);
981
+ const rs = tmpData.map((row, index) => {
982
+ if (!rangeState?.rowRange.includes(row.rowId)) {
983
+ return row;
984
+ }
985
+ const updatedRow = {
986
+ ...row
987
+ };
988
+ for (const field of rangeState.colRange) {
989
+ const col = leafColumns.find(it => it.field === field);
990
+ const columnIndex = leafColumns.findIndex(it => it.field === field);
991
+ if (isEditable(col, row)) {
992
+ let newValue = '';
993
+ if (col.type === 'number') {
994
+ newValue = 0;
995
+ }
996
+ if (col.type === 'boolean' || col.type === 'checkbox') {
997
+ newValue = false;
998
+ }
999
+ const cellEditType = getEditType(col, row);
1000
+ if (col.type === 'date' || col.type === 'datetime' || col.type === 'time' || col.type === 'month' || col.type === 'week' || col.type === 'year' || col.type === 'quarter' || cellEditType === 'asyncSelect' || cellEditType === 'select' || cellEditType === 'treeSelect' || cellEditType === 'selectTable') {
1001
+ newValue = undefined;
1002
+ }
1003
+ updatedRow[col.field] = newValue;
1004
+ handleCellChange({
1005
+ key: row[rowKey],
1006
+ field: col.field,
1007
+ record: updatedRow,
1008
+ prevState: row[col.field],
1009
+ newState: newValue,
1010
+ option: '',
1011
+ indexCol: columnIndex,
1012
+ indexRow: index,
1013
+ type: e.key
1014
+ });
1015
+ }
1016
+ }
1017
+ return updatedRow;
1018
+ });
1019
+ const newData = unFlattenData(rs);
1020
+ triggerChangeData?.(newData, {
1021
+ type: 'DELETE_CONTENT'
1022
+ });
1023
+ }
1024
+ copySelectionToClipboard();
1025
+ }
1026
+ };
1027
+ document.addEventListener('keydown', handleKeyDown);
1028
+ return () => document.removeEventListener('keydown', handleKeyDown);
1029
+ }, [startCell, endCell, copySelectionToClipboard, isEditing, convertData, triggerChangeData, leafColumns, handleCellChange, rangeState?.rowRange, rangeState?.colRange]);
1030
+
1031
+ // Copy
1032
+
1033
+ // paste
1034
+
1035
+ const handlePasted = React.useCallback(pasteData => {
1036
+ if (!startCell) {
1037
+ return;
1038
+ }
1039
+ const rows = pasteData.slice(0, onCellPaste?.maxRowsPaste ?? 200);
1040
+ const allRows = convertData;
1041
+ const allCols = collectNodes(pipeline.getColumns(), 'leaf-only');
1042
+ const startRowIdx = rowIndexMap.get(startCell.rowId);
1043
+ const startColIdx = allCols.findIndex(c => c.field === startCell.colId);
1044
+
1045
+ // const record: any = allRows[startRowIdx].original
1046
+
1047
+ // const record: any = table.getRow(startCell.rowId).original
1048
+ const recordIndex = rowIndexMap.get(startCell.rowId);
1049
+ const row = recordIndex ? allRows[recordIndex] : {};
1050
+ if (!row.parentId) {
1051
+ // Cập nhật data mới
1052
+ const newData = flattenArray(convertData);
1053
+
1054
+ // Lấy vị trí bắt đầu
1055
+
1056
+ // const startRow = newData.findIndex((it) => it[rowKey as any] === record[rowKey])
1057
+ const startRow = startRowIdx;
1058
+ const startCol = startColIdx;
1059
+ const pastedRows = [];
1060
+ const pastedColumns = new Set();
1061
+ rows.forEach((rowValues, rowIndex) => {
1062
+ const targetRow = startRow + rowIndex;
1063
+
1064
+ // Nếu vượt quá số dòng hiện có, thêm dòng mới
1065
+ if (targetRow >= newData.length) {
1066
+ const newID = newGuid();
1067
+ const defaultRowValue = getDefaultValue(defaultValue);
1068
+ newData.push(defaultValue ? {
1069
+ ...defaultRowValue,
1070
+ id: undefined,
1071
+ rowId: newID
1072
+ } : {
1073
+ id: undefined,
1074
+ rowId: newID
1075
+ });
1076
+ }
1077
+ rowValues.forEach((cellValue, colIndex) => {
1078
+ const targetCol = startCol + colIndex;
1079
+ if (targetCol >= allCols.length) {
1080
+ // Không vượt quá số cột
1081
+ return;
1082
+ }
1083
+ const columnOri = allCols[targetCol];
1084
+ const isEdit = typeof columnOri?.editEnable === 'function' ? columnOri.editEnable(newData[targetRow]) : columnOri.editEnable;
1085
+ if (isEdit) {
1086
+ const columnKey = allCols[targetCol].field;
1087
+ if (columnOri.type === 'number') {
1088
+ if (cellValue.trim() === '') {
1089
+ newData[targetRow] = {
1090
+ ...newData[targetRow],
1091
+ [columnKey]: null
1092
+ };
1093
+ }
1094
+ if (isFormattedNumber(cellValue.trim()) || !isNaN(Number(cellValue.trim()))) {
1095
+ const colFormat = typeof columnOri.format === 'function' ? columnOri.format(row) : columnOri.format;
1096
+ const valuePasteFormat = detectSeparators(cellValue.trim());
1097
+ const cellFormat = getFormat(colFormat, format);
1098
+ const thousandSeparator = valuePasteFormat?.thousandSeparator;
1099
+ const decimalSeparator = valuePasteFormat?.decimalSeparator;
1100
+ const dec = cellFormat?.decimalScale;
1101
+ const numericFormatProps = {
1102
+ thousandSeparator: checkThousandSeparator(thousandSeparator, decimalSeparator),
1103
+ decimalSeparator: checkDecimalSeparator(thousandSeparator, decimalSeparator),
1104
+ allowNegative: cellFormat?.allowNegative ?? true,
1105
+ prefix: cellFormat?.prefix,
1106
+ suffix: cellFormat?.suffix,
1107
+ decimalScale: dec,
1108
+ fixedDecimalScale: cellFormat?.fixedDecimalScale ?? false
1109
+ };
1110
+ const val = removeNumericFormat(cellValue.trim(), undefined, numericFormatProps);
1111
+ newData[targetRow] = {
1112
+ ...newData[targetRow],
1113
+ [columnKey]: Number(val)
1114
+ };
1115
+ }
1116
+ } else if (columnOri.type === 'data' || columnOri.type === 'datatime' || columnOri.editType === 'date' || columnOri.editType === 'datetime') {
1117
+ const isDate = isDateValue(cellValue.trim());
1118
+ const date = isDate && !isEmpty(cellValue.trim()) ? moment(cellValue.trim()).format() : null;
1119
+ newData[targetRow] = {
1120
+ ...newData[targetRow],
1121
+ [columnKey]: date
1122
+ };
1123
+ } else {
1124
+ newData[targetRow] = {
1125
+ ...newData[targetRow],
1126
+ [columnKey]: cellValue.trim()
1127
+ };
1128
+ }
1129
+ pastedColumns.add(columnKey);
1130
+ }
1131
+ });
1132
+
1133
+ // Lưu dòng được paste
1134
+ pastedRows.push(newData[targetRow]);
1135
+ });
1136
+ const pastedColumnsArray = Array.from(pastedColumns) ?? [];
1137
+ const rsFilterData = updateOrInsert(convertData, newData);
1138
+ const rs = unFlattenData(rsFilterData);
1139
+ triggerPaste?.(pastedRows, pastedColumnsArray, rs, []);
1140
+ } else {
1141
+ // const parent = findItemByKey(convertData, rowKey as any, record.parentId)
1142
+ const parent = row.getParentRow()?.original;
1143
+
1144
+ // Cập nhật childData mới
1145
+ const childData = parent?.children ? [...parent.children] : [];
1146
+
1147
+ // Lấy vị trí bắt đầu
1148
+ // const { row: startRow, col: startCol } = selectedCell;
1149
+ const startRow = childData.findIndex(it => it[rowKey] === row[rowKey]);
1150
+ const startCol = startColIdx;
1151
+ const pastedRows = [];
1152
+ const pastedColumns = new Set();
1153
+ rows.forEach((rowValues, rowIndex) => {
1154
+ const targetRow = startRow + rowIndex;
1155
+
1156
+ // Nếu vượt quá số dòng hiện có, thêm dòng mới
1157
+ if (targetRow >= childData.length) {
1158
+ childData.push({
1159
+ id: undefined,
1160
+ rowId: newGuid(),
1161
+ parentId: parent[rowKey ?? 'id']
1162
+ });
1163
+ }
1164
+ rowValues.forEach((cellValue, colIndex) => {
1165
+ const targetCol = startCol + colIndex;
1166
+ if (targetCol >= allCols.length) {
1167
+ // Không vượt quá số cột
1168
+ return;
1169
+ }
1170
+ const columnTarget = allCols[targetCol];
1171
+ const columnOri = columnTarget.columnDef.meta ?? {};
1172
+ const isEdit = typeof columnOri?.editEnable === 'function' ? columnOri.editEnable(childData[targetRow]) : columnOri.editEnable;
1173
+ if (isEdit) {
1174
+ const columnKey = allCols[targetCol].id;
1175
+ if (columnOri.type === 'number') {
1176
+ if (cellValue.trim() === '') {
1177
+ childData[targetRow] = {
1178
+ ...childData[targetRow],
1179
+ [columnKey]: null
1180
+ };
1181
+ }
1182
+ if (isFormattedNumber(cellValue.trim()) || !isNaN(Number(cellValue.trim()))) {
1183
+ const colFormat = typeof columnOri.format === 'function' ? columnOri.format(row) : columnOri.format;
1184
+ const valuePasteFormat = detectSeparators(cellValue.trim());
1185
+ const cellFormat = getFormat(colFormat, format);
1186
+ const thousandSeparator = valuePasteFormat?.thousandSeparator;
1187
+ const decimalSeparator = valuePasteFormat?.decimalSeparator;
1188
+ const dec = cellFormat?.decimalScale;
1189
+ const numericFormatProps = {
1190
+ thousandSeparator: checkThousandSeparator(thousandSeparator, decimalSeparator),
1191
+ decimalSeparator: checkDecimalSeparator(thousandSeparator, decimalSeparator),
1192
+ allowNegative: cellFormat?.allowNegative ?? true,
1193
+ prefix: cellFormat?.prefix,
1194
+ suffix: cellFormat?.suffix,
1195
+ decimalScale: dec,
1196
+ fixedDecimalScale: cellFormat?.fixedDecimalScale ?? false
1197
+ };
1198
+ const val = removeNumericFormat(cellValue.trim(), undefined, numericFormatProps);
1199
+ childData[targetRow] = {
1200
+ ...childData[targetRow],
1201
+ [columnKey]: Number(val)
1202
+ };
1203
+ }
1204
+ } else if (columnOri.type === 'data' || columnOri.type === 'datatime' || columnOri.editType === 'date' || columnOri.editType === 'datetime') {
1205
+ const isDate = isDateValue(cellValue.trim());
1206
+ const date = isDate && !isEmpty(cellValue.trim()) ? moment(cellValue.trim()).format() : null;
1207
+ childData[targetRow] = {
1208
+ ...childData[targetRow],
1209
+ [columnKey]: date
1210
+ };
1211
+ } else {
1212
+ childData[targetRow] = {
1213
+ ...childData[targetRow],
1214
+ [columnKey]: cellValue.trim()
1215
+ };
1216
+ }
1217
+ pastedColumns.add(columnKey);
1218
+ }
1219
+ });
1220
+
1221
+ // Lưu dòng được paste
1222
+ pastedRows.push(childData[targetRow]);
1223
+ });
1224
+ const pastedColumnsArray = Array.from(pastedColumns) ?? [];
1225
+ const newRowData = {
1226
+ ...parent,
1227
+ children: childData
1228
+ }; // item đã được filter
1229
+
1230
+ const newDataSource = updateArrayByKey(convertData, newRowData, rowKey);
1231
+ triggerPaste?.(pastedRows, pastedColumnsArray, newDataSource, []);
1232
+ }
1233
+ }, [convertData, defaultValue, format, onCellPaste?.maxRowsPaste, pipeline, rowIndexMap, startCell, triggerPaste]);
1234
+ const handlePasteToTable = React.useCallback((pasteData, e) => {
1235
+ if (!startCell) return;
1236
+ const rowsPasted = parseExcelClipboard(e);
1237
+ if (rowsPasted.length > (onCellPaste?.maxRowsPaste ?? 200)) {
1238
+ // bật popup thông báo
1239
+
1240
+ Modal.confirm({
1241
+ content: /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Paragraph, {
1242
+ style: {
1243
+ marginBottom: '.25rem',
1244
+ fontSize: 14
1245
+ }
1246
+ }, "D\u1EEF li\u1EC7u sao ch\xE9p v\u01B0\u1EE3t qu\xE1 s\u1ED1 d\xF2ng cho ph\xE9p (", onCellPaste?.maxRowsPaste ?? 200, " d\xF2ng).Ph\u1EA7n m\u1EC1n s\u1EBD ch\u1EC9 l\u1EA5y (", onCellPaste?.maxRowsPaste ?? 200, " d\xF2ng \u0111\u1EA7u ti\xEAn."), /*#__PURE__*/React.createElement(Title, {
1247
+ level: 5,
1248
+ style: {
1249
+ marginTop: '.75rem'
1250
+ }
1251
+ }, "B\u1EA1n c\xF3 mu\u1ED1n ti\u1EBFp t\u1EE5c sao ch\xE9p kh\xF4ng?")),
1252
+ centered: true,
1253
+ className: 'be-popup-container',
1254
+ onOk: () => {
1255
+ handlePasted(rowsPasted);
1256
+ }
1257
+ });
1258
+ } else {
1259
+ handlePasted(rowsPasted);
1260
+ }
1261
+ }, [handlePasted, onCellPaste?.maxRowsPaste, startCell]);
1262
+
1263
+ // const handleDeleteContent = () => {
1264
+
1265
+ // if (startCell && endCell) {
1266
+
1267
+ // const tmpData = flattenArray([...convertData])
1268
+
1269
+ // const rs = tmpData.map((row, index: number) => {
1270
+
1271
+ // if (!rangeState?.rowRange.includes(row.rowId)) {
1272
+ // return row
1273
+ // }
1274
+
1275
+ // const updatedRow = { ...row }
1276
+
1277
+ // for (const colId of rangeState.colRange) {
1278
+
1279
+ // const column = leafColumns.find((it) => it.field === colId)
1280
+ // const columnIndex = leafColumns.findIndex((it) => it.field === colId)
1281
+
1282
+ // if (isEditable(column as any, row)) {
1283
+
1284
+ // let newValue: any = ''
1285
+
1286
+ // if (column.type === 'number') {
1287
+ // newValue = 0
1288
+ // }
1289
+
1290
+ // if (column.type === 'boolean' || column.type === 'checkbox') {
1291
+ // newValue = false
1292
+ // }
1293
+
1294
+ // const editType = getEditType(column as any, row)
1295
+
1296
+ // if (column.type === 'date' ||
1297
+ // column.type === 'datetime' ||
1298
+ // column.type === 'time' ||
1299
+ // column.type === 'month' ||
1300
+ // column.type === 'week' ||
1301
+ // column.type === 'year' ||
1302
+ // column.type === 'quarter' ||
1303
+ // editType === 'asyncSelect' ||
1304
+ // editType === 'select' ||
1305
+ // editType === 'treeSelect' ||
1306
+ // editType === 'selectTable'
1307
+ // ) {
1308
+ // newValue = undefined
1309
+ // }
1310
+
1311
+ // updatedRow[colId] = newValue
1312
+
1313
+ // handleCellChange({
1314
+ // key: row[rowKey] as any,
1315
+ // field: column.field ?? column.dataIndex as any,
1316
+ // record: updatedRow,
1317
+ // prevState: row[column.field],
1318
+ // newState: newValue,
1319
+ // option: '',
1320
+ // indexCol: columnIndex,
1321
+ // indexRow: index,
1322
+ // type: 'blur'
1323
+ // }
1324
+ // )
1325
+
1326
+ // }
1327
+ // }
1328
+
1329
+ // return updatedRow;
1330
+ // })
1331
+
1332
+ // const newData = unFlattenData(rs)
1333
+
1334
+ // triggerChangeData?.([...newData], { type: 'DELETE_CONTENT' })
1335
+
1336
+ // }
1337
+
1338
+ // }
1339
+
1340
+ React.useEffect(() => {
1341
+ const handlePaste = e => {
1342
+ if (startCell && !isEditing) {
1343
+ e.preventDefault(); // Chặn hành vi mặc định
1344
+ const clipboardText = e.clipboardData?.getData('text/plain') ?? '';
1345
+ handlePasteToTable(clipboardText, e);
1346
+ }
1347
+ };
1348
+ document.addEventListener('paste', handlePaste);
1349
+ return () => document.removeEventListener('paste', handlePaste);
1350
+ }, [startCell, endCell, handlePasteToTable, isEditing, copiedText]);
1351
+
1352
+ // Paste
1353
+
903
1354
  const toolbarItemsBottom = React.useMemo(() => {
904
1355
  // Chưa có dòng| ô được chọn
905
1356
  if (!rowsFocus || rowsFocus.length === 0) {
@@ -1488,7 +1939,7 @@ const InternalTable = props => {
1488
1939
  // {...pipeline.getProps()}
1489
1940
  ,
1490
1941
  originData: convertData,
1491
- dataSource: dataSource,
1942
+ dataSource: mergedData,
1492
1943
  originColumns: columns,
1493
1944
  groupColumns: groupColumns,
1494
1945
  setVisibleKeys: setVisibleKeys,