UNPKG

es-grid-template

Version:

es-grid-template

1,545 lines (1,506 loc) 74.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.addRowIdArray = exports.addClassCellIndexSelected = exports.addClassBorderPasteCell = exports.addBorderPasteClass = exports.addBorderClass = void 0; exports.addRows8 = addRows8; exports.addRowsUp = addRowsUp; exports.checkThousandSeparator = exports.checkFieldKey = exports.checkDecimalSeparator = exports.checkChild = exports.buildConnectedRegions = void 0; exports.compareDate = compareDate; exports.compareDates = compareDates; exports.convertFlatColumn = exports.convertDayjsToDate = exports.convertDateToDayjs = exports.convertColumns = exports.convertArrayWithIndent = void 0; exports.convertFormat = convertFormat; exports.filterDataByColumns3 = exports.filterDataByColumns2 = exports.filterDataByColumns = exports.editAbleColumns = exports.customWeekStartEndFormat = exports.countItemsBeforeIndex = exports.convertLabelToTitle = void 0; exports.filterDataByColumns4 = filterDataByColumns4; exports.findAllChildrenKeys = findAllChildrenKeys; exports.getAllVisibleKeys = exports.genPresets = exports.flattenData = exports.flattenArray = exports.findItemPath = exports.findItemByKey = void 0; exports.getBottomRowCells = getBottomRowCells; exports.getCellsByPosition = getCellsByPosition; exports.getCellsByPosition2 = getCellsByPosition2; exports.getFormat = exports.getFirstSelectCell = exports.getEditType = exports.getDefaultValue = exports.getDatepickerFormat = exports.getDateString = exports.getDateRangeFormat = exports.getColumnsVisible = void 0; exports.getHiddenParentKeys = getHiddenParentKeys; exports.getVisibleColumnKeys = exports.getTypeFilter = exports.getTemplate = exports.getRowsPasteIndex = exports.getRowNumber = exports.getLastSelectCell = void 0; exports.groupArrayByColumns = groupArrayByColumns; exports.hideDraggingPoint = void 0; exports.invalidDate = invalidDate; exports.isArraysEqual = void 0; exports.isBottomMostInRanges = isBottomMostInRanges; exports.isContinuous = exports.isColor = exports.isBottomMostInRegion = void 0; exports.isDateString = isDateString; exports.isEmpty = exports.isEditable = exports.isDisable = void 0; exports.isEqualSet = isEqualSet; exports.isRangeCell = exports.isObjEmpty = exports.isNullOrUndefined = exports.isNameColor = void 0; exports.isRightMostInRegion = isRightMostInRegion; exports.updateColumns = exports.updateArrayByKey = exports.transformColumns1 = exports.transformColumns = exports.totalFixedWidth = exports.sumDataByField = exports.sortedSetDSC = exports.sortedSetASC = exports.showDraggingPoint = exports.shouldInclude = exports.removeFieldRecursive = exports.removeColumns = exports.removeClassCellIndexSelected = exports.removeClassBorderPasteCell = exports.removeBorderPasteClass = exports.removeBorderClass2 = exports.removeBorderClass = exports.parseCells = exports.parseBooleanToValue = exports.onRemoveBorderSelectedCell = exports.onRemoveBgSelectedCell = exports.onRemoveBgCellIndex = exports.onAddBorderSelectedCell = exports.onAddBgSelectedCell = exports.onAddBgCellIndex = exports.newGuid = exports.mergedSets = exports.isTopMostInRegion = exports.isSelectedCell = void 0; exports.updateData = exports.updateColumnsByGroup = void 0; var _dayjs = _interopRequireDefault(require("dayjs")); var _moment = _interopRequireDefault(require("moment/moment")); var _uuid = require("uuid"); var _colors = require("@ant-design/colors"); var _rcMasterUi = require("rc-master-ui"); var _columns = require("./columns"); var _useColumns = require("./useColumns"); const newGuid = () => { for (let i = 0; i < 20; i++) { // @ts-ignore // const id = crypto.randomUUID() return (0, _uuid.v4)(); } }; exports.newGuid = newGuid; const sumDataByField = (data, field) => { if (data && data.length > 0) { return data.reduce((accumulator, currentValue) => { const val = typeof currentValue[field] === 'number' || !isNaN(currentValue[field]) ? Number(currentValue[field]) : 0; return accumulator + val; }, 0); } else { return 0; } }; exports.sumDataByField = sumDataByField; const checkThousandSeparator = (thousandSeparator, decimalSeparator) => { if (thousandSeparator) { if (decimalSeparator) { if (thousandSeparator === decimalSeparator) { return ','; } else { return thousandSeparator; } } else { return thousandSeparator; } } else { return undefined; } }; exports.checkThousandSeparator = checkThousandSeparator; const checkDecimalSeparator = (thousandSeparator, decimalSeparator) => { if (decimalSeparator) { if (thousandSeparator) { if (thousandSeparator === decimalSeparator) { return '.'; } else { return decimalSeparator; } } else { return decimalSeparator; } } else { if (thousandSeparator && thousandSeparator === '.') { return ','; } return '.'; } }; exports.checkDecimalSeparator = checkDecimalSeparator; const isEmpty = d => { return d === null || d === undefined || d === ''; }; exports.isEmpty = isEmpty; const isNullOrUndefined = d => { return d === null || d === undefined; }; exports.isNullOrUndefined = isNullOrUndefined; const convertDayjsToDate = (dateString, format) => { const dayjsDate = (0, _dayjs.default)(dateString, format); // Parse using the provided format if (!dayjsDate.isValid()) { throw new Error('Invalid date or format'); } // return moment(dayjsDate.toDate()).format() // Convert to JavaScript Date return dayjsDate.toDate(); // Convert to JavaScript Date }; exports.convertDayjsToDate = convertDayjsToDate; const convertDateToDayjs = (date, format) => { const dateValue = date ? (0, _dayjs.default)(date).format(format) : null; return dateValue ? (0, _dayjs.default)(dateValue, format) : null; }; exports.convertDateToDayjs = convertDateToDayjs; const isNameColor = strColor => { const s = new Option().style; s.color = strColor; return s.color === strColor; }; exports.isNameColor = isNameColor; const isColor = value => { const hexRegex = /^#([0-9A-F]{3}){1,2}$/i; const rgbRegex = /^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/; const rgbaRegex = /^rgba\((\d{1,3}), (\d{1,3}), (\d{1,3}), (0|1|0?\.\d+)\)$/; const hslRegex = /^hsl\(\d{1,3}, \d{1,3}%, \d{1,3}%\)$/; const hslaRegex = /^hsla\(\d{1,3}, \d{1,3}%, \d{1,3}%, (0|1|0?\.\d+)\)$/; const namedColors = /^(?:aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)$/i; return hexRegex.test(value) || rgbRegex.test(value) || rgbaRegex.test(value) || hslRegex.test(value) || hslaRegex.test(value) || namedColors.test(value) || isNameColor(value); }; exports.isColor = isColor; const getAllVisibleKeys = columns => { const keys = []; const traverse = (cols, parentHidden = false) => { for (const col of cols) { if (col.hidden || parentHidden) { continue; } if (col.key) { keys.push(col.key); } if (col.children) { traverse(col.children, col.hidden); } } }; traverse(columns); return keys; }; exports.getAllVisibleKeys = getAllVisibleKeys; const getVisibleColumnKeys = columns => { const allKeys = getAllVisibleKeys(columns); const allParentKeys = getHiddenParentKeys(columns); return allKeys.filter(item => !allParentKeys.includes(item)); }; exports.getVisibleColumnKeys = getVisibleColumnKeys; function getHiddenParentKeys(columns, parentKeys = []) { const hiddenParents = new Set(); for (const column of columns) { if (column.children) { const currentPath = column.key ? [...parentKeys, column.key] : [...parentKeys]; const childHiddenParents = getHiddenParentKeys(column.children, currentPath); if (childHiddenParents.length > 0) { childHiddenParents.forEach(key => hiddenParents.add(key)); currentPath.forEach(key => hiddenParents.add(key)); } } else if (column.hidden) { parentKeys.forEach(key => hiddenParents.add(key)); } } return Array.from(hiddenParents); } const updateColumns = (columns, includes) => { return columns.map(column => { const newColumn = { ...column }; let hasVisibleChild = false; if (!column.key && !column.dataIndex) { return column; } if (newColumn.children) { newColumn.children = updateColumns(newColumn.children, includes); hasVisibleChild = newColumn.children.some(child => !child.hidden); } // newColumn.hidden = newColumn.key && !includes.includes(newColumn.key) newColumn.hidden = newColumn.field && !includes.includes(newColumn.field); // newColumn.fixed = newColumn.field && !includes.includes(newColumn.field) ? undefined : newColumn.fixed if (newColumn.children && newColumn.children.length > 0) { newColumn.hidden = !hasVisibleChild; // newColumn.fixed = !hasVisibleChild ? undefined : newColumn.fixed } return newColumn; }); }; exports.updateColumns = updateColumns; const updateColumnsByGroup = (columns, columnsGroup) => { return columns.map(column => { const newColumn = { ...column }; let hasVisibleChild = false; if (!column.key && !column.dataIndex) { return column; } if (newColumn.children) { newColumn.children = updateColumnsByGroup(newColumn.children, columnsGroup); hasVisibleChild = newColumn.children.some(child => !child.hidden); } newColumn.hidden = newColumn.key && columnsGroup.includes(newColumn.key); if (newColumn.children && newColumn.children.length > 0) { newColumn.hidden = !hasVisibleChild; } return newColumn; }); }; exports.updateColumnsByGroup = updateColumnsByGroup; const getFormat = (colFormat, format) => { return { thousandSeparator: colFormat?.thousandSeparator ?? format?.thousandSeparator, decimalSeparator: colFormat?.decimalSeparator ?? format?.decimalSeparator, decimalScale: colFormat?.decimalScale ?? format?.decimalScale, allowNegative: colFormat?.allowNegative ?? format?.allowNegative, // check nhập số âm prefix: colFormat?.prefix ?? format?.prefix, suffix: colFormat?.suffix ?? format?.suffix, fixedDecimalScale: colFormat?.fixedDecimalScale ?? format?.fixedDecimalScale, // mặc định thêm số 0 sau số thập phân dateFormat: colFormat?.dateFormat ?? format?.dateFormat, datetimeFormat: colFormat?.datetimeFormat ?? format?.datetimeFormat, timeFormat: colFormat?.timeFormat ?? format?.timeFormat, weekFormat: colFormat?.weekFormat ?? format?.weekFormat, monthFormat: colFormat?.monthFormat ?? format?.monthFormat, yearFormat: colFormat?.yearFormat ?? format?.yearFormat }; }; exports.getFormat = getFormat; function convertFormat(formatStr) { // return formatStr.split('').map((char, i) => { // if (char === 'D' || char === 'd') { // return 'd'; // ngày: lowercase // } // if (char === 'Y' || char === 'y') { // return 'y'; // năm: lowercase // } // if (char === 'M' || char === 'm') { // return char; // tháng: giữ nguyên // } // return char; // separator // }).join(''); return formatStr.split('').map(char => { if (char === 'D' || char === 'd') return 'd'; if (char === 'Y' || char === 'y') return 'y'; if ('Hhmsa'.includes(char)) return char; // giờ, phút, giây, am/pm if (char === 'M' || char === 'm') return char; // tháng: giữ nguyên return char; // dấu phân cách }).join(''); } const getDatepickerFormat = (type, format) => { const typeFormat = type ? type.toLowerCase() : ''; switch (typeFormat) { case "date": case "daterange": return format?.dateFormat ?? 'dd/MM/YYYY'; case "datetime": return format?.datetimeFormat ?? 'dd/MM/YYYY HH:mm'; case "week": return format?.weekFormat ?? 'dd/MM'; case "month": return format?.monthFormat ?? 'MM/YYYY'; case "quarter": return format?.dateFormat ?? 'dd/MM/YYYY'; case "year": return format?.yearFormat ?? 'YYYY'; case "time": return format?.timeFormat ?? 'HH:mm'; default: return 'dd/MM/YYYY'; } }; exports.getDatepickerFormat = getDatepickerFormat; const getDateRangeFormat = (type, format) => { const typeFormat = type ? type.toLowerCase() : ''; switch (typeFormat) { case "date": case "daterange": return convertFormat(format?.dateFormat ?? 'DD/MM/YYYY'); case "datetime": return format?.datetimeFormat ?? 'DD/MM/YYYY HH:mm'; case "week": return format?.weekFormat ?? 'DD/MM'; case "month": return format?.monthFormat ?? 'MM/YYYY'; case "quarter": return format?.dateFormat ?? 'DD/MM/YYYY'; case "year": return format?.yearFormat ?? 'YYYY'; case "time": return format?.timeFormat ?? 'HH:mm'; default: return 'DD/MM/YYYY'; } }; exports.getDateRangeFormat = getDateRangeFormat; const customWeekStartEndFormat = (value, weekFormat) => { return `${(0, _dayjs.default)(value).startOf('week').format(weekFormat)} ~ ${(0, _dayjs.default)(value).endOf('week').format(weekFormat)}`; }; exports.customWeekStartEndFormat = customWeekStartEndFormat; const getTypeFilter = col => { if (col?.typeFilter) { return col.typeFilter; } const type = col?.type ?? 'Text'; switch (type) { case "number": return 'Number'; case "date": return 'Date'; case "datetime": return 'Datetime'; case "boolean": return 'Checkbox'; case "checkbox": return 'Checkbox'; // case "week": return '' // case "month": return 'Month' // case "quarter": return col.format?.dateFormat ? col.format?.dateFormat : 'DD/MM/YYYY' // case "year": return col.format?.yearFormat ? col.format?.yearFormat : 'YYYY' // case "time": return col.format?.timeFormat ? col.format?.timeFormat : 'HH:mm' case "string": default: return 'Text'; } }; exports.getTypeFilter = getTypeFilter; const updateArrayByKey = (arr, element, key) => { if (arr) { return arr.map(it => { const item = { ...it }; if (item[key] === element[key]) { return { ...item, ...element }; } else if (item.children && item.children.length > 0) { item.children = updateArrayByKey(item.children, element, key); } return item; }); } else { return []; } }; exports.updateArrayByKey = updateArrayByKey; const getDateString = (column, value) => { if (value instanceof Date) { return (0, _moment.default)(value).format(); } return value; }; exports.getDateString = getDateString; const getEditType = (column, rowData) => { if (column && typeof column.editType === 'function') { return column.editType(rowData); } return column.editType ?? 'text'; }; exports.getEditType = getEditType; const isDisable = (column, rowData) => { if (column && typeof column?.disable === 'function') { return column.disable(rowData); } return !!column?.disable; }; exports.isDisable = isDisable; const checkFieldKey = key => { if (key) { return key; } else { return 'value'; } }; exports.checkFieldKey = checkFieldKey; const convertLabelToTitle = data => { return data.map(item => { const { label, title, value, key, ...rest } = item; const newItem = { ...rest, value, label, key: key ?? value, title: title ?? label }; if (item.children) { newItem.children = convertLabelToTitle(item.children); } return newItem; }); }; exports.convertLabelToTitle = convertLabelToTitle; const convertArrayWithIndent = (inputArray, parentIndent = 0) => { if (inputArray) { return inputArray.map(item => { const indent = parentIndent; if (item.children && item.children.length > 0) { item.children = convertArrayWithIndent(item.children, indent + 1); } return { ...item, indent, rowId: item.rowId ? item.rowId : item.id ? item.id : newGuid() }; }); } else { return []; } }; exports.convertArrayWithIndent = convertArrayWithIndent; const getTemplate = template => { if (template && typeof template === 'function') { return template(); } return template; }; exports.getTemplate = getTemplate; const totalFixedWidth = (columns, type, selectionSettings) => { const totalFixedLeftWidth = columns.filter(column => column.fixed === type) // Lọc các cột có fixed .reduce((sum, column) => { const width = typeof column.width === 'number' ? column.width : parseInt(column.width, 10) || 0; // Chuyển từ chuỗi sang số, nếu không hợp lệ thì lấy 0 return sum + width; }, 0); const selectColumnWidth = !selectionSettings?.mode ? 0 : typeof selectionSettings?.columnWidth === 'number' ? selectionSettings?.columnWidth : parseInt(selectionSettings?.columnWidth, 10) || 50; return totalFixedLeftWidth + selectColumnWidth; }; exports.totalFixedWidth = totalFixedWidth; const isObjEmpty = obj => { if (isNullOrUndefined(obj)) { return true; } else { return Object.keys(obj).length === 0; } }; exports.isObjEmpty = isObjEmpty; const getColumnsVisible = (columns, index) => { const itemsBeforeIndex = columns.slice(0, index); const itemsAfterIndex = columns.slice(index); itemsAfterIndex.map(it => { if (it.hidden !== false) { itemsBeforeIndex.push(it); } }); return itemsBeforeIndex; }; exports.getColumnsVisible = getColumnsVisible; const updateData = (initData, rows, key) => { const updatedData = initData.map(item => { const newData = rows.find(row => row[key] === item[key]); return newData ? { ...item, ...newData } : item; }); // Thêm các phần tử mới chưa có trong initialData const newRows = rows.filter(row => !initData.some(item => item[key] === row[key])); return [...updatedData, ...newRows]; }; exports.updateData = updateData; const parseBooleanToValue = (value, type) => { return type === 'boolean' ? value : Number(value); }; exports.parseBooleanToValue = parseBooleanToValue; const genPresets = (presets = _colors.presetPalettes) => { return Object.entries(presets).map(([label, colors]) => ({ label, colors, key: label })); }; exports.genPresets = genPresets; function findAllChildrenKeys(data, getRowKey, childrenColumnName) { const keys = []; function dig(list) { (list || []).forEach((item, index) => { keys.push(getRowKey(item, index)); dig(item[childrenColumnName]); }); } dig(data); return keys; } const flattenArray = arr => { if (!arr) { return []; } return arr.reduce((r, { children, ...rest }) => { r.push(rest); if (children) { r.push(...flattenArray(children)); } return r; }, []); }; exports.flattenArray = flattenArray; const flattenData = (childrenColumnName, data) => { let list = []; (data || []).forEach(record => { list.push(record); if (record && typeof record === 'object' && childrenColumnName in record) { list = [...list, ...flattenData(childrenColumnName, record[childrenColumnName])]; } }); return list; }; exports.flattenData = flattenData; const countItemsBeforeIndex = (array, index) => { let count = 0; for (let i = 0; i < index; i++) { if (array[i].children && array[i].children.length > 0) { const rs = flattenData('children', [array[i]]); count += rs.length; } else { count++; } } return count; }; exports.countItemsBeforeIndex = countItemsBeforeIndex; const getRowNumber = (array, rowKey, key) => { // const flattArray = flattenArray(array) const flattArray = flattenData('children', array); return flattArray.findIndex(it => it[key] === rowKey); }; exports.getRowNumber = getRowNumber; const getDefaultValue = defaultValue => { if (defaultValue && typeof defaultValue === 'function') { return defaultValue(); } return defaultValue; }; exports.getDefaultValue = getDefaultValue; const addRowIdArray = inputArray => { if (inputArray) { return inputArray.map(item => { if (typeof item.children !== "string" && item.children && item.children.length > 0) { item.children = addRowIdArray(item.children); } return { ...item, rowId: item.rowId ?? item.id ?? newGuid() }; }); } else { return []; } }; exports.addRowIdArray = addRowIdArray; const findItemByKey = (array, key, value) => { for (let i = 0; i < array.length; i++) { const item = array[i]; if (item[key] === value) { return item; } if (item.children && item.children.length > 0) { const foundInChildren = findItemByKey(item.children, key, value); if (foundInChildren) { return foundInChildren; } } } return null; }; exports.findItemByKey = findItemByKey; const getLastSelectCell = selectCells => { if (selectCells.size === 0) { return { row: 0, col: 0 }; } const lastValue = [...selectCells].at(-1); const [row, col] = lastValue.split("-").map(Number); return { row, col }; }; exports.getLastSelectCell = getLastSelectCell; const getFirstSelectCell = selectCells => { if (selectCells.size === 0) { return { row: 0, col: 0 }; } const firstValue = selectCells.values().next().value; const [row, col] = firstValue.split("-").map(Number); return { row, col }; }; exports.getFirstSelectCell = getFirstSelectCell; const getRowsPasteIndex = pasteRows => { if (!pasteRows) { return []; } const result = Array.from(pasteRows).map(item => parseInt(item.split("-")[0])); return [...new Set(result)]; }; exports.getRowsPasteIndex = getRowsPasteIndex; function addRows8(arr, n) { if (!Array.isArray(arr) || arr.length === 0) { return { combined: arr, addedRows: [] }; } const m = arr.length; const numCols = arr[0].length; const addedRows = []; // Hàm kiểm tra kiểu date hợp lệ const isValidDate = item => { // console.log('!isNaN(Date.parse(d))', !isNaN(Date.parse(d))) // return !isNaN(Date.parse(d)) if (typeof item === 'number') { // return 'number' return false; } if (typeof item === 'string') { // Kiểm tra nếu là chuỗi ISO date hợp lệ const date = new Date(item); if (!isNaN(date.getTime()) && item.includes('T')) { // return 'date' return true; } // return 'string' return false; } return !isNaN(Date.parse(item)); }; // Lấy giá trị mẫu của cột j từ hàng đầu tiên const getSample = j => arr[0][j]; // Xác định chế độ xử lý cho mỗi cột: // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle' const modes = []; const steps = []; // bước tăng, nếu có (cho number hoặc date) for (let j = 0; j < numCols; j++) { const sample = getSample(j); if (m === 1) { // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên. if (typeof sample === "number") { modes[j] = "number-constant"; } else if (isValidDate(sample)) { modes[j] = "date-stepping"; steps[j] = 24 * 3600 * 1000; // 1 ngày = 86400000 ms } else { modes[j] = "cycle"; } } else if (m === 2) { // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date const first = arr[0][j], second = arr[1][j]; if (typeof first === "number" && typeof second === "number") { modes[j] = "number-stepping"; steps[j] = second - first; } else if (isValidDate(first) && isValidDate(second)) { modes[j] = "date-stepping"; steps[j] = Date.parse(second) - Date.parse(first); } else { modes[j] = "cycle"; } } else { // Nếu mảng có >2 hàng const first = arr[0][j], second = arr[1][j], third = arr[2][j]; if (typeof first === "number" && typeof second === "number" && typeof third === "number") { const step1 = second - first; const step2 = third - second; if (step1 === step2) { modes[j] = "number-stepping"; steps[j] = step1; } else { modes[j] = "cycle"; } } else if (isValidDate(first) && isValidDate(second) && isValidDate(third)) { const step1 = Date.parse(second) - Date.parse(first); const step2 = Date.parse(third) - Date.parse(second); if (step1 === step2) { modes[j] = "date-stepping"; steps[j] = step1; } else { modes[j] = "cycle"; } } else { modes[j] = "cycle"; } } } // Tạo các dòng mới (thêm n dòng) // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step // Nếu chế độ là cycle thì dùng arr[i mod m][j] for (let i = 0; i < n; i++) { const newRow = []; for (let j = 0; j < numCols; j++) { let newValue; switch (modes[j]) { case "number-constant": // Mảng có 1 hàng, số giữ nguyên newValue = arr[0][j]; break; case "number-stepping": { // Lấy giá trị cuối của cột j trong mảng ban đầu const lastValue = arr[m - 1][j]; newValue = lastValue + (i + 1) * steps[j]; } break; case "date-stepping": { // Lấy giá trị cuối, chuyển về date, cộng thêm (i+1)*step, chuyển lại về định dạng ISO const lastDate = new Date(arr[m - 1][j]); const newTime = lastDate.getTime() + (i + 1) * steps[j]; newValue = (0, _moment.default)(new Date(newTime)).format(); } break; case "cycle": default: // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m) newValue = arr[i % m][j]; break; } newRow.push(newValue); } addedRows.push(newRow); } const combined = arr.concat(addedRows); return { combined, addedRows }; } function addRowsUp(array, n) { const arr = array.reverse(); if (!Array.isArray(arr) || arr.length === 0) { return { combined: arr, addedRows: [] }; } const m = arr.length; const numCols = arr[0].length; const addedRows = []; // Hàm kiểm tra kiểu date hợp lệ const isValidDate = item => { // console.log('!isNaN(Date.parse(d))', !isNaN(Date.parse(d))) // return !isNaN(Date.parse(d)) if (typeof item === 'number') { // return 'number' return false; } if (typeof item === 'string') { // Kiểm tra nếu là chuỗi ISO date hợp lệ const date = new Date(item); if (!isNaN(date.getTime()) && item.includes('T')) { // return 'date' return true; } // return 'string' return false; } return !isNaN(Date.parse(item)); }; // Lấy giá trị mẫu của cột j từ hàng đầu tiên const getSample = j => arr[0][j]; // Xác định chế độ xử lý cho mỗi cột: // mode = 'number-stepping' | 'date-stepping' | 'number-constant' | 'cycle' const modes = []; const steps = []; // bước tăng, nếu có (cho number hoặc date) for (let j = 0; j < numCols; j++) { const sample = getSample(j); if (m === 1) { // Nếu mảng chỉ có 1 hàng: nếu là số thì giữ nguyên; nếu là date thì tăng 1 ngày; còn lại giữ nguyên. if (typeof sample === "number") { modes[j] = "number-constant"; } else if (isValidDate(sample)) { modes[j] = "date-stepping"; steps[j] = 24 * 3600 * 1000; // 1 ngày = 86400000 ms } else { modes[j] = "cycle"; } } else if (m === 2) { // Nếu mảng có 2 hàng: nếu là số thì tính bước = row2 - row1, tương tự với date const first = arr[0][j], second = arr[1][j]; if (typeof first === "number" && typeof second === "number") { modes[j] = "number-stepping"; steps[j] = second - first; } else if (isValidDate(first) && isValidDate(second)) { modes[j] = "date-stepping"; steps[j] = Date.parse(second) - Date.parse(first); } else { modes[j] = "cycle"; } } else { // Nếu mảng có >2 hàng const first = arr[0][j], second = arr[1][j], third = arr[2][j]; if (typeof first === "number" && typeof second === "number" && typeof third === "number") { const step1 = second - first; const step2 = third - second; if (step1 === step2) { modes[j] = "number-stepping"; steps[j] = step1; } else { modes[j] = "cycle"; } } else if (isValidDate(first) && isValidDate(second) && isValidDate(third)) { const step1 = Date.parse(second) - Date.parse(first); const step2 = Date.parse(third) - Date.parse(second); if (step1 === step2) { modes[j] = "date-stepping"; steps[j] = step1; } else { modes[j] = "cycle"; } } else { modes[j] = "cycle"; } } } // Tạo các dòng mới (thêm n dòng) // Với mỗi cột, nếu chế độ là stepping thì lấy giá trị cuối của mảng ban đầu và cộng thêm (i+1)*step // Nếu chế độ là cycle thì dùng arr[i mod m][j] for (let i = n - 1; i >= 0; i--) { const newRow = []; for (let j = 0; j < numCols; j++) { let newValue; switch (modes[j]) { case "number-constant": // Mảng có 1 hàng, số giữ nguyên newValue = arr[0][j]; break; case "number-stepping": { // Lấy giá trị cuối của cột j trong mảng ban đầu const lastValue = arr[m - 1][j]; newValue = lastValue - (i + 1) * steps[j] * -1; } break; case "date-stepping": { // Lấy giá trị cuối, chuyển về date, cộng thêm (i+1)*step, chuyển lại về định dạng ISO const lastDate = new Date(arr[m - 1][j]); const newTime = m === 1 ? lastDate.getTime() - (i + 1) * steps[j] : lastDate.getTime() - (i + 1) * steps[j] * -1; newValue = (0, _moment.default)(new Date(newTime)).format(); } break; case "cycle": default: // Lặp lại nội dung theo vòng tròn: dùng hàng thứ (i mod m) newValue = arr[i % m][j]; break; } newRow.push(newValue); } addedRows.push(newRow); } const combined = arr.concat(addedRows); return { combined, addedRows }; } const transformColumns = (cols, convertColumns, t) => { // @ts-ignore return cols.map(column => { const find = convertColumns.find(it => it.key === column.field); if (!column?.field && !column?.key) { return _rcMasterUi.Table.SELECTION_COLUMN; } if (find) { return { ...find }; } // Xử lý đệ quy cho children if (column.children?.length) { return { ...column, key: column.field ?? column.dataIndex ?? column.key, title: t ? t(column.headerText) : column.headerText, ellipsis: column.ellipsis !== false, align: column.textAlign ?? column.align, children: transformColumns(column.children, convertColumns) }; } }); }; exports.transformColumns = transformColumns; const transformColumns1 = (cols, sortMultiple) => { const convertColumns = (0, _columns.flatColumns2)(cols).map((column, colIndex) => { if (!column?.field && !column?.key) { return _rcMasterUi.Table.SELECTION_COLUMN; } if (column.dataIndex === '#' || column.dataIndex === '#') { return { ...column }; } if ((column.key || column.field) === 'command') { return { ...column }; } return { ...column, key: column.field ?? column.dataIndex ?? column.key, sorter: column.sorter === false ? undefined : { compare: a => a, multiple: sortMultiple ? colIndex : undefined } }; }); // @ts-ignore return cols.map(column => { const find = convertColumns.find(it => it.key === column.field); if (!column?.field && !column?.key) { return _rcMasterUi.Table.SELECTION_COLUMN; } if (find) { return { ...find }; } // Xử lý đệ quy cho children if (column.children?.length) { return { ...column, key: column.field ?? column.dataIndex ?? column.key, ellipsis: column.ellipsis !== false, align: column.textAlign ?? column.align, children: transformColumns(column.children, convertColumns) }; } }); }; exports.transformColumns1 = transformColumns1; const removeColumns = (columns, groupColumns) => { const ttt = [...columns]; return ttt.filter(column => !groupColumns.includes(column.field)).map(column => { const newCol = { ...column }; if (newCol?.children && newCol?.children.length > 0) { newCol.children = removeColumns(newCol.children, groupColumns); } return newCol; }); }; exports.removeColumns = removeColumns; const convertFlatColumn = array => { const tmp = [...array]; let result = []; tmp.forEach(item => { if (item.children) { result = result.concat(convertFlatColumn(item.children)); } else { result.push(item); } }); return result; }; exports.convertFlatColumn = convertFlatColumn; const convertColumns = cols => { return cols.map(col => { if (col === _useColumns.SELECTION_COLUMN) { return _useColumns.SELECTION_COLUMN; } const transformedColumn = { ...col, dataIndex: col.field ?? col.dataIndex, key: col.field ?? col.dataIndex ?? col.key, // title: t ? t(col.columnGroupText ?? col.headerText ?? col.title) : col.columnGroupText ?? col.headerText ?? col.title, // title: () => (<span>aaa</span>), // title: () => (<HeaderContent column={{...col} as any} t={t}/>), // title: () => (<span>{t ? t(col.columnGroupText ?? col.headerText ?? col.title) : col.columnGroupText ?? col.headerText ?? col.title}</span>), ellipsis: col.ellipsis !== false, align: col.textAlign ?? col.align, fixed: col.fixedType ?? col.fixed }; if (transformedColumn.children && transformedColumn.children?.length) { return { ...transformedColumn, children: convertColumns(transformedColumn.children) }; } if (["index", "#"].includes(col.field)) { return { ...transformedColumn, onCell: () => ({ className: 'cell-number' }), render: (_, __, rowIndex) => rowIndex + 1 }; } if (col.key === 'command') { return { ...transformedColumn, onCell: () => ({ className: 'cell-number', style: { padding: '2px 8px' } }) }; } return { ...transformedColumn }; }); }; exports.convertColumns = convertColumns; const checkChild = inputArray => { return inputArray.some(item => item.children && item.children.length > 0); }; exports.checkChild = checkChild; const isEditable = (column, rowData) => { if (column && typeof column.editEnable === 'function') { return column.editEnable(rowData); } return column?.editEnable; }; exports.isEditable = isEditable; const isArraysEqual = (arr1, arr2) => { if (arr1.length !== arr2.length) { return false; } return arr1.every((element, index) => element === arr2[index]); }; exports.isArraysEqual = isArraysEqual; const editAbleColumns = columns => { return columns.filter(col => col.field !== '#' && col.field !== 'index' && col.field !== 'command' && col.visible !== false); }; exports.editAbleColumns = editAbleColumns; const findItemPath = (tree, targetItem, rowKey, currentPage, pageSize) => { let result = null; function dfs(nodes, path = []) { for (let i = 0; i < nodes.length; i++) { const currentPath = currentPage && pageSize ? [...path, i + 1 + (currentPage - 1) * pageSize] : [...path, i + 1]; const node = nodes[i]; if (node?.[rowKey] === targetItem?.[rowKey]) { result = currentPath.join('.'); return true; } if (node?.children) { if (dfs(node.children, currentPath)) { return true; } } } return false; } dfs(tree); return result; }; exports.findItemPath = findItemPath; const filterDataByColumns = (data, queries) => { if (!queries || queries.length === 0) { return data; } return data.filter(item => { let result = null; for (const query of queries) { const { field, value, operator, predicate } = query; const itemValue = item[field]; let condition = false; // Normalize string values for comparison const itemStr = itemValue?.toString().toLowerCase?.(); const queryStr = value?.toString().toLowerCase?.(); switch (operator.toLowerCase()) { case "equal": if (isDateString(value)) { condition = compareDate(itemValue, value); } else { condition = itemValue == value; } break; case "notequal": if (isDateString(value)) { condition = !compareDate(itemValue, value); } else { condition = itemValue != value; } break; case "greaterthan": condition = itemValue > value; break; case "greaterthanorequal": condition = itemValue >= value; break; case "lessthan": condition = itemValue < value; break; case "lessthanorequal": condition = itemValue <= value; break; case "contains": condition = itemStr?.includes(queryStr); break; case "startswith": condition = itemStr?.startsWith(queryStr); break; case "endswith": condition = itemStr?.endsWith(queryStr); break; default: console.warn(`Unknown operator: ${operator}`); break; } if (predicate === "and") { result = result === null ? condition : result && condition; } else if (predicate === "or") { result = result === null ? condition : result || condition; } } return result; }); }; exports.filterDataByColumns = filterDataByColumns; const filterDataByColumns2 = (data, queries) => { if (!queries || queries.length === 0) { return data; } return data.filter(item => { // Nếu isFilterState = true thì giữ lại dòng này, không cần kiểm tra filter if (item.isFilterState) { return true; } let result = null; for (const query of queries) { const { field, value, operator, predicate } = query; const itemValue = item[field]; let condition = false; // Normalize string values for comparison const itemStr = itemValue?.toString().toLowerCase?.(); const queryStr = value?.toString().toLowerCase?.(); switch (operator.toLowerCase()) { case "equal": condition = isDateString(value) ? compareDate(itemValue, value) : itemValue == value; break; case "notequal": condition = isDateString(value) ? !compareDate(itemValue, value) : itemValue != value; break; case "greaterthan": condition = itemValue > value; break; case "greaterthanorequal": condition = itemValue >= value; break; case "lessthan": condition = itemValue < value; break; case "lessthanorequal": condition = itemValue <= value; break; case "contains": condition = itemStr?.includes(queryStr); break; case "startswith": condition = itemStr?.startsWith(queryStr); break; case "endswith": condition = itemStr?.endsWith(queryStr); break; default: console.warn(`Unknown operator: ${operator}`); break; } // Áp dụng toán tử logic (and/or) if (predicate === "and") { result = result === null ? condition : result && condition; } else if (predicate === "or") { result = result === null ? condition : result || condition; } } return result; }); }; exports.filterDataByColumns2 = filterDataByColumns2; const removeFieldRecursive = (data, field) => { return data.map(item => { const { [field]: _, ...rest } = item; if (rest.children && Array.isArray(rest.children)) { rest.children = removeFieldRecursive(rest.children, field); } return rest; }); }; exports.removeFieldRecursive = removeFieldRecursive; const filterDataByColumns3 = (data, queries) => { if (!queries || queries.length === 0) { return data; } return data.filter(item => { if (item.isFilterState === true) { return true; } let result = null; for (const query of queries) { const { field, value, operator, predicate } = query; const itemValue = item[field]; let condition = false; const isDateComparison = isDate(itemValue) || isDateString(value); const itemDate = isDateComparison ? new Date(itemValue) : null; const queryDate = isDateComparison ? parseToDate(value) : null; const itemStr = itemValue?.toString().toLowerCase?.(); const queryStr = value?.toString().toLowerCase?.(); switch (operator.toLowerCase()) { case "equal": condition = isDateComparison ? compareDates(itemDate, queryDate) : itemValue === value; break; case "notequal": condition = isDateComparison ? !compareDates(itemDate, queryDate) : itemValue !== value; break; case "greaterthan": // @ts-ignore condition = isDateComparison ? itemDate > queryDate : itemValue > value; // condition = isDateComparison ? invalidDate(itemDate) && invalidDate(queryDate) && itemDate > queryDate : itemValue > value; break; case "greaterthanorequal": // @ts-ignore condition = isDateComparison ? itemDate >= queryDate : itemValue >= value; break; case "lessthan": // @ts-ignore condition = isDateComparison ? itemDate < queryDate : itemValue < value; break; case "lessthanorequal": // @ts-ignore condition = isDateComparison ? itemDate <= queryDate : itemValue <= value; break; case "contains": condition = itemStr?.includes(queryStr); break; case "startswith": condition = itemStr?.startsWith(queryStr); break; case "endswith": condition = itemStr?.endsWith(queryStr); break; default: console.warn(`Unknown operator: ${operator}`); break; } if (predicate === "and") { result = result === null ? condition : result && condition; } else if (predicate === "or") { result = result === null ? condition : result || condition; } } return result; }); }; exports.filterDataByColumns3 = filterDataByColumns3; const shouldInclude = (item, queries) => { if (item.isFilterState === true) { return true; } let result = null; for (const query of queries) { const { field, value, operator, predicate } = query; const itemValue = item[field]; let condition = false; const isDateComparison = isDate(itemValue) || isDateString(value); const itemDate = isDateComparison ? new Date(itemValue) : null; const queryDate = isDateComparison ? parseToDate(value) : null; const itemStr = itemValue?.toString().toLowerCase?.(); const queryStr = value?.toString().toLowerCase?.(); switch (operator.toLowerCase()) { case "equal": condition = isDateComparison ? compareDates(itemDate, queryDate) : itemValue === value; break; case "notequal": condition = isDateComparison ? !compareDates(itemDate, queryDate) : itemValue !== value; break; case "greaterthan": // @ts-ignore condition = isDateComparison ? itemDate > queryDate : itemValue > value; // condition = isDateComparison ? invalidDate(itemDate) && invalidDate(queryDate) && itemDate > queryDate : itemValue > value; break; case "greaterthanorequal": // @ts-ignore condition = isDateComparison ? itemDate >= queryDate : itemValue >= value; break; case "lessthan": // @ts-ignore condition = isDateComparison ? itemDate < queryDate : itemValue < value; break; case "lessthanorequal": // @ts-ignore condition = isDateComparison ? itemDate <= queryDate : itemValue <= value; break; case "contains": condition = itemStr?.includes(queryStr); break; case "startswith": condition = itemStr?.startsWith(queryStr); break; case "endswith": condition = itemStr?.endsWith(queryStr); break; default: console.warn(`Unknown operator: ${operator}`); break; } if (predicate === "and") { result = result === null ? condition : result && condition; } else if (predicate === "or") { result = result === null ? condition : result || condition; } } return result; }; exports.shouldInclude = shouldInclude; function filterDataByColumns4(data, queries) { if (!queries || queries.length === 0) { return data; } return data.map(item => { const newItem = { ...item }; if (Array.isArray(item.children)) { newItem.children = filterDataByColumns4(item.children, queries); } const isSelfMatched = shouldInclude(item, queries); // Nếu chính item thỏa hoặc có con thỏa → giữ lại if (isSelfMatched || newItem.children && newItem.children.length > 0) { return newItem; } return null; // loại bỏ node không phù hợp }).filter(Boolean); // xóa các null } // ======= Helper functions ======== // Kiểm tra có phải Date object không // function isDate(value: any) { // return value instanceof Date || !isNaN(Date.parse(value)); // } function isDate(value) { if (value instanceof Date) { return !isNaN(value.getTime()); } if (typeof value === "string") { // Chỉ chấp nhận định dạng yyyy-mm-dd hoặc mm/yyyy return /^\d{4}-\d{2}-\d{2}$/.test(value) || /^\d{2}\/\d{4}$/.test(value); } return false; } // Chuỗi MM/YYYY → Date function isDateString(str) { return typeof str === "string" && (/^\d{2}\/\d{4}$/.test(str) || /^\d{4}-\d{2}-\d{2}$/.test(str)); } // // Helper: check if a string is in MM/YYYY format // export function isDateString(str: any) { // return typeof str === "string" && /^\d{2}\/\d{4}$/.test(str); // } function parseToDate(str) { if (/^\d{2}\/\d{4}$/.test(str)) { const [month, year] = str.split('/'); return new Date(parseInt(year), parseInt(month) - 1, 1); } return new Date(str); } // So sánh ngày (cùng ngày/tháng/năm) function compareDates(date1, date2) { return date1.getDate() === date2.getDate() && date1.getMonth() === date2.getMonth() && date1.getFullYear() === date2.getFullYear(); } // Helper: compare MM/YYYY date string with itemValue function compareDate(itemValue, value) { const [month, year] = value.split('/').map(Number); const date = new Date(itemValue); return date.getMonth() + 1 === month && date.getFullYear() === year; } function invalidDate(date) { return date instanceof Date && !isNaN(date.getTime()); } const isContinuous = set => { const rows = []; const cols = []; for (const item of set) { const [row, col] = item.split('-').map(Number); rows.push(row); cols.push(col); } // Lấy danh sách duy nhất và sắp xếp const uniqueSorted = arr => [...new Set(arr)].sort((a, b) => a - b); const isSequential = arr => { for (let i = 1; i < arr.length; i++) { if (arr[i] !== arr[i - 1] + 1) { return false; } } return true; }; const sortedRows = uniqueSorted(rows); const sortedCols = uniqueSorted(cols); return isSequential(sortedRows) && isSequential(sortedCols); }; exports.isContinuous = isContinuous; const parseCells = cellSet => { return Array.from(cellSet).map(cell => { const [row, col] = cell.split('-').map(Number); return { row, col, key: cell }; }); }; exports.parseCells = parseCells; const buildConnectedRegions = cells => { const cellMap = new Map(); const visited = new Set(); for (const cell of cells) { cellMap.set(cell.key, cell); } const directions = [[1, 0], // down [-1, 0], // up [0, 1], // right [0, -1] // left ]; const regions = []; for (const cell of cells) { if (visited.has(cell.key)) { continue; } const stack = [cell]; const region = []; while (stack.length) { const current = stack.pop(); if (visited.has(current.key)) { continue; } visited.add(current.key); region.push(current); for (const [dr, dc] of directions) { const neighborKey = `${current.row + dr}-${current.col + dc}`; if (cellMap.has(neighborKey) && !visited.has(neighborKey)) { stack.push(cellMap.get(neighborKey)); } } } regions.push(region); } return regions; }; exports.buildConnectedRegions = buildConnectedRegions; const isBottomMostInRegion = (rowIndex, colIndex, listSelectCell) => { const cells = parseCells(listSelectCell); const regions = buildConnectedRegions(cells); for (const region of regions) { if (region.some(cell => cell.row === rowIndex && cell.col === colIndex)) { const maxRow = Math.max(...re