Skip to content

Commit

Permalink
feat(tree): add ways to reapply Tree Collapse previous state
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Jun 7, 2021
1 parent 1d71d59 commit 3702ed3
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 16 deletions.
Expand Up @@ -42,6 +42,11 @@ <h6 class="title is-6 italic">
<span class="icon mdi mdi-arrow-expand"></span>
<span>Expand All</span>
</button>
<button onclick.delegate="reapplyCollapsedItems()" data-test="reapply-collapsing-btn" class="button is-small"
disabled.bind="hasNoExpandCollapseChanged">
<span class="icon mdi mdi-history"></span>
<span>Re-Apply Previous Expanded/Collapsed Items</span>
</button>
<button onclick.delegate="logFlatStructure()" class="button is-small">
<span>Log Flat Structure</span>
</button>
Expand All @@ -54,4 +59,4 @@ <h6 class="title is-6 italic">
</div>

<div class="grid5">
</div>
</div>
30 changes: 28 additions & 2 deletions examples/webpack-demo-vanilla-bundle/src/examples/example05.ts
Expand Up @@ -22,6 +22,8 @@ export class Example5 {
sgb: SlickVanillaGridBundle;
loadingClass = '';
isLargeDataset = false;
hasNoExpandCollapseChanged = true;
collapsedTreeItems: { parentId: number | string; isCollapsed: boolean; }[] = [];

constructor() {
this._bindingEventService = new BindingEventService();
Expand All @@ -44,7 +46,14 @@ export class Example5 {
this._bindingEventService.bind(gridContainerElm, 'onbeforesortchange', this.showSpinner.bind(this));
this._bindingEventService.bind(gridContainerElm, 'onsortchanged', this.hideSpinner.bind(this));
this._bindingEventService.bind(gridContainerElm, 'onbeforetoggletreecollapse', this.showSpinner.bind(this));
this._bindingEventService.bind(gridContainerElm, 'ontoggletreecollapsed', this.hideSpinner.bind(this));
this._bindingEventService.bind(gridContainerElm, 'ontreetoggleallrequested', this.hideSpinner.bind(this));
// @ts-ignore
this._bindingEventService.bind(gridContainerElm, 'onTreeToggleStageChanged', (e: CustomEvent) => {
this.hasNoExpandCollapseChanged = false;
const treeToggleExecution = e.detail;
this.collapsedTreeItems = treeToggleExecution.treeChanges;
console.log('onTreeCollapseChanged', treeToggleExecution);
});
}

dispose() {
Expand Down Expand Up @@ -142,7 +151,9 @@ export class Example5 {
},
multiColumnSort: false, // multi-column sorting is not supported with Tree Data, so you need to disable it
presets: {
filters: [{ columnId: 'percentComplete', searchTerms: [25], operator: '>=' }]
filters: [{ columnId: 'percentComplete', searchTerms: [25], operator: '>=' }],
// @ts-ignore
treeCollapsedParents: [{ parentId: 12, isCollapsed: true }],
},
// if you're dealing with lots of data, it is recommended to use the filter debounce
filterTypingDebounce: 250,
Expand Down Expand Up @@ -192,6 +203,21 @@ export class Example5 {
this.sgb.filterService.updateFilters([{ columnId: 'percentComplete', operator: '<', searchTerms: [40] }]);
}

reapplyCollapsedItems() {
// const collapsedItems = Object.keys(this.collapsedTreeItems);
const collapsedItems = this.collapsedTreeItems.filter(item => item.isCollapsed);
if (Array.isArray(collapsedItems)) {
this.sgb.dataView.beginUpdate(true);
for (const collapsedItem of collapsedItems) {
if (collapsedItem) {
this.sgb.dataView.updateItem(collapsedItem.parentId, { ...this.sgb.dataView.getItemById(collapsedItem.parentId), __collapsed: collapsedItem.isCollapsed });
}
}
this.sgb.dataView.endUpdate();
this.sgb.dataView.refresh();
}
}

logHierarchicalStructure() {
console.log('hierarchical array', this.sgb.treeDataService.datasetHierarchical);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/common/src/interfaces/gridState.interface.ts
Expand Up @@ -18,4 +18,7 @@ export interface GridState {

/** Row Selections (by their dataContext IDs and/or grid row indexes) */
rowSelection?: CurrentRowSelection | null;

/** Tree Data with optional collapsed parents array */
treeCollapsedParents?: { parentId: number | string; isCollapsed: boolean; }[] | null;
}
Expand Up @@ -259,7 +259,7 @@ describe('TreeData Service', () => {
await service.toggleTreeDataCollapse(true);

expect(pubSubSpy).toHaveBeenCalledWith(`onBeforeToggleTreeCollapse`, { collapsing: true });
expect(pubSubSpy).toHaveBeenCalledWith(`onToggleTreeCollapsed`, { collapsing: true });
expect(pubSubSpy).toHaveBeenCalledWith(`onTreeToggleAllRequested`, { collapsing: true });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(dataSetItemsSpy).toHaveBeenCalledWith([
{ __collapsed: true, file: 'myFile.txt', size: 0.5, },
Expand All @@ -277,7 +277,7 @@ describe('TreeData Service', () => {
await service.toggleTreeDataCollapse(true);

expect(pubSubSpy).toHaveBeenCalledWith(`onBeforeToggleTreeCollapse`, { collapsing: true });
expect(pubSubSpy).toHaveBeenCalledWith(`onToggleTreeCollapsed`, { collapsing: true });
expect(pubSubSpy).toHaveBeenCalledWith(`onTreeToggleAllRequested`, { collapsing: true });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(dataSetItemsSpy).toHaveBeenCalledWith([
{ customCollapsed: true, file: 'myFile.txt', size: 0.5, },
Expand All @@ -294,7 +294,7 @@ describe('TreeData Service', () => {
await service.toggleTreeDataCollapse(false);

expect(pubSubSpy).toHaveBeenCalledWith(`onBeforeToggleTreeCollapse`, { collapsing: false });
expect(pubSubSpy).toHaveBeenCalledWith(`onToggleTreeCollapsed`, { collapsing: false });
expect(pubSubSpy).toHaveBeenCalledWith(`onTreeToggleAllRequested`, { collapsing: false });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(dataSetItemsSpy).toHaveBeenCalledWith([
{ __collapsed: false, file: 'myFile.txt', size: 0.5, },
Expand Down
25 changes: 18 additions & 7 deletions packages/common/src/services/filter.service.ts
Expand Up @@ -518,21 +518,23 @@ export class FilterService {
* This will then be passed to the DataView setFilter(customLocalFilter), which will itself loop through the list of IDs and display/hide the row if found that array of IDs
* We do this in 2 steps so that we can still use the DataSet setFilter()
*/
preFilterTreeData(inputArray: any[], columnFilters: ColumnFilters) {
preFilterTreeData(inputItems: any[], columnFilters: ColumnFilters) {
const treeDataOptions = this._gridOptions?.treeDataOptions;
const collapsedPropName = treeDataOptions?.collapsedPropName ?? '__collapsed';
const parentPropName = treeDataOptions?.parentPropName ?? '__parentId';
const dataViewIdIdentifier = this._gridOptions?.datasetIdPropertyName ?? 'id';
const treeCollapsedParents = this._gridOptions.presets?.treeCollapsedParents;

const treeObj = {};
const filteredChildrenAndParents: any[] = [];

if (Array.isArray(inputArray)) {
for (let i = 0; i < inputArray.length; i++) {
(treeObj as any)[inputArray[i][dataViewIdIdentifier]] = inputArray[i];
if (Array.isArray(inputItems)) {
for (let i = 0; i < inputItems.length; i++) {
(treeObj as any)[inputItems[i][dataViewIdIdentifier]] = inputItems[i];
// as the filtered data is then used again as each subsequent letter
// we need to delete the .__used property, otherwise the logic below
// in the while loop (which checks for parents) doesn't work:
delete (treeObj as any)[inputArray[i][dataViewIdIdentifier]].__used;
delete (treeObj as any)[inputItems[i][dataViewIdIdentifier]].__used;
}

// Step 1. prepare search filter by getting their parsed value(s), for example if it's a date filter then parse it to a Moment object
Expand All @@ -553,8 +555,8 @@ export class FilterService {
}

// Step 2. loop through every item data context to execute filter condition check
for (let i = 0; i < inputArray.length; i++) {
const item = inputArray[i];
for (let i = 0; i < inputItems.length; i++) {
const item = inputItems[i];
let matchFilter = true; // valid until proven otherwise

// loop through all column filters and execute filter condition(s)
Expand Down Expand Up @@ -588,10 +590,19 @@ export class FilterService {
(treeObj as any)[parent[dataViewIdIdentifier]].__used = true;
// try to find parent of the current parent, if exists:
parent = (treeObj as any)[parent[parentPropName]] || false;

// if there are any presets of collapsed parents, let's processed them
if (Array.isArray(treeCollapsedParents)) {
if (treeCollapsedParents.some(collapsedItem => collapsedItem.parentId === parent.id && collapsedItem.isCollapsed)) {
parent[collapsedPropName] = true;
}
}
}
}

}
}

return filteredChildrenAndParents;
}

Expand Down
16 changes: 13 additions & 3 deletions packages/common/src/services/treeData.service.ts
Expand Up @@ -8,6 +8,7 @@ import { SortService } from './sort.service';
declare const Slick: SlickNamespace;

export class TreeDataService {
private _collapsedTreeItems: { parentId: number | string; isCollapsed: boolean; }[] = [];
private _grid!: SlickGrid;
private _eventHandler: SlickEventHandler;

Expand Down Expand Up @@ -131,8 +132,17 @@ export class TreeDataService {
if (hasToggleClass) {
const item = this.dataView.getItem(args.row);
if (item) {
item[collapsedPropName] = !item[collapsedPropName] ? true : false;
this.dataView.updateItem(item[idPropName], item);
item[collapsedPropName] = !item[collapsedPropName] ? true : false; // toggle the collapsed flag
const isCollapsed = item[collapsedPropName];
const parentId = item[idPropName];
const parentFoundIdx = this._collapsedTreeItems.findIndex(treeChange => treeChange.parentId === parentId);
if (parentFoundIdx >= 0) {
this._collapsedTreeItems[parentFoundIdx].isCollapsed = isCollapsed;
} else {
this._collapsedTreeItems.push({ parentId, isCollapsed });
}
this.pubSubService.publish('onTreeToggleStageChanged', { fromParentId: parentId, treeChanges: this._collapsedTreeItems });
this.dataView.updateItem(parentId, item);
this._grid.invalidate();
}
event.stopImmediatePropagation();
Expand Down Expand Up @@ -168,7 +178,7 @@ export class TreeDataService {
}
}

this.pubSubService.publish('onToggleTreeCollapsed', { collapsing });
this.pubSubService.publish('onTreeToggleAllRequested', { collapsing });
return true;
}
}

0 comments on commit 3702ed3

Please sign in to comment.