@myrmidon/paged-data-browsers 1.1.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@ import * as i5 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
13
  import * as i8 from '@myrmidon/ng-tools';
14
14
  import { NgToolsModule } from '@myrmidon/ng-tools';
15
- import { BehaviorSubject, of, tap, switchMap, forkJoin } from 'rxjs';
15
+ import { BehaviorSubject, of, tap, forkJoin } from 'rxjs';
16
16
  import { ReactiveFormsModule } from '@angular/forms';
17
17
  import { MatChipsModule } from '@angular/material/chips';
18
18
  import { MatDialogModule } from '@angular/material/dialog';
@@ -53,10 +53,10 @@ class CompactPagerComponent {
53
53
  this.pagingChange.emit(this.paging);
54
54
  }
55
55
  }
56
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CompactPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
57
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.3", type: CompactPagerComponent, selector: "pdb-compact-pager", inputs: { paging: "paging" }, outputs: { pagingChange: "pagingChange" }, ngImport: i0, template: "@if (paging.pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging.pageNumber }}/{{ paging.pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging.total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
56
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: CompactPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
57
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.6", type: CompactPagerComponent, selector: "pdb-compact-pager", inputs: { paging: "paging" }, outputs: { pagingChange: "pagingChange" }, ngImport: i0, template: "@if (paging.pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging.pageNumber }}/{{ paging.pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging.total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
58
58
  }
59
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: CompactPagerComponent, decorators: [{
59
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: CompactPagerComponent, decorators: [{
60
60
  type: Component,
61
61
  args: [{ selector: 'pdb-compact-pager', template: "@if (paging.pageCount) {\n <div class=\"form-row\">\n <span id=\"pages\">{{ paging.pageNumber }}/{{ paging.pageCount }}</span>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onFirst()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>first_page</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onPrevious()\"\n [disabled]=\"paging.pageNumber < 2\"\n >\n <mat-icon>navigate_before</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onNext()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>navigate_next</mat-icon>\n </button>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"onLast()\"\n [disabled]=\"paging.pageNumber === paging.pageCount\"\n >\n <mat-icon>last_page</mat-icon>\n </button>\n <span id=\"total\">{{ paging.total }} </span>\n </div>\n}\n", styles: ["#pages,#total{color:silver}.form-row{display:flex;gap:2px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
62
62
  }], ctorParameters: () => [], propDecorators: { paging: [{
@@ -82,10 +82,10 @@ class RangeViewComponent {
82
82
  ((rangeStart + rangeWidth) / domainWidth) * this.width,
83
83
  ];
84
84
  }
85
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: RangeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
86
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.1.3", type: RangeViewComponent, selector: "pdb-range-view", inputs: { domain: "domain", range: "range", width: "width", height: "height" }, usesOnChanges: true, ngImport: i0, template: "<svg [attr.width]=\"width\" [attr.height]=\"height\">\n <rect id=\"rdomain\" [attr.width]=\"width\" [attr.height]=\"height\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange[1] - scaledRange[0]\"\n [attr.height]=\"height\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }); }
85
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: RangeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
86
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.6", type: RangeViewComponent, selector: "pdb-range-view", inputs: { domain: "domain", range: "range", width: "width", height: "height" }, usesOnChanges: true, ngImport: i0, template: "<svg [attr.width]=\"width\" [attr.height]=\"height\">\n <rect id=\"rdomain\" [attr.width]=\"width\" [attr.height]=\"height\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange[1] - scaledRange[0]\"\n [attr.height]=\"height\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }); }
87
87
  }
88
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: RangeViewComponent, decorators: [{
88
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: RangeViewComponent, decorators: [{
89
89
  type: Component,
90
90
  args: [{ selector: 'pdb-range-view', template: "<svg [attr.width]=\"width\" [attr.height]=\"height\">\n <rect id=\"rdomain\" [attr.width]=\"width\" [attr.height]=\"height\" />\n <rect\n id=\"rrange\"\n [attr.x]=\"scaledRange[0]\"\n [attr.y]=\"0\"\n [attr.width]=\"scaledRange[1] - scaledRange[0]\"\n [attr.height]=\"height\"\n />\n</svg>\n", styles: ["#rdomain{fill:#d3d3d3;stroke-width:3;stroke:#c1ba9b}#rrange{fill:#91aad3;stroke-width:3}\n"] }]
91
91
  }], ctorParameters: () => [], propDecorators: { domain: [{
@@ -121,6 +121,10 @@ class BrowserTreeNodeComponent {
121
121
  this._node = value;
122
122
  }
123
123
  constructor() {
124
+ /**
125
+ * The indent size for the node's children.
126
+ */
127
+ this.indentSize = 30;
124
128
  this.toggleExpandedRequest = new EventEmitter();
125
129
  this.changePageRequest = new EventEmitter();
126
130
  this.editNodeFilterRequest = new EventEmitter();
@@ -142,12 +146,12 @@ class BrowserTreeNodeComponent {
142
146
  this.editNodeFilterRequest.emit(this._node);
143
147
  }
144
148
  }
145
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
146
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.1.3", type: BrowserTreeNodeComponent, selector: "pdb-browser-tree-node", inputs: { node: "node", paging: "paging", debug: "debug", hideLabel: "hideLabel", hidePaging: "hidePaging" }, outputs: { toggleExpandedRequest: "toggleExpandedRequest", changePageRequest: "changePageRequest", editNodeFilterRequest: "editNodeFilterRequest" }, ngImport: i0, template: "@if (node) {\n<div id=\"node\" [style.margin-left.px]=\"(node.y - 1) * 20\">\n <!-- pager -->\n @if ($any(node).expanded && paging && paging.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging\"\n (pagingChange)=\"onPagingChange($any(node), $event)\"\n />\n <pdb-range-view\n [width]=\"250\"\n [domain]=\"[0, paging.pageCount]\"\n [range]=\"[paging.pageNumber - 1, paging.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n @if (node.y > 0) {\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\">{{\n node.hasChildren === true || node.hasChildren === undefined\n ? $any(node).expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n }\n <!-- tag -->\n @if (!hideLabel) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node.tag | stringToColor),\n color: node.tag | stringToColor | colorToContrast\n }\"\n >{{ node.tag }}</span\n >\n <!-- loc and label -->\n <span class=\"loc\">{{ node.y }}.{{ node.x }}</span> - {{ node.label }}\n }\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n <!-- debug -->\n @if (debug) {\n <span class=\"debug\"\n >#{{ node.id }}\n <span\n >| {{ $any(node).paging.pageNumber }}/{{\n $any(node).paging.pageCount\n }}\n ({{ $any(node).paging.total }})</span\n ></span\n >\n }\n <!-- filter -->\n @if (!$any(node).filter && node.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if ($any(node).filter && node.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"$any(node).filter ? 'F' : ''\"\n >filter_alt</mat-icon\n >\n </button>\n </div>\n }\n </div>\n</div>\n}\n", styles: [".form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}#node{margin-bottom:4px;padding:4px 6px;border:1px solid transparent;border-radius:6px}#node:hover{border:1px solid #98a8d4;border-radius:6px}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CompactPagerComponent, selector: "pdb-compact-pager", inputs: ["paging"], outputs: ["pagingChange"] }, { kind: "component", type: RangeViewComponent, selector: "pdb-range-view", inputs: ["domain", "range", "width", "height"] }, { kind: "pipe", type: i8.ColorToContrastPipe, name: "colorToContrast" }, { kind: "pipe", type: i8.StringToColorPipe, name: "stringToColor" }] }); }
149
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
150
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.6", type: BrowserTreeNodeComponent, selector: "pdb-browser-tree-node", inputs: { node: "node", paging: "paging", debug: "debug", hideLabel: "hideLabel", hidePaging: "hidePaging", indentSize: "indentSize" }, outputs: { toggleExpandedRequest: "toggleExpandedRequest", changePageRequest: "changePageRequest", editNodeFilterRequest: "editNodeFilterRequest" }, ngImport: i0, template: "@if (node) {\n<div id=\"node\" [style.margin-left.px]=\"(node.y - 1) * indentSize\">\n <!-- pager -->\n @if ($any(node).expanded && paging && paging.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging\"\n (pagingChange)=\"onPagingChange($any(node), $event)\"\n />\n <pdb-range-view\n [width]=\"250\"\n [domain]=\"[0, paging.pageCount]\"\n [range]=\"[paging.pageNumber - 1, paging.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\">{{\n node.hasChildren === true || node.hasChildren === undefined\n ? $any(node).expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n\n <!-- tag -->\n @if (!hideLabel) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node.tag | stringToColor),\n color: node.tag | stringToColor | colorToContrast\n }\"\n >{{ node.tag }}</span\n >\n\n <!-- loc and label -->\n <span class=\"loc\">{{ node.y }}.{{ node.x }}</span> - {{ node.label }}\n }\n\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n\n <!-- debug -->\n @if (debug) {\n <span class=\"debug\"\n >#{{ node.id }}\n <span\n >| {{ $any(node).paging.pageNumber }}/{{\n $any(node).paging.pageCount\n }}\n ({{ $any(node).paging.total }})</span\n ></span\n >\n }\n\n <!-- filter -->\n @if (!$any(node).filter && node.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if ($any(node).filter && node.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"$any(node).filter ? 'F' : ''\"\n >filter_alt</mat-icon\n >\n </button>\n </div>\n }\n </div>\n</div>\n}\n", styles: [".form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}#node{margin-bottom:4px;padding:4px 6px;border:1px solid #98a8d4;border-radius:6px}#node:hover{background-color:#d6dee9}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"], dependencies: [{ kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CompactPagerComponent, selector: "pdb-compact-pager", inputs: ["paging"], outputs: ["pagingChange"] }, { kind: "component", type: RangeViewComponent, selector: "pdb-range-view", inputs: ["domain", "range", "width", "height"] }, { kind: "pipe", type: i8.ColorToContrastPipe, name: "colorToContrast" }, { kind: "pipe", type: i8.StringToColorPipe, name: "stringToColor" }] }); }
147
151
  }
148
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: BrowserTreeNodeComponent, decorators: [{
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: BrowserTreeNodeComponent, decorators: [{
149
153
  type: Component,
150
- args: [{ selector: 'pdb-browser-tree-node', template: "@if (node) {\n<div id=\"node\" [style.margin-left.px]=\"(node.y - 1) * 20\">\n <!-- pager -->\n @if ($any(node).expanded && paging && paging.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging\"\n (pagingChange)=\"onPagingChange($any(node), $event)\"\n />\n <pdb-range-view\n [width]=\"250\"\n [domain]=\"[0, paging.pageCount]\"\n [range]=\"[paging.pageNumber - 1, paging.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n @if (node.y > 0) {\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\">{{\n node.hasChildren === true || node.hasChildren === undefined\n ? $any(node).expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n }\n <!-- tag -->\n @if (!hideLabel) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node.tag | stringToColor),\n color: node.tag | stringToColor | colorToContrast\n }\"\n >{{ node.tag }}</span\n >\n <!-- loc and label -->\n <span class=\"loc\">{{ node.y }}.{{ node.x }}</span> - {{ node.label }}\n }\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n <!-- debug -->\n @if (debug) {\n <span class=\"debug\"\n >#{{ node.id }}\n <span\n >| {{ $any(node).paging.pageNumber }}/{{\n $any(node).paging.pageCount\n }}\n ({{ $any(node).paging.total }})</span\n ></span\n >\n }\n <!-- filter -->\n @if (!$any(node).filter && node.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if ($any(node).filter && node.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"$any(node).filter ? 'F' : ''\"\n >filter_alt</mat-icon\n >\n </button>\n </div>\n }\n </div>\n</div>\n}\n", styles: [".form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}#node{margin-bottom:4px;padding:4px 6px;border:1px solid transparent;border-radius:6px}#node:hover{border:1px solid #98a8d4;border-radius:6px}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"] }]
154
+ args: [{ selector: 'pdb-browser-tree-node', template: "@if (node) {\n<div id=\"node\" [style.margin-left.px]=\"(node.y - 1) * indentSize\">\n <!-- pager -->\n @if ($any(node).expanded && paging && paging.pageCount > 1) {\n <div id=\"pager\" [style.display]=\"hidePaging ? 'inherit' : 'block'\">\n <pdb-compact-pager\n [paging]=\"paging\"\n (pagingChange)=\"onPagingChange($any(node), $event)\"\n />\n <pdb-range-view\n [width]=\"250\"\n [domain]=\"[0, paging.pageCount]\"\n [range]=\"[paging.pageNumber - 1, paging.pageNumber]\"\n />\n </div>\n }\n <!-- node -->\n <div class=\"form-row\">\n <!-- expand/collapse button -->\n <button\n type=\"button\"\n mat-icon-button\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon class=\"mat-primary\">{{\n node.hasChildren === true || node.hasChildren === undefined\n ? $any(node).expanded\n ? \"expand_less\"\n : \"expand_more\"\n : \"stop\"\n }}</mat-icon>\n </button>\n\n <!-- tag -->\n @if (!hideLabel) {\n <span\n class=\"tag\"\n [ngStyle]=\"{\n 'background-color': (node.tag | stringToColor),\n color: node.tag | stringToColor | colorToContrast\n }\"\n >{{ node.tag }}</span\n >\n\n <!-- loc and label -->\n <span class=\"loc\">{{ node.y }}.{{ node.x }}</span> - {{ node.label }}\n }\n\n <!-- PROJECTED NODE -->\n <ng-content></ng-content>\n\n <!-- debug -->\n @if (debug) {\n <span class=\"debug\"\n >#{{ node.id }}\n <span\n >| {{ $any(node).paging.pageNumber }}/{{\n $any(node).paging.pageCount\n }}\n ({{ $any(node).paging.total }})</span\n ></span\n >\n }\n\n <!-- filter -->\n @if (!$any(node).filter && node.y) {\n <div class=\"muted\">\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Add filter\"\n (click)=\"onEditFilter()\"\n >\n <mat-icon>filter_list</mat-icon>\n </button>\n </div>\n } @if ($any(node).filter && node.y) {\n <div class=\"muted\">\n <button type=\"button\" mat-icon-button (click)=\"onEditFilter()\">\n <mat-icon [matBadge]=\"$any(node).filter ? 'F' : ''\"\n >filter_alt</mat-icon\n >\n </button>\n </div>\n }\n </div>\n</div>\n}\n", styles: [".form-row{display:flex;gap:8px;align-items:center}.form-row *{flex:0 0 auto}.form-row span{flex:0 1 auto;white-space:normal}#node #pager{display:none}#node:hover #pager{display:block}#node{margin-bottom:4px;padding:4px 6px;border:1px solid #98a8d4;border-radius:6px}#node:hover{background-color:#d6dee9}span.loc{font-size:.85em;color:#666;vertical-align:middle}span.tag{border:1px solid #aaa;border-radius:4px;padding:0 4px}fieldset{border:1px solid silver;border-radius:6px;padding:4px 6px;margin:4px 0}legend{color:silver}.muted{opacity:.3}.muted:hover{opacity:1}.debug{font-size:.85em;color:#9c3d3e}\n"] }]
151
155
  }], ctorParameters: () => [], propDecorators: { node: [{
152
156
  type: Input
153
157
  }], paging: [{
@@ -158,6 +162,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImpor
158
162
  type: Input
159
163
  }], hidePaging: [{
160
164
  type: Input
165
+ }], indentSize: [{
166
+ type: Input
161
167
  }], toggleExpandedRequest: [{
162
168
  type: Output
163
169
  }], changePageRequest: [{
@@ -464,7 +470,7 @@ class PagedTreeStore {
464
470
  return;
465
471
  }
466
472
  this._pageSize = value;
467
- this.reset(this._radix?.label || this._radixLabel);
473
+ this.reset();
468
474
  }
469
475
  /**
470
476
  * Create an instance of the store.
@@ -476,21 +482,12 @@ class PagedTreeStore {
476
482
  this._pageSize = options.pageSize;
477
483
  this._cache = new LRUCache(options.cacheSize);
478
484
  this._customCacheKeyBuilder = options.buildCacheKey;
479
- this._radixLabel = '(root)';
480
- this._roots = [];
481
485
  this._nodes$ = new BehaviorSubject([]);
482
486
  this.nodes$ = this._nodes$.asObservable();
483
- this._tags$ = new BehaviorSubject([]);
484
- this.tags$ = this._tags$.asObservable();
485
487
  this._filter$ = new BehaviorSubject({});
486
488
  this.filter$ = this._filter$.asObservable();
487
489
  this._dirty = true;
488
- this.updateTags();
489
- }
490
- updateTags() {
491
- this._service.getTags().subscribe((tags) => {
492
- this._tags$.next(tags);
493
- });
490
+ this._hasMockRoot = options.hasMockRoot || false;
494
491
  }
495
492
  /**
496
493
  * Gets the global filter, eventually overridden with values
@@ -506,6 +503,13 @@ class PagedTreeStore {
506
503
  }
507
504
  : this._filter$.value;
508
505
  }
506
+ /**
507
+ * Checks if this store is empty.
508
+ * @returns True if this store is empty.
509
+ */
510
+ isEmpty() {
511
+ return this._nodes$.value.length === 0;
512
+ }
509
513
  /**
510
514
  * Gets all the nodes in the store.
511
515
  * @returns The nodes.
@@ -514,11 +518,11 @@ class PagedTreeStore {
514
518
  return this._nodes$.value;
515
519
  }
516
520
  /**
517
- * Gets the list of nodes tags.
518
- * @returns The tags.
521
+ * Get the root node of the tree.
522
+ * @returns The root node of the tree or undefined if empty.
519
523
  */
520
- getTags() {
521
- return this._tags$.value;
524
+ getRootNode() {
525
+ return this._nodes$.value[0];
522
526
  }
523
527
  /**
524
528
  * Build the cache key for the given page number and filter.
@@ -535,6 +539,13 @@ class PagedTreeStore {
535
539
  }
536
540
  return JSON.stringify({ pageNumber, ...filter });
537
541
  }
542
+ /**
543
+ * Get the specified page of nodes, either from cache or from the server.
544
+ * When the page is retrieved from the server, it is stored in cache.
545
+ * @param filter The filter to apply.
546
+ * @param pageNumber The page number to get.
547
+ * @returns Observable of the page of nodes.
548
+ */
538
549
  getPageFromCacheOrServer(filter, pageNumber) {
539
550
  const key = this.buildCacheKey(pageNumber, filter);
540
551
  const pageInCache = this._cache.get(key);
@@ -542,11 +553,19 @@ class PagedTreeStore {
542
553
  return of(pageInCache);
543
554
  }
544
555
  else {
545
- return this._service.getNodes(filter, pageNumber, this._pageSize).pipe(tap((page) => {
556
+ return this._service
557
+ .getNodes(filter, pageNumber, this._pageSize, this._hasMockRoot)
558
+ .pipe(tap((page) => {
546
559
  this._cache.put(key, page, 0);
547
560
  }));
548
561
  }
549
562
  }
563
+ /**
564
+ * Create paged tree nodes from a page of tree nodes, by providing further
565
+ * metadata like page number, page count and total items.
566
+ * @param page The page to create nodes from.
567
+ * @returns Paged nodes.
568
+ */
550
569
  createPageNodes(page) {
551
570
  return page.items.map((n) => {
552
571
  return {
@@ -564,94 +583,44 @@ class PagedTreeStore {
564
583
  * Sets the filter for this store. Whenever the filter is set,
565
584
  * the store is reset.
566
585
  * @param filter The filter.
567
- * @param radixLabel The label of the radix node, if this needs to be set.
568
586
  * @returns true if tree was changed, false otherwise.
569
587
  */
570
- setFilter(filter, radixLabel) {
588
+ setFilter(filter) {
571
589
  if (this._filter$.value === filter) {
572
590
  return Promise.resolve(false);
573
591
  }
574
592
  this._filter$.next(filter);
575
593
  this._dirty = true;
576
- return this.reset(this._radix?.label || radixLabel || this._radixLabel);
594
+ return this.reset();
577
595
  }
578
596
  /**
579
597
  * Reset the store, loading the root nodes and their children.
580
- * @param label The label of the radix node.
581
598
  * @returns true if tree was changed, false otherwise.
582
599
  */
583
- reset(label) {
600
+ reset() {
584
601
  if (!this._dirty) {
585
602
  return Promise.resolve(false);
586
603
  }
587
604
  this._cache.clear();
588
605
  const filter = this._filter$.value;
589
- this._radix = {
590
- id: 0,
591
- y: 0,
592
- x: 1,
593
- label: label,
594
- paging: {
595
- pageNumber: 0,
596
- pageCount: 0,
597
- total: 0,
598
- },
599
- };
600
606
  return new Promise((resolve, reject) => {
601
607
  this._service
602
- .getRootNodes(filter.tags)
603
- .pipe(switchMap((nodes) => {
604
- // no roots, clear and return empty set
605
- if (!nodes || nodes.length === 0) {
606
- this._roots = [];
607
- return of([]);
608
- }
609
- else {
610
- // got roots, set them and get their children
611
- this._roots = nodes.map((node) => ({
612
- ...node,
613
- paging: {
614
- pageNumber: 1,
615
- pageCount: 1,
616
- total: 1,
617
- },
618
- }));
619
- // fetch children for each root node
620
- return forkJoin(this._roots.map((root) => this.getPageFromCacheOrServer({ ...filter, parentId: root.id }, 1)));
621
- }
622
- }))
608
+ .getNodes({
609
+ ...filter,
610
+ parentId: undefined,
611
+ }, 1, this._pageSize, this._hasMockRoot)
623
612
  .subscribe({
624
- next: (pages) => {
625
- this._dirty = false;
626
- if (pages.some((page) => page.total)) {
627
- // radix
628
- this._radix.hasChildren = true;
629
- this._radix.expanded = true;
630
- this._radix.paging = {
631
- pageNumber: 1,
632
- pageCount: 1,
633
- total: pages.length,
634
- };
635
- // roots
636
- this._roots.forEach((root, i) => {
637
- root.hasChildren = !!pages[i].total;
638
- root.expanded = !!pages[i].total;
639
- });
640
- const nodes = this._roots.flatMap((root, i) => [
641
- root,
642
- ...this.createPageNodes(pages[i]),
643
- ]);
644
- this._nodes$.next([this._radix, ...nodes]);
645
- resolve(true);
646
- }
647
- else {
648
- this._roots.forEach((root) => {
649
- root.hasChildren = false;
650
- root.expanded = false;
613
+ next: (page) => {
614
+ this._nodes$.next(this.createPageNodes(page));
615
+ // get the children of each node thus calculating their hasChildren property
616
+ const childrenObservables = this._nodes$.value.map((node) => this.getPageFromCacheOrServer({ ...filter, parentId: node.id }, 1));
617
+ forkJoin(childrenObservables).subscribe((childrenPages) => {
618
+ childrenPages.forEach((page, i) => {
619
+ this._nodes$.value[i].hasChildren = page.total > 0;
651
620
  });
652
- this._nodes$.next([this._radix, ...this._roots]);
653
- resolve(true);
654
- }
621
+ });
622
+ this._dirty = false;
623
+ resolve(true);
655
624
  },
656
625
  error: (error) => {
657
626
  reject(error);
@@ -719,6 +688,12 @@ class PagedTreeStore {
719
688
  });
720
689
  });
721
690
  }
691
+ /**
692
+ * Expand all the descendants of the node with the specified ID.
693
+ *
694
+ * @param id The ID of the node to expand.
695
+ * @returns Promise.
696
+ */
722
697
  expandAll(id) {
723
698
  if (!id) {
724
699
  return Promise.resolve(false);
@@ -854,7 +829,12 @@ class PagedTreeStore {
854
829
  * Collapse all the nodes in the store.
855
830
  */
856
831
  collapseAll() {
857
- this._nodes$.next([this._radix, ...this._roots]);
832
+ if (this.isEmpty()) {
833
+ return;
834
+ }
835
+ // collapse the first node
836
+ const root = this._nodes$.value[0];
837
+ this.collapse(root.id);
858
838
  }
859
839
  /**
860
840
  * Clear the cache.
@@ -875,8 +855,8 @@ class PagedTreeStore {
875
855
  }
876
856
 
877
857
  class PagedDataBrowsersModule {
878
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
879
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, declarations: [CompactPagerComponent,
858
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: PagedDataBrowsersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
859
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.6", ngImport: i0, type: PagedDataBrowsersModule, declarations: [CompactPagerComponent,
880
860
  RangeViewComponent,
881
861
  BrowserTreeNodeComponent], imports: [CommonModule,
882
862
  ReactiveFormsModule,
@@ -896,7 +876,7 @@ class PagedDataBrowsersModule {
896
876
  NgToolsModule], exports: [CompactPagerComponent,
897
877
  RangeViewComponent,
898
878
  BrowserTreeNodeComponent] }); }
899
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, imports: [CommonModule,
879
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: PagedDataBrowsersModule, imports: [CommonModule,
900
880
  ReactiveFormsModule,
901
881
  // material
902
882
  MatBadgeModule,
@@ -913,7 +893,7 @@ class PagedDataBrowsersModule {
913
893
  // myrmidon
914
894
  NgToolsModule] }); }
915
895
  }
916
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, decorators: [{
896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.6", ngImport: i0, type: PagedDataBrowsersModule, decorators: [{
917
897
  type: NgModule,
918
898
  args: [{
919
899
  declarations: [