es-grid-template
Version:
es-grid-template
1,556 lines (1,485 loc) • 74.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addRowIdArray = void 0;
exports.addRowsDown = addRowsDown;
exports.addRowsDownWithCtrl = addRowsDownWithCtrl;
exports.addRowsUp = addRowsUp;
exports.addRowsUpWithCtrl = addRowsUpWithCtrl;
exports.appendIfNotExists = void 0;
exports.areStringArraysEqual = areStringArraysEqual;
exports.arraysEqualIgnoreOrderFast = arraysEqualIgnoreOrderFast;
exports.checkThousandSeparator = exports.checkFieldKey = exports.checkDecimalSeparator = void 0;
exports.compareDate = compareDate;
exports.compareDates = compareDates;
exports.convertArrayWithIndent = void 0;
exports.convertColumnsToTreeData = convertColumnsToTreeData;
exports.convertFlatColumn1 = exports.convertFilters = exports.convertDayjsToDate = exports.convertDateToDayjs = void 0;
exports.convertFormat = convertFormat;
exports.convertToObjTrue = exports.convertToObj = exports.convertLabelToTitle = void 0;
exports.countUnselectedChildren = countUnselectedChildren;
exports.detectSeparators = exports.customWeekStartEndFormat = void 0;
exports.excludeItems = excludeItems;
exports.extendsObject = void 0;
exports.filterByIds = filterByIds;
exports.filterDataByColumns = filterDataByColumns;
exports.findAllChildrenKeys2 = findAllChildrenKeys2;
exports.findFirst = findFirst;
exports.genPresets = exports.flattenData = exports.flattenArray = exports.flatColumns2 = exports.fixColumnsLeft = exports.findItemByKey = void 0;
exports.getAllChildren = getAllChildren;
exports.getAllVisibleKeys1 = exports.getAllVisibleKeys = exports.getAllRowKey = void 0;
exports.getCellsByPosition = getCellsByPosition;
exports.getColIdsBetween = getColIdsBetween;
exports.getFormat = exports.getFixedFields = exports.getEditType = exports.getDiffent2Array = exports.getDefaultValue = exports.getDefaultOperator = exports.getDatepickerFormat = exports.getDateRangeFormat = exports.getCommonPinningStyles2 = exports.getCommonPinningStyles = void 0;
exports.getHiddenParentKeys = getHiddenParentKeys;
exports.getHiddenParentKeys1 = getHiddenParentKeys1;
exports.getInvisibleColumns = getInvisibleColumns;
exports.getNewItemsOnly = exports.getLastSelectCell = void 0;
exports.getRowIdsBetween = getRowIdsBetween;
exports.getVisibleColumnKeys1 = exports.getVisibleColumnKeys = exports.getTypeFilter = exports.getTableHeight = exports.getSelectedCellMatrix = void 0;
exports.groupArrayByColumns = groupArrayByColumns;
exports.isColor = void 0;
exports.isDateString = isDateString;
exports.isEmpty = exports.isEditable = exports.isDisable = void 0;
exports.isEqualSet = isEqualSet;
exports.isObjEmpty = exports.isNullOrUndefined = exports.isNameColor = exports.isFormattedNumber = void 0;
exports.isObjEqual = isObjEqual;
exports.isTreeArray = isTreeArray;
exports.removeColumns = exports.parseBooleanToValue = exports.onRemoveBgSelectedCell = exports.onAddBgSelectedCell = exports.newGuid = void 0;
exports.removeDuplicatesByKey = removeDuplicatesByKey;
exports.sortByType = exports.shouldInclude = exports.removeVietnameseTones = void 0;
exports.sortColumnsByField = sortColumnsByField;
exports.sortData = sortData;
exports.sumSize = void 0;
exports.toggleRowAndChildren = toggleRowAndChildren;
exports.updateArrayByKey = exports.unFlattenData = void 0;
exports.updateColumnWidthsRecursive = updateColumnWidthsRecursive;
exports.updateColumnsByGroup = exports.updateColumns1 = void 0;
exports.updateOrInsert = updateOrInsert;
exports.updateWidthsByOther = updateWidthsByOther;
var _uuid = require("uuid");
var _colors = require("@ant-design/colors");
var _dayjs = _interopRequireDefault(require("dayjs"));
var _moment = _interopRequireDefault(require("moment"));
const newGuid = () => {
for (let i = 0; i < 20; i++) {
// @ts-ignore
// const id = crypto.randomUUID()
return (0, _uuid.v4)();
}
};
exports.newGuid = newGuid;
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 getCommonPinningStyles = column => {
const isPinned = column.getIsPinned();
return {
left: isPinned === "left" ? `${column.getStart("left")}px` : undefined,
right: isPinned === "right" ? `${column.getAfter("right")}px` : undefined,
opacity: 1,
position: isPinned ? "sticky" : "relative",
width: 'auto',
zIndex: isPinned ? 2 : 0
};
};
exports.getCommonPinningStyles = getCommonPinningStyles;
const getCommonPinningStyles2 = header => {
const isPinned = header.column.getIsPinned();
// const isLastLeftPinnedColumn = isPinned === "left" && column.getIsLastColumn("left");
// const isFirstRightPinnedColumn =isPinned === "right" && column.getIsFirstColumn("right");
return {
// boxShadow: isFirstRightPinnedColumn
// ? "#e0e0e0 2px 0px 1px -1px inset"
// : undefined,
left: isPinned === "left" ? `${header.getStart("left")}px` : undefined,
right: isPinned === "right" ? `${header.getAfter("right")}px` : undefined,
opacity: 1,
position: isPinned ? "sticky" : "relative",
width: 'auto',
zIndex: isPinned ? 2 : 0
};
};
exports.getCommonPinningStyles2 = getCommonPinningStyles2;
const sumSize = items => {
return items.reduce((total, item) => total + item.size, 0);
};
exports.sumSize = sumSize;
const appendIfNotExists = (a, b) => {
const existingKeys = new Set(a.map(item => item.index));
b.forEach(item => {
if (!existingKeys.has(item.index)) {
a.push(item);
}
});
return a;
};
exports.appendIfNotExists = appendIfNotExists;
const getNewItemsOnly = (a, b) => {
const existingKeys = new Set(a.map(item => item.key));
return b.filter(item => !existingKeys.has(item.key));
};
exports.getNewItemsOnly = getNewItemsOnly;
const extendsObject = (...list) => {
const result = {
...list[0]
};
for (let i = 1; i < list.length; i++) {
const obj = list[i];
if (obj) {
Object.keys(obj).forEach(key => {
const val = obj[key];
if (val !== undefined) {
result[key] = val;
}
});
}
}
return result;
};
exports.extendsObject = extendsObject;
const isEmpty = d => {
return d === null || d === undefined || d === '';
};
exports.isEmpty = isEmpty;
const getFormat = (colFormat, format) => {
return {
thousandSeparator: colFormat?.thousandSeparator ?? format?.thousandSeparator,
decimalSeparator: colFormat?.decimalSeparator ?? format?.decimalSeparator,
decimalScale: colFormat?.decimalScale ?? format?.decimalScale ? Number(colFormat?.decimalScale ?? format?.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 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 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() }
return {
...item,
rowId: item.id ?? item.rowId ?? newGuid()
};
});
} else {
return [];
}
};
exports.addRowIdArray = addRowIdArray;
function groupArrayByColumns(arr, columns) {
const result = [];
const checkEmpty = d => {
return d === null || d === undefined || d === '';
};
if (columns) {
arr.forEach(item => {
let currentLevel = result;
columns.forEach((column, index) => {
const value = item[column];
const existingItem = currentLevel.find(i => i[column] === value);
if (existingItem) {
currentLevel = existingItem.children;
} else {
// const newItem = {[column]: value, field: column, rowId: !isEmpty(value) ? (value) : newGuid(), parentId: !isEmpty(item[columns[index - 1]]) ? (item[columns[index - 1]]) : null, indent: index, children: [] }
const newItem = {
[column]: value,
field: column,
rowId: newGuid(),
// rowId: item[column],
parentId: !isEmpty(item[columns[index - 1]]) ? item[columns[index - 1]] : null,
// parentId: item.rowId[index - 1],
// indent: index,
children: []
};
currentLevel.push(newItem);
currentLevel = newItem.children;
}
});
currentLevel.push({
...item,
rowId: item.id ?? item.rowId,
parentId: !checkEmpty(columns[columns.length - 1]) ? item[columns[columns.length - 1]] : null
// parentId: item.rowId[columns.length - 1],
// indent: columns.length
});
});
return result;
} else {
return arr;
}
}
const flatColumns2 = columns => {
return columns.reduce((list, column) => {
const subColumns = column.children;
if (column.field === 'selection_column') {
return [...list, {
...column
}];
}
if (subColumns && subColumns.length > 0) {
return [...list, ...flatColumns2(subColumns).map(subColum => ({
...subColum
}))];
}
return [...list, {
...column
}];
}, []);
};
exports.flatColumns2 = flatColumns2;
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 getFixedFields = (columns, type) => {
const result = [];
function traverse(cols) {
for (const col of cols) {
if ((col.fixed ?? col.fixedType) === type && col.field && (col.visible !== false || col.hidden)) {
result.push(col.field);
}
if (col.children && col.children.length > 0) {
traverse(col.children);
}
}
}
traverse(columns);
return result;
};
exports.getFixedFields = getFixedFields;
function areStringArraysEqual(a, b) {
if (a.length !== b.length) return false;
const sortedA = [...a].sort();
const sortedB = [...b].sort();
return sortedA.every((val, index) => val === sortedB[index]);
}
const getDefaultOperator = col => {
if (col.operator) {
return col.operator;
}
if (col.typeFilter) {
switch (col.typeFilter) {
case 'Number':
case 'Date':
case 'Datetime':
case 'Time':
case 'Month':
case 'Quarter':
case 'Year':
case 'Week':
case 'Dropdown':
case 'Checkbox':
case 'CheckboxDropdown':
case 'CheckboxTree':
case 'DropTree':
return 'equal';
case 'Text':
default:
return 'contains';
}
}
switch (col.type) {
case 'number':
case 'date':
case 'datetime':
case 'week':
case 'year':
case 'quarter':
return 'equal';
case 'string':
default:
return 'contains';
}
};
exports.getDefaultOperator = getDefaultOperator;
function isEqualSet(setA, setB) {
if (setA.size !== setB.size) {
return false;
}
for (const item of setA) {
if (!setB.has(item)) {
return false;
}
}
return true;
}
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;
function getCellsByPosition(cellSet, position = "bottom") {
const cells = Array.from(cellSet).map(key => {
const [row, col] = key.split("-").map(Number);
return {
row,
col,
key
};
});
switch (position) {
case "top":
{
// const minRow = Math.min(...cells.map(c => c.row));
// return cells.filter(c => c.row === minRow).map(c => c.key);
// const rows = cells.map(c => c.row).filter(r => r > 0);
// if (rows.length === 0) return [];
// const minRow = Math.min(...rows);
// return cells.filter(c => c.row === minRow).map(c => c.key);
const minRow = Math.min(...cells.map(c => c.row));
if (minRow === 0) {
return [];
} // Bỏ qua nếu rowIndex = 0
return cells.filter(c => c.row === minRow).map(c => `${c.row}-${c.col}`);
}
case "bottom":
{
const maxRow = Math.max(...cells.map(c => c.row));
return cells.filter(c => c.row === maxRow).map(c => c.key);
}
case "left":
{
// const minCol = Math.min(...cells.map(c => c.col));
// return cells.filter(c => c.col === minCol).map(c => c.key);
// const cols = cells.map(c => c.col).filter(c => c > 0);
// if (cols.length === 0) return [];
// const minCol = Math.min(...cols);
// return cells.filter(c => c.col === minCol).map(c => c.key);
const minCol = Math.min(...cells.map(c => c.col));
if (minCol === 0) {
return [];
} // Bỏ qua nếu colIndex = 0
// Trả về các ô cùng row, nhưng ở cột bên trái
return cells.filter(c => c.col === minCol).map(c => `${c.row}-${c.col}`);
}
case "right":
{
const maxCol = Math.max(...cells.map(c => c.col));
return cells.filter(c => c.col === maxCol).map(c => c.key);
}
default:
return [];
}
}
const onAddBgSelectedCell = (selectedCells, id, isFocusCellIndex) => {
const selectors = Array.from(selectedCells).map(pos => {
const [row1, col1] = pos.split('-');
return `[data-row-index="${row1}"][data-col-index="${col1}"]`;
});
const table = document.querySelector(`#${id}`);
//// xóa class các ô đã chọn trước đó
const cellsSelected = table ? table?.querySelectorAll('.ui-rc-table-cell.selected-bg') : null;
if (cellsSelected) {
cellsSelected.forEach(cell => {
cell.classList.remove('selected-bg');
});
}
/// thêm class
const cells = table && selectors.length > 0 ? table?.querySelectorAll(selectors.join(',')) : null;
if (cells) {
cells.forEach(cell => {
cell.classList.add('selected-bg');
});
}
const rowsArray = [...new Set([...selectedCells].map(item => item.split("-")[0]))];
const rowsSelectors = rowsArray.map(r => `.rc-ui-cell-index[data-row-index="${r}"]`).join(", ");
const cellsIndex = table && rowsSelectors.length > 0 ? table?.querySelectorAll(rowsSelectors) : null;
if (cellsIndex && isFocusCellIndex !== false) {
cellsIndex.forEach(cell => {
cell.classList.add('focus');
});
}
// // tăng z-index để hiển thị round point paste
// const row = getLastSelectCell(selectedCells).row
// const col = getLastSelectCell(selectedCells).col
// const cell: any = table?.querySelector(`.ui-rc-table-cell[data-row-index="${row}"][data-col-index="${col}"]`)
//
// if (cell) {
// cell.style.zIndex = 1
// }
//
// if (cell && cell.classList.contains('ui-rc-table-cell-fix-left')) {
// cell.style.zIndex = 3;
// }
// thêm class border selected
// addBorderClass(selectedCells, 'bottom', 'cell-border-bottom', id)
// addBorderClass(selectedCells, 'right', 'cell-border-right', id)
// addBorderClass(selectedCells, 'top', 'cell-border-top', id)
// addBorderClass(selectedCells, 'left', 'cell-border-left', id)
};
exports.onAddBgSelectedCell = onAddBgSelectedCell;
const onRemoveBgSelectedCell = (selectedCells, id, rowsSelected) => {
const table = document.querySelector(`#${id}`);
const cells = table ? table?.querySelectorAll('.ui-rc-table-cell.selected-bg') : null;
if (cells) {
cells.forEach(cell => {
cell.classList.remove('selected-bg');
});
}
const cellsIndex = table ? table?.querySelectorAll('.ui-rc-table-cell.focus') : null;
if (cellsIndex) {
cellsIndex.forEach(cell => {
cell.classList.remove('focus');
});
}
// xóa class selected ô STT
if (rowsSelected && rowsSelected.size > 0) {
const rowsSelectedArray = [...new Set([...rowsSelected].map(item => item.split("-")[0]))];
const rowsSelectedSelectors = rowsSelectedArray.map(r => `.rc-ui-cell-index[data-row-index="${r}"]`).join(", ");
const cellsSelectedIndex = table && rowsSelectedSelectors.length > 0 ? table?.querySelectorAll(rowsSelectedSelectors) : null;
if (cellsSelectedIndex) {
cellsSelectedIndex.forEach(cell => {
cell.classList.remove('selected');
});
}
}
};
exports.onRemoveBgSelectedCell = onRemoveBgSelectedCell;
function getColIdsBetween(table, a, b) {
const ids = table.getVisibleLeafColumns().map(c => c.id);
const [start, end] = [ids.indexOf(a), ids.indexOf(b)].sort((x, y) => x - y);
return ids.slice(start, end + 1);
}
function getRowIdsBetween(table, a, b) {
// const ids = table.getRowModel().rows.map(r => r.id);
const ids = table.getRowModel().flatRows.map(r => r.id);
const [start, end] = [ids.indexOf(a), ids.indexOf(b)].sort((x, y) => x - y);
return ids.slice(start, end + 1);
}
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 unFlattenData = data => {
const idToNodeMap = {};
const tree = [];
// Bước 1: Tạo map id -> node
data.forEach(item => {
// idToNodeMap[item.rowId] = { ...item, children: [] }
idToNodeMap[item.rowId] = {
...item
};
});
// Bước 2: Gắn vào parent hoặc đẩy lên root nếu không có parent
data.forEach(item => {
const currentNode = idToNodeMap[item.rowId];
if (!item.parentId) {
tree.push(currentNode);
} else {
const parentNode = idToNodeMap[item.parentId];
if (parentNode) {
parentNode.children = parentNode.children ?? [];
parentNode.children.push(currentNode);
} else {
// Nếu parentId không tồn tại thì xem như root
tree.push(currentNode);
}
}
});
return tree;
};
exports.unFlattenData = unFlattenData;
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;
function updateOrInsert(dataArray, dataFilter) {
const updatedArray = [...dataArray];
dataFilter.forEach(filterItem => {
const existingIndex = updatedArray.findIndex(item => item.rowId === filterItem.rowId);
if (existingIndex !== -1) {
// Cập nhật item đã tồn tại
updatedArray[existingIndex] = {
...updatedArray[existingIndex],
...filterItem
};
} else {
// Tìm vị trí cuối cùng của item trước đó trong dataFilter
const prevIndexInFilter = dataFilter.findIndex(f => f.rowId === filterItem.rowId) - 1;
if (prevIndexInFilter >= 0) {
const prevId = dataFilter[prevIndexInFilter].rowId;
const prevIndexInArray = updatedArray.findIndex(item => item.rowId === prevId);
if (prevIndexInArray !== -1) {
// Thêm ngay sau phần tử trước đó
updatedArray.splice(prevIndexInArray + 1, 0, filterItem);
return;
}
}
// Nếu không tìm thấy phần tử trước đó, hoặc là phần đầu tiên, thì push vào cuối
updatedArray.push(filterItem);
}
});
return updatedArray;
}
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 isFormattedNumber = str => {
if (!str) return false;
if (typeof str !== 'string') return false;
const regexUS = /^\d{1,3}(,\d{3})*(\.\d+)?$/; // 100,000.111
const regexEU = /^\d{1,3}(\.\d{3})*(,\d+)?$/; // 100.000,111
// Không có dấu hàng nghìn, chỉ dấu thập phân: 100000.1 hoặc 100000,01
const regexDecimalOnly = /^-?\d+([.,]\d{1,})$/;
return regexUS.test(str) || regexEU.test(str) || regexDecimalOnly.test(str);
};
exports.isFormattedNumber = isFormattedNumber;
const detectSeparators = str => {
if (typeof str !== 'string') return null;
const hasComma = str.includes(',');
const hasDot = str.includes('.');
// Trường hợp có cả dấu , và .
if (hasComma && hasDot) {
const lastComma = str.lastIndexOf(',');
const lastDot = str.lastIndexOf('.');
return lastComma > lastDot ? {
thousandSeparator: '.',
decimalSeparator: ','
} : {
thousandSeparator: ',',
decimalSeparator: '.'
};
}
// Trường hợp chỉ có dấu phẩy
if (hasComma && !hasDot) {
const parts = str.split(',');
if (parts.length === 2) {
return parts[1].length === 3 ? {
thousandSeparator: ',',
decimalSeparator: undefined
} : {
thousandSeparator: undefined,
decimalSeparator: ','
};
}
}
// Trường hợp chỉ có dấu chấm
if (hasDot && !hasComma) {
const parts = str.split('.');
if (parts.length === 2) {
return parts[1].length === 3 ? {
thousandSeparator: '.',
decimalSeparator: undefined
} : {
thousandSeparator: undefined,
decimalSeparator: '.'
};
}
}
// Không có dấu hoặc không hợp lệ
return null;
};
exports.detectSeparators = detectSeparators;
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));
}
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;
}
const removeVietnameseTones = str => {
if (!str) {
return '';
}
return str.normalize('NFD') // Tách các ký tự có dấu thành ký tự cơ bản + dấu
.replace(/[\u0300-\u036f]/g, '') // Xóa dấu
.replace(/đ/g, 'd') // Thay thế đ
.replace(/Đ/g, 'D').replace(/[^a-zA-Z0-9\s]/g, '') // Loại bỏ ký tự đặc biệt
.replace(/\s+/g, ' ') // Thay nhiều khoảng trắng thành 1 khoảng trắng
.trim();
};
exports.removeVietnameseTones = removeVietnameseTones;
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 = removeVietnameseTones(itemValue?.toString().toLowerCase?.() ?? '');
const queryStr = removeVietnameseTones(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;
};
// function compareValues(a: any, b: any, order: "ascend" | "descend") {
// const desc = order === "descend";
// if (a == null && b == null) return 0;
// if (a == null) return desc ? 1 : -1;
// if (b == null) return desc ? -1 : 1;
// // Nếu là số
// if (typeof a === "number" && typeof b === "number") {
// return desc ? b - a : a - b;
// }
// // Nếu là ngày hợp lệ
// const dateA = new Date(a);
// const dateB = new Date(b);
// if (!isNaN(dateA.getTime()) && !isNaN(dateB.getTime())) {
// return desc
// ? dateB.getTime() - dateA.getTime()
// : dateA.getTime() - dateB.getTime();
// }
// // Mặc định coi như string
// return desc
// ? String(b).localeCompare(String(a))
// : String(a).localeCompare(String(b));
// }
// export function sortData(data: any[], sorter: Sorter[]): any[] {
// const sorted = [...data].sort((a, b) => {
// for (const { field, order } of sorter) {
// const result = compareValues(a[field], b[field], order);
// if (result !== 0) return result;
// }
// return 0;
// });
// return sorted.map(item => ({
// ...item,
// children: item.children
// ? sortData(item.children, sorter)
// : undefined
// }));
// }
// export function filterDataByColumns(data: any[], queries: any[], sorter: Sorter[], keysFilter: string[] | undefined) {
// if (!queries || queries.length === 0) {
// return sorter ? sortData(data, sorter) : data;
// }
// let filtered = data.map(item => {
// const newItem = { ...item }
// if (Array.isArray(item.children)) {
// newItem.children = filterDataByColumns(item.children, queries, sorter, keysFilter)
// }
// const isSelfMatched = shouldInclude(item, queries) || keysFilter?.includes(newItem?.rowId)
// // 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
// if (sorter && sorter.length > 0) {
// filtered = sortData(filtered, sorter);
// }
// return filtered;
// }
exports.shouldInclude = shouldInclude;
function getSortValue(item, field) {
if (item[field] !== undefined) return item[field];
if (item.children && item.children.length > 0) {
return getSortValue(item.children[0], field);
}
return undefined;
}
function compareValues(a, b, order) {
const desc = order === "descend";
if (a == null && b == null) return 0;
if (a == null) return desc ? 1 : -1;
if (b == null) return desc ? -1 : 1;
// Nếu là số
if (typeof a === "number" && typeof b === "number") {
return desc ? b - a : a - b;
}
// Nếu là ngày hợp lệ
const dateA = new Date(a);
const dateB = new Date(b);
if (!isNaN(dateA.getTime()) && !isNaN(dateB.getTime())) {
return desc ? dateB.getTime() - dateA.getTime() : dateA.getTime() - dateB.getTime();
}
// Mặc định coi như string
return desc ? String(b).localeCompare(String(a)) : String(a).localeCompare(String(b));
}
function sortData(data, sorter) {
const sorted = [...data].sort((a, b) => {
for (const {
field,
order
} of sorter) {
const result = compareValues(getSortValue(a, field), getSortValue(b, field), order);
if (result !== 0) return result;
}
return 0;
});
return sorted.map(item => ({
...item,
children: item.children ? sortData(item.children, sorter) : undefined
}));
}
function filterDataByColumns(data, queries, sorter, keysFilter) {
if (!queries || queries.length === 0) {
return sorter ? sortData(data, sorter) : data;
}
let filtered = data.map(item => {
const newItem = {
...item
};
if (Array.isArray(item.children)) {
newItem.children = filterDataByColumns(item.children, queries, sorter, keysFilter);
}
const isSelfMatched = shouldInclude(item, queries) || keysFilter?.includes(newItem?.rowId);
// 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
if (sorter && sorter.length > 0) {
filtered = sortData(filtered, sorter);
}
return filtered;
}
const getAllRowKey = data => {
const a = flattenArray(data);
return a.length ? a.map(it => it.rowId) : undefined;
};
exports.getAllRowKey = getAllRowKey;
const isEditable = (column, rowData) => {
if (column && typeof column.editEnable === 'function') {
return column.editEnable(rowData);
}
return column?.editEnable;
};
exports.isEditable = isEditable;
const checkFieldKey = key => {
if (key) {
return key;
} else {
return 'value';
}
};
exports.checkFieldKey = checkFieldKey;
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 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 isNullOrUndefined = d => {
return d === null || d === undefined;
};
exports.isNullOrUndefined = isNullOrUndefined;
const isObjEmpty = obj => {
if (isNullOrUndefined(obj)) {
return true;
} else {
return Object.keys(obj).length === 0;
}
};
exports.isObjEmpty = isObjEmpty;
const isDisable = (column, rowData) => {
if (column && typeof column?.disable === 'function') {
return column.disable(rowData);
}
return !!column?.disable;
};
exports.isDisable = isDisable;
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 parseBooleanToValue = (value, type) => {
return type === 'boolean' ? value : Number(value);
};
exports.parseBooleanToValue = parseBooleanToValue;
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 genPresets = (presets = _colors.presetPalettes) => {
return Object.entries(presets).map(([label, colors]) => ({
label,
colors,
key: label
}));
};
exports.genPresets = genPresets;
const getEditType = (column, rowData) => {
if (column && typeof column.editType === 'function') {
return column.editType(rowData);
}
return column?.editType ?? 'text';
};
exports.getEditType = getEditType;
const getDefaultValue = defaultValue => {
if (defaultValue && typeof defaultValue === 'function') {
return defaultValue();
}
return defaultValue;
};
exports.getDefaultValue = getDefaultValue;
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 getSelectedCellMatrix = (table, startCell, endCell) => {
if (!startCell || !endCell) return {
rowIds: [],
colIds: [],
startRowIndex: undefined,
endRowIndex: undefined,
startColIndex: undefined,
endColIndex: undefined,
colRange: [],
rowRange: []
};
// const rowIds = table.getRowModel().rows.map(r => r.id);
const rowIds = table.getRowModel().flatRows.map(r => r.id);
const colIds = table.getVisibleLeafColumns().map(c => c.id);
// const colIds = table.getAllLeafColumns().map(c => c.id);
const [startRowIndex, endRowIndex] = [rowIds.indexOf(startCell.rowId), rowIds.indexOf(endCell.rowId)].sort((a, b) => a - b);
const [startColIndex, endColIndex] = [colIds.indexOf(startCell.colId), colIds.indexOf(endCell.colId)].sort((a, b) => a - b);
return {
rowRange: rowIds.slice(startRowIndex, endRowIndex + 1),
colRange: colIds.slice(startColIndex, endColIndex + 1),
startRowIndex,
endRowIndex,
startColIndex,
endColIndex,
rowIds,
colIds
};
};
exports.getSelectedCellMatrix = getSelectedCellMatrix;
function addRowsDownWithCtrl(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 => {
// 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 addRowsDown(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: any) => {
//
//
// // 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 = moment(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 addRowsUpWithCtrl(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 => {
// 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