es-grid-template 1.9.23 → 1.9.25
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.
- package/es/grid-component/type.d.ts +107 -7
- package/es/group-component/hook/utils.d.ts +3 -3
- package/es/table-component/TableContainerEdit.js +9 -6
- package/es/table-component/body/TableBodyCell.js +7 -1
- package/es/table-component/hook/utils.js +12 -4
- package/es/table-component/table/TableWrapper.js +1 -0
- package/lib/grid-component/type.d.ts +107 -7
- package/lib/table-component/TableContainerEdit.js +6 -5
- package/lib/table-component/body/TableBodyCell.js +7 -1
- package/lib/table-component/hook/utils.js +12 -4
- package/lib/table-component/table/TableWrapper.js +1 -0
- package/package.json +1 -1
|
@@ -312,105 +312,205 @@ export type IFTheme = {
|
|
|
312
312
|
cssVariables?: BaseTableCSSVariables;
|
|
313
313
|
};
|
|
314
314
|
export type TableProps<RecordType = AnyObject> = {
|
|
315
|
+
/** Unique id của table */
|
|
315
316
|
id?: string;
|
|
317
|
+
/** Theme hiển thị của table */
|
|
316
318
|
theme?: IFTheme;
|
|
319
|
+
/** Hiển thị border bao quanh toàn bộ table */
|
|
317
320
|
useOuterBorder?: boolean;
|
|
321
|
+
/** Hiển thị header */
|
|
318
322
|
showHeader?: boolean;
|
|
323
|
+
/** CSS class cho container table */
|
|
319
324
|
className?: string;
|
|
325
|
+
/** Tiêu đề table */
|
|
320
326
|
title?: ReactNode | ((data: RecordType) => ReactNode);
|
|
327
|
+
/** Tiêu đề khi table ở chế độ fullscreen */
|
|
321
328
|
fullScreenTitle?: ReactNode | (() => ReactNode);
|
|
329
|
+
/** Tên field dùng làm key duy nhất cho mỗi record */
|
|
322
330
|
rowKey?: string;
|
|
331
|
+
/** Nguồn dữ liệu */
|
|
323
332
|
dataSource: RecordType[];
|
|
333
|
+
/** Danh sách cột */
|
|
324
334
|
columns: ColumnsTable<RecordType>;
|
|
335
|
+
/** Hiển thị loading */
|
|
325
336
|
loading?: boolean;
|
|
337
|
+
/** Chiều cao table */
|
|
326
338
|
height?: number;
|
|
339
|
+
/** Chiều cao tối thiểu table */
|
|
327
340
|
minHeight?: number;
|
|
341
|
+
/** Format dữ liệu mặc định */
|
|
328
342
|
format?: IFormat;
|
|
329
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Hàm translate
|
|
345
|
+
* useTranslation 'react-i18next'
|
|
346
|
+
* {t} = useTranslation()
|
|
347
|
+
* */
|
|
330
348
|
t?: any;
|
|
331
349
|
/** Language code: exp: vi || en || ja || zh .... */
|
|
332
350
|
lang?: string;
|
|
351
|
+
/** custom Locale cấu hình cho table */
|
|
333
352
|
locale?: Locale;
|
|
353
|
+
/** Cấu hình wrap text */
|
|
334
354
|
wrapSettings?: IWrapSettings;
|
|
355
|
+
/** Bật chế độ tải dữ liệu vô hạn */
|
|
335
356
|
infiniteScroll?: boolean;
|
|
357
|
+
/** Hàm gọi khi cuộn đến cuối */
|
|
336
358
|
next?: () => void;
|
|
359
|
+
/** Ngưỡng trigger load thêm dữ liệu */
|
|
337
360
|
onEndReachedThreshold?: number;
|
|
338
|
-
/**
|
|
361
|
+
/** Cho phép group dữ liệu */
|
|
339
362
|
groupAble?: boolean;
|
|
363
|
+
/** Danh sách cột dùng để group */
|
|
340
364
|
groupColumns?: string[];
|
|
365
|
+
/** Cấu hình group */
|
|
341
366
|
groupSetting?: IGroupSetting;
|
|
342
|
-
/**
|
|
367
|
+
/** Hiển thị toolbar */
|
|
343
368
|
showToolbar?: boolean;
|
|
369
|
+
/** Các action trên toolbar
|
|
370
|
+
*
|
|
371
|
+
* các Key mặc định:
|
|
372
|
+
* key: 'ADD' : Thêm dòng mới vào cuối
|
|
373
|
+
* key: 'DUPLICATE' : Nhân bản dòng được chọn (nếu có)
|
|
374
|
+
* key: 'INSERT_AFTER' : Chèn dòng mới sau dòng được chọn
|
|
375
|
+
* key: 'INSERT_BEFORE' : Chèn dòng mới trước dòng được chọn
|
|
376
|
+
* key: 'INSERT_CHILDREN' : Chèn dòng con vào dòng được chọn
|
|
377
|
+
* key: 'DELETE' : Xóa tất cả dòng
|
|
378
|
+
* key: 'DELETE_ROWS' : Xóa các dòng được chọn
|
|
379
|
+
*/
|
|
344
380
|
toolbarItems?: ToolbarItem[];
|
|
381
|
+
/** Chế độ hiển thị toolbar */
|
|
345
382
|
toolbarMode?: 'scroll';
|
|
383
|
+
/** Hiển thị chức năng chọn cột */
|
|
346
384
|
showColumnChoose?: boolean;
|
|
385
|
+
/** Callback khi thay đổi cột hiển thị */
|
|
347
386
|
onChooseColumns?: (props: IOnChooseColumns) => void;
|
|
387
|
+
/** Cho phép fullscreen */
|
|
348
388
|
fullScreen?: boolean;
|
|
389
|
+
/** Cấu hình phân trang, false để tắt */
|
|
349
390
|
pagination?: false | PaginationConfig;
|
|
391
|
+
/**
|
|
392
|
+
* @deprecated
|
|
393
|
+
* @since 1.9.25
|
|
394
|
+
*/
|
|
350
395
|
showCustomTooltip?: boolean;
|
|
351
396
|
/** Context Menu */
|
|
397
|
+
/** Danh sách menu chuột phải */
|
|
352
398
|
contextMenuItems?: ContextMenuItem[];
|
|
399
|
+
/**
|
|
400
|
+
* @deprecated
|
|
401
|
+
* @since 1.9.25
|
|
402
|
+
*/
|
|
353
403
|
showDefaultContext?: boolean;
|
|
404
|
+
/**
|
|
405
|
+
* Danh sách menu cần ẩn
|
|
406
|
+
* Có thể trả về động theo row hiện tại
|
|
407
|
+
*/
|
|
354
408
|
contextMenuHidden?: string[] | ((args?: Omit<ContextInfo<RecordType>, 'item' | 'event'>) => string[]);
|
|
409
|
+
/** func khi mở context menu */
|
|
355
410
|
contextMenuOpen?: (args: Omit<ContextInfo<RecordType>, 'item'>) => void;
|
|
411
|
+
/** func khi click menu */
|
|
356
412
|
contextMenuClick?: (args: ContextInfo<RecordType>) => void;
|
|
413
|
+
/** Double click vào row */
|
|
357
414
|
recordDoubleClick?: (args: RecordDoubleClickEventArgs<RecordType>) => void;
|
|
415
|
+
/** Click vào row */
|
|
358
416
|
recordClick?: (args: RecordDoubleClickEventArgs<RecordType>) => void;
|
|
359
417
|
/** Filter */
|
|
418
|
+
/** Hiển thị filter nâng cao */
|
|
360
419
|
showAdvanceFilter?: boolean;
|
|
420
|
+
/** Cho phép filter */
|
|
361
421
|
allowFiltering?: boolean;
|
|
422
|
+
/** Filter mặc định */
|
|
362
423
|
defaultFilter?: FilterItem[];
|
|
424
|
+
/** func khi filter thay đổi */
|
|
363
425
|
onFilter?: (query: FilterItem[]) => void;
|
|
426
|
+
/** Nguồn dữ liệu filter */
|
|
364
427
|
dataSourceFilter?: SourceFilter[];
|
|
428
|
+
/**
|
|
429
|
+
* Custom popup filter cho từng cột
|
|
430
|
+
*/
|
|
365
431
|
onFilterClick?: (column: ColumnTable<RecordType>, callback: (key: string, data: any) => void) => void;
|
|
366
432
|
/** mặc định so sánh không dấu + lowercase
|
|
367
433
|
* ignoreAccents !== false => không khai báo hoặc ignoreAccents = true => so sánh không dấu
|
|
368
434
|
* ignoreAccents = false => so sánh có dấu + uperrcase sensitive
|
|
369
435
|
* **/
|
|
370
436
|
ignoreAccents?: boolean;
|
|
371
|
-
/**
|
|
372
|
-
sortMultiple?: boolean;
|
|
437
|
+
/** Cho phép sort */
|
|
373
438
|
allowSortering?: boolean;
|
|
439
|
+
/** Cho phép sort nhiều cột */
|
|
440
|
+
sortMultiple?: boolean;
|
|
441
|
+
/** func khi sort */
|
|
374
442
|
onSorter?: (args: Sorter[]) => void;
|
|
443
|
+
/** Sort mặc định */
|
|
375
444
|
defaultSorter?: Sorter[];
|
|
376
|
-
/** selected record feature */
|
|
377
445
|
selectionSettings?: SelectionSettings;
|
|
446
|
+
/** Cấu hình chọn row */
|
|
378
447
|
rowSelection?: RowSelection<RecordType>;
|
|
448
|
+
/** func khi chọn row */
|
|
379
449
|
rowSelected?: (args: {
|
|
380
450
|
type: string;
|
|
381
451
|
rowData: RecordType;
|
|
382
452
|
selected: RecordType | RecordType[];
|
|
383
453
|
}) => void;
|
|
384
|
-
/** resize
|
|
454
|
+
/** Cho phép resize độ rộng cột */
|
|
385
455
|
allowResizing?: boolean;
|
|
456
|
+
/** Hiển thị dòng tổng hợp */
|
|
386
457
|
summary?: boolean;
|
|
458
|
+
/** Dữ liệu footer */
|
|
387
459
|
footerDataSource?: any[];
|
|
460
|
+
/** Hiển thị text khi không có dữ liệu */
|
|
388
461
|
showEmptyText?: boolean;
|
|
462
|
+
/** Cấu hình command column */
|
|
389
463
|
commandSettings?: CommandSettings;
|
|
464
|
+
/**
|
|
465
|
+
* Func khi click command
|
|
466
|
+
* Ví dụ: Edit, Delete, Copy...
|
|
467
|
+
*/
|
|
390
468
|
commandClick?: (args: CommandClick<RecordType>) => void;
|
|
469
|
+
/** Callback khi expand/collapse row */
|
|
391
470
|
onExpandClick?: (args: {
|
|
392
471
|
expandedKeys: string[];
|
|
393
472
|
key: string;
|
|
394
473
|
rowData: any;
|
|
395
474
|
}) => void;
|
|
475
|
+
/** Cấu hình expandable row */
|
|
396
476
|
expandable?: ExpandableConfig<RecordType>;
|
|
477
|
+
/** custom Action area bên trái thanh toolbar */
|
|
397
478
|
actionTemplate?: ReactNode | ReactElement | (() => ReactNode | ReactElement);
|
|
479
|
+
/** Nội dung hiển thị cuối table */
|
|
398
480
|
bottom?: ReactNode;
|
|
399
481
|
rowClassName?: string | RowClassName<RecordType>;
|
|
400
482
|
onRowStyles?: CSSProperties | ((data: RecordType) => CSSProperties);
|
|
401
483
|
onRowHeaderStyles?: Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'> | (() => Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'>);
|
|
402
484
|
onRowFooterStyles?: Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'> | (() => Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'>);
|
|
485
|
+
/** Bật virtual scroll */
|
|
403
486
|
useVirtual?: {
|
|
487
|
+
/** Virtual theo chiều ngang */
|
|
404
488
|
horizontal?: boolean;
|
|
489
|
+
/** Virtual theo chiều dọc */
|
|
405
490
|
vertical?: boolean;
|
|
406
491
|
};
|
|
492
|
+
/** Cho phép chỉnh sửa dữ liệu */
|
|
407
493
|
editAble?: boolean;
|
|
494
|
+
/** Callback khi data thay đổi */
|
|
408
495
|
onDataChange?: (data: RecordType[]) => void;
|
|
496
|
+
/** Giá trị mặc định khi thêm mới */
|
|
409
497
|
defaultValue?: AnyObject | (() => AnyObject);
|
|
498
|
+
/** Callback xử lý paste dữ liệu */
|
|
410
499
|
onCellPaste?: ICellPasteModel<RecordType>;
|
|
500
|
+
/**
|
|
501
|
+
* Callback khi cell thay đổi giá trị
|
|
502
|
+
*
|
|
503
|
+
* handleCallback dùng để cập nhật giá trị sau khi xử lý custom
|
|
504
|
+
*/
|
|
411
505
|
onCellChange?: (args: CellChangeArgs<RecordType>, handleCallback: (rowData: any, index: any, value?: any) => void) => void;
|
|
506
|
+
/** Callback click cell */
|
|
412
507
|
onCellClick?: (args: ICellClick, callback?: any) => void;
|
|
508
|
+
/** Xác định row có được edit hay không */
|
|
413
509
|
rowEditable?: (rowData: RecordType) => boolean;
|
|
510
|
+
/** Validate dữ liệu
|
|
511
|
+
*
|
|
512
|
+
* exp: validate= yup.object().shape({})
|
|
513
|
+
*/
|
|
414
514
|
validate?: any;
|
|
415
515
|
onBlur?: (data: RecordType[]) => void;
|
|
416
516
|
};
|
|
@@ -194,10 +194,10 @@ export declare const fixColumnsLeft: <RecordType>(columns: ColumnTable<RecordTyp
|
|
|
194
194
|
value: any;
|
|
195
195
|
rowData: RecordType;
|
|
196
196
|
}) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | import("react").ReactNode);
|
|
197
|
-
onCellStyles?: Omit<CSSProperties, "position" | "
|
|
198
|
-
onCellHeaderStyles?: Omit<CSSProperties, "position" | "
|
|
197
|
+
onCellStyles?: Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth"> | ((cellValue: any, cell: import("@tanstack/react-table").Cell<RecordType, unknown>) => Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth">);
|
|
198
|
+
onCellHeaderStyles?: Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth"> | ((cell: import("@tanstack/react-table").Header<RecordType, unknown>) => Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth">);
|
|
199
199
|
onCell?: (rowData: RecordType, index: number) => import("react").TdHTMLAttributes<HTMLTableCellElement>;
|
|
200
|
-
onCellFooterStyles?: Omit<CSSProperties, "position" | "
|
|
200
|
+
onCellFooterStyles?: Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth"> | ((cellValue: any, cell: import("@tanstack/react-table").Header<RecordType, unknown>) => Omit<CSSProperties, "position" | "left" | "right" | "display" | "width" | "minWidth">);
|
|
201
201
|
getValue?: (row: any, rowIndex: number) => any;
|
|
202
202
|
getCellProps?: (value: any, row: any, rowIndex: number) => import("./../../grid-component/type").CellProps;
|
|
203
203
|
headerCellProps?: import("./../../grid-component/type").CellProps;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import React, { Fragment } from "react";
|
|
3
3
|
import { useCopyToClipboard } from 'usehooks-ts';
|
|
4
|
-
import { checkDecimalSeparator, checkThousandSeparator, detectSeparators, findItemByKey, flattenArray, flattenData, getAllRowKey, getColIdsBetween, getDefaultValue, getEditType, getFormat, getRowIdsBetween, getSelectedCellMatrix, getTableHeight, isEditable, isFormattedNumber, newGuid,
|
|
4
|
+
import { checkDecimalSeparator, checkThousandSeparator, detectSeparators, findItemByKey, flattenArray, flattenData, getAllRowKey, getColIdsBetween, getDefaultValue, getEditType, getFormat, getRowIdsBetween, getSelectedCellMatrix, getTableHeight, isEditable, isFormattedNumber, newGuid, parseExcelClipboard,
|
|
5
|
+
// parseExcelClipboardText,
|
|
6
|
+
sumSize,
|
|
5
7
|
// sumSize,
|
|
6
8
|
unFlattenData, updateArrayByKey, updateColumnWidthsRecursive, updateOrInsert } from "./hook/utils";
|
|
7
9
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
@@ -522,7 +524,7 @@ const TableContainerEdit = props => {
|
|
|
522
524
|
triggerPaste?.(pastedRows, pastedColumnsArray, newDataSource, []);
|
|
523
525
|
}
|
|
524
526
|
}, [dataSource, format, onCellPaste?.maxRowsPaste, originData, rowKey, startCell, table, triggerPaste]);
|
|
525
|
-
const handlePasteToTable = React.useCallback(pasteData => {
|
|
527
|
+
const handlePasteToTable = React.useCallback((pasteData, e) => {
|
|
526
528
|
if (!startCell) return;
|
|
527
529
|
|
|
528
530
|
// const pastedRows = pasted.trim().split('\n').map(row => row.split('\t'));
|
|
@@ -535,8 +537,9 @@ const TableContainerEdit = props => {
|
|
|
535
537
|
|
|
536
538
|
// )
|
|
537
539
|
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
+
const rowsPasted = parseExcelClipboard(e);
|
|
541
|
+
// const rowsPasted2 = parseExcelClipboardText(pasteData)
|
|
542
|
+
|
|
540
543
|
if (rowsPasted.length > (onCellPaste?.maxRowsPaste ?? 200)) {
|
|
541
544
|
// bật popup thông báo
|
|
542
545
|
|
|
@@ -583,7 +586,7 @@ const TableContainerEdit = props => {
|
|
|
583
586
|
if (startCell && !editingKey) {
|
|
584
587
|
e.preventDefault(); // Chặn hành vi mặc định
|
|
585
588
|
const clipboardText = e.clipboardData?.getData('text/plain') ?? '';
|
|
586
|
-
handlePasteToTable(clipboardText);
|
|
589
|
+
handlePasteToTable(clipboardText, e);
|
|
587
590
|
}
|
|
588
591
|
};
|
|
589
592
|
document.addEventListener('paste', handlePaste);
|
|
@@ -631,7 +634,7 @@ const TableContainerEdit = props => {
|
|
|
631
634
|
return () => {
|
|
632
635
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
633
636
|
};
|
|
634
|
-
}, [dataSource, editingKey, id, onBlur]);
|
|
637
|
+
}, [dataSource, editingKey, endCell, focusedCell, id, onBlur, rangeState, startCell]);
|
|
635
638
|
const columnSizingState = table.getState().columnSizing;
|
|
636
639
|
React.useEffect(() => {
|
|
637
640
|
requestAnimationFrame(() => {
|
|
@@ -399,7 +399,13 @@ const TableBodyCell = props => {
|
|
|
399
399
|
if (e.target.firstChild?.clientWidth < e.target.firstChild?.scrollWidth) {
|
|
400
400
|
setIsOpenTooltip(true);
|
|
401
401
|
}
|
|
402
|
-
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// onMouseLeave={() => {
|
|
405
|
+
// setIsOpenTooltip(false)
|
|
406
|
+
// }}
|
|
407
|
+
,
|
|
408
|
+
|
|
403
409
|
onKeyDown: e => {
|
|
404
410
|
const flatRows = table.getRowModel().flatRows;
|
|
405
411
|
if (e.key === 'ArrowDown' && rowNumber < flatRows.length - 1) {
|
|
@@ -2389,12 +2389,20 @@ export function parseExcelClipboardText(text) {
|
|
|
2389
2389
|
}
|
|
2390
2390
|
export function parseExcelClipboard(e) {
|
|
2391
2391
|
const text = e.clipboardData?.getData('text/plain') ?? '';
|
|
2392
|
-
if (!text)
|
|
2392
|
+
if (!text) {
|
|
2393
|
+
return [];
|
|
2394
|
+
}
|
|
2393
2395
|
|
|
2394
2396
|
// Excel thường dùng \r\n giữa dòng, \t giữa cột
|
|
2395
|
-
const
|
|
2396
|
-
|
|
2397
|
-
|
|
2397
|
+
const rawRows = text.split(/\r?\n/); // tách theo dòng
|
|
2398
|
+
|
|
2399
|
+
// Loại bỏ chỉ các dòng trống ở cuối (trailing empty rows), giữ nguyên dòng trống ở giữa
|
|
2400
|
+
let lastNonEmpty = rawRows.length - 1;
|
|
2401
|
+
while (lastNonEmpty >= 0 && rawRows[lastNonEmpty].trim() === '') {
|
|
2402
|
+
lastNonEmpty--;
|
|
2403
|
+
}
|
|
2404
|
+
const trimmedRows = lastNonEmpty >= 0 ? rawRows.slice(0, lastNonEmpty + 1) : [];
|
|
2405
|
+
const rows = trimmedRows.map(row => row.split('\t') // tách theo cột
|
|
2398
2406
|
.map(cell => cell.replace(/\r/g, '').replace(/\n/g, '\n')) // giữ xuống dòng trong ô
|
|
2399
2407
|
);
|
|
2400
2408
|
return rows;
|
|
@@ -312,105 +312,205 @@ export type IFTheme = {
|
|
|
312
312
|
cssVariables?: BaseTableCSSVariables;
|
|
313
313
|
};
|
|
314
314
|
export type TableProps<RecordType = AnyObject> = {
|
|
315
|
+
/** Unique id của table */
|
|
315
316
|
id?: string;
|
|
317
|
+
/** Theme hiển thị của table */
|
|
316
318
|
theme?: IFTheme;
|
|
319
|
+
/** Hiển thị border bao quanh toàn bộ table */
|
|
317
320
|
useOuterBorder?: boolean;
|
|
321
|
+
/** Hiển thị header */
|
|
318
322
|
showHeader?: boolean;
|
|
323
|
+
/** CSS class cho container table */
|
|
319
324
|
className?: string;
|
|
325
|
+
/** Tiêu đề table */
|
|
320
326
|
title?: ReactNode | ((data: RecordType) => ReactNode);
|
|
327
|
+
/** Tiêu đề khi table ở chế độ fullscreen */
|
|
321
328
|
fullScreenTitle?: ReactNode | (() => ReactNode);
|
|
329
|
+
/** Tên field dùng làm key duy nhất cho mỗi record */
|
|
322
330
|
rowKey?: string;
|
|
331
|
+
/** Nguồn dữ liệu */
|
|
323
332
|
dataSource: RecordType[];
|
|
333
|
+
/** Danh sách cột */
|
|
324
334
|
columns: ColumnsTable<RecordType>;
|
|
335
|
+
/** Hiển thị loading */
|
|
325
336
|
loading?: boolean;
|
|
337
|
+
/** Chiều cao table */
|
|
326
338
|
height?: number;
|
|
339
|
+
/** Chiều cao tối thiểu table */
|
|
327
340
|
minHeight?: number;
|
|
341
|
+
/** Format dữ liệu mặc định */
|
|
328
342
|
format?: IFormat;
|
|
329
|
-
/**
|
|
343
|
+
/**
|
|
344
|
+
* Hàm translate
|
|
345
|
+
* useTranslation 'react-i18next'
|
|
346
|
+
* {t} = useTranslation()
|
|
347
|
+
* */
|
|
330
348
|
t?: any;
|
|
331
349
|
/** Language code: exp: vi || en || ja || zh .... */
|
|
332
350
|
lang?: string;
|
|
351
|
+
/** custom Locale cấu hình cho table */
|
|
333
352
|
locale?: Locale;
|
|
353
|
+
/** Cấu hình wrap text */
|
|
334
354
|
wrapSettings?: IWrapSettings;
|
|
355
|
+
/** Bật chế độ tải dữ liệu vô hạn */
|
|
335
356
|
infiniteScroll?: boolean;
|
|
357
|
+
/** Hàm gọi khi cuộn đến cuối */
|
|
336
358
|
next?: () => void;
|
|
359
|
+
/** Ngưỡng trigger load thêm dữ liệu */
|
|
337
360
|
onEndReachedThreshold?: number;
|
|
338
|
-
/**
|
|
361
|
+
/** Cho phép group dữ liệu */
|
|
339
362
|
groupAble?: boolean;
|
|
363
|
+
/** Danh sách cột dùng để group */
|
|
340
364
|
groupColumns?: string[];
|
|
365
|
+
/** Cấu hình group */
|
|
341
366
|
groupSetting?: IGroupSetting;
|
|
342
|
-
/**
|
|
367
|
+
/** Hiển thị toolbar */
|
|
343
368
|
showToolbar?: boolean;
|
|
369
|
+
/** Các action trên toolbar
|
|
370
|
+
*
|
|
371
|
+
* các Key mặc định:
|
|
372
|
+
* key: 'ADD' : Thêm dòng mới vào cuối
|
|
373
|
+
* key: 'DUPLICATE' : Nhân bản dòng được chọn (nếu có)
|
|
374
|
+
* key: 'INSERT_AFTER' : Chèn dòng mới sau dòng được chọn
|
|
375
|
+
* key: 'INSERT_BEFORE' : Chèn dòng mới trước dòng được chọn
|
|
376
|
+
* key: 'INSERT_CHILDREN' : Chèn dòng con vào dòng được chọn
|
|
377
|
+
* key: 'DELETE' : Xóa tất cả dòng
|
|
378
|
+
* key: 'DELETE_ROWS' : Xóa các dòng được chọn
|
|
379
|
+
*/
|
|
344
380
|
toolbarItems?: ToolbarItem[];
|
|
381
|
+
/** Chế độ hiển thị toolbar */
|
|
345
382
|
toolbarMode?: 'scroll';
|
|
383
|
+
/** Hiển thị chức năng chọn cột */
|
|
346
384
|
showColumnChoose?: boolean;
|
|
385
|
+
/** Callback khi thay đổi cột hiển thị */
|
|
347
386
|
onChooseColumns?: (props: IOnChooseColumns) => void;
|
|
387
|
+
/** Cho phép fullscreen */
|
|
348
388
|
fullScreen?: boolean;
|
|
389
|
+
/** Cấu hình phân trang, false để tắt */
|
|
349
390
|
pagination?: false | PaginationConfig;
|
|
391
|
+
/**
|
|
392
|
+
* @deprecated
|
|
393
|
+
* @since 1.9.25
|
|
394
|
+
*/
|
|
350
395
|
showCustomTooltip?: boolean;
|
|
351
396
|
/** Context Menu */
|
|
397
|
+
/** Danh sách menu chuột phải */
|
|
352
398
|
contextMenuItems?: ContextMenuItem[];
|
|
399
|
+
/**
|
|
400
|
+
* @deprecated
|
|
401
|
+
* @since 1.9.25
|
|
402
|
+
*/
|
|
353
403
|
showDefaultContext?: boolean;
|
|
404
|
+
/**
|
|
405
|
+
* Danh sách menu cần ẩn
|
|
406
|
+
* Có thể trả về động theo row hiện tại
|
|
407
|
+
*/
|
|
354
408
|
contextMenuHidden?: string[] | ((args?: Omit<ContextInfo<RecordType>, 'item' | 'event'>) => string[]);
|
|
409
|
+
/** func khi mở context menu */
|
|
355
410
|
contextMenuOpen?: (args: Omit<ContextInfo<RecordType>, 'item'>) => void;
|
|
411
|
+
/** func khi click menu */
|
|
356
412
|
contextMenuClick?: (args: ContextInfo<RecordType>) => void;
|
|
413
|
+
/** Double click vào row */
|
|
357
414
|
recordDoubleClick?: (args: RecordDoubleClickEventArgs<RecordType>) => void;
|
|
415
|
+
/** Click vào row */
|
|
358
416
|
recordClick?: (args: RecordDoubleClickEventArgs<RecordType>) => void;
|
|
359
417
|
/** Filter */
|
|
418
|
+
/** Hiển thị filter nâng cao */
|
|
360
419
|
showAdvanceFilter?: boolean;
|
|
420
|
+
/** Cho phép filter */
|
|
361
421
|
allowFiltering?: boolean;
|
|
422
|
+
/** Filter mặc định */
|
|
362
423
|
defaultFilter?: FilterItem[];
|
|
424
|
+
/** func khi filter thay đổi */
|
|
363
425
|
onFilter?: (query: FilterItem[]) => void;
|
|
426
|
+
/** Nguồn dữ liệu filter */
|
|
364
427
|
dataSourceFilter?: SourceFilter[];
|
|
428
|
+
/**
|
|
429
|
+
* Custom popup filter cho từng cột
|
|
430
|
+
*/
|
|
365
431
|
onFilterClick?: (column: ColumnTable<RecordType>, callback: (key: string, data: any) => void) => void;
|
|
366
432
|
/** mặc định so sánh không dấu + lowercase
|
|
367
433
|
* ignoreAccents !== false => không khai báo hoặc ignoreAccents = true => so sánh không dấu
|
|
368
434
|
* ignoreAccents = false => so sánh có dấu + uperrcase sensitive
|
|
369
435
|
* **/
|
|
370
436
|
ignoreAccents?: boolean;
|
|
371
|
-
/**
|
|
372
|
-
sortMultiple?: boolean;
|
|
437
|
+
/** Cho phép sort */
|
|
373
438
|
allowSortering?: boolean;
|
|
439
|
+
/** Cho phép sort nhiều cột */
|
|
440
|
+
sortMultiple?: boolean;
|
|
441
|
+
/** func khi sort */
|
|
374
442
|
onSorter?: (args: Sorter[]) => void;
|
|
443
|
+
/** Sort mặc định */
|
|
375
444
|
defaultSorter?: Sorter[];
|
|
376
|
-
/** selected record feature */
|
|
377
445
|
selectionSettings?: SelectionSettings;
|
|
446
|
+
/** Cấu hình chọn row */
|
|
378
447
|
rowSelection?: RowSelection<RecordType>;
|
|
448
|
+
/** func khi chọn row */
|
|
379
449
|
rowSelected?: (args: {
|
|
380
450
|
type: string;
|
|
381
451
|
rowData: RecordType;
|
|
382
452
|
selected: RecordType | RecordType[];
|
|
383
453
|
}) => void;
|
|
384
|
-
/** resize
|
|
454
|
+
/** Cho phép resize độ rộng cột */
|
|
385
455
|
allowResizing?: boolean;
|
|
456
|
+
/** Hiển thị dòng tổng hợp */
|
|
386
457
|
summary?: boolean;
|
|
458
|
+
/** Dữ liệu footer */
|
|
387
459
|
footerDataSource?: any[];
|
|
460
|
+
/** Hiển thị text khi không có dữ liệu */
|
|
388
461
|
showEmptyText?: boolean;
|
|
462
|
+
/** Cấu hình command column */
|
|
389
463
|
commandSettings?: CommandSettings;
|
|
464
|
+
/**
|
|
465
|
+
* Func khi click command
|
|
466
|
+
* Ví dụ: Edit, Delete, Copy...
|
|
467
|
+
*/
|
|
390
468
|
commandClick?: (args: CommandClick<RecordType>) => void;
|
|
469
|
+
/** Callback khi expand/collapse row */
|
|
391
470
|
onExpandClick?: (args: {
|
|
392
471
|
expandedKeys: string[];
|
|
393
472
|
key: string;
|
|
394
473
|
rowData: any;
|
|
395
474
|
}) => void;
|
|
475
|
+
/** Cấu hình expandable row */
|
|
396
476
|
expandable?: ExpandableConfig<RecordType>;
|
|
477
|
+
/** custom Action area bên trái thanh toolbar */
|
|
397
478
|
actionTemplate?: ReactNode | ReactElement | (() => ReactNode | ReactElement);
|
|
479
|
+
/** Nội dung hiển thị cuối table */
|
|
398
480
|
bottom?: ReactNode;
|
|
399
481
|
rowClassName?: string | RowClassName<RecordType>;
|
|
400
482
|
onRowStyles?: CSSProperties | ((data: RecordType) => CSSProperties);
|
|
401
483
|
onRowHeaderStyles?: Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'> | (() => Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'>);
|
|
402
484
|
onRowFooterStyles?: Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'> | (() => Omit<React.CSSProperties, 'display' | 'transform' | 'gridTemplateColumns' | 'height' | 'minHeight'>);
|
|
485
|
+
/** Bật virtual scroll */
|
|
403
486
|
useVirtual?: {
|
|
487
|
+
/** Virtual theo chiều ngang */
|
|
404
488
|
horizontal?: boolean;
|
|
489
|
+
/** Virtual theo chiều dọc */
|
|
405
490
|
vertical?: boolean;
|
|
406
491
|
};
|
|
492
|
+
/** Cho phép chỉnh sửa dữ liệu */
|
|
407
493
|
editAble?: boolean;
|
|
494
|
+
/** Callback khi data thay đổi */
|
|
408
495
|
onDataChange?: (data: RecordType[]) => void;
|
|
496
|
+
/** Giá trị mặc định khi thêm mới */
|
|
409
497
|
defaultValue?: AnyObject | (() => AnyObject);
|
|
498
|
+
/** Callback xử lý paste dữ liệu */
|
|
410
499
|
onCellPaste?: ICellPasteModel<RecordType>;
|
|
500
|
+
/**
|
|
501
|
+
* Callback khi cell thay đổi giá trị
|
|
502
|
+
*
|
|
503
|
+
* handleCallback dùng để cập nhật giá trị sau khi xử lý custom
|
|
504
|
+
*/
|
|
411
505
|
onCellChange?: (args: CellChangeArgs<RecordType>, handleCallback: (rowData: any, index: any, value?: any) => void) => void;
|
|
506
|
+
/** Callback click cell */
|
|
412
507
|
onCellClick?: (args: ICellClick, callback?: any) => void;
|
|
508
|
+
/** Xác định row có được edit hay không */
|
|
413
509
|
rowEditable?: (rowData: RecordType) => boolean;
|
|
510
|
+
/** Validate dữ liệu
|
|
511
|
+
*
|
|
512
|
+
* exp: validate= yup.object().shape({})
|
|
513
|
+
*/
|
|
414
514
|
validate?: any;
|
|
415
515
|
onBlur?: (data: RecordType[]) => void;
|
|
416
516
|
};
|
|
@@ -529,7 +529,7 @@ const TableContainerEdit = props => {
|
|
|
529
529
|
triggerPaste?.(pastedRows, pastedColumnsArray, newDataSource, []);
|
|
530
530
|
}
|
|
531
531
|
}, [dataSource, format, onCellPaste?.maxRowsPaste, originData, rowKey, startCell, table, triggerPaste]);
|
|
532
|
-
const handlePasteToTable = _react.default.useCallback(pasteData => {
|
|
532
|
+
const handlePasteToTable = _react.default.useCallback((pasteData, e) => {
|
|
533
533
|
if (!startCell) return;
|
|
534
534
|
|
|
535
535
|
// const pastedRows = pasted.trim().split('\n').map(row => row.split('\t'));
|
|
@@ -542,8 +542,9 @@ const TableContainerEdit = props => {
|
|
|
542
542
|
|
|
543
543
|
// )
|
|
544
544
|
|
|
545
|
-
|
|
546
|
-
const
|
|
545
|
+
const rowsPasted = (0, _utils.parseExcelClipboard)(e);
|
|
546
|
+
// const rowsPasted2 = parseExcelClipboardText(pasteData)
|
|
547
|
+
|
|
547
548
|
if (rowsPasted.length > (onCellPaste?.maxRowsPaste ?? 200)) {
|
|
548
549
|
// bật popup thông báo
|
|
549
550
|
|
|
@@ -590,7 +591,7 @@ const TableContainerEdit = props => {
|
|
|
590
591
|
if (startCell && !editingKey) {
|
|
591
592
|
e.preventDefault(); // Chặn hành vi mặc định
|
|
592
593
|
const clipboardText = e.clipboardData?.getData('text/plain') ?? '';
|
|
593
|
-
handlePasteToTable(clipboardText);
|
|
594
|
+
handlePasteToTable(clipboardText, e);
|
|
594
595
|
}
|
|
595
596
|
};
|
|
596
597
|
document.addEventListener('paste', handlePaste);
|
|
@@ -638,7 +639,7 @@ const TableContainerEdit = props => {
|
|
|
638
639
|
return () => {
|
|
639
640
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
640
641
|
};
|
|
641
|
-
}, [dataSource, editingKey, id, onBlur]);
|
|
642
|
+
}, [dataSource, editingKey, endCell, focusedCell, id, onBlur, rangeState, startCell]);
|
|
642
643
|
const columnSizingState = table.getState().columnSizing;
|
|
643
644
|
_react.default.useEffect(() => {
|
|
644
645
|
requestAnimationFrame(() => {
|
|
@@ -406,7 +406,13 @@ const TableBodyCell = props => {
|
|
|
406
406
|
if (e.target.firstChild?.clientWidth < e.target.firstChild?.scrollWidth) {
|
|
407
407
|
setIsOpenTooltip(true);
|
|
408
408
|
}
|
|
409
|
-
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// onMouseLeave={() => {
|
|
412
|
+
// setIsOpenTooltip(false)
|
|
413
|
+
// }}
|
|
414
|
+
,
|
|
415
|
+
|
|
410
416
|
onKeyDown: e => {
|
|
411
417
|
const flatRows = table.getRowModel().flatRows;
|
|
412
418
|
if (e.key === 'ArrowDown' && rowNumber < flatRows.length - 1) {
|
|
@@ -2518,12 +2518,20 @@ function parseExcelClipboardText(text) {
|
|
|
2518
2518
|
}
|
|
2519
2519
|
function parseExcelClipboard(e) {
|
|
2520
2520
|
const text = e.clipboardData?.getData('text/plain') ?? '';
|
|
2521
|
-
if (!text)
|
|
2521
|
+
if (!text) {
|
|
2522
|
+
return [];
|
|
2523
|
+
}
|
|
2522
2524
|
|
|
2523
2525
|
// Excel thường dùng \r\n giữa dòng, \t giữa cột
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
|
|
2526
|
+
const rawRows = text.split(/\r?\n/); // tách theo dòng
|
|
2527
|
+
|
|
2528
|
+
// Loại bỏ chỉ các dòng trống ở cuối (trailing empty rows), giữ nguyên dòng trống ở giữa
|
|
2529
|
+
let lastNonEmpty = rawRows.length - 1;
|
|
2530
|
+
while (lastNonEmpty >= 0 && rawRows[lastNonEmpty].trim() === '') {
|
|
2531
|
+
lastNonEmpty--;
|
|
2532
|
+
}
|
|
2533
|
+
const trimmedRows = lastNonEmpty >= 0 ? rawRows.slice(0, lastNonEmpty + 1) : [];
|
|
2534
|
+
const rows = trimmedRows.map(row => row.split('\t') // tách theo cột
|
|
2527
2535
|
.map(cell => cell.replace(/\r/g, '').replace(/\n/g, '\n')) // giữ xuống dòng trong ô
|
|
2528
2536
|
);
|
|
2529
2537
|
return rows;
|
|
@@ -253,6 +253,7 @@ const TableWrapper = props => {
|
|
|
253
253
|
content,
|
|
254
254
|
activeAnchor
|
|
255
255
|
}) => activeAnchor?.getAttribute('data-tooltip-sort') || content
|
|
256
|
+
// float
|
|
256
257
|
}), /*#__PURE__*/_react.default.createElement(_reactTooltip.Tooltip, {
|
|
257
258
|
id: `${id}-tooltip-error`,
|
|
258
259
|
style: {
|