UNPKG

@revolist/revogrid

Version:

Virtual reactive data grid spreadsheet component - RevoGrid.

178 lines (177 loc) 6.66 kB
/*! * Built by Revolist OU ❤️ */ import { GROUP_DEPTH, GROUP_EXPANDED, PSEUDO_GROUP_COLUMN, PSEUDO_GROUP_ITEM, PSEUDO_GROUP_ITEM_ID, PSEUDO_GROUP_ITEM_VALUE, GROUP_ORIGINAL_INDEX, GROUP_COLUMN_PROP, } from "./grouping.const"; function getGroupValueDefault(item, prop) { return item[prop] || null; } // get source based on proxy item collection to preserve rgRow order export function getSource(source, items, withoutGrouping = false) { let index = 0; const result = { source: [], prevExpanded: {}, oldNewIndexes: {}, }; // order important here, expected parent is first, then others items.forEach(i => { const model = source[i]; if (!withoutGrouping) { result.source.push(model); return; } // grouping filter if (isGrouping(model)) { if (getExpanded(model)) { result.prevExpanded[model[PSEUDO_GROUP_ITEM_VALUE]] = true; } } else { result.source.push(model); result.oldNewIndexes[i] = index; index++; } }); return result; } export function getExpanded(model = {}) { return model[GROUP_EXPANDED]; } function flattenGroupMaps({ groupedValues, parentIds, isExpanded, itemIndex, expandedAll, prevExpanded, columnProps, }) { const depth = parentIds.length; const sourceWithGroups = []; // collapse all groups in the beginning let trimmed = {}; // index mapping let oldNewIndexMap = {}; groupedValues.forEach((innerGroupedValues, groupId) => { const levelIds = [...parentIds, groupId]; const mergedIds = levelIds.join(','); const isGroupExpanded = isExpanded && (!!expandedAll || !!prevExpanded[mergedIds]); sourceWithGroups.push({ [PSEUDO_GROUP_ITEM]: groupId, [GROUP_DEPTH]: depth, [PSEUDO_GROUP_ITEM_ID]: JSON.stringify(levelIds), [PSEUDO_GROUP_ITEM_VALUE]: mergedIds, [GROUP_EXPANDED]: isGroupExpanded, [GROUP_COLUMN_PROP]: columnProps[depth], [columnProps[depth]]: groupId, }); itemIndex += 1; // If parent group is collapsed, mark all items as hidden if (!isExpanded && depth) { trimmed[itemIndex] = true; } if (Array.isArray(innerGroupedValues)) { // This branch handles leaf nodes (actual data items) innerGroupedValues.forEach(value => { itemIndex += 1; if (!isGroupExpanded) { trimmed[itemIndex] = true; // Mark items as hidden if group is collapsed } oldNewIndexMap[value[GROUP_ORIGINAL_INDEX]] = itemIndex; // Keep track of new positions }); sourceWithGroups.push(...innerGroupedValues); } else { // This branch handles nested groups (further subgroups) const children = flattenGroupMaps({ groupedValues: innerGroupedValues, parentIds: levelIds, isExpanded: isGroupExpanded, itemIndex, expandedAll, prevExpanded, columnProps, }); // Recursively process subgroups sourceWithGroups.push(...children.source); trimmed = Object.assign(Object.assign({}, children.trimmed), trimmed); oldNewIndexMap = Object.assign(Object.assign({}, children.oldNewIndexMap), oldNewIndexMap); itemIndex = children.itemIndex; } }); return { source: sourceWithGroups, oldNewIndexMap, trimmed, itemIndex, }; } /** * Gather data for grouping * @param array - flat data array * @param columnProps - ids of groups * @param expanded - potentially expanded items if present */ export function gatherGrouping(array, columnProps, { prevExpanded = {}, expandedAll = false, getGroupValue = getGroupValueDefault, }) { const groupedItems = new Map(); array.forEach((item, originalIndex) => { const groupLevelValues = columnProps.map(groupId => getGroupValue(item, groupId)); const lastLevelValue = groupLevelValues.pop(); let currentGroupLevel = groupedItems; groupLevelValues.forEach(value => { if (!currentGroupLevel.has(value)) { currentGroupLevel.set(value, new Map()); } currentGroupLevel = currentGroupLevel.get(value); }); if (!currentGroupLevel.has(lastLevelValue)) { const groupItems = []; currentGroupLevel.set(lastLevelValue, groupItems); } const lastLevelItems = currentGroupLevel.get(lastLevelValue); lastLevelItems.push(Object.assign(Object.assign({}, item), { [GROUP_ORIGINAL_INDEX]: originalIndex })); }); const groupingDepth = columnProps.length; const { source: sourceWithGroups, trimmed, oldNewIndexMap } = flattenGroupMaps({ groupedValues: groupedItems, parentIds: [], isExpanded: true, itemIndex: -1, expandedAll, prevExpanded, columnProps }); return { sourceWithGroups, // updates source mirror depth: groupingDepth, // largest depth for grouping trimmed, // used for expand/collapse grouping values oldNewIndexMap, // used for mapping old values to new }; } export function getGroupingName(rgRow) { return rgRow === null || rgRow === void 0 ? void 0 : rgRow[PSEUDO_GROUP_ITEM]; } export function isGrouping(rgRow) { return typeof (rgRow === null || rgRow === void 0 ? void 0 : rgRow[PSEUDO_GROUP_ITEM]) !== 'undefined'; } export function isGroupingColumn(column) { return typeof (column === null || column === void 0 ? void 0 : column[PSEUDO_GROUP_COLUMN]) !== 'undefined'; } export function measureEqualDepth(groupA, groupB) { const ln = groupA.length; let i = 0; for (; i < ln; i++) { if (groupA[i] !== groupB[i]) { return i; } } return i; } export function getParsedGroup(id) { const parseGroup = JSON.parse(id); // extra precaution and type safeguard if (!Array.isArray(parseGroup)) { return null; } return parseGroup; } // check if items is child of current clicked group export function isSameGroup(currentGroup, currentModel, nextModel) { const nextGroup = getParsedGroup(nextModel[PSEUDO_GROUP_ITEM_ID]); if (!nextGroup) { return false; } const depth = measureEqualDepth(currentGroup, nextGroup); return currentModel[GROUP_DEPTH] < depth; }