@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
178 lines (177 loc) • 6.66 kB
JavaScript
/*!
* 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;
}