@clr/angular 18.2.2 → 18.4.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.
- package/fesm2022/clr-angular-accordion.mjs +28 -28
- package/fesm2022/clr-angular-accordion.mjs.map +1 -1
- package/fesm2022/clr-angular-button.mjs +27 -27
- package/fesm2022/clr-angular-button.mjs.map +1 -1
- package/fesm2022/clr-angular-card.mjs +417 -0
- package/fesm2022/clr-angular-card.mjs.map +1 -0
- package/fesm2022/clr-angular-collapsible-panel.mjs +6 -6
- package/fesm2022/clr-angular-collapsible-panel.mjs.map +1 -1
- package/fesm2022/clr-angular-data-datagrid.mjs +1578 -415
- package/fesm2022/clr-angular-data-datagrid.mjs.map +1 -1
- package/fesm2022/clr-angular-data-stack-view.mjs +23 -23
- package/fesm2022/clr-angular-data-stack-view.mjs.map +1 -1
- package/fesm2022/clr-angular-data-tree-view.mjs +283 -48
- package/fesm2022/clr-angular-data-tree-view.mjs.map +1 -1
- package/fesm2022/clr-angular-data.mjs +4 -4
- package/fesm2022/clr-angular-data.mjs.map +1 -1
- package/fesm2022/clr-angular-emphasis-alert.mjs +26 -26
- package/fesm2022/clr-angular-emphasis-alert.mjs.map +1 -1
- package/fesm2022/clr-angular-emphasis-badge.mjs +3 -3
- package/fesm2022/clr-angular-emphasis-badge.mjs.map +1 -1
- package/fesm2022/clr-angular-emphasis-label.mjs +3 -3
- package/fesm2022/clr-angular-emphasis-label.mjs.map +1 -1
- package/fesm2022/clr-angular-emphasis.mjs +4 -4
- package/fesm2022/clr-angular-emphasis.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-checkbox.mjs +23 -14
- package/fesm2022/clr-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-combobox.mjs +35 -35
- package/fesm2022/clr-angular-forms-combobox.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-common.mjs +69 -69
- package/fesm2022/clr-angular-forms-common.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-datalist.mjs +17 -17
- package/fesm2022/clr-angular-forms-datalist.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-datepicker.mjs +73 -73
- package/fesm2022/clr-angular-forms-datepicker.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-file-input.mjs +36 -36
- package/fesm2022/clr-angular-forms-file-input.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-input.mjs +10 -10
- package/fesm2022/clr-angular-forms-input.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-number-input.mjs +12 -12
- package/fesm2022/clr-angular-forms-number-input.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-password.mjs +11 -11
- package/fesm2022/clr-angular-forms-password.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-radio.mjs +17 -24
- package/fesm2022/clr-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-range.mjs +10 -10
- package/fesm2022/clr-angular-forms-range.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-select.mjs +10 -10
- package/fesm2022/clr-angular-forms-select.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-textarea.mjs +10 -10
- package/fesm2022/clr-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/clr-angular-forms.mjs +4 -4
- package/fesm2022/clr-angular-forms.mjs.map +1 -1
- package/fesm2022/clr-angular-icon.mjs +101 -47
- package/fesm2022/clr-angular-icon.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-breadcrumbs.mjs +10 -10
- package/fesm2022/clr-angular-layout-breadcrumbs.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-main-container.mjs +7 -7
- package/fesm2022/clr-angular-layout-main-container.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-nav.mjs +22 -22
- package/fesm2022/clr-angular-layout-nav.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-tabs.mjs +35 -35
- package/fesm2022/clr-angular-layout-tabs.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-vertical-nav.mjs +31 -31
- package/fesm2022/clr-angular-layout-vertical-nav.mjs.map +1 -1
- package/fesm2022/clr-angular-layout.mjs +4 -4
- package/fesm2022/clr-angular-layout.mjs.map +1 -1
- package/fesm2022/clr-angular-modal.mjs +29 -27
- package/fesm2022/clr-angular-modal.mjs.map +1 -1
- package/fesm2022/clr-angular-popover-common.mjs +70 -35
- package/fesm2022/clr-angular-popover-common.mjs.map +1 -1
- package/fesm2022/clr-angular-popover-dropdown.mjs +63 -26
- package/fesm2022/clr-angular-popover-dropdown.mjs.map +1 -1
- package/fesm2022/clr-angular-popover-signpost.mjs +27 -25
- package/fesm2022/clr-angular-popover-signpost.mjs.map +1 -1
- package/fesm2022/clr-angular-popover-tooltip.mjs +19 -19
- package/fesm2022/clr-angular-popover-tooltip.mjs.map +1 -1
- package/fesm2022/clr-angular-popover.mjs +4 -4
- package/fesm2022/clr-angular-popover.mjs.map +1 -1
- package/fesm2022/clr-angular-progress-progress-bars.mjs +7 -7
- package/fesm2022/clr-angular-progress-progress-bars.mjs.map +1 -1
- package/fesm2022/clr-angular-progress-spinner.mjs +7 -7
- package/fesm2022/clr-angular-progress-spinner.mjs.map +1 -1
- package/fesm2022/clr-angular-stepper.mjs +31 -31
- package/fesm2022/clr-angular-stepper.mjs.map +1 -1
- package/fesm2022/clr-angular-timeline.mjs +24 -24
- package/fesm2022/clr-angular-timeline.mjs.map +1 -1
- package/fesm2022/clr-angular-utils-conditional.mjs +16 -16
- package/fesm2022/clr-angular-utils-conditional.mjs.map +1 -1
- package/fesm2022/clr-angular-utils-loading.mjs +7 -7
- package/fesm2022/clr-angular-utils-loading.mjs.map +1 -1
- package/fesm2022/clr-angular-utils.mjs +149 -150
- package/fesm2022/clr-angular-utils.mjs.map +1 -1
- package/fesm2022/clr-angular-wizard.mjs +51 -51
- package/fesm2022/clr-angular-wizard.mjs.map +1 -1
- package/fesm2022/clr-angular.mjs +9 -4
- package/fesm2022/clr-angular.mjs.map +1 -1
- package/package.json +9 -5
- package/types/clr-angular-card.d.ts +110 -0
- package/types/clr-angular-data-datagrid.d.ts +427 -57
- package/types/clr-angular-data-tree-view.d.ts +126 -88
- package/types/clr-angular-forms-checkbox.d.ts +3 -0
- package/types/clr-angular-forms-datepicker.d.ts +1 -1
- package/types/clr-angular-forms-file-input.d.ts +1 -1
- package/types/clr-angular-forms-radio.d.ts +1 -2
- package/types/clr-angular-icon.d.ts +22 -2
- package/types/clr-angular-popover-common.d.ts +6 -0
- package/types/clr-angular-popover-dropdown.d.ts +33 -4
- package/types/clr-angular-utils.d.ts +61 -20
- package/types/clr-angular.d.ts +8 -6
- package/schematics/vitest.config.d.ts +0 -2
- package/schematics/vitest.config.js +0 -17
- package/schematics/vitest.config.js.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, Optional, SkipSelf, Directive, Input, Component, EventEmitter, PLATFORM_ID, ElementRef, ContentChildren, ViewChild, Output, Inject, NgModule } from '@angular/core';
|
|
3
|
-
import { Subject, BehaviorSubject, fromEvent, isObservable } from 'rxjs';
|
|
2
|
+
import { Injectable, Optional, SkipSelf, HostBinding, Directive, Input, Component, EventEmitter, PLATFORM_ID, ElementRef, ContentChildren, ViewChild, Output, Inject, NgModule } from '@angular/core';
|
|
3
|
+
import { Subject, BehaviorSubject, asapScheduler, fromEvent, isObservable } from 'rxjs';
|
|
4
4
|
import { trigger, transition, style, animate, state } from '@angular/animations';
|
|
5
5
|
import * as i3 from '@angular/common';
|
|
6
6
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
7
7
|
import * as i2 from '@clr/angular/utils';
|
|
8
8
|
import { uniqueIdFactory, preventArrowKeyScroll, isKeyEitherLetterOrNumber, Keys, IfExpandService, LoadingListener, ClrLoadingModule } from '@clr/angular/utils';
|
|
9
|
-
import { filter, debounceTime } from 'rxjs/operators';
|
|
9
|
+
import { filter, skip, debounceTime } from 'rxjs/operators';
|
|
10
10
|
import * as i5 from '@clr/angular/icon';
|
|
11
11
|
import { ClarityIcons, angleIcon, ClrIcon } from '@clr/angular/icon';
|
|
12
12
|
|
|
@@ -38,11 +38,17 @@ class TreeFeaturesService {
|
|
|
38
38
|
this.selectable = false;
|
|
39
39
|
this.eager = true;
|
|
40
40
|
this.childrenFetched = new Subject();
|
|
41
|
+
/*
|
|
42
|
+
* Internal. Whether a bulk expansion is currently in effect for the whole tree, so that nodes created
|
|
43
|
+
* afterwards (lazy-loaded children, dynamic nodes) come in expanded. See `ClrTree.expandAll()`.
|
|
44
|
+
* Cleared as soon as any node of the tree is collapsed on its own.
|
|
45
|
+
*/
|
|
46
|
+
this._allExpanded = false;
|
|
41
47
|
}
|
|
42
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
43
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
48
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFeaturesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
49
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFeaturesService }); }
|
|
44
50
|
}
|
|
45
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
51
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFeaturesService, decorators: [{
|
|
46
52
|
type: Injectable
|
|
47
53
|
}] });
|
|
48
54
|
function treeFeaturesFactory(existing) {
|
|
@@ -242,10 +248,10 @@ class TreeFocusManagerService {
|
|
|
242
248
|
// Now look from its own direct parent
|
|
243
249
|
return this.findNodeStartsWith(searchString, model.parent);
|
|
244
250
|
}
|
|
245
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
246
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.
|
|
251
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFocusManagerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
252
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFocusManagerService }); }
|
|
247
253
|
}
|
|
248
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
254
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TreeFocusManagerService, decorators: [{
|
|
249
255
|
type: Injectable
|
|
250
256
|
}] });
|
|
251
257
|
|
|
@@ -257,6 +263,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
257
263
|
*/
|
|
258
264
|
class TreeNodeModel {
|
|
259
265
|
constructor() {
|
|
266
|
+
/*
|
|
267
|
+
* Internal. Whether a bulk expansion is currently in effect for this node's subtree, so that descendants
|
|
268
|
+
* created afterwards (lazy-loaded children, dynamic nodes) come in expanded.
|
|
269
|
+
* Cleared as soon as any node of the subtree is collapsed on its own.
|
|
270
|
+
*/
|
|
271
|
+
this._descendantsExpanded = false;
|
|
272
|
+
/*
|
|
273
|
+
* Internal, the node rendering this model. Bulk operations walk the model tree and need the node itself,
|
|
274
|
+
* for its expandable state and its animation.
|
|
275
|
+
* Imported as a type only, so the models pull in nothing from the components at runtime.
|
|
276
|
+
*/
|
|
277
|
+
this._node = null;
|
|
260
278
|
this.loading$ = new BehaviorSubject(false);
|
|
261
279
|
this.selected = new BehaviorSubject(ClrSelectedState.UNSELECTED);
|
|
262
280
|
/*
|
|
@@ -273,6 +291,12 @@ class TreeNodeModel {
|
|
|
273
291
|
this._loading = isLoading;
|
|
274
292
|
this.loading$.next(isLoading);
|
|
275
293
|
}
|
|
294
|
+
/*
|
|
295
|
+
* Internal. The children that are already known, without triggering a lazy fetch.
|
|
296
|
+
*/
|
|
297
|
+
get _loadedChildren() {
|
|
298
|
+
return this.children;
|
|
299
|
+
}
|
|
276
300
|
get disabled() {
|
|
277
301
|
// when both parameters are undefined, double negative is needed to cast to false, otherwise will return undefined.
|
|
278
302
|
return !!(this._disabled || this.parent?.disabled);
|
|
@@ -281,9 +305,37 @@ class TreeNodeModel {
|
|
|
281
305
|
this._disabled = value;
|
|
282
306
|
}
|
|
283
307
|
destroy() {
|
|
308
|
+
this._node = null;
|
|
284
309
|
// Just to be safe
|
|
285
310
|
this.selected.complete();
|
|
286
311
|
}
|
|
312
|
+
/*
|
|
313
|
+
* Internal. Expands or collapses this node and every descendant that is already known.
|
|
314
|
+
* Disabled branches are left untouched and excluded, the same way selection excludes them.
|
|
315
|
+
*/
|
|
316
|
+
_setExpandedRecursive(expanded) {
|
|
317
|
+
if (this.disabled) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (this._node) {
|
|
321
|
+
this._node._setExpandedInBulk(expanded);
|
|
322
|
+
}
|
|
323
|
+
for (const child of this._loadedChildren) {
|
|
324
|
+
child._setExpandedRecursive(expanded);
|
|
325
|
+
}
|
|
326
|
+
this._descendantsExpanded = expanded;
|
|
327
|
+
}
|
|
328
|
+
/*
|
|
329
|
+
* Internal. Whether this node, or one of its ancestors, expects all of its descendants to be expanded.
|
|
330
|
+
*/
|
|
331
|
+
_isInExpandedSubtree() {
|
|
332
|
+
for (let current = this; current; current = current.parent) {
|
|
333
|
+
if (current._descendantsExpanded) {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
287
339
|
// Propagate by default when eager, don't propagate in the lazy-loaded tree.
|
|
288
340
|
setSelected(state, propagateUp, propagateDown) {
|
|
289
341
|
if (state === this.selected.value) {
|
|
@@ -311,6 +363,15 @@ class TreeNodeModel {
|
|
|
311
363
|
// It should never be called from lifecycle hooks or app-provided inputs.
|
|
312
364
|
this.setSelected(newState, true, propagate);
|
|
313
365
|
}
|
|
366
|
+
/*
|
|
367
|
+
* Internal, called when this node is collapsed on its own. No ancestor can still expect its whole subtree
|
|
368
|
+
* to be expanded, so nodes created afterwards must not keep cascading open.
|
|
369
|
+
*/
|
|
370
|
+
_clearExpandedSubtree() {
|
|
371
|
+
for (let current = this; current; current = current.parent) {
|
|
372
|
+
current._descendantsExpanded = false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
314
375
|
/*
|
|
315
376
|
* Internal, but needs to be called by other nodes
|
|
316
377
|
*/
|
|
@@ -408,6 +469,10 @@ class DeclarativeTreeNodeModel extends TreeNodeModel {
|
|
|
408
469
|
class ClrTreeNodeLink {
|
|
409
470
|
constructor(el) {
|
|
410
471
|
this.el = el;
|
|
472
|
+
// The tree node's content container owns the roving tabindex for the whole treeitem, so
|
|
473
|
+
// the link itself must be removed from the natural tab sequence to avoid a double Tab stop.
|
|
474
|
+
// It remains mouse-clickable and can still be activated programmatically via .click().
|
|
475
|
+
this.tabindex = -1;
|
|
411
476
|
}
|
|
412
477
|
get active() {
|
|
413
478
|
return this.el.nativeElement.classList.contains('active');
|
|
@@ -417,16 +482,19 @@ class ClrTreeNodeLink {
|
|
|
417
482
|
this.el.nativeElement.click();
|
|
418
483
|
}
|
|
419
484
|
}
|
|
420
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
421
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.
|
|
485
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeNodeLink, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
486
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.25", type: ClrTreeNodeLink, isStandalone: false, selector: ".clr-treenode-link", host: { properties: { "attr.tabindex": "this.tabindex" } }, ngImport: i0 }); }
|
|
422
487
|
}
|
|
423
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
488
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeNodeLink, decorators: [{
|
|
424
489
|
type: Directive,
|
|
425
490
|
args: [{
|
|
426
491
|
selector: '.clr-treenode-link',
|
|
427
492
|
standalone: false,
|
|
428
493
|
}]
|
|
429
|
-
}], ctorParameters: () => [{ type: i0.ElementRef }]
|
|
494
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { tabindex: [{
|
|
495
|
+
type: HostBinding,
|
|
496
|
+
args: ['attr.tabindex']
|
|
497
|
+
}] } });
|
|
430
498
|
|
|
431
499
|
/*
|
|
432
500
|
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
@@ -442,6 +510,9 @@ class RecursiveChildren {
|
|
|
442
510
|
constructor(featuresService, expandService) {
|
|
443
511
|
this.featuresService = featuresService;
|
|
444
512
|
this.expandService = expandService;
|
|
513
|
+
// One context object per node: a new object on every change detection pass would make ngTemplateOutlet
|
|
514
|
+
// run ngOnChanges and rewrite the context of every node on every pass.
|
|
515
|
+
this.contexts = new WeakMap();
|
|
445
516
|
if (expandService) {
|
|
446
517
|
this.subscription = expandService.expandChange.subscribe(value => {
|
|
447
518
|
if (!value && this.parent && !featuresService.eager && featuresService.recursion) {
|
|
@@ -464,10 +535,18 @@ class RecursiveChildren {
|
|
|
464
535
|
(this.featuresService.eager || !this.expandService || this.expandService.expanded));
|
|
465
536
|
}
|
|
466
537
|
getContext(node) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
538
|
+
let context = this.contexts.get(node);
|
|
539
|
+
if (!context) {
|
|
540
|
+
context = {
|
|
541
|
+
// Read through, so the context keeps following the node if its model gets reassigned.
|
|
542
|
+
get $implicit() {
|
|
543
|
+
return node.model;
|
|
544
|
+
},
|
|
545
|
+
clrModel: node,
|
|
546
|
+
};
|
|
547
|
+
this.contexts.set(node, context);
|
|
548
|
+
}
|
|
549
|
+
return context;
|
|
471
550
|
}
|
|
472
551
|
ngOnDestroy() {
|
|
473
552
|
if (this.subscription) {
|
|
@@ -477,8 +556,8 @@ class RecursiveChildren {
|
|
|
477
556
|
setAriaRoles() {
|
|
478
557
|
this.role = this.parent ? 'group' : null;
|
|
479
558
|
}
|
|
480
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
481
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
559
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RecursiveChildren, deps: [{ token: TreeFeaturesService }, { token: i2.IfExpandService, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
560
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: RecursiveChildren, isStandalone: false, selector: "clr-recursive-children", inputs: { parent: "parent", children: "children" }, host: { properties: { "attr.role": "role" } }, ngImport: i0, template: `
|
|
482
561
|
@if (shouldRender()) {
|
|
483
562
|
@for (child of parent?.children || children; track child) {
|
|
484
563
|
<ng-container *ngTemplateOutlet="featuresService.recursion.template; context: getContext(child)"></ng-container>
|
|
@@ -486,7 +565,7 @@ class RecursiveChildren {
|
|
|
486
565
|
}
|
|
487
566
|
`, isInline: true, dependencies: [{ kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] }); }
|
|
488
567
|
}
|
|
489
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
568
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RecursiveChildren, decorators: [{
|
|
490
569
|
type: Component,
|
|
491
570
|
args: [{
|
|
492
571
|
selector: 'clr-recursive-children',
|
|
@@ -536,6 +615,8 @@ class ClrTreeNode {
|
|
|
536
615
|
this.isModelLoading = false;
|
|
537
616
|
this.nodeId = uniqueIdFactory();
|
|
538
617
|
this.contentContainerTabindex = -1;
|
|
618
|
+
this.bulkChange = false;
|
|
619
|
+
this.skipAnimation = false;
|
|
539
620
|
this.skipEmitChange = false;
|
|
540
621
|
this.typeAheadKeyBuffer = '';
|
|
541
622
|
this.typeAheadKeyEvent = new Subject();
|
|
@@ -558,6 +639,7 @@ class ClrTreeNode {
|
|
|
558
639
|
this._model = new DeclarativeTreeNodeModel(parent ? parent._model : null);
|
|
559
640
|
}
|
|
560
641
|
this._model.nodeId = this.nodeId;
|
|
642
|
+
this._model._node = this;
|
|
561
643
|
}
|
|
562
644
|
get disabled() {
|
|
563
645
|
return this._model.disabled;
|
|
@@ -611,6 +693,12 @@ class ClrTreeNode {
|
|
|
611
693
|
get treeNodeLink() {
|
|
612
694
|
return this.treeNodeLinkList && this.treeNodeLinkList.first;
|
|
613
695
|
}
|
|
696
|
+
get childrenAnimationState() {
|
|
697
|
+
if (this.expandService.expanded) {
|
|
698
|
+
return this.skipAnimation ? 'expandedInstant' : 'expanded';
|
|
699
|
+
}
|
|
700
|
+
return this.skipAnimation ? 'collapsedInstant' : 'collapsed';
|
|
701
|
+
}
|
|
614
702
|
get isParent() {
|
|
615
703
|
return this._model.children && this._model.children.length > 0;
|
|
616
704
|
}
|
|
@@ -621,8 +709,15 @@ class ClrTreeNode {
|
|
|
621
709
|
this.selectedChange.emit(value);
|
|
622
710
|
}));
|
|
623
711
|
this.subscriptions.push(this.expandService.expandChange.subscribe(value => {
|
|
712
|
+
this.skipAnimation = this.bulkChange;
|
|
624
713
|
this.expandedChange.emit(value);
|
|
625
714
|
this._model.expanded = value;
|
|
715
|
+
if (!this.bulkChange && !value) {
|
|
716
|
+
// Collapsed on its own, so nothing above can still expect its whole subtree to be expanded and
|
|
717
|
+
// nodes created afterwards must not keep cascading open.
|
|
718
|
+
this._model._clearExpandedSubtree();
|
|
719
|
+
this.featuresService._allExpanded = false;
|
|
720
|
+
}
|
|
626
721
|
}));
|
|
627
722
|
this.subscriptions.push(this.focusManager.focusRequest.subscribe(nodeId => {
|
|
628
723
|
if (this.nodeId === nodeId) {
|
|
@@ -631,9 +726,27 @@ class ClrTreeNode {
|
|
|
631
726
|
}), this.focusManager.focusChange.subscribe(nodeId => {
|
|
632
727
|
this.checkTabIndex(nodeId);
|
|
633
728
|
}));
|
|
634
|
-
|
|
729
|
+
// The loading state can flip in the middle of a change detection pass (a lazy fetch starts when the template
|
|
730
|
+
// reads the children), so it is applied in a microtask rather than synchronously. Using a macrotask timer here
|
|
731
|
+
// would schedule one timer per node when a large tree gets created, and one extra change detection pass per
|
|
732
|
+
// fetched node in lazy trees. The current value is read directly instead of debouncing the replayed initial one.
|
|
733
|
+
this.isModelLoading = this._model.loading;
|
|
734
|
+
this.subscriptions.push(this._model.loading$
|
|
735
|
+
.pipe(skip(1), debounceTime(0, asapScheduler))
|
|
736
|
+
.subscribe(isLoading => (this.isModelLoading = isLoading)));
|
|
635
737
|
}
|
|
636
738
|
ngAfterContentInit() {
|
|
739
|
+
// Nodes created while everything above them is expected to be expanded (lazy-loaded children, dynamic
|
|
740
|
+
// nodes) come in expanded. Children are only known at this point, not in ngOnInit, which is why the check
|
|
741
|
+
// happens here. This is a continuation of the bulk operation, not a toggle of its own, so it goes through
|
|
742
|
+
// the same path: no animation, and the cascade keeps going for this node's own descendants.
|
|
743
|
+
if (!this.expanded &&
|
|
744
|
+
(this.featuresService._allExpanded || this._model._isInExpandedSubtree()) &&
|
|
745
|
+
!this.disabled &&
|
|
746
|
+
this.isExpandable()) {
|
|
747
|
+
this._setExpandedInBulk(true);
|
|
748
|
+
this._model._descendantsExpanded = true;
|
|
749
|
+
}
|
|
637
750
|
this.subscriptions.push(this.typeAheadKeyEvent.pipe(debounceTime(TREE_TYPE_AHEAD_TIMEOUT)).subscribe((bufferedKeys) => {
|
|
638
751
|
this.focusManager.focusNodeStartsWith(bufferedKeys, this._model);
|
|
639
752
|
// reset once bufferedKeys are used
|
|
@@ -658,10 +771,54 @@ class ClrTreeNode {
|
|
|
658
771
|
isSelectable() {
|
|
659
772
|
return this.featuresService.selectable;
|
|
660
773
|
}
|
|
774
|
+
/**
|
|
775
|
+
* Expands this node and every expandable node below it, without animation. Disabled nodes are left untouched.
|
|
776
|
+
* Descendants added afterwards, including lazy-loaded children, come in expanded until any node of the subtree
|
|
777
|
+
* gets collapsed.
|
|
778
|
+
*/
|
|
779
|
+
expandDescendants() {
|
|
780
|
+
this._model._setExpandedRecursive(true);
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Collapses this node and every node below it, without animation. Disabled nodes are left untouched.
|
|
784
|
+
*/
|
|
785
|
+
collapseDescendants() {
|
|
786
|
+
this._model._setExpandedRecursive(false);
|
|
787
|
+
this.reclaimTabStop();
|
|
788
|
+
}
|
|
789
|
+
/*
|
|
790
|
+
* Internal, called while the model tree is walked during a bulk operation.
|
|
791
|
+
*/
|
|
792
|
+
_setExpandedInBulk(expanded) {
|
|
793
|
+
// Leaves are left alone when expanding, so that they don't emit a meaningless clrExpandedChange.
|
|
794
|
+
if (expanded && !this.isExpandable()) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
this.bulkChange = true;
|
|
798
|
+
try {
|
|
799
|
+
// Consumers run synchronously on clrExpandedChange from here, so the flag is restored even if they throw.
|
|
800
|
+
this.expandService.expanded = expanded;
|
|
801
|
+
}
|
|
802
|
+
finally {
|
|
803
|
+
this.bulkChange = false;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/*
|
|
807
|
+
* Internal. Takes over the tree's single tab stop without moving focus, so that a collapse cannot leave it
|
|
808
|
+
* on a node that is now hidden.
|
|
809
|
+
*/
|
|
810
|
+
_takeTabStop() {
|
|
811
|
+
this.setTabIndex(0);
|
|
812
|
+
this.focusManager.broadcastFocusedNode(this.nodeId);
|
|
813
|
+
}
|
|
661
814
|
focusTreeNode() {
|
|
815
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
662
818
|
const containerEl = this.contentContainer.nativeElement;
|
|
663
|
-
|
|
664
|
-
|
|
819
|
+
this.setTabIndex(0);
|
|
820
|
+
if (document.activeElement !== containerEl) {
|
|
821
|
+
this.focusManager.broadcastFocusedNode(this.nodeId);
|
|
665
822
|
containerEl.focus();
|
|
666
823
|
containerEl.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
667
824
|
}
|
|
@@ -717,6 +874,17 @@ class ClrTreeNode {
|
|
|
717
874
|
// if non-letter keys are pressed, do reset.
|
|
718
875
|
this.typeAheadKeyBuffer = '';
|
|
719
876
|
}
|
|
877
|
+
/*
|
|
878
|
+
* A collapsed subtree is made inert, so the tree's single tab stop must not be left inside the one that was
|
|
879
|
+
* just collapsed: the tree host gives up its own tabindex the first time it is focused, which would leave the
|
|
880
|
+
* whole tree unreachable by keyboard. This node is still visible, so it takes the tab stop over.
|
|
881
|
+
*/
|
|
882
|
+
reclaimTabStop() {
|
|
883
|
+
const stranded = this.elementRef.nativeElement.querySelector('.clr-treenode-children .clr-tree-node-content-container[tabindex="0"]');
|
|
884
|
+
if (stranded) {
|
|
885
|
+
this._takeTabStop();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
720
888
|
setTabIndex(value) {
|
|
721
889
|
this.contentContainerTabindex = value;
|
|
722
890
|
this.contentContainer.nativeElement.setAttribute('tabindex', value.toString());
|
|
@@ -776,29 +944,53 @@ class ClrTreeNode {
|
|
|
776
944
|
}
|
|
777
945
|
}
|
|
778
946
|
}
|
|
779
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
780
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
947
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeNode, deps: [{ token: PLATFORM_ID }, { token: ClrTreeNode, optional: true, skipSelf: true }, { token: TreeFeaturesService }, { token: i2.IfExpandService }, { token: i2.ClrCommonStringsService }, { token: TreeFocusManagerService }, { token: i0.ElementRef }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
948
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ClrTreeNode, isStandalone: false, selector: "clr-tree-node", inputs: { expandable: ["clrExpandable", "expandable"], disabled: ["clrDisabled", "disabled"], selected: ["clrSelected", "selected"], expanded: ["clrExpanded", "expanded"], clrForTypeAhead: "clrForTypeAhead" }, outputs: { selectedChange: "clrSelectedChange", expandedChange: "clrExpandedChange" }, host: { properties: { "class.clr-tree-node": "true", "class.disabled": "this._model.disabled" } }, providers: [TREE_FEATURES_PROVIDER, IfExpandService, { provide: LoadingListener, useExisting: IfExpandService }], queries: [{ propertyName: "treeNodeLinkList", predicate: ClrTreeNodeLink }], viewQueries: [{ propertyName: "contentContainer", first: true, predicate: ["contentContainer"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<div\n #contentContainer\n role=\"treeitem\"\n class=\"clr-tree-node-content-container\"\n tabindex=\"-1\"\n [class.clr-form-control-disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"isExpandable() ? expanded : null\"\n [attr.aria-selected]=\"ariaSelected\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"broadcastFocusOnContainer()\"\n>\n @if (isExpandable() && !isModelLoading && !expandService.loading) {\n <button\n aria-hidden=\"true\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"clr-treenode-caret\"\n (click)=\"expandService.toggle();\"\n (focus)=\"focusTreeNode()\"\n [disabled]=\"disabled\"\n >\n <cds-icon\n class=\"clr-treenode-caret-icon\"\n shape=\"angle\"\n [direction]=\"expandService.expanded ? 'down' : 'right'\"\n ></cds-icon>\n </button>\n } @if (expandService.loading || isModelLoading) {\n <div class=\"clr-treenode-spinner-container\">\n <span class=\"clr-treenode-spinner spinner\"></span>\n </div>\n } @if (featuresService.selectable) {\n <div class=\"clr-checkbox-wrapper clr-treenode-checkbox\">\n <input\n aria-hidden=\"true\"\n type=\"checkbox\"\n [id]=\"nodeId + '-check'\"\n class=\"clr-checkbox\"\n [disabled]=\"disabled\"\n [checked]=\"_model.selected.value === STATES.SELECTED\"\n [indeterminate]=\"_model.selected.value === STATES.INDETERMINATE\"\n (change)=\"_model.toggleSelection(featuresService.eager)\"\n (focus)=\"focusTreeNode()\"\n tabindex=\"-1\"\n />\n <label [for]=\"nodeId + '-check'\" class=\"clr-control-label\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </label>\n </div>\n } @if (!featuresService.selectable) {\n <div class=\"clr-treenode-content\" (mouseup)=\"focusTreeNode()\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </div>\n }\n\n <ng-template #treenodeContent>\n <ng-content></ng-content>\n @if (featuresService.selectable || ariaSelected) {\n <div class=\"clr-sr-only\">\n <span> {{ariaSelected ? commonStrings.keys.selectedTreeNode : commonStrings.keys.unselectedTreeNode}}</span>\n </div>\n }\n </ng-template>\n</div>\n<div\n class=\"clr-treenode-children\"\n [@toggleChildrenAnim]=\"childrenAnimationState\"\n [attr.role]=\"isExpandable() && !featuresService.recursion ? 'group' : null\"\n [attr.inert]=\"expandService.expanded ? null : ''\"\n>\n <ng-content select=\"clr-tree-node\"></ng-content>\n <ng-content select=\"[clrIfExpanded]\"></ng-content>\n <clr-recursive-children [parent]=\"_model\"></clr-recursive-children>\n</div>\n", dependencies: [{ kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge", "innerOffset"] }, { kind: "component", type: RecursiveChildren, selector: "clr-recursive-children", inputs: ["parent", "children"] }], animations: [
|
|
781
949
|
trigger('toggleChildrenAnim', [
|
|
782
|
-
|
|
783
|
-
transition
|
|
784
|
-
|
|
785
|
-
|
|
950
|
+
// The "instant" states are used by bulk operations (expand all, expand descendants): they have the same
|
|
951
|
+
// styles but no transition leads to them, so hundreds of nested containers don't animate at once.
|
|
952
|
+
transition('collapsed => expanded, collapsedInstant => expanded', [
|
|
953
|
+
// The animation starts from the collapsed state's styles, so the children must be made visible explicitly.
|
|
954
|
+
style({ height: 0, 'content-visibility': 'visible' }),
|
|
955
|
+
animate(200, style({ height: '*', 'content-visibility': 'visible' })),
|
|
956
|
+
]),
|
|
957
|
+
transition('expanded => collapsed, expandedInstant => collapsed', [
|
|
958
|
+
style({ height: '*' }),
|
|
959
|
+
animate(200, style({ height: 0 })),
|
|
960
|
+
]),
|
|
961
|
+
state('expanded, expandedInstant', style({ height: '*', 'overflow-y': 'visible' })),
|
|
962
|
+
// Once collapsed, the browser skips the style, layout and paint of the whole subtree. The state style only
|
|
963
|
+
// applies when the collapse animation is over, so the children stay painted while they slide out; they are
|
|
964
|
+
// inert from the start (see the template), so they cannot be reached in the meantime.
|
|
965
|
+
state('collapsed, collapsedInstant', style({ height: 0, 'content-visibility': 'hidden' })),
|
|
786
966
|
]),
|
|
787
967
|
] }); }
|
|
788
968
|
}
|
|
789
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
969
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeNode, decorators: [{
|
|
790
970
|
type: Component,
|
|
791
971
|
args: [{ selector: 'clr-tree-node', providers: [TREE_FEATURES_PROVIDER, IfExpandService, { provide: LoadingListener, useExisting: IfExpandService }], animations: [
|
|
792
972
|
trigger('toggleChildrenAnim', [
|
|
793
|
-
|
|
794
|
-
transition
|
|
795
|
-
|
|
796
|
-
|
|
973
|
+
// The "instant" states are used by bulk operations (expand all, expand descendants): they have the same
|
|
974
|
+
// styles but no transition leads to them, so hundreds of nested containers don't animate at once.
|
|
975
|
+
transition('collapsed => expanded, collapsedInstant => expanded', [
|
|
976
|
+
// The animation starts from the collapsed state's styles, so the children must be made visible explicitly.
|
|
977
|
+
style({ height: 0, 'content-visibility': 'visible' }),
|
|
978
|
+
animate(200, style({ height: '*', 'content-visibility': 'visible' })),
|
|
979
|
+
]),
|
|
980
|
+
transition('expanded => collapsed, expandedInstant => collapsed', [
|
|
981
|
+
style({ height: '*' }),
|
|
982
|
+
animate(200, style({ height: 0 })),
|
|
983
|
+
]),
|
|
984
|
+
state('expanded, expandedInstant', style({ height: '*', 'overflow-y': 'visible' })),
|
|
985
|
+
// Once collapsed, the browser skips the style, layout and paint of the whole subtree. The state style only
|
|
986
|
+
// applies when the collapse animation is over, so the children stay painted while they slide out; they are
|
|
987
|
+
// inert from the start (see the template), so they cannot be reached in the meantime.
|
|
988
|
+
state('collapsed, collapsedInstant', style({ height: 0, 'content-visibility': 'hidden' })),
|
|
797
989
|
]),
|
|
798
990
|
], host: {
|
|
799
991
|
'[class.clr-tree-node]': 'true',
|
|
800
992
|
'[class.disabled]': 'this._model.disabled',
|
|
801
|
-
}, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<div\n #contentContainer\n role=\"treeitem\"\n class=\"clr-tree-node-content-container\"\n tabindex=\"-1\"\n [class.clr-form-control-disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"isExpandable() ? expanded : null\"\n [attr.aria-selected]=\"ariaSelected\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"broadcastFocusOnContainer()\"\n>\n @if (isExpandable() && !isModelLoading && !expandService.loading) {\n <button\n aria-hidden=\"true\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"clr-treenode-caret\"\n (click)=\"expandService.toggle();\"\n (focus)=\"focusTreeNode()\"\n [disabled]=\"disabled\"\n >\n <cds-icon\n class=\"clr-treenode-caret-icon\"\n shape=\"angle\"\n [direction]=\"expandService.expanded ? 'down' : 'right'\"\n ></cds-icon>\n </button>\n } @if (expandService.loading || isModelLoading) {\n <div class=\"clr-treenode-spinner-container\">\n <span class=\"clr-treenode-spinner spinner\"></span>\n </div>\n } @if (featuresService.selectable) {\n <div class=\"clr-checkbox-wrapper clr-treenode-checkbox\">\n <input\n aria-hidden=\"true\"\n type=\"checkbox\"\n [id]=\"nodeId + '-check'\"\n class=\"clr-checkbox\"\n [disabled]=\"disabled\"\n [checked]=\"_model.selected.value === STATES.SELECTED\"\n [indeterminate]=\"_model.selected.value === STATES.INDETERMINATE\"\n (change)=\"_model.toggleSelection(featuresService.eager)\"\n (focus)=\"focusTreeNode()\"\n tabindex=\"-1\"\n />\n <label [for]=\"nodeId + '-check'\" class=\"clr-control-label\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </label>\n </div>\n } @if (!featuresService.selectable) {\n <div class=\"clr-treenode-content\" (mouseup)=\"focusTreeNode()\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </div>\n }\n\n <ng-template #treenodeContent>\n <ng-content></ng-content>\n @if (featuresService.selectable || ariaSelected) {\n <div class=\"clr-sr-only\">\n <span> {{ariaSelected ? commonStrings.keys.selectedTreeNode : commonStrings.keys.unselectedTreeNode}}</span>\n </div>\n }\n </ng-template>\n</div>\n<div\n class=\"clr-treenode-children\"\n [@toggleChildrenAnim]=\"
|
|
993
|
+
}, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<div\n #contentContainer\n role=\"treeitem\"\n class=\"clr-tree-node-content-container\"\n tabindex=\"-1\"\n [class.clr-form-control-disabled]=\"disabled\"\n [attr.aria-disabled]=\"disabled\"\n [attr.aria-expanded]=\"isExpandable() ? expanded : null\"\n [attr.aria-selected]=\"ariaSelected\"\n (keydown)=\"onKeyDown($event)\"\n (focus)=\"broadcastFocusOnContainer()\"\n>\n @if (isExpandable() && !isModelLoading && !expandService.loading) {\n <button\n aria-hidden=\"true\"\n type=\"button\"\n tabindex=\"-1\"\n class=\"clr-treenode-caret\"\n (click)=\"expandService.toggle();\"\n (focus)=\"focusTreeNode()\"\n [disabled]=\"disabled\"\n >\n <cds-icon\n class=\"clr-treenode-caret-icon\"\n shape=\"angle\"\n [direction]=\"expandService.expanded ? 'down' : 'right'\"\n ></cds-icon>\n </button>\n } @if (expandService.loading || isModelLoading) {\n <div class=\"clr-treenode-spinner-container\">\n <span class=\"clr-treenode-spinner spinner\"></span>\n </div>\n } @if (featuresService.selectable) {\n <div class=\"clr-checkbox-wrapper clr-treenode-checkbox\">\n <input\n aria-hidden=\"true\"\n type=\"checkbox\"\n [id]=\"nodeId + '-check'\"\n class=\"clr-checkbox\"\n [disabled]=\"disabled\"\n [checked]=\"_model.selected.value === STATES.SELECTED\"\n [indeterminate]=\"_model.selected.value === STATES.INDETERMINATE\"\n (change)=\"_model.toggleSelection(featuresService.eager)\"\n (focus)=\"focusTreeNode()\"\n tabindex=\"-1\"\n />\n <label [for]=\"nodeId + '-check'\" class=\"clr-control-label\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </label>\n </div>\n } @if (!featuresService.selectable) {\n <div class=\"clr-treenode-content\" (mouseup)=\"focusTreeNode()\">\n <ng-container [ngTemplateOutlet]=\"treenodeContent\"></ng-container>\n </div>\n }\n\n <ng-template #treenodeContent>\n <ng-content></ng-content>\n @if (featuresService.selectable || ariaSelected) {\n <div class=\"clr-sr-only\">\n <span> {{ariaSelected ? commonStrings.keys.selectedTreeNode : commonStrings.keys.unselectedTreeNode}}</span>\n </div>\n }\n </ng-template>\n</div>\n<div\n class=\"clr-treenode-children\"\n [@toggleChildrenAnim]=\"childrenAnimationState\"\n [attr.role]=\"isExpandable() && !featuresService.recursion ? 'group' : null\"\n [attr.inert]=\"expandService.expanded ? null : ''\"\n>\n <ng-content select=\"clr-tree-node\"></ng-content>\n <ng-content select=\"[clrIfExpanded]\"></ng-content>\n <clr-recursive-children [parent]=\"_model\"></clr-recursive-children>\n</div>\n" }]
|
|
802
994
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
803
995
|
type: Inject,
|
|
804
996
|
args: [PLATFORM_ID]
|
|
@@ -850,6 +1042,7 @@ class ClrTree {
|
|
|
850
1042
|
this.focusManagerService = focusManagerService;
|
|
851
1043
|
this.renderer = renderer;
|
|
852
1044
|
this.el = el;
|
|
1045
|
+
this.rootModels = [];
|
|
853
1046
|
this.subscriptions = [];
|
|
854
1047
|
this._isMultiSelectable = false;
|
|
855
1048
|
const subscription = ngZone.runOutsideAngular(() => fromEvent(el.nativeElement, 'focusin').subscribe((event) => {
|
|
@@ -880,6 +1073,40 @@ class ClrTree {
|
|
|
880
1073
|
ngOnDestroy() {
|
|
881
1074
|
this.subscriptions.forEach(sub => sub.unsubscribe());
|
|
882
1075
|
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Expands every expandable node of the tree, without animation. Disabled nodes are left untouched.
|
|
1078
|
+
* Nodes added to the tree afterwards, including lazy-loaded children, come in expanded until any node gets collapsed.
|
|
1079
|
+
*/
|
|
1080
|
+
expandAll() {
|
|
1081
|
+
this.setAllExpanded(true);
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Collapses every node of the tree, without animation. Disabled nodes are left untouched.
|
|
1085
|
+
*/
|
|
1086
|
+
collapseAll() {
|
|
1087
|
+
this.setAllExpanded(false);
|
|
1088
|
+
}
|
|
1089
|
+
setAllExpanded(expanded) {
|
|
1090
|
+
this.featuresService._allExpanded = expanded;
|
|
1091
|
+
this.rootModels.forEach(model => model._setExpandedRecursive(expanded));
|
|
1092
|
+
if (!expanded) {
|
|
1093
|
+
this.reclaimTabStop();
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
/*
|
|
1097
|
+
* A collapsed subtree is made inert, so the tree's single tab stop must not be left inside one: the host
|
|
1098
|
+
* gives up its own tabindex the first time it is focused, which would leave the whole tree unreachable by
|
|
1099
|
+
* keyboard. Only the roots stay visible after a collapse, so the first one takes the tab stop over.
|
|
1100
|
+
*/
|
|
1101
|
+
reclaimTabStop() {
|
|
1102
|
+
const stranded = this.el.nativeElement.querySelector('.clr-treenode-children .clr-tree-node-content-container[tabindex="0"]');
|
|
1103
|
+
if (stranded) {
|
|
1104
|
+
const firstRoot = this.rootNodes.find(node => !node._model.parent);
|
|
1105
|
+
if (firstRoot) {
|
|
1106
|
+
firstRoot._takeTabStop();
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
883
1110
|
setMultiSelectable() {
|
|
884
1111
|
if (this.featuresService.selectable && this.rootNodes.length > 0) {
|
|
885
1112
|
this._isMultiSelectable = true;
|
|
@@ -894,17 +1121,18 @@ class ClrTree {
|
|
|
894
1121
|
// if node has no parent, it's a root node
|
|
895
1122
|
// for recursive tree, this.rootNodes registers also nested children
|
|
896
1123
|
// so we have to use filter to extract the ones that are truly root nodes
|
|
897
|
-
this.
|
|
1124
|
+
this.rootModels = this.rootNodes.map(node => node._model).filter(node => !node.parent);
|
|
1125
|
+
this.focusManagerService.rootNodeModels = this.rootModels;
|
|
898
1126
|
}
|
|
899
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
900
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.
|
|
1127
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTree, deps: [{ token: TreeFeaturesService }, { token: TreeFocusManagerService }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1128
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ClrTree, isStandalone: false, selector: "clr-tree", inputs: { lazy: ["clrLazy", "lazy"] }, host: { attributes: { "tabindex": "0" }, properties: { "attr.role": "\"tree\"" } }, providers: [TREE_FEATURES_PROVIDER, TreeFocusManagerService], queries: [{ propertyName: "rootNodes", predicate: ClrTreeNode }], ngImport: i0, template: `
|
|
901
1129
|
<ng-content></ng-content>
|
|
902
1130
|
@if (featuresService.recursion) {
|
|
903
1131
|
<clr-recursive-children [children]="featuresService.recursion.root"></clr-recursive-children>
|
|
904
1132
|
}
|
|
905
1133
|
`, isInline: true, dependencies: [{ kind: "component", type: RecursiveChildren, selector: "clr-recursive-children", inputs: ["parent", "children"] }] }); }
|
|
906
1134
|
}
|
|
907
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1135
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTree, decorators: [{
|
|
908
1136
|
type: Component,
|
|
909
1137
|
args: [{
|
|
910
1138
|
selector: 'clr-tree',
|
|
@@ -967,6 +1195,9 @@ class RecursiveTreeNodeModel extends TreeNodeModel {
|
|
|
967
1195
|
set children(value) {
|
|
968
1196
|
this._children = value;
|
|
969
1197
|
}
|
|
1198
|
+
get _loadedChildren() {
|
|
1199
|
+
return this._children || [];
|
|
1200
|
+
}
|
|
970
1201
|
destroy() {
|
|
971
1202
|
if (this.subscription) {
|
|
972
1203
|
this.subscription.unsubscribe();
|
|
@@ -1010,7 +1241,7 @@ class RecursiveTreeNodeModel extends TreeNodeModel {
|
|
|
1010
1241
|
}
|
|
1011
1242
|
}
|
|
1012
1243
|
wrapChildren(rawModels) {
|
|
1013
|
-
return rawModels.map(m => new RecursiveTreeNodeModel(m, this, this.getChildren, this.featuresService));
|
|
1244
|
+
return (rawModels || []).map(m => new RecursiveTreeNodeModel(m, this, this.getChildren, this.featuresService));
|
|
1014
1245
|
}
|
|
1015
1246
|
}
|
|
1016
1247
|
|
|
@@ -1036,7 +1267,11 @@ class ClrRecursiveForOf {
|
|
|
1036
1267
|
wrapped = [new RecursiveTreeNodeModel(this.nodes, null, this.getChildren, this.featuresService)];
|
|
1037
1268
|
}
|
|
1038
1269
|
if (!this.childrenFetchSubscription) {
|
|
1039
|
-
|
|
1270
|
+
// Children fetched synchronously while the tree renders are picked up once, in a microtask,
|
|
1271
|
+
// instead of one macrotask timer (and its own change detection pass) per node.
|
|
1272
|
+
this.childrenFetchSubscription = this.featuresService.childrenFetched
|
|
1273
|
+
.pipe(debounceTime(0, asapScheduler))
|
|
1274
|
+
.subscribe(() => {
|
|
1040
1275
|
this.cdr.detectChanges();
|
|
1041
1276
|
});
|
|
1042
1277
|
}
|
|
@@ -1050,10 +1285,10 @@ class ClrRecursiveForOf {
|
|
|
1050
1285
|
this.childrenFetchSubscription.unsubscribe();
|
|
1051
1286
|
}
|
|
1052
1287
|
}
|
|
1053
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
1054
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.
|
|
1288
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrRecursiveForOf, deps: [{ token: i0.TemplateRef }, { token: TreeFeaturesService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1289
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.25", type: ClrRecursiveForOf, isStandalone: false, selector: "[clrRecursiveFor][clrRecursiveForOf]", inputs: { nodes: ["clrRecursiveForOf", "nodes"], getChildren: ["clrRecursiveForGetChildren", "getChildren"] }, usesOnChanges: true, ngImport: i0 }); }
|
|
1055
1290
|
}
|
|
1056
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrRecursiveForOf, decorators: [{
|
|
1057
1292
|
type: Directive,
|
|
1058
1293
|
args: [{
|
|
1059
1294
|
selector: '[clrRecursiveFor][clrRecursiveForOf]',
|
|
@@ -1078,11 +1313,11 @@ class ClrTreeViewModule {
|
|
|
1078
1313
|
constructor() {
|
|
1079
1314
|
ClarityIcons.addIcons(angleIcon);
|
|
1080
1315
|
}
|
|
1081
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.
|
|
1082
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.
|
|
1083
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.
|
|
1316
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeViewModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1317
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeViewModule, declarations: [ClrTree, ClrTreeNode, ClrRecursiveForOf, ClrTreeNodeLink, RecursiveChildren], imports: [CommonModule, ClrIcon, ClrLoadingModule], exports: [ClrTree, ClrTreeNode, ClrRecursiveForOf, ClrTreeNodeLink] }); }
|
|
1318
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeViewModule, imports: [CommonModule, ClrIcon, ClrLoadingModule] }); }
|
|
1084
1319
|
}
|
|
1085
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.
|
|
1320
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ClrTreeViewModule, decorators: [{
|
|
1086
1321
|
type: NgModule,
|
|
1087
1322
|
args: [{
|
|
1088
1323
|
imports: [CommonModule, ClrIcon, ClrLoadingModule],
|