@truenas/ui-components 0.3.24 → 0.3.26
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.
|
@@ -13,7 +13,7 @@ import { mdiCheckCircle, mdiAlertCircle, mdiAlert, mdiInformation, mdiOpenInNew,
|
|
|
13
13
|
import * as i1$1 from '@angular/platform-browser';
|
|
14
14
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
15
15
|
import { HttpClient } from '@angular/common/http';
|
|
16
|
-
import { firstValueFrom, BehaviorSubject,
|
|
16
|
+
import { firstValueFrom, BehaviorSubject, merge, animationFrameScheduler, asapScheduler, Subject, take } from 'rxjs';
|
|
17
17
|
import { RouterLink } from '@angular/router';
|
|
18
18
|
import { filesize } from 'filesize';
|
|
19
19
|
import { CdkMenu, CdkMenuItem, CdkMenuTrigger } from '@angular/cdk/menu';
|
|
@@ -25,7 +25,7 @@ import { SelectionModel, DataSource } from '@angular/cdk/collections';
|
|
|
25
25
|
import * as i2 from '@angular/cdk/tree';
|
|
26
26
|
import { CdkTree, CdkTreeModule, CdkTreeNode, CDK_TREE_NODE_OUTLET_NODE, CdkNestedTreeNode, CdkTreeNodeOutlet, FlatTreeControl, NestedTreeControl, CdkTreeNodeOutletContext } from '@angular/cdk/tree';
|
|
27
27
|
export { FlatTreeControl } from '@angular/cdk/tree';
|
|
28
|
-
import { map, filter, debounceTime, distinctUntilChanged,
|
|
28
|
+
import { map, filter, debounceTime, distinctUntilChanged, auditTime } from 'rxjs/operators';
|
|
29
29
|
import { CdkVirtualScrollViewport, CdkVirtualScrollableWindow, CdkFixedSizeVirtualScroll, CdkVirtualForOf, ScrollingModule } from '@angular/cdk/scrolling';
|
|
30
30
|
import { DialogRef, DIALOG_DATA, Dialog } from '@angular/cdk/dialog';
|
|
31
31
|
|
|
@@ -11694,7 +11694,6 @@ class TnTreeFlatDataSource extends DataSource {
|
|
|
11694
11694
|
_filteredData = new BehaviorSubject([]);
|
|
11695
11695
|
filterValue = '';
|
|
11696
11696
|
filterChanged$ = new BehaviorSubject('');
|
|
11697
|
-
disconnect$ = new Subject();
|
|
11698
11697
|
constructor(_treeControl, _treeFlattener) {
|
|
11699
11698
|
super();
|
|
11700
11699
|
this._treeControl = _treeControl;
|
|
@@ -11709,9 +11708,9 @@ class TnTreeFlatDataSource extends DataSource {
|
|
|
11709
11708
|
}
|
|
11710
11709
|
this.flattenData();
|
|
11711
11710
|
}
|
|
11712
|
-
/** The currently filtered (unflattened) data; equals `data`
|
|
11711
|
+
/** The currently filtered (unflattened) data; equals `data` while no filter query is active. */
|
|
11713
11712
|
get filteredData() {
|
|
11714
|
-
return this._filteredData.value;
|
|
11713
|
+
return this.filterValue ? this._filteredData.value : this.data;
|
|
11715
11714
|
}
|
|
11716
11715
|
connect() {
|
|
11717
11716
|
return merge(this._treeControl.expansionModel.changed, this._flattenedData).pipe(map(() => {
|
|
@@ -11719,10 +11718,7 @@ class TnTreeFlatDataSource extends DataSource {
|
|
|
11719
11718
|
return this._expandedData.value;
|
|
11720
11719
|
}));
|
|
11721
11720
|
}
|
|
11722
|
-
disconnect() {
|
|
11723
|
-
this.disconnect$.next();
|
|
11724
|
-
this.disconnect$.complete();
|
|
11725
|
-
}
|
|
11721
|
+
disconnect() { }
|
|
11726
11722
|
/** Filters the tree using `filterPredicate` (debounced). No-op until `filterPredicate` is set. */
|
|
11727
11723
|
filter(query) {
|
|
11728
11724
|
this.filterChanged$.next(query);
|
|
@@ -11731,8 +11727,15 @@ class TnTreeFlatDataSource extends DataSource {
|
|
|
11731
11727
|
this._flattenedData.next(this._treeFlattener.flattenNodes(this.filterValue ? this._filteredData.value : this.data));
|
|
11732
11728
|
this._treeControl.dataNodes = this._flattenedData.value;
|
|
11733
11729
|
}
|
|
11730
|
+
/**
|
|
11731
|
+
* Deliberately NOT torn down in `disconnect()`: CdkTree disconnects the
|
|
11732
|
+
* datasource when the tree is destroyed, but the same instance may be
|
|
11733
|
+
* reconnected (e.g. a tree behind an `@if`), and filtering must survive that.
|
|
11734
|
+
* The pipeline only references the datasource's own subjects, so it holds no
|
|
11735
|
+
* external resources and is collected with the instance.
|
|
11736
|
+
*/
|
|
11734
11737
|
detectFilterChanges() {
|
|
11735
|
-
this.filterChanged$.pipe(filter(() => !!this.filterPredicate), debounceTime(200), distinctUntilChanged()
|
|
11738
|
+
this.filterChanged$.pipe(filter(() => !!this.filterPredicate), debounceTime(200), distinctUntilChanged()).subscribe((changedValue) => {
|
|
11736
11739
|
if (this.filterValue === changedValue || !this.filterPredicate) {
|
|
11737
11740
|
return;
|
|
11738
11741
|
}
|
|
@@ -11767,7 +11770,7 @@ class TnTreeComponent extends CdkTree {
|
|
|
11767
11770
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11768
11771
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.0", type: TnTreeComponent, isStandalone: true, selector: "tn-tree", host: { attributes: { "role": "tree" }, classAttribute: "tn-tree" }, providers: [
|
|
11769
11772
|
{ provide: CdkTree, useExisting: TnTreeComponent }
|
|
11770
|
-
], exportAs: ["tnTree"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<ng-container cdkTreeNodeOutlet />", styles: ["
|
|
11773
|
+
], exportAs: ["tnTree"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<ng-container cdkTreeNodeOutlet />", styles: [".tn-tree{display:block;width:100%;background-color:var(--tn-bg1);border:1px solid var(--tn-lines);border-radius:6px;overflow:hidden}tn-tree .tn-tree-node-wrapper>.tn-tree-node,tn-tree .tn-nested-tree-node-wrapper>.tn-nested-tree-node__content{border-bottom:none;border-top:1px solid var(--tn-lines)}tn-tree>.tn-tree-node-wrapper:first-child>.tn-tree-node,tn-tree>.tn-nested-tree-node-wrapper:first-child>.tn-nested-tree-node__content{border-top:none}\n"], dependencies: [{ kind: "ngmodule", type: CdkTreeModule }, { kind: "directive", type: i2.CdkTreeNodeOutlet, selector: "[cdkTreeNodeOutlet]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
11771
11774
|
}
|
|
11772
11775
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnTreeComponent, decorators: [{
|
|
11773
11776
|
type: Component,
|
|
@@ -11776,7 +11779,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
11776
11779
|
], hostDirectives: [{ directive: TnTestIdDirective, inputs: ['tnTestId: testId'] }], host: {
|
|
11777
11780
|
'class': 'tn-tree',
|
|
11778
11781
|
'role': 'tree'
|
|
11779
|
-
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container cdkTreeNodeOutlet />", styles: ["
|
|
11782
|
+
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container cdkTreeNodeOutlet />", styles: [".tn-tree{display:block;width:100%;background-color:var(--tn-bg1);border:1px solid var(--tn-lines);border-radius:6px;overflow:hidden}tn-tree .tn-tree-node-wrapper>.tn-tree-node,tn-tree .tn-nested-tree-node-wrapper>.tn-nested-tree-node__content{border-bottom:none;border-top:1px solid var(--tn-lines)}tn-tree>.tn-tree-node-wrapper:first-child>.tn-tree-node,tn-tree>.tn-nested-tree-node-wrapper:first-child>.tn-nested-tree-node__content{border-top:none}\n"] }]
|
|
11780
11783
|
}], ctorParameters: () => [] });
|
|
11781
11784
|
|
|
11782
11785
|
class TnTreeNodeComponent extends CdkTreeNode {
|
|
@@ -11818,15 +11821,39 @@ class TnNestedTreeNodeComponent extends CdkNestedTreeNode {
|
|
|
11818
11821
|
* defaults to the previous hardcoded value.
|
|
11819
11822
|
*/
|
|
11820
11823
|
toggleAriaLabel = input('Toggle node', ...(ngDevMode ? [{ debugName: "toggleAriaLabel" }] : []));
|
|
11821
|
-
/**
|
|
11824
|
+
/**
|
|
11825
|
+
* Semantic test-id base applied to the built-in toggle button (`button-`
|
|
11826
|
+
* prefixed). When unset, derives from the node's own `testId` — a row with
|
|
11827
|
+
* `[testId]="['vdev', guid]"` gets `button-toggle-vdev-<guid>` — so every
|
|
11828
|
+
* identified row has an addressable expand control without repeating the id.
|
|
11829
|
+
*/
|
|
11822
11830
|
toggleTestId = input(...(ngDevMode ? [undefined, { debugName: "toggleTestId" }] : []));
|
|
11823
11831
|
/**
|
|
11824
11832
|
* Suppresses the built-in toggle button (and its alignment spacer) so the
|
|
11825
11833
|
* consumer can render a fully custom toggle in the projected content.
|
|
11834
|
+
*
|
|
11835
|
+
* The built-in button is the only keyboard/AT-operable expand control this
|
|
11836
|
+
* component guarantees — when hiding it, the projected replacement must be
|
|
11837
|
+
* an accessible control (a real button with an aria-label, not a bare icon).
|
|
11826
11838
|
*/
|
|
11827
11839
|
hideToggle = input(false, { ...(ngDevMode ? { debugName: "hideToggle" } : {}), transform: booleanAttribute });
|
|
11828
11840
|
changeDetectorRef = inject(ChangeDetectorRef);
|
|
11841
|
+
hostTestId = inject(TnTestIdDirective, { self: true });
|
|
11829
11842
|
destroyed = false;
|
|
11843
|
+
/** Explicit `toggleTestId` wins; otherwise `toggle`-scoped from the node's `testId`. */
|
|
11844
|
+
resolvedToggleTestId = computed(() => {
|
|
11845
|
+
const explicit = this.toggleTestId();
|
|
11846
|
+
if (explicit !== undefined && explicit !== null) {
|
|
11847
|
+
return explicit;
|
|
11848
|
+
}
|
|
11849
|
+
const nodeTestId = this.hostTestId.testId();
|
|
11850
|
+
if (nodeTestId === undefined || nodeTestId === null || nodeTestId === '') {
|
|
11851
|
+
return undefined;
|
|
11852
|
+
}
|
|
11853
|
+
// Role leads (button-toggle-<node id>), mirroring tn-dialog-shell's
|
|
11854
|
+
// button-close-<base> convention for fixed chrome.
|
|
11855
|
+
return scopeTestId('toggle', ...(Array.isArray(nodeTestId) ? nodeTestId : [nodeTestId]));
|
|
11856
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedToggleTestId" }] : []));
|
|
11830
11857
|
constructor() {
|
|
11831
11858
|
super(inject((ElementRef)), inject(CdkTree, { optional: true }), inject(CDK_TREE_NODE_OUTLET_NODE, { optional: true }), inject(ChangeDetectorRef, { optional: true }));
|
|
11832
11859
|
inject(DestroyRef).onDestroy(() => this.destroyed = true);
|
|
@@ -11911,13 +11938,16 @@ class TnNestedTreeNodeComponent extends CdkNestedTreeNode {
|
|
|
11911
11938
|
else {
|
|
11912
11939
|
this._tree.toggle(this.data);
|
|
11913
11940
|
}
|
|
11941
|
+
// Mirrors CdkTreeNodeToggle's focus handling. `_keyManager` is a CDK
|
|
11942
|
+
// underscore member, so a CDK upgrade may rename it — the optional call
|
|
11943
|
+
// degrades to "focus not moved" rather than breaking the toggle.
|
|
11914
11944
|
this._tree._keyManager?.focusItem(this);
|
|
11915
11945
|
}
|
|
11916
11946
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnNestedTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11917
11947
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnNestedTreeNodeComponent, isStandalone: true, selector: "tn-nested-tree-node", inputs: { toggleAriaLabel: { classPropertyName: "toggleAriaLabel", publicName: "toggleAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, toggleTestId: { classPropertyName: "toggleTestId", publicName: "toggleTestId", isSignal: true, isRequired: false, transformFunction: null }, hideToggle: { classPropertyName: "hideToggle", publicName: "hideToggle", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "treeitem" }, properties: { "attr.aria-level": "level + 1", "attr.aria-expanded": "isExpandable ? isExpanded : null" }, classAttribute: "tn-nested-tree-node-wrapper" }, providers: [
|
|
11918
11948
|
{ provide: CdkNestedTreeNode, useExisting: TnNestedTreeNodeComponent },
|
|
11919
11949
|
{ provide: CdkTreeNode, useExisting: TnNestedTreeNodeComponent }
|
|
11920
|
-
], exportAs: ["tnNestedTreeNode"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<div class=\"tn-nested-tree-node__content\">\n @if (!hideToggle()) {\n <!-- Toggle button for expandable nodes (provided by component).\n Alt+click / Alt+Enter toggles the node together with all descendants.\n tabindex=\"-1\" keeps it out of the tab order \u2014 the tree's key manager\n drives focus at the treeitem level, matching cdkTreeNodeToggle. -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n type=\"button\"\n tabindex=\"-1\"\n tnTestIdType=\"button\"\n [tnTestId]=\"
|
|
11950
|
+
], exportAs: ["tnNestedTreeNode"], usesInheritance: true, hostDirectives: [{ directive: TnTestIdDirective, inputs: ["tnTestId", "testId"] }], ngImport: i0, template: "<div class=\"tn-nested-tree-node__content\">\n @if (!hideToggle()) {\n <!-- Toggle button for expandable nodes (provided by component).\n Alt+click / Alt+Enter toggles the node together with all descendants.\n tabindex=\"-1\" keeps it out of the tab order \u2014 the tree's key manager\n drives focus at the treeitem level, matching cdkTreeNodeToggle. -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n type=\"button\"\n tabindex=\"-1\"\n tnTestIdType=\"button\"\n [tnTestId]=\"resolvedToggleTestId()\"\n [class.tn-nested-tree-node__toggle--expanded]=\"isExpanded\"\n [attr.aria-label]=\"toggleAriaLabel()\"\n (click)=\"toggleNode($event); $event.stopPropagation()\"\n (keydown.enter)=\"toggleNode($event); $event.preventDefault()\"\n (keydown.space)=\"toggleNode($event); $event.preventDefault()\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </button>\n }\n\n <!-- Spacer for non-expandable nodes to maintain alignment -->\n @if (!isExpandable) {\n <div class=\"tn-nested-tree-node__spacer\"></div>\n }\n }\n\n <!-- Consumer content -->\n <ng-content />\n</div>\n\n<!-- Children container -->\n@if (isExpandable) {\n <div class=\"tn-nested-tree-node-container\" role=\"group\" [class.tn-tree-invisible]=\"!isExpanded\">\n <ng-content select=\"[slot=children]\" />\n </div>\n}\n", styles: [":host{display:block;width:100%}.tn-nested-tree-node{display:block;width:100%;font-family:var(--tn-font-family-body);font-size:1rem;line-height:1.4;color:var(--tn-fg1)}.tn-nested-tree-node--expandable .tn-nested-tree-node__content{cursor:pointer}.tn-nested-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 8px;border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-nested-tree-node__content:hover{background-color:var(--tn-alt-bg2)}.tn-nested-tree-node__content:focus-within{background-color:var(--tn-alt-bg2);outline:2px solid var(--tn-primary);outline-offset:-2px}.tn-tree-invisible{display:none}.tn-nested-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;margin-right:8px;padding:0;border:none;background:transparent;border-radius:4px;cursor:pointer;color:var(--tn-fg2);transition:background-color .2s ease,color .2s ease}.tn-nested-tree-node__toggle:hover{background-color:var(--tn-bg3);color:var(--tn-fg1)}.tn-nested-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:2px}.tn-nested-tree-node__toggle svg{transition:transform .2s ease}.tn-nested-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-nested-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-nested-tree-node__text{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--tn-fg1)}div.tn-nested-tree-node-container{padding-left:40px}@media(prefers-reduced-motion:reduce){.tn-nested-tree-node__toggle svg,.tn-nested-tree-node__content,.tn-nested-tree-node__children{transition:none}}@media(prefers-contrast:high){.tn-nested-tree-node__content{border:1px solid transparent}.tn-nested-tree-node__content:hover,.tn-nested-tree-node__content:focus-within{border-color:var(--tn-fg1)}.tn-nested-tree-node__toggle{border:1px solid var(--tn-fg2)}.tn-nested-tree-node__toggle:hover,.tn-nested-tree-node__toggle:focus{border-color:var(--tn-fg1)}}\n"], dependencies: [{ kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11921
11951
|
}
|
|
11922
11952
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnNestedTreeNodeComponent, decorators: [{
|
|
11923
11953
|
type: Component,
|
|
@@ -11929,7 +11959,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
11929
11959
|
'[attr.aria-level]': 'level + 1',
|
|
11930
11960
|
'[attr.aria-expanded]': 'isExpandable ? isExpanded : null',
|
|
11931
11961
|
'role': 'treeitem'
|
|
11932
|
-
}, encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"tn-nested-tree-node__content\">\n @if (!hideToggle()) {\n <!-- Toggle button for expandable nodes (provided by component).\n Alt+click / Alt+Enter toggles the node together with all descendants.\n tabindex=\"-1\" keeps it out of the tab order \u2014 the tree's key manager\n drives focus at the treeitem level, matching cdkTreeNodeToggle. -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n type=\"button\"\n tabindex=\"-1\"\n tnTestIdType=\"button\"\n [tnTestId]=\"
|
|
11962
|
+
}, encapsulation: ViewEncapsulation.Emulated, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"tn-nested-tree-node__content\">\n @if (!hideToggle()) {\n <!-- Toggle button for expandable nodes (provided by component).\n Alt+click / Alt+Enter toggles the node together with all descendants.\n tabindex=\"-1\" keeps it out of the tab order \u2014 the tree's key manager\n drives focus at the treeitem level, matching cdkTreeNodeToggle. -->\n @if (isExpandable) {\n <button\n class=\"tn-nested-tree-node__toggle\"\n type=\"button\"\n tabindex=\"-1\"\n tnTestIdType=\"button\"\n [tnTestId]=\"resolvedToggleTestId()\"\n [class.tn-nested-tree-node__toggle--expanded]=\"isExpanded\"\n [attr.aria-label]=\"toggleAriaLabel()\"\n (click)=\"toggleNode($event); $event.stopPropagation()\"\n (keydown.enter)=\"toggleNode($event); $event.preventDefault()\"\n (keydown.space)=\"toggleNode($event); $event.preventDefault()\">\n <tn-icon\n library=\"mdi\"\n size=\"sm\"\n style=\"transition: transform 0.2s ease;\"\n [name]=\"isExpanded ? 'chevron-down' : 'chevron-right'\" />\n </button>\n }\n\n <!-- Spacer for non-expandable nodes to maintain alignment -->\n @if (!isExpandable) {\n <div class=\"tn-nested-tree-node__spacer\"></div>\n }\n }\n\n <!-- Consumer content -->\n <ng-content />\n</div>\n\n<!-- Children container -->\n@if (isExpandable) {\n <div class=\"tn-nested-tree-node-container\" role=\"group\" [class.tn-tree-invisible]=\"!isExpanded\">\n <ng-content select=\"[slot=children]\" />\n </div>\n}\n", styles: [":host{display:block;width:100%}.tn-nested-tree-node{display:block;width:100%;font-family:var(--tn-font-family-body);font-size:1rem;line-height:1.4;color:var(--tn-fg1)}.tn-nested-tree-node--expandable .tn-nested-tree-node__content{cursor:pointer}.tn-nested-tree-node__content{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 8px;border-bottom:1px solid var(--tn-lines);transition:background-color .2s ease}.tn-nested-tree-node__content:hover{background-color:var(--tn-alt-bg2)}.tn-nested-tree-node__content:focus-within{background-color:var(--tn-alt-bg2);outline:2px solid var(--tn-primary);outline-offset:-2px}.tn-tree-invisible{display:none}.tn-nested-tree-node__toggle{display:flex;align-items:center;justify-content:center;width:24px;height:24px;margin-right:8px;padding:0;border:none;background:transparent;border-radius:4px;cursor:pointer;color:var(--tn-fg2);transition:background-color .2s ease,color .2s ease}.tn-nested-tree-node__toggle:hover{background-color:var(--tn-bg3);color:var(--tn-fg1)}.tn-nested-tree-node__toggle:focus{outline:2px solid var(--tn-primary);outline-offset:2px}.tn-nested-tree-node__toggle svg{transition:transform .2s ease}.tn-nested-tree-node__toggle--expanded svg{transform:rotate(90deg)}.tn-nested-tree-node__spacer{width:24px;height:24px;flex-shrink:0}.tn-nested-tree-node__text{flex:1;display:flex;align-items:center;gap:8px;min-width:0;color:var(--tn-fg1)}div.tn-nested-tree-node-container{padding-left:40px}@media(prefers-reduced-motion:reduce){.tn-nested-tree-node__toggle svg,.tn-nested-tree-node__content,.tn-nested-tree-node__children{transition:none}}@media(prefers-contrast:high){.tn-nested-tree-node__content{border:1px solid transparent}.tn-nested-tree-node__content:hover,.tn-nested-tree-node__content:focus-within{border-color:var(--tn-fg1)}.tn-nested-tree-node__toggle{border:1px solid var(--tn-fg2)}.tn-nested-tree-node__toggle:hover,.tn-nested-tree-node__toggle:focus{border-color:var(--tn-fg1)}}\n"] }]
|
|
11933
11963
|
}], ctorParameters: () => [], propDecorators: { toggleAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleAriaLabel", required: false }] }], toggleTestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleTestId", required: false }] }], hideToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideToggle", required: false }] }] } });
|
|
11934
11964
|
|
|
11935
11965
|
class TnTreeNodeOutletDirective {
|
|
@@ -11959,13 +11989,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
11959
11989
|
class TnNestedTreeDataSource extends DataSource {
|
|
11960
11990
|
/** When set, `filter(query)` re-renders the tree from `filterPredicate(data, query)`. */
|
|
11961
11991
|
filterPredicate;
|
|
11962
|
-
/**
|
|
11992
|
+
/**
|
|
11993
|
+
* When set, incoming `data` is sorted recursively before rendering.
|
|
11994
|
+
*
|
|
11995
|
+
* Note: sorting REWRITES `children` arrays on the caller's node objects
|
|
11996
|
+
* (a new sorted array is assigned per node). Node object identity is
|
|
11997
|
+
* preserved — deliberately, since tree controls track expansion state by
|
|
11998
|
+
* node identity — but don't pass an object graph that is shared with code
|
|
11999
|
+
* relying on the original child order.
|
|
12000
|
+
*/
|
|
11963
12001
|
sortComparer;
|
|
11964
12002
|
filterValue = '';
|
|
11965
12003
|
filterChanged$ = new BehaviorSubject('');
|
|
11966
12004
|
_data = new BehaviorSubject([]);
|
|
11967
12005
|
_filteredData = new BehaviorSubject([]);
|
|
11968
|
-
disconnect$ = new Subject();
|
|
11969
12006
|
get data() {
|
|
11970
12007
|
return this._data.value;
|
|
11971
12008
|
}
|
|
@@ -11982,16 +12019,20 @@ class TnNestedTreeDataSource extends DataSource {
|
|
|
11982
12019
|
connect(collectionViewer) {
|
|
11983
12020
|
return merge(collectionViewer.viewChange, this._data, this._filteredData).pipe(map(() => (this.filterValue ? this._filteredData.value : this.data)));
|
|
11984
12021
|
}
|
|
11985
|
-
disconnect() {
|
|
11986
|
-
this.disconnect$.next();
|
|
11987
|
-
this.disconnect$.complete();
|
|
11988
|
-
}
|
|
12022
|
+
disconnect() { }
|
|
11989
12023
|
/** Filters the tree using `filterPredicate` (debounced). No-op until `filterPredicate` is set. */
|
|
11990
12024
|
filter(query) {
|
|
11991
12025
|
this.filterChanged$.next(query);
|
|
11992
12026
|
}
|
|
12027
|
+
/**
|
|
12028
|
+
* Deliberately NOT torn down in `disconnect()`: CdkTree disconnects the
|
|
12029
|
+
* datasource when the tree is destroyed, but the same instance may be
|
|
12030
|
+
* reconnected (e.g. a tree behind an `@if`), and filtering must survive that.
|
|
12031
|
+
* The pipeline only references the datasource's own subjects, so it holds no
|
|
12032
|
+
* external resources and is collected with the instance.
|
|
12033
|
+
*/
|
|
11993
12034
|
detectFilterChanges() {
|
|
11994
|
-
this.filterChanged$.pipe(filter(() => !!this.filterPredicate), debounceTime(200), distinctUntilChanged()
|
|
12035
|
+
this.filterChanged$.pipe(filter(() => !!this.filterPredicate), debounceTime(200), distinctUntilChanged()).subscribe((changedValue) => {
|
|
11995
12036
|
if (this.filterValue === changedValue || !this.filterPredicate) {
|
|
11996
12037
|
return;
|
|
11997
12038
|
}
|
|
@@ -12128,6 +12169,10 @@ class TnTreeNodeHarness extends ComponentHarness {
|
|
|
12128
12169
|
/**
|
|
12129
12170
|
* Toggles the node together with all of its descendants (Alt+click on the
|
|
12130
12171
|
* built-in nested-node toggle).
|
|
12172
|
+
*
|
|
12173
|
+
* Only nested nodes honor the Alt modifier — on a flat node the row's
|
|
12174
|
+
* `cdkTreeNodeToggle` is not Alt-aware, so this degrades to a plain
|
|
12175
|
+
* single-level toggle.
|
|
12131
12176
|
*/
|
|
12132
12177
|
async toggleWithDescendants() {
|
|
12133
12178
|
await (await this.getToggle()).click({ alt: true });
|
|
@@ -12156,6 +12201,7 @@ class TnTreeNodeHarness extends ComponentHarness {
|
|
|
12156
12201
|
return toggle;
|
|
12157
12202
|
}
|
|
12158
12203
|
}
|
|
12204
|
+
|
|
12159
12205
|
/**
|
|
12160
12206
|
* Harness for interacting with `tn-tree` in tests.
|
|
12161
12207
|
*
|