Skip to content

Commit

Permalink
Merge pull request #791 from ghiscoding/feat/tree-data-collapsed-state
Browse files Browse the repository at this point in the history
feat(tree): add Tree Collapse Grid State/Preset
  • Loading branch information
ghiscoding authored Jun 12, 2021
2 parents 619ab66 + fe91b02 commit 60ae015
Show file tree
Hide file tree
Showing 32 changed files with 1,065 additions and 506 deletions.
15 changes: 15 additions & 0 deletions src/app/examples/grid-tree-data-parent-child.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ <h2>
<span class="mdi mdi-filter-outline"></span>
<span>Dynamically Change Filter (% complete &lt; 40)</span>
</button>
<button (click)="collapseAllWithoutEvent()" data-test="collapse-all-noevent-btn"
class="btn btn-outline-secondary btn-sm">
<span class="icon mdi mdi-arrow-collapse"></span>
<span>Collapse All (without triggering event)</span>
</button>
<button (click)="reapplyToggledItems()" data-test="reapply-toggled-items-btn"
class="btn btn-outline-secondary btn-sm"
[disabled]="hasNoExpandCollapseChanged">
<span class="icon mdi mdi-history"></span>
<span>Reapply Previous Toggled Items</span>
</button>
<div class.bind="loadingClass"></div>
</div>
</div>
Expand All @@ -42,6 +53,9 @@ <h2>
<span class="icon mdi mdi-arrow-expand"></span>
<span>Expand All</span>
</button>
<button (click)="logTreeDataToggledItems()" class="btn btn-outline-secondary btn-sm">
<span>Log Tree Toggled Items</span>
</button>
<button (click)="logFlatStructure()" class="btn btn-outline-secondary btn-sm">
<span>Log Flat Structure</span>
</button>
Expand All @@ -60,6 +74,7 @@ <h2>
[columnDefinitions]="columnDefinitions"
[gridOptions]="gridOptions"
[dataset]="dataset"
(onGridStateChanged)="handleOnGridStateChanged($event)"
(onAngularGridCreated)="angularGridReady($event)">
</angular-slickgrid>
</div>
67 changes: 62 additions & 5 deletions src/app/examples/grid-tree-data-parent-child.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
Filters,
Formatters,
GridOption,
GridStateChange,
GridStateType,
TreeToggledItem,
TreeToggleStateChange,
} from './../modules/angular-slickgrid';

const NB_ITEMS = 500;
Expand Down Expand Up @@ -38,6 +42,10 @@ export class GridTreeDataParentChildComponent implements OnInit {
columnDefinitions!: Column[];
dataset!: any[];
datasetHierarchical: any[] = [];
loadingClass = '';
isLargeDataset = false;
hasNoExpandCollapseChanged = true;
treeToggleItems: TreeToggledItem[] = [];

constructor() { }

Expand Down Expand Up @@ -108,6 +116,7 @@ export class GridTreeDataParentChildComponent implements OnInit {
// this is optional, you can define the tree level property name that will be used for the sorting/indentation, internally it will use "__treeLevel"
levelPropName: 'treeLevel',
indentMarginLeft: 15,
initiallyCollapsed: true,

// you can optionally sort by a different column and/or sort direction
// this is the recommend approach, unless you are 100% that your original array is already sorted (in most cases it's not)
Expand All @@ -126,7 +135,8 @@ export class GridTreeDataParentChildComponent implements OnInit {
},
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: '>=' }],
treeData: { toggledItems: [{ itemId: 1, isCollapsed: false }] },
},
// change header/cell row height for material design theme
headerRowHeight: 45,
Expand Down Expand Up @@ -207,6 +217,10 @@ export class GridTreeDataParentChildComponent implements OnInit {
this.angularGrid.treeDataService.toggleTreeDataCollapse(true);
}

collapseAllWithoutEvent() {
this.angularGrid.treeDataService.toggleTreeDataCollapse(true, false);
}

expandAll() {
this.angularGrid.treeDataService.toggleTreeDataCollapse(false);
}
Expand All @@ -216,6 +230,16 @@ export class GridTreeDataParentChildComponent implements OnInit {
this.angularGrid.filterService.updateFilters([{ columnId: 'percentComplete', operator: '<', searchTerms: [40] }]);
}

hideSpinner() {
setTimeout(() => this.loadingClass = '', 200); // delay the hide spinner a bit to avoid show/hide too quickly
}

showSpinner() {
if (this.isLargeDataset) {
this.loadingClass = 'mdi mdi-load mdi-spin-1s mdi-24px color-alt-success';
}
}

logHierarchicalStructure() {
console.log('exploded array', this.angularGrid.treeDataService.datasetHierarchical);
}
Expand All @@ -225,6 +249,7 @@ export class GridTreeDataParentChildComponent implements OnInit {
}

loadData(rowCount: number) {
this.isLargeDataset = rowCount > 5000; // we'll show a spinner when it's large, else don't show show since it should be fast enough
let indent = 0;
const parents = [];
const data = [];
Expand All @@ -237,11 +262,25 @@ export class GridTreeDataParentChildComponent implements OnInit {
const item: any = (data[i] = {});
let parentId;

// for implementing filtering/sorting, don't go over indent of 2
if (Math.random() > 0.8 && i > 0 && indent < 3) {
/*
for demo & E2E testing purposes, let's make "Task 0" empty and then "Task 1" a parent that contains at least "Task 2" and "Task 3" which the latter will also contain "Task 4" (as shown below)
also for all other rows don't go over indent tree level depth of 2
Task 0
Task 1
Task 2
Task 3
Task 4
...
*/
if (i === 1 || i === 0) {
indent = 0;
parents.pop();
} if (i === 3) {
indent = 1;
} else if (i === 2 || i === 4 || (Math.random() > 0.8 && i > 0 && indent < 3 && i - 1 !== 0 && i - 1 !== 2)) { // also make sure Task 0, 2 remains empty
indent++;
parents.push(i - 1);
} else if (Math.random() < 0.3 && indent > 0) {
} else if ((Math.random() < 0.3 && indent > 0)) {
indent--;
parents.pop();
}
Expand All @@ -262,7 +301,25 @@ export class GridTreeDataParentChildComponent implements OnInit {
item['effortDriven'] = (i % 5 === 0);
}
this.dataset = data;

return data;
}

/** Dispatched event of a Grid State Changed event */
handleOnGridStateChanged(gridStateChange: GridStateChange) {
this.hasNoExpandCollapseChanged = false;

if (gridStateChange.change!.type === GridStateType.treeData) {
console.log('Tree Data gridStateChange', gridStateChange.gridState!.treeData);
this.treeToggleItems = gridStateChange.gridState!.treeData!.toggledItems as TreeToggledItem[];
}
}

logTreeDataToggledItems() {
console.log(this.angularGrid.treeDataService.getToggledItems());
}

reapplyToggledItems() {
this.angularGrid.treeDataService.applyToggledItemStateChanges(this.treeToggleItems);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
if (newHierarchicalDataset && this.grid && this.sortService?.processTreeDataInitialSort) {
this.dataView.setItems([], this.gridOptions.datasetIdPropertyName);
this.sortService.processTreeDataInitialSort();

this.sortTreeDataset([]);
// we also need to reset/refresh the Tree Data filters because if we inserted new item(s) then it might not show up without doing this refresh
// however we need 1 cpu cycle before having the DataView refreshed, so we need to wrap this check in a setTimeout
setTimeout(() => {
Expand Down Expand Up @@ -333,18 +333,18 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
// dispose the Components
this.slickEmptyWarning.dispose();

if (this._eventHandler && this._eventHandler.unsubscribeAll) {
if (this._eventHandler?.unsubscribeAll) {
this._eventHandler.unsubscribeAll();
}
if (this.dataView) {
if (this.dataView && this.dataView.setItems) {
if (this.dataView?.setItems) {
this.dataView.setItems([]);
}
if (this.dataView.destroy) {
this.dataView.destroy();
}
}
if (this.grid && this.grid.destroy) {
if (this.grid?.destroy) {
this.grid.destroy(shouldEmptyDomElementContainer);
}

Expand Down Expand Up @@ -445,7 +445,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
this.displayEmptyDataWarning(finalTotalCount < 1);
}

if (Array.isArray(dataset) && this.grid && this.dataView && typeof this.dataView.setItems === 'function') {
if (Array.isArray(dataset) && this.grid && this.dataView?.setItems) {
this.dataView.setItems(dataset, this.gridOptions.datasetIdPropertyName);
if (!this.gridOptions.backendServiceApi && !this.gridOptions.enableTreeData) {
this.dataView.reSort();
Expand Down Expand Up @@ -587,7 +587,6 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
})
);
}

if (!this.customDataView) {
// bind external sorting (backend) when available or default onSort (dataView)
if (gridOptions.enableSorting) {
Expand Down Expand Up @@ -1090,8 +1089,10 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
private loadFilterPresetsWhenDatasetInitialized() {
if (this.gridOptions && !this.customDataView) {
// if user entered some Filter "presets", we need to reflect them all in the DOM
if (this.gridOptions.presets && Array.isArray(this.gridOptions.presets.filters)) {
this.filterService.populateColumnFilterSearchTermPresets(this.gridOptions.presets.filters);
// also note that a presets of Tree Data Toggling will also call this method because Tree Data toggling does work with data filtering
// (collapsing a parent will basically use Filter for hidding (aka collapsing) away the child underneat it)
if (this.gridOptions.presets && (Array.isArray(this.gridOptions.presets.filters) || Array.isArray(this.gridOptions.presets?.treeData?.toggledItems))) {
this.filterService.populateColumnFilterSearchTermPresets(this.gridOptions.presets?.filters || []);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/app/modules/angular-slickgrid/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ export class Constants {
TEXT_X_OF_Y_SELECTED: '# of % selected',
};

static readonly treeDataProperties = {
CHILDREN_PROP: 'children',
COLLAPSED_PROP: '__collapsed',
HAS_CHILDREN_PROP: '__hasChildren',
TREE_LEVEL_PROP: '__treeLevel',
PARENT_PROP: '__parentId',
};

// some Validation default texts
static readonly VALIDATION_REQUIRED_FIELD = 'Field is required';
static readonly VALIDATION_EDITOR_VALID_NUMBER = 'Please enter a valid number';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { TranslateService } from '@ngx-translate/core';
import { Constants } from '../constants';
import {
Column,
ColumnSort,
CurrentSorter,
EmitterType,
Extension,
Expand Down
Loading

0 comments on commit 60ae015

Please sign in to comment.