@myrmidon/paged-data-browsers 1.1.0 → 2.0.0

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.0.0", ngImport: i0, type: CompactPagerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
57
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", 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.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"] }] }); }
58
58
  }
59
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: CompactPagerComponent, decorators: [{
59
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", 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.0.0", ngImport: i0, type: RangeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
86
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.0", 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.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"] }); }
87
87
  }
88
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: RangeViewComponent, decorators: [{
88
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", 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.0.0", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
146
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.0", 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\n id=\"pager\"\n [style.display]=\"hidePaging ? 'inherit' : 'block'\"\n >\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 color=\"primary\"\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon>{{\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]=\"{\r\n 'background-color': (node.tag | stringToColor),\r\n color: node.tag | stringToColor | colorToContrast\r\n }\"\r\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 }\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.1.3", ngImport: i0, type: BrowserTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
150
+ 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", 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.0.0", ngImport: i0, type: BrowserTreeNodeComponent, decorators: [{
152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", 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\n id=\"pager\"\n [style.display]=\"hidePaging ? 'inherit' : 'block'\"\n >\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 color=\"primary\"\n [matTooltip]=\"$any(node).expanded ? 'Collapse' : 'Expand'\"\n [disabled]=\"node.hasChildren === false\"\n (click)=\"onToggleExpanded()\"\n >\n <mat-icon>{{\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]=\"{\r\n 'background-color': (node.tag | stringToColor),\r\n color: node.tag | stringToColor | colorToContrast\r\n }\"\r\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 }\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.0.0", 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.
@@ -513,13 +517,6 @@ class PagedTreeStore {
513
517
  getNodes() {
514
518
  return this._nodes$.value;
515
519
  }
516
- /**
517
- * Gets the list of nodes tags.
518
- * @returns The tags.
519
- */
520
- getTags() {
521
- return this._tags$.value;
522
- }
523
520
  /**
524
521
  * Build the cache key for the given page number and filter.
525
522
  * The default implementation just returns a stringified object
@@ -535,6 +532,13 @@ class PagedTreeStore {
535
532
  }
536
533
  return JSON.stringify({ pageNumber, ...filter });
537
534
  }
535
+ /**
536
+ * Get the specified page of nodes, either from cache or from the server.
537
+ * When the page is retrieved from the server, it is stored in cache.
538
+ * @param filter The filter to apply.
539
+ * @param pageNumber The page number to get.
540
+ * @returns Observable of the page of nodes.
541
+ */
538
542
  getPageFromCacheOrServer(filter, pageNumber) {
539
543
  const key = this.buildCacheKey(pageNumber, filter);
540
544
  const pageInCache = this._cache.get(key);
@@ -542,11 +546,19 @@ class PagedTreeStore {
542
546
  return of(pageInCache);
543
547
  }
544
548
  else {
545
- return this._service.getNodes(filter, pageNumber, this._pageSize).pipe(tap((page) => {
549
+ return this._service
550
+ .getNodes(filter, pageNumber, this._pageSize, this._hasMockRoot)
551
+ .pipe(tap((page) => {
546
552
  this._cache.put(key, page, 0);
547
553
  }));
548
554
  }
549
555
  }
556
+ /**
557
+ * Create paged tree nodes from a page of tree nodes, by providing further
558
+ * metadata like page number, page count and total items.
559
+ * @param page The page to create nodes from.
560
+ * @returns Paged nodes.
561
+ */
550
562
  createPageNodes(page) {
551
563
  return page.items.map((n) => {
552
564
  return {
@@ -564,94 +576,44 @@ class PagedTreeStore {
564
576
  * Sets the filter for this store. Whenever the filter is set,
565
577
  * the store is reset.
566
578
  * @param filter The filter.
567
- * @param radixLabel The label of the radix node, if this needs to be set.
568
579
  * @returns true if tree was changed, false otherwise.
569
580
  */
570
- setFilter(filter, radixLabel) {
581
+ setFilter(filter) {
571
582
  if (this._filter$.value === filter) {
572
583
  return Promise.resolve(false);
573
584
  }
574
585
  this._filter$.next(filter);
575
586
  this._dirty = true;
576
- return this.reset(this._radix?.label || radixLabel || this._radixLabel);
587
+ return this.reset();
577
588
  }
578
589
  /**
579
590
  * Reset the store, loading the root nodes and their children.
580
- * @param label The label of the radix node.
581
591
  * @returns true if tree was changed, false otherwise.
582
592
  */
583
- reset(label) {
593
+ reset() {
584
594
  if (!this._dirty) {
585
595
  return Promise.resolve(false);
586
596
  }
587
597
  this._cache.clear();
588
598
  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
599
  return new Promise((resolve, reject) => {
601
600
  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
- }))
601
+ .getNodes({
602
+ ...filter,
603
+ parentId: undefined,
604
+ }, 1, this._pageSize, this._hasMockRoot)
623
605
  .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;
606
+ next: (page) => {
607
+ this._nodes$.next(this.createPageNodes(page));
608
+ // get the children of each node thus calculating their hasChildren property
609
+ const childrenObservables = this._nodes$.value.map((node) => this.getPageFromCacheOrServer({ ...filter, parentId: node.id }, 1));
610
+ forkJoin(childrenObservables).subscribe((childrenPages) => {
611
+ childrenPages.forEach((page, i) => {
612
+ this._nodes$.value[i].hasChildren = page.total > 0;
651
613
  });
652
- this._nodes$.next([this._radix, ...this._roots]);
653
- resolve(true);
654
- }
614
+ });
615
+ this._dirty = false;
616
+ resolve(true);
655
617
  },
656
618
  error: (error) => {
657
619
  reject(error);
@@ -854,7 +816,12 @@ class PagedTreeStore {
854
816
  * Collapse all the nodes in the store.
855
817
  */
856
818
  collapseAll() {
857
- this._nodes$.next([this._radix, ...this._roots]);
819
+ if (this.isEmpty()) {
820
+ return;
821
+ }
822
+ // collapse the first node
823
+ const root = this._nodes$.value[0];
824
+ this.collapse(root.id);
858
825
  }
859
826
  /**
860
827
  * Clear the cache.
@@ -875,8 +842,8 @@ class PagedTreeStore {
875
842
  }
876
843
 
877
844
  class PagedDataBrowsersModule {
878
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: PagedDataBrowsersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
879
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0", ngImport: i0, type: PagedDataBrowsersModule, declarations: [CompactPagerComponent,
845
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
846
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, declarations: [CompactPagerComponent,
880
847
  RangeViewComponent,
881
848
  BrowserTreeNodeComponent], imports: [CommonModule,
882
849
  ReactiveFormsModule,
@@ -896,7 +863,7 @@ class PagedDataBrowsersModule {
896
863
  NgToolsModule], exports: [CompactPagerComponent,
897
864
  RangeViewComponent,
898
865
  BrowserTreeNodeComponent] }); }
899
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: PagedDataBrowsersModule, imports: [CommonModule,
866
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, imports: [CommonModule,
900
867
  ReactiveFormsModule,
901
868
  // material
902
869
  MatBadgeModule,
@@ -913,7 +880,7 @@ class PagedDataBrowsersModule {
913
880
  // myrmidon
914
881
  NgToolsModule] }); }
915
882
  }
916
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: PagedDataBrowsersModule, decorators: [{
883
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.3", ngImport: i0, type: PagedDataBrowsersModule, decorators: [{
917
884
  type: NgModule,
918
885
  args: [{
919
886
  declarations: [