@mozaic-ds/angular 2.0.51 → 2.0.53
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.
|
@@ -17330,6 +17330,12 @@ class TreeStateService {
|
|
|
17330
17330
|
loadingIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadingIds" }] : /* istanbul ignore next */ []));
|
|
17331
17331
|
internalNodes = signal([], ...(ngDevMode ? [{ debugName: "internalNodes" }] : /* istanbul ignore next */ []));
|
|
17332
17332
|
loadChildrenFn = signal(null, ...(ngDevMode ? [{ debugName: "loadChildrenFn" }] : /* istanbul ignore next */ []));
|
|
17333
|
+
/**
|
|
17334
|
+
* Parent IDs for which children have been patched in (via lazy-load).
|
|
17335
|
+
* Grows monotonically — consumers diff against their own "processed" set.
|
|
17336
|
+
* Not emitted for statically-populated children present at init.
|
|
17337
|
+
*/
|
|
17338
|
+
loadedParentIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "loadedParentIds" }] : /* istanbul ignore next */ []));
|
|
17333
17339
|
flatVisibleNodes = computed(() => {
|
|
17334
17340
|
const result = [];
|
|
17335
17341
|
this._flatten(this.internalNodes(), result, 0, null);
|
|
@@ -17393,6 +17399,11 @@ class TreeStateService {
|
|
|
17393
17399
|
}
|
|
17394
17400
|
patchChildren(nodeId, children) {
|
|
17395
17401
|
this.internalNodes.update((nodes) => this._patchNode(nodes, nodeId, children));
|
|
17402
|
+
this.loadedParentIds.update((set) => {
|
|
17403
|
+
const next = new Set(set);
|
|
17404
|
+
next.add(nodeId);
|
|
17405
|
+
return next;
|
|
17406
|
+
});
|
|
17396
17407
|
}
|
|
17397
17408
|
_patchNode(nodes, targetId, children) {
|
|
17398
17409
|
return nodes.map((n) => {
|
|
@@ -17449,22 +17460,30 @@ class TreeSelectionService {
|
|
|
17449
17460
|
isSelected(id) {
|
|
17450
17461
|
return this.selectedIds().has(id);
|
|
17451
17462
|
}
|
|
17452
|
-
|
|
17453
|
-
|
|
17454
|
-
|
|
17455
|
-
|
|
17463
|
+
/**
|
|
17464
|
+
* A node is considered disabled only by its own `disabled` flag — a disabled
|
|
17465
|
+
* parent does not propagate the state to its descendants. Children of a
|
|
17466
|
+
* disabled parent remain individually interactable; the parent itself just
|
|
17467
|
+
* has its bulk "toggle all" action locked. This mirrors the combobox's
|
|
17468
|
+
* disabled-section semantics.
|
|
17469
|
+
*/
|
|
17470
|
+
isDisabled(node) {
|
|
17471
|
+
return !!node.disabled;
|
|
17456
17472
|
}
|
|
17457
17473
|
/**
|
|
17458
|
-
* A node is indeterminate when it has loaded children AND
|
|
17459
|
-
*
|
|
17474
|
+
* A node is indeterminate when it has loaded children AND at least one
|
|
17475
|
+
* descendant is selected without the node being fully checked. Disabled
|
|
17476
|
+
* descendants count so a pre-selected disabled leaf still surfaces on the
|
|
17477
|
+
* parent (useful when the parent itself is also disabled).
|
|
17460
17478
|
*/
|
|
17461
17479
|
isIndeterminate(node) {
|
|
17480
|
+
if (this.isCheckedComputed(node))
|
|
17481
|
+
return false;
|
|
17462
17482
|
const resolved = this._resolveNode(node);
|
|
17463
|
-
const
|
|
17464
|
-
if (
|
|
17483
|
+
const allLeaves = this._collectLeafIds(resolved, { includeDisabled: true });
|
|
17484
|
+
if (allLeaves.length === 0)
|
|
17465
17485
|
return false;
|
|
17466
|
-
|
|
17467
|
-
return selectedCount > 0 && selectedCount < leaves.length;
|
|
17486
|
+
return allLeaves.some((id) => this.selectedIds().has(id));
|
|
17468
17487
|
}
|
|
17469
17488
|
/**
|
|
17470
17489
|
* A node is checked when:
|
|
@@ -17573,18 +17592,21 @@ class TreeSelectionService {
|
|
|
17573
17592
|
/**
|
|
17574
17593
|
* Collect leaf-level IDs (terminal nodes that have no loaded children).
|
|
17575
17594
|
* Returns empty array if the node itself is a leaf.
|
|
17595
|
+
* When `includeDisabled` is true, disabled descendants are also collected —
|
|
17596
|
+
* used by the indeterminate computation so pre-selected disabled leaves
|
|
17597
|
+
* count toward a parent's state.
|
|
17576
17598
|
*/
|
|
17577
|
-
_collectLeafIds(node) {
|
|
17599
|
+
_collectLeafIds(node, opts = {}) {
|
|
17578
17600
|
const resolved = this._resolveNode(node);
|
|
17579
17601
|
if (!resolved.children || resolved.children.length === 0) {
|
|
17580
17602
|
return [];
|
|
17581
17603
|
}
|
|
17582
17604
|
const ids = [];
|
|
17583
17605
|
for (const child of resolved.children) {
|
|
17584
|
-
if (child.disabled)
|
|
17606
|
+
if (!opts.includeDisabled && child.disabled)
|
|
17585
17607
|
continue;
|
|
17586
17608
|
const resolvedChild = this._resolveNode(child);
|
|
17587
|
-
const childLeaves = this._collectLeafIds(resolvedChild);
|
|
17609
|
+
const childLeaves = this._collectLeafIds(resolvedChild, opts);
|
|
17588
17610
|
if (childLeaves.length === 0) {
|
|
17589
17611
|
// This child is a leaf
|
|
17590
17612
|
ids.push(resolvedChild.id);
|
|
@@ -17662,7 +17684,7 @@ class TreeKeyboardService {
|
|
|
17662
17684
|
if (!current)
|
|
17663
17685
|
break;
|
|
17664
17686
|
const isExpanded = this.state.expandedIds().has(current.node.id);
|
|
17665
|
-
if (this._hasChildren(current.node) && !isExpanded
|
|
17687
|
+
if (this._hasChildren(current.node) && !isExpanded) {
|
|
17666
17688
|
this.state.expandAndLoad(current.node).subscribe();
|
|
17667
17689
|
}
|
|
17668
17690
|
else if (isExpanded) {
|
|
@@ -17690,7 +17712,7 @@ class TreeKeyboardService {
|
|
|
17690
17712
|
event.preventDefault();
|
|
17691
17713
|
if (!current)
|
|
17692
17714
|
break;
|
|
17693
|
-
if (this._hasChildren(current.node)
|
|
17715
|
+
if (this._hasChildren(current.node)) {
|
|
17694
17716
|
this.state.expandAndLoad(current.node).subscribe();
|
|
17695
17717
|
}
|
|
17696
17718
|
break;
|
|
@@ -17768,7 +17790,7 @@ class MozTreeNodeComponent {
|
|
|
17768
17790
|
selectionChange = output();
|
|
17769
17791
|
isExpanded = computed(() => this.stateService.expandedIds().has(this.node().id), ...(ngDevMode ? [{ debugName: "isExpanded" }] : /* istanbul ignore next */ []));
|
|
17770
17792
|
isLoading = computed(() => this.stateService.loadingIds().has(this.node().id), ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
|
|
17771
|
-
isDisabled = computed(() => this.selectionService.isDisabled(this.node()
|
|
17793
|
+
isDisabled = computed(() => this.selectionService.isDisabled(this.node()), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
17772
17794
|
isSelected = computed(() => this.selectionService.isCheckedComputed(this.node()), ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
|
|
17773
17795
|
isIndeterminate = computed(() => this.selectionService.isIndeterminate(this.node()), ...(ngDevMode ? [{ debugName: "isIndeterminate" }] : /* istanbul ignore next */ []));
|
|
17774
17796
|
isFocused = computed(() => this.keyboardService.focusedNodeId() === this.node().id, ...(ngDevMode ? [{ debugName: "isFocused" }] : /* istanbul ignore next */ []));
|
|
@@ -17804,7 +17826,7 @@ class MozTreeNodeComponent {
|
|
|
17804
17826
|
}, ...(ngDevMode ? [{ debugName: "resolvedChildren" }] : /* istanbul ignore next */ []));
|
|
17805
17827
|
onHeaderClick() {
|
|
17806
17828
|
this.keyboardService.focusedNodeId.set(this.node().id);
|
|
17807
|
-
if (this.hasChildren()
|
|
17829
|
+
if (this.hasChildren()) {
|
|
17808
17830
|
this.onToggleExpand();
|
|
17809
17831
|
}
|
|
17810
17832
|
}
|
|
@@ -17844,7 +17866,7 @@ class MozTreeNodeComponent {
|
|
|
17844
17866
|
return null;
|
|
17845
17867
|
}
|
|
17846
17868
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17847
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozTreeNodeComponent, isStandalone: true, selector: "moz-tree-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplates: { classPropertyName: "nodeTemplates", publicName: "nodeTemplates", isSignal: true, isRequired: false, transformFunction: null }, loadChildren: { classPropertyName: "loadChildren", publicName: "loadChildren", isSignal: true, isRequired: false, transformFunction: null }, ancestors: { classPropertyName: "ancestors", publicName: "ancestors", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, noResultText: { classPropertyName: "noResultText", publicName: "noResultText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandChange: "expandChange", selectionChange: "selectionChange" }, host: { styleAttribute: "display: block" }, ngImport: i0, template: "<li\n class=\"tree-node\"\n [class.tree-node--selected]=\"isSelected()\"\n [class.tree-node--disabled]=\"isDisabled()\"\n [class.tree-node--focused]=\"isFocused()\"\n role=\"treeitem\"\n [id]=\"'tree-node-' + node().id\"\n [attr.aria-level]=\"depth() + 1\"\n [attr.aria-expanded]=\"hasChildren() ? isExpanded() : null\"\n [attr.aria-selected]=\"selectionMode() !== 'none' ? isSelected() : null\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n [attr.data-tree-node-id]=\"node().id\"\n [tabindex]=\"isFocused() ? 0 : -1\"\n>\n <div\n class=\"tree-node__header\"\n [class.tree-node__header--expandable]=\"hasChildren()\"\n (click)=\"onHeaderClick()\"\n >\n <div class=\"tree-node__indent\" [style.width.px]=\"indentPx()\"></div>\n\n <div class=\"tree-node__row\">\n <span class=\"tree-node__chevron\" [class.tree-node__chevron--leaf]=\"!hasChildren()\">\n @if (hasChildren()) { @if (isExpanded()) {\n <ChevronDown20 />\n } @else {\n <ChevronRight20 />\n } }\n </span>\n\n <div class=\"tree-node__content\">\n @if (resolvedTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"resolvedTemplate()!\"\n [ngTemplateOutletContext]=\"templateContext()\"\n />\n } @else {\n <span class=\"tree-node__label\">{{ node().id }}</span>\n }\n </div>\n\n @if (selectionMode() === 'checkbox') {\n <moz-checkbox\n class=\"tree-node__selection\"\n [id]=\"'tree-checkbox-' + node().id\"\n [indeterminate]=\"isIndeterminate()\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @else if (selectionMode() === 'radio') {\n <moz-radio\n class=\"tree-node__selection\"\n [id]=\"'tree-radio-' + node().id\"\n [name]=\"radioName\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onRadioChange($event)\"\n />\n }\n </div>\n </div>\n\n @if (isExpanded() && !flat()) {\n <ul class=\"tree-node__children\" role=\"group\">\n @if (isLoading()) {\n <li class=\"tree-node__loading\" role=\"presentation\">\n <moz-loader size=\"s\" [appearance]=\"'accent'\" />\n </li>\n } @else { @if (resolvedChildren().length === 0) {\n <li class=\"tree-node__empty\" role=\"presentation\">\n <span>{{ noResultText() }}</span>\n </li>\n } @for (child of resolvedChildren(); track child.id) {\n <moz-tree-node\n [node]=\"child\"\n [depth]=\"depth() + 1\"\n [selectionMode]=\"selectionMode()\"\n [indentSize]=\"indentSize()\"\n [nodeTemplate]=\"nodeTemplate()\"\n [nodeTemplates]=\"nodeTemplates()\"\n [loadChildren]=\"loadChildren()\"\n [ancestors]=\"ancestorsWithSelf()\"\n (expandChange)=\"expandChange.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\"\n />\n } }\n </ul>\n }\n</li>\n", styles: [".tree-node{list-style:none;margin:0;padding:0;outline:none;display:flex;flex-direction:column;gap:4px}.tree-node--selected>.tree-node__header>.tree-node__row{background:var(--color-background-accent);border-radius:var(--border-radius-m, 8px)}.tree-node--disabled{
|
|
17869
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozTreeNodeComponent, isStandalone: true, selector: "moz-tree-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, indentSize: { classPropertyName: "indentSize", publicName: "indentSize", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, nodeTemplates: { classPropertyName: "nodeTemplates", publicName: "nodeTemplates", isSignal: true, isRequired: false, transformFunction: null }, loadChildren: { classPropertyName: "loadChildren", publicName: "loadChildren", isSignal: true, isRequired: false, transformFunction: null }, ancestors: { classPropertyName: "ancestors", publicName: "ancestors", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, noResultText: { classPropertyName: "noResultText", publicName: "noResultText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandChange: "expandChange", selectionChange: "selectionChange" }, host: { styleAttribute: "display: block" }, ngImport: i0, template: "<li\n class=\"tree-node\"\n [class.tree-node--selected]=\"isSelected()\"\n [class.tree-node--disabled]=\"isDisabled()\"\n [class.tree-node--focused]=\"isFocused()\"\n role=\"treeitem\"\n [id]=\"'tree-node-' + node().id\"\n [attr.aria-level]=\"depth() + 1\"\n [attr.aria-expanded]=\"hasChildren() ? isExpanded() : null\"\n [attr.aria-selected]=\"selectionMode() !== 'none' ? isSelected() : null\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n [attr.data-tree-node-id]=\"node().id\"\n [tabindex]=\"isFocused() ? 0 : -1\"\n>\n <div\n class=\"tree-node__header\"\n [class.tree-node__header--expandable]=\"hasChildren()\"\n (click)=\"onHeaderClick()\"\n >\n <div class=\"tree-node__indent\" [style.width.px]=\"indentPx()\"></div>\n\n <div class=\"tree-node__row\">\n <span class=\"tree-node__chevron\" [class.tree-node__chevron--leaf]=\"!hasChildren()\">\n @if (hasChildren()) { @if (isExpanded()) {\n <ChevronDown20 />\n } @else {\n <ChevronRight20 />\n } }\n </span>\n\n <div class=\"tree-node__content\">\n @if (resolvedTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"resolvedTemplate()!\"\n [ngTemplateOutletContext]=\"templateContext()\"\n />\n } @else {\n <span class=\"tree-node__label\">{{ node().id }}</span>\n }\n </div>\n\n @if (selectionMode() === 'checkbox') {\n <moz-checkbox\n class=\"tree-node__selection\"\n [id]=\"'tree-checkbox-' + node().id\"\n [indeterminate]=\"isIndeterminate()\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @else if (selectionMode() === 'radio') {\n <moz-radio\n class=\"tree-node__selection\"\n [id]=\"'tree-radio-' + node().id\"\n [name]=\"radioName\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onRadioChange($event)\"\n />\n }\n </div>\n </div>\n\n @if (isExpanded() && !flat()) {\n <ul class=\"tree-node__children\" role=\"group\">\n @if (isLoading()) {\n <li class=\"tree-node__loading\" role=\"presentation\">\n <moz-loader size=\"s\" [appearance]=\"'accent'\" />\n </li>\n } @else { @if (resolvedChildren().length === 0) {\n <li class=\"tree-node__empty\" role=\"presentation\">\n <span>{{ noResultText() }}</span>\n </li>\n } @for (child of resolvedChildren(); track child.id) {\n <moz-tree-node\n [node]=\"child\"\n [depth]=\"depth() + 1\"\n [selectionMode]=\"selectionMode()\"\n [indentSize]=\"indentSize()\"\n [nodeTemplate]=\"nodeTemplate()\"\n [nodeTemplates]=\"nodeTemplates()\"\n [loadChildren]=\"loadChildren()\"\n [ancestors]=\"ancestorsWithSelf()\"\n (expandChange)=\"expandChange.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\"\n />\n } }\n </ul>\n }\n</li>\n", styles: [".tree-node{list-style:none;margin:0;padding:0;outline:none;display:flex;flex-direction:column;gap:4px}.tree-node--selected>.tree-node__header>.tree-node__row{background:var(--color-background-accent);border-radius:var(--border-radius-m, 8px)}.tree-node--disabled{color:var(--color-text-disabled)}.tree-node--disabled>.tree-node__header{opacity:.5}.tree-node--focused>.tree-node__header>.tree-node__row,.tree-node:focus-visible>.tree-node__header>.tree-node__row{outline:2px solid var(--color-background-accent-inverse);outline-offset:-2px;border-radius:var(--border-radius-m, 8px)}.tree-node__header{display:flex;align-items:center;width:100%;min-height:57px;-webkit-user-select:none;user-select:none}.tree-node__header--expandable{cursor:pointer}.tree-node__row{display:flex;align-items:center;height:57px;flex:1;min-width:0;border-radius:var(--border-radius-m, 8px);transition:background .15s ease}.tree-node__header:hover>.tree-node__row{background:var(--color-background-secondary)}.tree-node--selected>.tree-node__header:hover>.tree-node__row{background:var(--color-background-accent)}.tree-node__indent{flex-shrink:0}.tree-node__chevron{display:flex;align-items:center;justify-content:center;flex-shrink:0;padding-left:16px;color:var(--color-text-primary)}.tree-node__chevron--leaf{width:0;padding-left:0;overflow:hidden}.tree-node__content{flex:1;min-width:0;display:flex;align-items:center;padding:4px 8px}.tree-node__selection{flex-shrink:0;margin-left:auto;margin-right:8px}.tree-node__label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-node__children{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:4px}.tree-node__loading{display:flex;align-items:center;justify-content:center;padding:8px 16px}.tree-node__empty{display:flex;align-items:center;padding:8px 16px;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}\n"], dependencies: [{ kind: "component", type: MozTreeNodeComponent, selector: "moz-tree-node", inputs: ["node", "depth", "selectionMode", "indentSize", "nodeTemplate", "nodeTemplates", "loadChildren", "ancestors", "flat", "noResultText"], outputs: ["expandChange", "selectionChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MozCheckboxComponent, selector: "moz-checkbox", inputs: ["id", "name", "label", "indeterminate", "isInvalid", "disabled", "indented"] }, { kind: "component", type: MozRadioComponent, selector: "moz-radio", inputs: ["id", "name", "label", "isInvalid", "disabled"] }, { kind: "component", type: MozLoaderComponent, selector: "moz-loader", inputs: ["appearance", "size", "text"] }, { kind: "component", type: ChevronDown20, selector: "ChevronDown20", inputs: ["hostClass"] }, { kind: "component", type: ChevronRight20, selector: "ChevronRight20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
17848
17870
|
}
|
|
17849
17871
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozTreeNodeComponent, decorators: [{
|
|
17850
17872
|
type: Component,
|
|
@@ -17856,7 +17878,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
17856
17878
|
MozLoaderComponent,
|
|
17857
17879
|
ChevronDown20,
|
|
17858
17880
|
ChevronRight20,
|
|
17859
|
-
], template: "<li\n class=\"tree-node\"\n [class.tree-node--selected]=\"isSelected()\"\n [class.tree-node--disabled]=\"isDisabled()\"\n [class.tree-node--focused]=\"isFocused()\"\n role=\"treeitem\"\n [id]=\"'tree-node-' + node().id\"\n [attr.aria-level]=\"depth() + 1\"\n [attr.aria-expanded]=\"hasChildren() ? isExpanded() : null\"\n [attr.aria-selected]=\"selectionMode() !== 'none' ? isSelected() : null\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n [attr.data-tree-node-id]=\"node().id\"\n [tabindex]=\"isFocused() ? 0 : -1\"\n>\n <div\n class=\"tree-node__header\"\n [class.tree-node__header--expandable]=\"hasChildren()\"\n (click)=\"onHeaderClick()\"\n >\n <div class=\"tree-node__indent\" [style.width.px]=\"indentPx()\"></div>\n\n <div class=\"tree-node__row\">\n <span class=\"tree-node__chevron\" [class.tree-node__chevron--leaf]=\"!hasChildren()\">\n @if (hasChildren()) { @if (isExpanded()) {\n <ChevronDown20 />\n } @else {\n <ChevronRight20 />\n } }\n </span>\n\n <div class=\"tree-node__content\">\n @if (resolvedTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"resolvedTemplate()!\"\n [ngTemplateOutletContext]=\"templateContext()\"\n />\n } @else {\n <span class=\"tree-node__label\">{{ node().id }}</span>\n }\n </div>\n\n @if (selectionMode() === 'checkbox') {\n <moz-checkbox\n class=\"tree-node__selection\"\n [id]=\"'tree-checkbox-' + node().id\"\n [indeterminate]=\"isIndeterminate()\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @else if (selectionMode() === 'radio') {\n <moz-radio\n class=\"tree-node__selection\"\n [id]=\"'tree-radio-' + node().id\"\n [name]=\"radioName\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onRadioChange($event)\"\n />\n }\n </div>\n </div>\n\n @if (isExpanded() && !flat()) {\n <ul class=\"tree-node__children\" role=\"group\">\n @if (isLoading()) {\n <li class=\"tree-node__loading\" role=\"presentation\">\n <moz-loader size=\"s\" [appearance]=\"'accent'\" />\n </li>\n } @else { @if (resolvedChildren().length === 0) {\n <li class=\"tree-node__empty\" role=\"presentation\">\n <span>{{ noResultText() }}</span>\n </li>\n } @for (child of resolvedChildren(); track child.id) {\n <moz-tree-node\n [node]=\"child\"\n [depth]=\"depth() + 1\"\n [selectionMode]=\"selectionMode()\"\n [indentSize]=\"indentSize()\"\n [nodeTemplate]=\"nodeTemplate()\"\n [nodeTemplates]=\"nodeTemplates()\"\n [loadChildren]=\"loadChildren()\"\n [ancestors]=\"ancestorsWithSelf()\"\n (expandChange)=\"expandChange.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\"\n />\n } }\n </ul>\n }\n</li>\n", styles: [".tree-node{list-style:none;margin:0;padding:0;outline:none;display:flex;flex-direction:column;gap:4px}.tree-node--selected>.tree-node__header>.tree-node__row{background:var(--color-background-accent);border-radius:var(--border-radius-m, 8px)}.tree-node--disabled{
|
|
17881
|
+
], template: "<li\n class=\"tree-node\"\n [class.tree-node--selected]=\"isSelected()\"\n [class.tree-node--disabled]=\"isDisabled()\"\n [class.tree-node--focused]=\"isFocused()\"\n role=\"treeitem\"\n [id]=\"'tree-node-' + node().id\"\n [attr.aria-level]=\"depth() + 1\"\n [attr.aria-expanded]=\"hasChildren() ? isExpanded() : null\"\n [attr.aria-selected]=\"selectionMode() !== 'none' ? isSelected() : null\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n [attr.data-tree-node-id]=\"node().id\"\n [tabindex]=\"isFocused() ? 0 : -1\"\n>\n <div\n class=\"tree-node__header\"\n [class.tree-node__header--expandable]=\"hasChildren()\"\n (click)=\"onHeaderClick()\"\n >\n <div class=\"tree-node__indent\" [style.width.px]=\"indentPx()\"></div>\n\n <div class=\"tree-node__row\">\n <span class=\"tree-node__chevron\" [class.tree-node__chevron--leaf]=\"!hasChildren()\">\n @if (hasChildren()) { @if (isExpanded()) {\n <ChevronDown20 />\n } @else {\n <ChevronRight20 />\n } }\n </span>\n\n <div class=\"tree-node__content\">\n @if (resolvedTemplate()) {\n <ng-container\n [ngTemplateOutlet]=\"resolvedTemplate()!\"\n [ngTemplateOutletContext]=\"templateContext()\"\n />\n } @else {\n <span class=\"tree-node__label\">{{ node().id }}</span>\n }\n </div>\n\n @if (selectionMode() === 'checkbox') {\n <moz-checkbox\n class=\"tree-node__selection\"\n [id]=\"'tree-checkbox-' + node().id\"\n [indeterminate]=\"isIndeterminate()\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @else if (selectionMode() === 'radio') {\n <moz-radio\n class=\"tree-node__selection\"\n [id]=\"'tree-radio-' + node().id\"\n [name]=\"radioName\"\n [disabled]=\"isDisabled()\"\n [ngModel]=\"isSelected()\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"onRadioChange($event)\"\n />\n }\n </div>\n </div>\n\n @if (isExpanded() && !flat()) {\n <ul class=\"tree-node__children\" role=\"group\">\n @if (isLoading()) {\n <li class=\"tree-node__loading\" role=\"presentation\">\n <moz-loader size=\"s\" [appearance]=\"'accent'\" />\n </li>\n } @else { @if (resolvedChildren().length === 0) {\n <li class=\"tree-node__empty\" role=\"presentation\">\n <span>{{ noResultText() }}</span>\n </li>\n } @for (child of resolvedChildren(); track child.id) {\n <moz-tree-node\n [node]=\"child\"\n [depth]=\"depth() + 1\"\n [selectionMode]=\"selectionMode()\"\n [indentSize]=\"indentSize()\"\n [nodeTemplate]=\"nodeTemplate()\"\n [nodeTemplates]=\"nodeTemplates()\"\n [loadChildren]=\"loadChildren()\"\n [ancestors]=\"ancestorsWithSelf()\"\n (expandChange)=\"expandChange.emit($event)\"\n (selectionChange)=\"selectionChange.emit($event)\"\n />\n } }\n </ul>\n }\n</li>\n", styles: [".tree-node{list-style:none;margin:0;padding:0;outline:none;display:flex;flex-direction:column;gap:4px}.tree-node--selected>.tree-node__header>.tree-node__row{background:var(--color-background-accent);border-radius:var(--border-radius-m, 8px)}.tree-node--disabled{color:var(--color-text-disabled)}.tree-node--disabled>.tree-node__header{opacity:.5}.tree-node--focused>.tree-node__header>.tree-node__row,.tree-node:focus-visible>.tree-node__header>.tree-node__row{outline:2px solid var(--color-background-accent-inverse);outline-offset:-2px;border-radius:var(--border-radius-m, 8px)}.tree-node__header{display:flex;align-items:center;width:100%;min-height:57px;-webkit-user-select:none;user-select:none}.tree-node__header--expandable{cursor:pointer}.tree-node__row{display:flex;align-items:center;height:57px;flex:1;min-width:0;border-radius:var(--border-radius-m, 8px);transition:background .15s ease}.tree-node__header:hover>.tree-node__row{background:var(--color-background-secondary)}.tree-node--selected>.tree-node__header:hover>.tree-node__row{background:var(--color-background-accent)}.tree-node__indent{flex-shrink:0}.tree-node__chevron{display:flex;align-items:center;justify-content:center;flex-shrink:0;padding-left:16px;color:var(--color-text-primary)}.tree-node__chevron--leaf{width:0;padding-left:0;overflow:hidden}.tree-node__content{flex:1;min-width:0;display:flex;align-items:center;padding:4px 8px}.tree-node__selection{flex-shrink:0;margin-left:auto;margin-right:8px}.tree-node__label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-node__children{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:4px}.tree-node__loading{display:flex;align-items:center;justify-content:center;padding:8px 16px}.tree-node__empty{display:flex;align-items:center;padding:8px 16px;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}\n"] }]
|
|
17860
17882
|
}], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], depth: [{ type: i0.Input, args: [{ isSignal: true, alias: "depth", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], indentSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "indentSize", required: false }] }], nodeTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeTemplate", required: false }] }], nodeTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeTemplates", required: false }] }], loadChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadChildren", required: false }] }], ancestors: [{ type: i0.Input, args: [{ isSignal: true, alias: "ancestors", required: false }] }], flat: [{ type: i0.Input, args: [{ isSignal: true, alias: "flat", required: false }] }], noResultText: [{ type: i0.Input, args: [{ isSignal: true, alias: "noResultText", required: false }] }], expandChange: [{ type: i0.Output, args: ["expandChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
|
|
17861
17883
|
|
|
17862
17884
|
class MozTreeNodeTemplateDirective {
|
|
@@ -17934,30 +17956,18 @@ class MozTreeComponent {
|
|
|
17934
17956
|
effect(() => {
|
|
17935
17957
|
this.stateService.loadChildrenFn.set(this.loadChildren());
|
|
17936
17958
|
});
|
|
17937
|
-
//
|
|
17938
|
-
//
|
|
17939
|
-
|
|
17959
|
+
// React to genuine lazy-load events emitted by patchChildren. Static
|
|
17960
|
+
// initial children never go through patchChildren, so pre-selected
|
|
17961
|
+
// descendants of statically-populated parents are left untouched.
|
|
17962
|
+
const processed = new Set();
|
|
17940
17963
|
effect(() => {
|
|
17941
|
-
const
|
|
17964
|
+
const loaded = this.stateService.loadedParentIds();
|
|
17942
17965
|
if (this.selectionService.selectionMode() !== 'checkbox')
|
|
17943
17966
|
return;
|
|
17944
|
-
const
|
|
17945
|
-
|
|
17946
|
-
|
|
17947
|
-
|
|
17948
|
-
const hadChildren = prevChildMap.get(n.id) ?? false;
|
|
17949
|
-
const hasChildrenNow = !!(n.children && n.children.length > 0);
|
|
17950
|
-
newChildMap.set(n.id, hasChildrenNow);
|
|
17951
|
-
if (!hadChildren && hasChildrenNow) {
|
|
17952
|
-
newlyLoadedParentIds.push(n.id);
|
|
17953
|
-
}
|
|
17954
|
-
if (n.children)
|
|
17955
|
-
visit(n.children);
|
|
17956
|
-
}
|
|
17957
|
-
};
|
|
17958
|
-
visit(nodes);
|
|
17959
|
-
prevChildMap = newChildMap;
|
|
17960
|
-
for (const parentId of newlyLoadedParentIds) {
|
|
17967
|
+
for (const parentId of loaded) {
|
|
17968
|
+
if (processed.has(parentId))
|
|
17969
|
+
continue;
|
|
17970
|
+
processed.add(parentId);
|
|
17961
17971
|
this.selectionService.propagateOnChildrenLoaded(parentId);
|
|
17962
17972
|
}
|
|
17963
17973
|
});
|