UNPKG

es-grid-template

Version:

es-grid-template

1,491 lines (1,419 loc) 90.2 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import React, { Fragment, useMemo, useRef, useState } from 'react'; import customParseFormat from 'dayjs/plugin/customParseFormat'; import classNames from "classnames"; import { Button, Dropdown, Form, Modal, Typography } from "antd"; import { useForm } from 'react-hook-form'; import { Toaster } from "react-hot-toast"; import { yupResolver } from "@hookform/resolvers/yup"; import { flatColumns2, getValueCell, renderContent } from "../hooks/columns"; import EditableCell from "../EditableCell"; import { GridStyle } from "../GridStyle"; import { TableContext } from "../useContext"; import dayjs from "dayjs"; import 'dayjs/locale/es'; import 'dayjs/locale/vi'; import TableGrid from "../TableGrid"; import { addClassBorderPasteCell, addClassCellIndexSelected, addRows8, addRowsUp, checkChild, editAbleColumns, findAllChildrenKeys, findItemByKey, findItemPath, flattenArray, flattenData, getDefaultValue, getEditType, getFirstSelectCell, getLastSelectCell, getRowNumber, getRowsPasteIndex, hideDraggingPoint, isEditable, isEqualSet, isObjEmpty, newGuid, onAddBgSelectedCell, // onRemoveBorderSelectedCell, onRemoveBgSelectedCell, removeClassBorderPasteCell, removeClassCellIndexSelected, showDraggingPoint, sortedSetASC, totalFixedWidth, updateArrayByKey, getFormat, // isRangeCell, // isSelectedCell, onRemoveBgCellIndex, onAddBgCellIndex, onAddBorderSelectedCell, onRemoveBorderSelectedCell } from "../hooks"; import Message from "../../Message/Message"; import { Toolbar, ConfigProvider, InputNumber } from "rc-master-ui"; import classnames from "classnames"; import { Plus, Trash2 } from "becoxy-icons"; // import {faker} from "@faker-js/faker"; import { SELECTION_COLUMN } from "../InternalTable"; const { Paragraph, Title } = Typography; dayjs.extend(customParseFormat); const toast = 'top-right'; const defaultContext = [{ key: 'INSERT_BEFORE', label: 'Thêm dòng bên trên', icon: /*#__PURE__*/React.createElement(Plus, { fontSize: 14 }), children: [{ parentKey: 'INSERT_BEFORE', key: 'INSERT_BEFORE_1', label: 'Thêm 1 dòng', row: 1 }, { parentKey: 'INSERT_BEFORE', key: 'INSERT_BEFORE_10', label: 'Thêm 10 dòng', row: 10 }, { parentKey: 'INSERT_BEFORE', key: 'INSERT_BEFORE_50', label: 'Thêm 50 dòng', row: 50 }, { parentKey: 'INSERT_BEFORE', key: 'INSERT_BEFORE_100', label: 'Thêm 100 dòng', row: 100 }, { parentKey: 'INSERT_BEFORE', key: 'INSERT_BEFORE_ADV', label: 'Tùy chỉnh' }] }, { key: 'INSERT_AFTER', label: 'Thêm dòng bên dưới', icon: /*#__PURE__*/React.createElement(Plus, { fontSize: 14 }), children: [{ parentKey: 'INSERT_AFTER', key: 'INSERT_AFTER_1', label: 'Thêm 1 dòng', row: 1 }, { parentKey: 'INSERT_AFTER', key: 'INSERT_AFTER_10', label: 'Thêm 10 dòng', row: 10 }, { parentKey: 'INSERT_AFTER', key: 'INSERT_AFTER_50', label: 'Thêm 50 dòng', row: 50 }, { parentKey: 'INSERT_AFTER', key: 'INSERT_AFTER_100', label: 'Thêm 100 dòng', row: 100 }, { parentKey: 'INSERT_AFTER', key: 'INSERT_AFTER_ADV', label: 'Tùy chỉnh' }] }, { key: 'INSERT_CHILDREN', // label: 'Insert item children', label: 'Thêm cấp con', icon: /*#__PURE__*/React.createElement(Plus, { fontSize: 14 }) // children: [ // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_1', // label: 'Thêm 1 dòng', // row: 1 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_10', // label: 'Thêm 10 dòng', // row: 10 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_50', // label: 'Thêm 50 dòng', // row: 50 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_100', // label: 'Thêm 100 dòng', // row: 100 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_ADV', // label: 'Tùy chỉnh' // } // ] }, { key: 'DELETE_CONTENT', label: 'Xóa nội dung', icon: /*#__PURE__*/React.createElement(Trash2, { fontSize: 14 }) }, { key: 'DELETE_ROWS', label: 'Xóa dòng', icon: /*#__PURE__*/React.createElement(Trash2, { fontSize: 14 }) }]; const validateData = async (data, formSchema) => { if (!formSchema) { return []; } else { const errors = []; for (let i = 0; i < data.length; i++) { try { await formSchema.validate(data[i], { abortEarly: false }); } catch (error) { errors.push(error.inner.reduce((acc, err) => { acc[err.path] = { field: err.path, index: i, message: err.message, type: err.type, ref: { name: err.path } }; return { ...acc, index: i }; }, {})); } } return errors; } }; const GridEdit = props => { const { id, tableRef, t, columns, dataSource, components, allowResizing, rowKey = 'id', selectionSettings, height, scrollHeight, format, triggerChangeData, triggerChangeColumns, onCellPaste, onCellChange, triggerPaste, style, getRowKey, className, toolbarItems, defaultValue, expandable, onCellClick, rowEditable, contextMenuItems: propsContext, showDefaultContext, validate, setTooltipContent, onBlur, ...rest } = props; const ref = useRef(null); const isSelecting = useRef(false); const startCell = useRef(null); const childrenColumnName = 'children'; const isSelectingRow = useRef(false); const rowStart = useRef(null); const rowsSelected = useRef(new Set()); const selectedCells = useRef(new Set()); const pasteCells = useRef(new Set()); const startSelectedCells = useRef(null); // quét vùng chọn const isDragMouse = useRef(false); const isMouseDown = useRef(false); const editingKey = useRef(''); const cellEditing = useRef({}); // quét vùng được paste - tiếp tục hiển thị con trỏ crosshair // const isPasteDragging = useRef(false); const visibleCols = React.useMemo(() => { return flatColumns2(columns.filter(it => it.visible !== false)); }, [columns]); // const id = React.useMemo(() => { // // return tableId ?? faker.string.alpha(20) // // return tableId ?? newGuid() // // }, [tableId]) const itemsAdd = React.useMemo(() => { return [{ key: '10', label: `10 ${t ? t('rows') : 'rows'}` }, { key: '50', label: `50 ${t ? t('rows') : 'rows'}` }, { key: '100', label: `100 ${t ? t('rows') : 'rows'}` }]; }, [t]); // const defaultContext = React.useMemo(() => { // return [ // { // key: 'INSERT_BEFORE', // label: 'Thêm dòng bên trên', // icon: <Plus fontSize={14} />, // children: [ // { // parentKey: 'INSERT_BEFORE', // key: 'INSERT_BEFORE_1', // label: 'Thêm 1 dòng', // row: 1 // }, // { // parentKey: 'INSERT_BEFORE', // key: 'INSERT_BEFORE_10', // label: 'Thêm 10 dòng', // row: 10 // }, // { // parentKey: 'INSERT_BEFORE', // key: 'INSERT_BEFORE_50', // label: 'Thêm 50 dòng', // row: 50 // }, // { // parentKey: 'INSERT_BEFORE', // key: 'INSERT_BEFORE_100', // label: 'Thêm 100 dòng', // row: 100 // }, // { // parentKey: 'INSERT_BEFORE', // key: 'INSERT_BEFORE_ADV', // label: 'Tùy chỉnh' // } // ] // }, // { // key: 'INSERT_AFTER', // label: 'Thêm dòng bên dưới', // icon: <Plus fontSize={14} />, // children: [ // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_1', // label: 'Thêm 1 dòng', // row: 1 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_10', // label: 'Thêm 10 dòng', // row: 10 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_50', // label: 'Thêm 50 dòng', // row: 50 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_100', // label: 'Thêm 100 dòng', // row: 100 // }, // { // parentKey: 'INSERT_AFTER', // key: 'INSERT_AFTER_ADV', // label: 'Tùy chỉnh' // } // ] // }, // { // key: 'INSERT_CHILDREN', // // label: 'Insert item children', // label: 'Thêm cấp con', // icon: <Plus fontSize={14} /> // // children: [ // // { // // parentKey: 'INSERT_AFTER', // // key: 'INSERT_AFTER_1', // // label: 'Thêm 1 dòng', // // row: 1 // // }, // // { // // parentKey: 'INSERT_AFTER', // // key: 'INSERT_AFTER_10', // // label: 'Thêm 10 dòng', // // row: 10 // // }, // // { // // parentKey: 'INSERT_AFTER', // // key: 'INSERT_AFTER_50', // // label: 'Thêm 50 dòng', // // row: 50 // // }, // // { // // parentKey: 'INSERT_AFTER', // // key: 'INSERT_AFTER_100', // // label: 'Thêm 100 dòng', // // row: 100 // // }, // // { // // parentKey: 'INSERT_AFTER', // // key: 'INSERT_AFTER_ADV', // // label: 'Tùy chỉnh' // // } // // ] // }, // { key: 'DELETE_CONTENT', label: 'Xóa nội dung', icon: <Trash2 fontSize={14} /> }, // { key: 'DELETE_ROWS', label: 'Xóa dòng', icon: <Trash2 fontSize={14} /> } // ] // }, []) const [form] = Form.useForm(); // const [editingKey, setEditingKey] = useState<string | number>('') const [dataErrors, setDataErrors] = useState([]); const [isFilter, setIsFilter] = React.useState(false); const [rangeCells, setRangeCells] = useState(new Set()); const [openModalAddRow, setOpenModalAddRow] = useState({ open: false, type: '' }); const [rowsAdd, setRowsAdd] = useState(1); // const [cellEditing, setCellEditing] = useState<any>(null) const [isPasteDragging, setIsPasteDragging] = useState(false); // isPasteDragging == tiếp tục hiển thị con trỏ crosshair const [innerExpandedKeys, setInnerExpandedKeys] = React.useState(() => { // if (defaultExpandedRowKeys) { // return defaultExpandedRowKeys; // } // if (defaultExpandAllRows) { return findAllChildrenKeys(dataSource, getRowKey, childrenColumnName) ?? []; // } // return []; }); const mergedExpandedKeys = React.useMemo(() => new Set(innerExpandedKeys || []), [innerExpandedKeys]); React.useEffect(() => { if (validate && dataSource && dataSource.length) { validateData(dataSource, validate).then(error => { setDataErrors([...error]); // if (getErrors) { // getErrors([...error]) // } }); } }, [validate, dataSource]); const rowsFocus = React.useMemo(() => { return [...new Set(Array.from(rangeCells).map(item => parseInt(item.split('-')[0])))] ?? []; }, [rangeCells]); const contextMenuItems = React.useMemo(() => { const a = showDefaultContext !== false ? [...defaultContext] : []; const b = propsContext ? [...propsContext, { type: 'divider' }] : []; return [...b, ...a]; }, [propsContext, showDefaultContext]); const onTriggerExpand = React.useCallback(record => { const key = getRowKey(record, dataSource.indexOf(record)); let newExpandedKeys; const hasKey = mergedExpandedKeys.has(key); if (hasKey) { mergedExpandedKeys.delete(key); newExpandedKeys = [...mergedExpandedKeys]; } else { newExpandedKeys = [...mergedExpandedKeys, key]; } setInnerExpandedKeys(newExpandedKeys); // // onAddBgSelectedCell(rangeCells, id) // onAddBorderSelectedCell(rangeCells, id) }, [getRowKey, dataSource, mergedExpandedKeys]); const handleAddSingle = React.useCallback(item => { const defaultRowValue = getDefaultValue(defaultValue); const rowId = defaultRowValue && defaultRowValue.id ? defaultRowValue.id : newGuid(); if (item && item.onClick) { item.onClick({ toolbar: item }); } else { const newData = [...dataSource, defaultValue ? { ...defaultRowValue, id: undefined, rowId } : { id: undefined, rowId }]; triggerChangeData?.(newData, 'Add'); } }, [dataSource, defaultValue, triggerChangeData]); const handleAddMulti = React.useCallback((item, e) => { if (item.onClick) { item.onClick({ toolbar: item }); } else { const defaultRowValue = getDefaultValue(defaultValue); const newRows = Array.from({ length: Number(e.key) }).map(() => defaultRowValue ? { ...defaultRowValue, id: undefined, rowId: newGuid() } : { id: undefined, rowId: newGuid() }); const newData = dataSource.concat(newRows); triggerChangeData?.(newData, 'Add'); } }, [dataSource, defaultValue, triggerChangeData]); const handleDuplicate = React.useCallback(() => { // không tính tree // Cập nhật data mới const newData = [...dataSource]; const duplicatedItems = rowsFocus.map(index => ({ ...newData[index], rowId: newGuid(), id: undefined, isDuplicate: true })); // Vị trí chèn là ngay sau phần tử lớn nhất trong rowsFocus const insertAfter = Math.max(...rowsFocus); const rs = [...newData.slice(0, insertAfter + 1), ...duplicatedItems, ...newData.slice(insertAfter + 1)]; triggerChangeData?.(rs, 'DUPLICATE'); }, [dataSource, rowsFocus, triggerChangeData]); // thêm n dòng bên trên const handleInsertBefore = React.useCallback((item, n) => { // // onRemoveBgSelectedCell(selectedCells.current, id) // onRemoveBorderSelectedCell(selectedCells.current, id) // // setTimeout(() => { // onAddBgSelectedCell(selectedCells.current, id) // onAddBorderSelectedCell(selectedCells.current, id) // }, 10) const defaultRowValue = getDefaultValue(defaultValue); // const rowId = defaultRowValue && defaultRowValue.id ? defaultRowValue.id : newGuid() const record = flattenData(childrenColumnName, dataSource)[rowsFocus[rowsFocus.length - 1]]; if (item.onClick) { item.onClick({ toolbar: item }); } else { if (!record?.parentId) { // Cập nhật data mới const newData = [...dataSource]; const newRows = Array.from({ length: n }).map(() => defaultRowValue ? isFilter ? { ...defaultRowValue, id: undefined, rowId: newGuid(), isFilterState: true } : { ...defaultRowValue, id: undefined, rowId: newGuid() } : isFilter ? { id: undefined, rowId: newGuid(), isFilterState: true } : { id: undefined, rowId: newGuid() }); const index = newData.findIndex(obj => obj[rowKey] === record[rowKey]); newData.splice(index, 0, ...newRows); triggerChangeData?.(newData, 'INSERT_BEFORE'); } else { const newData = [...dataSource]; const newRows = Array.from({ length: n }).map(() => defaultRowValue ? isFilter ? { ...defaultRowValue, id: undefined, rowId: newGuid(), isFilterState: true } : { ...defaultRowValue, id: undefined, rowId: newGuid() } : isFilter ? { id: undefined, rowId: newGuid(), isFilterState: true } : { id: undefined, rowId: newGuid() }); const parent = findItemByKey(newData, rowKey, record.parentId); // Cập nhật childData mới const childData = parent?.children ? [...parent.children] : []; const index = childData.findIndex(obj => obj[rowKey] === record[rowKey]); childData.splice(index, 0, ...newRows); const newRowData = { ...parent, children: childData }; const newDataSource = updateArrayByKey(newData, newRowData, rowKey); triggerChangeData?.(newDataSource, 'INSERT_BEFORE'); } } }, [dataSource, defaultValue, isFilter, rowKey, rowsFocus, triggerChangeData]); //thêm 1 dòng bên dưới const handleInsertAfter = React.useCallback((item, n) => { const defaultRowValue = getDefaultValue(defaultValue); const record = flattenData(childrenColumnName, dataSource)[rowsFocus[rowsFocus.length - 1]]; // const record = getRecordByKey() if (item.onClick) { item.onClick({ toolbar: item }); } else { if (!record?.parentId) { // Cập nhật data mới const newData = [...dataSource]; const newRows = Array.from({ length: n }).map(() => defaultRowValue ? isFilter ? { ...defaultRowValue, id: undefined, rowId: newGuid(), isFilterState: true } : { ...defaultRowValue, id: undefined, rowId: newGuid() } : isFilter ? { id: undefined, rowId: newGuid(), isFilterState: true } : { id: undefined, rowId: newGuid() }); const index = newData.findIndex(obj => obj[rowKey] === record[rowKey]); newData.splice(index + 1, 0, ...newRows); triggerChangeData?.(newData, 'INSERT_AFTER'); } else { const newData = [...dataSource]; const newRows = Array.from({ length: n }).map(() => defaultRowValue ? isFilter ? { ...defaultRowValue, id: undefined, rowId: newGuid(), isFilterState: true } : { ...defaultRowValue, id: undefined, rowId: newGuid() } : isFilter ? { id: undefined, rowId: newGuid(), isFilterState: true } : { id: undefined, rowId: newGuid() }); const parent = findItemByKey(newData, rowKey, record.parentId); // Cập nhật childData mới const childData = parent?.children ? [...parent.children] : []; const index = childData.findIndex(obj => obj[rowKey] === record[rowKey]); childData.splice(index + 1, 0, ...newRows); const newRowData = { ...parent, children: childData }; const newDataSource = updateArrayByKey(newData, newRowData, rowKey); triggerChangeData?.(newDataSource, 'INSERT_BEFORE'); } } }, [dataSource, defaultValue, isFilter, rowKey, rowsFocus, triggerChangeData]); const handleInsertChild = React.useCallback(item => { const defaultRowValue = getDefaultValue(defaultValue); const rowId = defaultRowValue && defaultRowValue.id ? defaultRowValue.id : newGuid(); const record = flattenData(childrenColumnName, dataSource)[rowsFocus[rowsFocus.length - 1]]; if (item.onClick) { item.onClick({ toolbar: item }); } else { const newData = [...dataSource]; let newElement; if (!record.children || record.children.length === 0) { newElement = { ...record, children: [{ ...defaultRowValue, parentId: record.rowId, rowId }] }; } else { newElement = { ...record, children: [...record.children, { ...defaultRowValue, parentId: record.rowId, rowId }] }; } const newDataSource = updateArrayByKey(newData, newElement, rowKey); triggerChangeData?.(newDataSource, 'INSERT_CHILDREN'); } const key = getRowKey(record, dataSource.indexOf(record)); // let newExpandedKeys: Key[]; const hasKey = mergedExpandedKeys.has(key); if (!hasKey) { const newExpandedKeys = [...mergedExpandedKeys, key]; setInnerExpandedKeys(newExpandedKeys); } }, [dataSource, defaultValue, getRowKey, mergedExpandedKeys, rowKey, rowsFocus, triggerChangeData]); const handleDeleteRows = React.useCallback(item => { // setTimeout(() => { // onAddBgSelectedCell(selectedCells.current, id) // onAddBorderSelectedCell(selectedCells.current, id) // }) if (item.onClick) { item.onClick({ toolbar: item }); } else { const newData = [...dataSource]; const indexesToDelete = [...rowsFocus]; // Sắp xếp giảm dần để xóa từ cuối lên đầu indexesToDelete.sort((a, b) => b - a).forEach(index => { newData.splice(index, 1); }); triggerChangeData?.([...newData], 'DELETE_ROWS'); } }, [dataSource, rowsFocus, triggerChangeData]); const handleDeleteAll = React.useCallback(() => { triggerChangeData?.([], 'INSERT_BEFORE'); }, [triggerChangeData]); const handleDeleteContent = React.useCallback(() => { if (selectedCells.current.size > 0) { const newData = [...dataSource]; // colIndex => field const colIndexToField = flatColumns2(visibleCols).map(col => col.field); // Duyệt qua mỗi ô cần xóa for (const cell of selectedCells.current) { const [rowIndexStr, colIndexStr] = cell.split("-"); const rowIndex = Number(rowIndexStr); const colIndex = Number(colIndexStr); const field = colIndexToField[colIndex]; const column = flatColumns2(visibleCols)[colIndex]; const rowData = flattenData(childrenColumnName, dataSource)[rowIndex]; if (newData[rowIndex] && field && field in newData[rowIndex] && isEditable(column, rowData)) { // @ts-ignore newData[rowIndex][field] = ''; } } triggerChangeData?.([...newData], 'DELETE_CONTENT'); } }, [dataSource, triggerChangeData, visibleCols]); const toolbarItemsBottom = useMemo(() => { if (!rowsFocus || rowsFocus.length === 0) { return toolbarItems?.filter(it => it.position === 'Bottom' && it.visible !== false && it.key !== 'DUPLICATE' && it.key !== 'INSERT_BEFORE' && it.key !== 'INSERT_AFTER' && it.key !== 'DELETE_ROWS' && it.key !== 'INSERT_CHILDREN').map(item => { if (item.key === 'ADD') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, item.key === 'ADD' && /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Dropdown.Button, { overlayClassName: 'be-popup-container', trigger: ['click'], style: { color: '#28c76f', borderColor: '#28c76f' }, className: 'toolbar-button toolbar-dropdown-button', menu: { items: itemsAdd, onClick: e => handleAddMulti(item, e) } }, /*#__PURE__*/React.createElement("span", { style: { color: '#28c76f' }, onClick: () => handleAddSingle(item) }, item.label ? t ? t(item.label) : item.label : t ? t('Add item') : 'Add item')))); } }; } if (item.key === 'DELETE') { return { ...item, template: () => { return /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Button, { style: { color: '#eb4619', borderColor: '#eb4619' }, variant: 'outlined', onClick: handleDeleteAll, className: "d-flex toolbar-button" }, item.label ? t ? t(item.label) : item.label : t ? t('Delete all item') : 'Delete all item')); } }; } return { ...item }; }); } return toolbarItems?.filter(it => it.position === 'Bottom' && it.visible !== false).map(item => { if (item.key === 'ADD') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, item.key === 'ADD' && /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Dropdown.Button, { overlayClassName: 'be-popup-container', style: { color: '#28c76f', borderColor: '#28c76f' }, className: 'toolbar-button toolbar-dropdown-button', menu: { items: itemsAdd, onClick: e => handleAddMulti(item, e) } }, /*#__PURE__*/React.createElement("span", { style: { color: '#28c76f' }, onClick: () => handleAddSingle(item) }, item.label ? t ? t(item.label) : item.label : t ? t('Add item') : 'Add item')))); } }; } if (item.key === 'DUPLICATE') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, item.key === 'DUPLICATE' && item.visible !== false && rowsFocus.length > 0 && /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Button, { style: { color: '#28c76f', borderColor: '#28c76f' }, variant: 'outlined', onClick: handleDuplicate, className: "d-flex toolbar-button" }, item.label ? t ? t(item.label) : item.label : t ? t('Duplicate') : 'Duplicate'))); } }; } if (item.key === 'INSERT_BEFORE') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Dropdown.Button, { overlayClassName: 'be-popup-container', style: { color: '#28c76f', borderColor: '#28c76f' }, className: 'toolbar-button toolbar-dropdown-button', menu: { items: itemsAdd, onClick: e => handleInsertBefore(item, Number(e.key)) } }, /*#__PURE__*/React.createElement("span", { style: { color: '#28c76f' }, onClick: () => handleInsertBefore(item, 1) }, item.label ? t ? t(item.label) : item.label : t ? t('Insert item before') : 'Insert item before')))); } }; } if (item.key === 'INSERT_AFTER') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Dropdown.Button, { overlayClassName: 'be-popup-container', style: { color: '#28c76f', borderColor: '#28c76f' }, className: 'toolbar-button toolbar-dropdown-button', menu: { items: itemsAdd, onClick: e => handleInsertAfter(item, Number(e.key)) } }, /*#__PURE__*/React.createElement("span", { style: { color: '#28c76f' }, onClick: () => handleInsertAfter(item, 1) }, item.label ? t ? t(item.label) : item.label : t ? t('Insert item after') : 'Insert item after')))); } }; } if (item.key === 'INSERT_CHILDREN') { return { ...item, template: () => { return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Button, { style: { color: '#28c76f', borderColor: '#28c76f' }, variant: 'outlined', onClick: () => handleInsertChild(item), className: "d-flex toolbar-button" }, item.label ? t ? t(item.label) : item.label : t ? t('Insert item children') : 'Insert item children'))); } }; } if (item.key === 'DELETE') { return { ...item, template: () => { return /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Button, { style: { color: '#eb4619', borderColor: '#eb4619' }, variant: 'outlined', onClick: handleDeleteAll, className: "d-flex toolbar-button" }, item.label ? t ? t(item.label) : item.label : t ? t('Delete all item') : 'Delete all item')); } }; } if (item.key === 'DELETE_ROWS') { return { ...item, template: () => { return /*#__PURE__*/React.createElement("div", { className: classnames(`be-toolbar-item`, item?.className) }, /*#__PURE__*/React.createElement(Button, { style: { color: '#eb4619', borderColor: '#eb4619' }, variant: 'outlined', onClick: () => handleDeleteRows(item), className: "d-flex toolbar-button" }, t ? `${t('Delete')} ${rowsFocus.length} ${t('row')}` : `Delete ${rowsFocus.length} item`)); } }; } return { ...item }; }); }, [handleAddMulti, handleAddSingle, handleDeleteAll, handleDeleteRows, handleDuplicate, handleInsertAfter, handleInsertBefore, handleInsertChild, itemsAdd, rowsFocus, t, toolbarItems]); const { control, handleSubmit, setValue, trigger, getValues, reset, formState: { errors } } = useForm({ mode: 'onChange', resolver: validate ? yupResolver(validate) : undefined }); // const isEditing = React.useCallback((record: RecordType) => { // return record[rowKey as any] === editingKey.current // }, [editingKey, rowKey]) // console.log('isEditing', isEditing) React.useEffect(() => { const handleClickOutside = event => { const element = event.target; // const tableId = id ? document.getElementById(id) : undefined // const tableBodies = document.getElementsByClassName("ui-rc-table-tbody"); const container = document.querySelector(".be-popup-container"); const containerContextMenu = document.querySelector(".popup-context-menu"); const tableBody = document.querySelector(`#${id} .ui-rc-table-tbody`); // const containerHidden = document.querySelector(".be-popup-container.ant-picker-dropdown-hidden") // const toolbarContainer = document.getElementsByClassName("ui-rc-toolbar"); const itemContainer = document.querySelector(`#${id} .ui-rc-toolbar-selection-overflow-item`); const itemHeader = document.querySelector(`#${id} .ui-rc-table-thead`); const isInsideContainer = element.closest(".be-popup-container") && container; const isInsideContainerContext = containerContextMenu && element.closest(".popup-context-menu"); const isInsideToolbar = element.closest(`.ui-rc-toolbar-selection-overflow-item`) && itemContainer; const isInsideHeader = itemHeader && itemHeader.contains(event.target); if (isInsideContainer || isInsideToolbar || isInsideHeader || isInsideContainerContext) { return; } // if (ref.current && !ref.current.contains(event.target)) { // if (ref.current && tableId && !tableId.contains(event.target as Node)) { if (ref.current && tableBody && !tableBody.contains(event.target)) { if (editingKey.current) { onBlur?.(dataSource); } setTimeout(() => { // setEditingKey('') editingKey.current = ''; }); isSelecting.current = false; startCell.current = null; // setSelectedCells(new Set()) // setRowsSelected(new Set()) onRemoveBgSelectedCell(selectedCells.current, id); onRemoveBorderSelectedCell(selectedCells.current, id); hideDraggingPoint(selectedCells.current, id); rowsSelected.current = new Set(); selectedCells.current = new Set(); setRangeCells(new Set()); } }; // document.addEventListener('click', handleClickOutside) document.addEventListener('mousedown', handleClickOutside); // document.addEventListener('touchstart', handleClickOutside) return () => { // document.removeEventListener('click', handleClickOutside) document.removeEventListener('mousedown', handleClickOutside); // document.removeEventListener('touchstart', handleClickOutside) }; }, [dataSource, id, onBlur]); const triggerDragPaste = pastesArray => { const mergedSet = new Set([...selectedCells.current, ...pastesArray]); const tmpCols = { ...visibleCols }; const rowSelectedFirst = getFirstSelectCell(selectedCells.current).row; const rowPasteLast = getLastSelectCell(pasteCells.current).row; const selectedArray = Array.from(selectedCells.current).map(key => { const [row, col] = key.split("-").map(Number); const columnKey = tmpCols[col].field; // @ts-ignore return { row, col, value: flattenData(childrenColumnName, dataSource)[row][columnKey] }; // return { row, col, value: '' }; }); // Xác định min/max row và col để sắp xếp dữ liệu const minRow = Math.min(...selectedArray.map(cell => cell.row)); const maxRow = Math.max(...selectedArray.map(cell => cell.row)); const minCol = Math.min(...selectedArray.map(cell => cell.col)); const maxCol = Math.max(...selectedArray.map(cell => cell.col)); // Tạo dữ liệu dạng bảng (mảng 2D) const table = Array.from({ length: maxRow - minRow + 1 }, () => Array(maxCol - minCol + 1).fill("")); // Gán giá trị vào bảng selectedArray.forEach(({ row, col, value }) => { table[row - minRow][col - minCol] = value; }); let newRange; if (rowPasteLast > rowSelectedFirst) { // kéo xuóng newRange = addRows8(table, getRowsPasteIndex(pastesArray).length); } else { // kéo lên newRange = addRowsUp(table, getRowsPasteIndex(pastesArray).length); } const record = flattenData(childrenColumnName, dataSource)[getFirstSelectCell(pastesArray).row]; if (!record?.parentId) { // Cập nhật data mới const newData = [...dataSource]; // Lấy vị trí bắt đầu // const { row: startRow, col: startCol } = selectedCell; const startRow = getFirstSelectCell(pastesArray).row; const startCol = getFirstSelectCell(selectedCells.current).col; const pastedRows = []; const pastedColumns = new Set(); newRange.addedRows.forEach((rowValues, rowIndex) => { const targetRow = startRow + rowIndex; // Nếu vượt quá số dòng hiện có, thêm dòng mới if (targetRow >= newData.length) { // @ts-ignore newData.push({ id: undefined, rowId: newGuid() }); } rowValues.forEach((cellValue, colIndex) => { const targetCol = startCol + colIndex; if (targetCol >= tmpCols.length) { // Không vượt quá số cột return; } // @ts-ignore const columnKey = tmpCols[targetCol].field; // @ts-ignore newData[targetRow] = { ...newData[targetRow], [columnKey]: typeof cellValue === 'string' ? cellValue.trim() : cellValue }; pastedColumns.add(columnKey); }); // Lưu dòng được paste pastedRows.push(newData[targetRow]); }); const pastedColumnsArray = Array.from(pastedColumns) ?? []; triggerPaste?.(pastedRows, pastedColumnsArray, newData); } /// cập nhật cell class if (selectedCells.current && selectedCells.current.size > 0) { // onRemoveBgSelectedCell(rangeCells, id) // onRemoveBorderSelectedCell(rangeCells, id) } // selectedCells.current = sortedSetASC(mergedSet) hideDraggingPoint(selectedCells.current, id); const newCells = sortedSetASC(mergedSet); selectedCells.current = newCells; onAddBgCellIndex(newCells, id); setRangeCells(newCells); onAddBgSelectedCell(newCells, id); onAddBorderSelectedCell(newCells, id); setTimeout(() => { showDraggingPoint(newCells, id); }, 50); // setPasteCells(new Set()) if (pasteCells.current && pasteCells.current.size > 0) { removeClassBorderPasteCell(pasteCells.current, 'up', id); } pasteCells.current = new Set(); }; const handlePasted = (record, indexCol, rowNumber, pasteData) => { const rows = pasteData.slice(0, onCellPaste?.maxRowsPaste ?? 200); if (!record?.parentId) { // Cập nhật data mới const newData = [...dataSource]; // const indexRows = newData.findIndex((it) => it[rowKey as any] === record[rowKey]) // Lấy vị trí bắt đầu // const { row: startRow, col: startCol } = selectedCell; const startRow = newData.findIndex(it => it[rowKey] === record[rowKey]); const startCol = indexCol; // const flattData = flattenArray(newData); const pastedRows = []; const pastedColumns = new Set(); rows.forEach((rowValues, rowIndex) => { const targetRow = startRow + rowIndex; // Nếu vượt quá số dòng hiện có, thêm dòng mới if (targetRow >= newData.length) { // @ts-ignore // newData.push({ id: newGuid()}); newData.push({ id: undefined, rowId: newGuid() }); } rowValues.forEach((cellValue, colIndex) => { const targetCol = startCol + colIndex; if (targetCol >= visibleCols.length) { // Không vượt quá số cột return; } if (visibleCols[targetCol].editEnable) { // @ts-ignore const columnKey = visibleCols[targetCol].field; // @ts-ignore newData[targetRow] = { ...newData[targetRow], [columnKey]: cellValue.trim() }; pastedColumns.add(columnKey); } }); // Lưu dòng được paste pastedRows.push(newData[targetRow]); }); const pastedColumnsArray = Array.from(pastedColumns) ?? []; triggerPaste?.(pastedRows, pastedColumnsArray, newData); } else { // Cập nhật data mới const newData = [...dataSource]; const parent = findItemByKey(newData, rowKey, record.parentId); // Cập nhật childData mới const childData = parent?.children ? [...parent.children] : []; // Lấy vị trí bắt đầu // const { row: startRow, col: startCol } = selectedCell; const startRow = childData.findIndex(it => it[rowKey] === record[rowKey]); const startCol = indexCol; const pastedRows = []; const pastedColumns = new Set(); rows.forEach((rowValues, rowIndex) => { const targetRow = startRow + rowIndex; // Nếu vượt quá số dòng hiện có, thêm dòng mới if (targetRow >= childData.length) { childData.push({ id: undefined, rowId: newGuid(), parentId: parent[rowKey ?? 'id'] }); } rowValues.forEach((cellValue, colIndex) => { const targetCol = startCol + colIndex; if (targetCol >= visibleCols.length) { // Không vượt quá số cột return; } if (visibleCols[targetCol].editEnable) { // @ts-ignore const columnKey = visibleCols[targetCol].field; // @ts-ignore childData[targetRow] = { ...childData[targetRow], [columnKey]: cellValue.trim() }; pastedColumns.add(columnKey); } }); // Lưu dòng được paste pastedRows.push(childData[targetRow]); }); const pastedColumnsArray = Array.from(pastedColumns) ?? []; const newRowData = { ...parent, children: childData }; const newDataSource = updateArrayByKey(newData, newRowData, rowKey); triggerPaste?.(pastedRows, pastedColumnsArray, newDataSource); } }; const handlePaste = (record, indexCol, rowNumber, e) => { // const clipboard: any = (e.clipboardData || (window && window?.Clipboard)).getData("text") const pasteData = e.clipboardData.getData("text/plain"); // Chuyển đổi dữ liệu từ clipboard thành mảng const rowsPasted = pasteData.split("\n").map(row => // const rows = pasteData.split("\n").map((row: any) => row.replace(/\r/g, "").split("\t")); if (rowsPasted.length > (onCellPaste?.maxRowsPaste ?? 200)) { // bật popup thông báo Modal.confirm({ content: /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Paragraph, { style: { marginBottom: '.25rem', fontSize: 14 } }, "D\u1EEF li\u1EC7u sao ch\xE9p v\u01B0\u1EE3t qu\xE1 s\u1ED1 d\xF2ng cho ph\xE9p (500 d\xF2ng).Ph\u1EA7n m\u1EC1n s\u1EBD ch\u1EC9 l\u1EA5y 500 d\xF2ng \u0111\u1EA7u ti\xEAn."), /*#__PURE__*/React.createElement(Title, { level: 5, style: { marginTop: '.75rem' } }, "B\u1EA1n c\xF3 mu\u1ED1n ti\u1EBFp t\u1EE5c sao ch\xE9p kh\xF4ng?")), centered: true, className: 'be-popup-container', onOk: () => { handlePasted(record, indexCol, rowNumber, rowsPasted); } // footer: (_, { OkBtn, CancelBtn }) => ( // <> // <OkBtn /> // <CancelBtn /> // </> // ), }); } else { handlePasted(record, indexCol, rowNumber, rowsPasted); } // const rows = rowsPasted.slice(0, (onCellPaste?.maxRowsPaste ?? 200)); // // // if (!record?.parentId ) { // // // Cập nhật data mới // const newData = [...dataSource]; // // // @ts-ignore // const indexRows = newData.findIndex((it) => it[rowKey] === record[rowKey]) // // // Lấy vị trí bắt đầu // // const { row: startRow, col: startCol } = selectedCell; // const startRow = indexRows // const startCol = indexCol // // // // // const flattData = flattenArray(newData); // // const pastedRows: RecordType[] = []; // const pastedColumns = new Set() // // // rows.forEach((rowValues: any, rowIndex: any) => { // const targetRow = startRow + rowIndex; // // // Nếu vượt quá số dòng hiện có, thêm dòng mới // if (targetRow >= newData.length) { // // @ts-ignore // // newData.push({ id: newGuid()}); // newData.push({ id: undefined, rowId: newGuid()}); // } // // rowValues.forEach((cellValue: any, colIndex: any) => { // const targetCol = startCol + colIndex; // if (targetCol >= columns.length) { // Không vượt quá số cột // return // } // // if (columns[targetCol].editEnable) { // // @ts-ignore // const columnKey = columns[targetCol].field; // // // @ts-ignore // newData[targetRow] = { ...newData[targetRow], [columnKey]: cellValue.trim() }; // pastedColumns.add(columnKey); // } // // }); // // // Lưu dòng được paste // pastedRows.push(newData[targetRow]); // // }); // // const pastedColumnsArray = Array.from(pastedColumns) ?? []; // // triggerPaste?.(pastedRows, pastedColumnsArray as string[], newData) // // // } else { // // // Cập nhật data mới // const newData = [...dataSource]; // // const parent = findItemByKey(newData, rowKey as any, record.parentId) // // // Cập nhật childData mới // const childData: any[] = parent?.children ? [...parent.children] : [] // // // // Lấy vị trí bắt đầu // // const { row: startRow, col: startCol } = selectedCell; // const startRow = childData.findIndex((it) => it[rowKey] === record[rowKey]) // const startCol = indexCol // // const pastedRows: RecordType[] = [] // const pastedColumns = new Set() // // // rows.forEach((rowValues: any, rowIndex: any) => { // const targetRow = startRow + rowIndex // // // Nếu vượt quá số dòng hiện có, thêm dòng mới // if (targetRow >= childData.length) { // // childData.push({ id: undefined, rowId: newGuid(), parentId: parent[rowKey ?? 'id']}) // } // // rowValues.forEach((cellValue: any, colIndex: any) => { // const targetCol = startCol + colIndex // if (targetCol >= columns.length) { // Không vượt quá số cột // return // } // // if (columns[targetCol].editEnable) { // // // @ts-ignore // const columnKey = columns[targetCol].field // // // @ts-ignore // childData[targetRow] = { ...childData[targetRow], [columnKey]: cellValue.trim() } // pastedColumns.add(columnKey) // } // // }) // // // Lưu dòng được paste // pastedRows.push(childData[targetRow]) // // }) // // const pastedColumnsArray = Array.from(pastedColumns) ?? [] // // const newRowData = {...parent, children: childData} // // const newDataSource = updateArrayByKey(newData, newRowData, rowKey as string) // // triggerPaste?.(pastedRows, pastedColumnsArray as string[], newDataSource ) // } }; const handlePointDoubleClick = e => { // e.preventDefault() e.stopPropagation(); const colStart = getFirstSelectCell(selectedCells.current).col; const colEnd = getLastSelectCell(selectedCells.current).col; const startPasteRow = getLastSelectCell(selectedCells.current).row; const newPasteCells = new Set(); for (let r = Math.min(startPasteRow, dataSource.length - 1) + 1; r <= Math.max(startPasteRow, dataSource.length - 1); r++) { for (let c = Math.min(colStart, colStart); c <= Math.max(colStart, colEnd); c++) { newPasteCells.add(`${r}-${c}`); } } hideDraggingPoint(selectedCells.current, id); triggerDragPaste(newPasteCells); }; const handleMouseDown = (record, row, col, e) => { if (e.button === 2) { e.stopPropagation(); return; } if (editingKey && editingKey.current === record[rowKey]) { return; } if (record[rowKey] !== editingKey.current && editingKey.current !== '') { setTimeout(() => { // setEditingKey('') editingKey.current = ''; onBlur?.(dataSource); }); } // isDragMouse.current = true isMouseDown.current = true; if (e.ctrlKey) { isSelecting.current = true; startCell.current = { row, col }; // const cell: any = new Set([`${row}-${col}`]) // setCurrentCtrlCells(cell) } else { isSelecting.current = true; startCell.current = { row, col }; const target = e.target; if (target.closest('.dragging-point')) { e.stopPropagation(); e.preventDefault(); // Không xử lý gì cả } else { // setStartSelectedCell({row, col}) startSelectedCells.current = { row, col }; // setSelectedCells(new Set([`${row}-${col}`])); const cells = new Set([`${row}-${col}`]); if (selectedCells.current && selectedCells.current.size > 0) { if (!isEqualSet(cells, rangeCells)) { // onRemoveBgSelectedCell(rangeCells, id) // onRemoveBorderSelectedCell(rangeCells, id) hideDraggingPoint(rangeCells, id); onRemoveBgCellIndex(selectedCells.current, id); } } if (rowsSelected.current && rowsSelected.current.size > 0) { removeClassCellIndexSelected(rowsSelected.current, id); } if (!isEqualSet(cells, selectedCells.current)) { onRemoveBgSelectedCell(selectedCells.current, id