@revolist/revogrid
Version:
Virtual reactive data grid spreadsheet component - RevoGrid.
246 lines (245 loc) • 11 kB
JavaScript
/*!
* Built by Revolist OU ❤️
*/
import { getPhysical, setItems, columnTypes, } from "../../store/index";
import { BasePlugin } from "../base.plugin";
import { FILTER_TRIMMED_TYPE } from "../filter/filter.plugin";
import { SortingPlugin } from "../sorting/sorting.plugin";
import { GROUP_EXPAND_EVENT, GROUPING_ROW_TYPE, PSEUDO_GROUP_COLUMN, } from "./grouping.const";
import { doExpand, doCollapse } from "./grouping.row.expand.service";
import { gatherGrouping, getExpanded, getSource, isGrouping, isGroupingColumn, } from "./grouping.service";
import { processDoubleConversionTrimmed, TRIMMED_GROUPING, } from "./grouping.trimmed.service";
export * from './grouping.const';
export * from './grouping.row.expand.service';
export * from './grouping.row.types';
export * from './grouping.service';
export * from './grouping.row.renderer';
export class GroupingRowPlugin extends BasePlugin {
getStore(type = GROUPING_ROW_TYPE) {
return this.providers.data.stores[type].store;
}
constructor(revogrid, providers) {
super(revogrid, providers);
}
// befoce cell focus
onFocus(e) {
if (isGrouping(e.detail.model)) {
e.preventDefault();
}
}
// expand event triggered
onExpand({ virtualIndex }) {
const { source } = getSource(this.getStore().get('source'), this.getStore().get('proxyItems'));
let newTrimmed = this.getStore().get('trimmed')[TRIMMED_GROUPING];
let i = getPhysical(this.getStore(), virtualIndex);
const isExpanded = getExpanded(source[i]);
if (!isExpanded) {
const { trimmed, items } = doExpand(virtualIndex, source, this.getStore().get('items'));
newTrimmed = Object.assign(Object.assign({}, newTrimmed), trimmed);
if (items) {
setItems(this.getStore(), items);
}
}
else {
const { trimmed } = doCollapse(i, source);
newTrimmed = Object.assign(Object.assign({}, newTrimmed), trimmed);
this.revogrid.clearFocus();
}
this.getStore().set('source', source);
this.revogrid.addTrimmed(newTrimmed, TRIMMED_GROUPING);
}
setColumnGrouping(cols) {
// if 0 column as holder
if (cols === null || cols === void 0 ? void 0 : cols.length) {
cols[0][PSEUDO_GROUP_COLUMN] = true;
return true;
}
return false;
}
setColumns({ columns }) {
for (let type of columnTypes) {
if (this.setColumnGrouping(columns[type])) {
break;
}
}
}
// evaluate drag between groups
onDrag(e) {
const { from, to } = e.detail;
const isDown = to - from >= 0;
const { source } = getSource(this.getStore().get('source'), this.getStore().get('proxyItems'));
const items = this.getStore().get('items');
let i = isDown ? from : to;
const end = isDown ? to : from;
for (; i < end; i++) {
const model = source[items[i]];
const isGroup = isGrouping(model);
if (isGroup) {
e.preventDefault();
return;
}
}
}
beforeTrimmedApply(trimmed, type) {
/** Before filter apply remove grouping filtering */
if (type === FILTER_TRIMMED_TYPE) {
const source = this.getStore().get('source');
for (let index in trimmed) {
if (trimmed[index] && isGrouping(source[index])) {
trimmed[index] = false;
}
}
}
}
isSortingRunning() {
const sortingPlugin = this.providers.plugins.getByClass(SortingPlugin);
return !!(sortingPlugin === null || sortingPlugin === void 0 ? void 0 : sortingPlugin.sortingPromise);
}
/**
* Starts global source update with group clearing and applying new one
* Initiated when need to reapply grouping
*/
doSourceUpdate(options) {
var _a;
/**
* Get source without grouping
* @param newOldIndexMap - provides us mapping with new indexes vs old indexes, we would use it for trimmed mapping
*/
const store = this.getStore();
const { source, prevExpanded, oldNewIndexes } = getSource(store.get('source'), store.get('proxyItems'), true);
const expanded = Object.assign({ prevExpanded }, options);
/**
* Group again
* @param oldNewIndexMap - provides us mapping with new indexes vs old indexes
*/
const { sourceWithGroups, depth, trimmed, oldNewIndexMap, } = gatherGrouping(source, ((_a = this.options) === null || _a === void 0 ? void 0 : _a.props) || [], expanded);
const customRenderer = options === null || options === void 0 ? void 0 : options.groupLabelTemplate;
// setup source
this.providers.data.setData(sourceWithGroups, GROUPING_ROW_TYPE, this.revogrid.disableVirtualY, { depth, customRenderer }, true);
this.updateTrimmed(trimmed, oldNewIndexes !== null && oldNewIndexes !== void 0 ? oldNewIndexes : {}, oldNewIndexMap);
}
/**
* Apply grouping on data set
* Clear grouping from source
* If source came from other plugin
*/
onDataSet(data) {
var _a, _b;
let preservedExpanded = {};
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.preserveGroupingOnUpdate) !== false) {
let { prevExpanded } = getSource(this.getStore().get('source'), this.getStore().get('proxyItems'), true);
preservedExpanded = prevExpanded;
}
const source = data.source.filter(s => !isGrouping(s));
const options = Object.assign(Object.assign({}, (this.revogrid.grouping || {})), { prevExpanded: preservedExpanded });
const { sourceWithGroups, depth, trimmed, oldNewIndexMap, } = gatherGrouping(source, ((_b = this.options) === null || _b === void 0 ? void 0 : _b.props) || [], options);
data.source = sourceWithGroups;
this.providers.data.setGrouping({ depth });
this.updateTrimmed(trimmed, oldNewIndexMap);
}
/**
* External call to apply grouping. Called by revogrid when prop changed.
*/
setGrouping(options) {
var _a, _b;
// unsubscribe from all events when group applied
this.clearSubscriptions();
this.options = options;
// clear props, no grouping exists
if (!((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.length)) {
this.clearGrouping();
return;
}
// props exist and source initd
const store = this.getStore();
const { source } = getSource(store.get('source'), store.get('proxyItems'));
if (source.length) {
this.doSourceUpdate(Object.assign({}, options));
}
// props exist and columns initd
for (let t of columnTypes) {
if (this.setColumnGrouping(this.providers.column.getColumns(t))) {
this.providers.column.refreshByType(t);
break;
}
}
// if has any grouping subscribe to events again
/** if grouping present and new data source arrived */
this.addEventListener('beforesourceset', ({ detail }) => {
var _a, _b, _c;
if (!(((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.length) && ((_c = detail === null || detail === void 0 ? void 0 : detail.source) === null || _c === void 0 ? void 0 : _c.length))) {
return;
}
// if sorting is running don't apply grouping, wait for sorting, then it'll apply in @aftersortingapply
if (this.isSortingRunning()) {
return;
}
this.onDataSet(detail);
});
this.addEventListener('beforecolumnsset', ({ detail }) => {
this.setColumns(detail);
});
/**
* filter applied need to clear grouping and apply again
* based on new results can be new grouping
*/
this.addEventListener('beforetrimmed', ({ detail: { trimmed, trimmedType } }) => this.beforeTrimmedApply(trimmed, trimmedType));
/**
* sorting applied need to clear grouping and apply again
* based on new results whole grouping order will changed
*/
this.addEventListener('aftersortingapply', () => {
var _a, _b;
if (!((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.length)) {
return;
}
this.doSourceUpdate(Object.assign({}, this.options));
});
/**
* Apply logic for focus inside of grouping
* We can't focus on grouping rows, navigation only inside of groups for now
*/
this.addEventListener('beforecellfocus', e => this.onFocus(e));
/**
* Prevent rgRow drag outside the group
*/
this.addEventListener('roworderchanged', e => this.onDrag(e));
/**
* When grouping expand icon was clicked
*/
this.addEventListener(GROUP_EXPAND_EVENT, e => this.onExpand(e.detail));
}
// clear grouping
clearGrouping() {
// clear columns
columnTypes.forEach(t => {
const cols = this.providers.column.getColumns(t);
let deleted = false;
cols.forEach(c => {
if (isGroupingColumn(c)) {
delete c[PSEUDO_GROUP_COLUMN];
deleted = true;
}
});
// if column store had grouping clear and refresh
if (deleted) {
this.providers.column.refreshByType(t);
}
});
// clear rows
const { source, oldNewIndexes } = getSource(this.getStore().get('source'), this.getStore().get('proxyItems'), true);
this.providers.data.setData(source, GROUPING_ROW_TYPE, this.revogrid.disableVirtualY, undefined, true);
this.updateTrimmed(undefined, undefined, oldNewIndexes);
}
updateTrimmed(trimmedGroup = {}, firstLevelMap = {}, secondLevelMap) {
// map previously trimmed data
const trimemedOptionsToUpgrade = processDoubleConversionTrimmed(this.getStore().get('trimmed'), firstLevelMap, secondLevelMap);
for (let type in trimemedOptionsToUpgrade) {
this.revogrid.addTrimmed(trimemedOptionsToUpgrade[type], type);
}
// const emptyGroups = this.filterOutEmptyGroups(trimemedOptionsToUpgrade, childrenByGroup);
// setup trimmed data for grouping
this.revogrid.addTrimmed(Object.assign({}, trimmedGroup), TRIMMED_GROUPING);
}
}
//# sourceMappingURL=grouping.row.plugin.js.map