@ngneers/controls 0.0.1-next.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.
Files changed (186) hide show
  1. package/fesm2022/ngneers-controls-accordion.mjs +234 -0
  2. package/fesm2022/ngneers-controls-accordion.mjs.map +1 -0
  3. package/fesm2022/ngneers-controls-api-ng.mjs +959 -0
  4. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -0
  5. package/fesm2022/ngneers-controls-api-resize.mjs +712 -0
  6. package/fesm2022/ngneers-controls-api-resize.mjs.map +1 -0
  7. package/fesm2022/ngneers-controls-api.mjs +347 -0
  8. package/fesm2022/ngneers-controls-api.mjs.map +1 -0
  9. package/fesm2022/ngneers-controls-avatar.mjs +77 -0
  10. package/fesm2022/ngneers-controls-avatar.mjs.map +1 -0
  11. package/fesm2022/ngneers-controls-base.mjs +620 -0
  12. package/fesm2022/ngneers-controls-base.mjs.map +1 -0
  13. package/fesm2022/ngneers-controls-breadcrumb.mjs +100 -0
  14. package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -0
  15. package/fesm2022/ngneers-controls-button-group.mjs +110 -0
  16. package/fesm2022/ngneers-controls-button-group.mjs.map +1 -0
  17. package/fesm2022/ngneers-controls-button.mjs +83 -0
  18. package/fesm2022/ngneers-controls-button.mjs.map +1 -0
  19. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs +74 -0
  20. package/fesm2022/ngneers-controls-calendar-time-CJT5AFlA.mjs.map +1 -0
  21. package/fesm2022/ngneers-controls-calendar.mjs +801 -0
  22. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -0
  23. package/fesm2022/ngneers-controls-checkbox.mjs +63 -0
  24. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -0
  25. package/fesm2022/ngneers-controls-chip.mjs +55 -0
  26. package/fesm2022/ngneers-controls-chip.mjs.map +1 -0
  27. package/fesm2022/ngneers-controls-default-icons.mjs +82 -0
  28. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
  29. package/fesm2022/ngneers-controls-defer.mjs +72 -0
  30. package/fesm2022/ngneers-controls-defer.mjs.map +1 -0
  31. package/fesm2022/ngneers-controls-dialog.mjs +407 -0
  32. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -0
  33. package/fesm2022/ngneers-controls-directives.mjs +498 -0
  34. package/fesm2022/ngneers-controls-directives.mjs.map +1 -0
  35. package/fesm2022/ngneers-controls-drawer.mjs +271 -0
  36. package/fesm2022/ngneers-controls-drawer.mjs.map +1 -0
  37. package/fesm2022/ngneers-controls-edit-inplace.mjs +127 -0
  38. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -0
  39. package/fesm2022/ngneers-controls-errors.mjs +329 -0
  40. package/fesm2022/ngneers-controls-errors.mjs.map +1 -0
  41. package/fesm2022/ngneers-controls-filter.mjs +708 -0
  42. package/fesm2022/ngneers-controls-filter.mjs.map +1 -0
  43. package/fesm2022/ngneers-controls-hint.mjs +153 -0
  44. package/fesm2022/ngneers-controls-hint.mjs.map +1 -0
  45. package/fesm2022/ngneers-controls-i18n-translations-de.mjs +170 -0
  46. package/fesm2022/ngneers-controls-i18n-translations-de.mjs.map +1 -0
  47. package/fesm2022/ngneers-controls-i18n-translations-en.mjs +170 -0
  48. package/fesm2022/ngneers-controls-i18n-translations-en.mjs.map +1 -0
  49. package/fesm2022/ngneers-controls-i18n.mjs +57 -0
  50. package/fesm2022/ngneers-controls-i18n.mjs.map +1 -0
  51. package/fesm2022/ngneers-controls-icon.mjs +195 -0
  52. package/fesm2022/ngneers-controls-icon.mjs.map +1 -0
  53. package/fesm2022/ngneers-controls-inplace.mjs +150 -0
  54. package/fesm2022/ngneers-controls-inplace.mjs.map +1 -0
  55. package/fesm2022/ngneers-controls-input-field.mjs +165 -0
  56. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -0
  57. package/fesm2022/ngneers-controls-input.mjs +72 -0
  58. package/fesm2022/ngneers-controls-input.mjs.map +1 -0
  59. package/fesm2022/ngneers-controls-item-view.mjs +473 -0
  60. package/fesm2022/ngneers-controls-item-view.mjs.map +1 -0
  61. package/fesm2022/ngneers-controls-list-box.mjs +281 -0
  62. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -0
  63. package/fesm2022/ngneers-controls-mask-input.mjs +888 -0
  64. package/fesm2022/ngneers-controls-mask-input.mjs.map +1 -0
  65. package/fesm2022/ngneers-controls-menu.mjs +371 -0
  66. package/fesm2022/ngneers-controls-menu.mjs.map +1 -0
  67. package/fesm2022/ngneers-controls-message.mjs +30 -0
  68. package/fesm2022/ngneers-controls-message.mjs.map +1 -0
  69. package/fesm2022/ngneers-controls-number-input.mjs +366 -0
  70. package/fesm2022/ngneers-controls-number-input.mjs.map +1 -0
  71. package/fesm2022/ngneers-controls-paginator.mjs +129 -0
  72. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -0
  73. package/fesm2022/ngneers-controls-popover.mjs +215 -0
  74. package/fesm2022/ngneers-controls-popover.mjs.map +1 -0
  75. package/fesm2022/ngneers-controls-progress.mjs +98 -0
  76. package/fesm2022/ngneers-controls-progress.mjs.map +1 -0
  77. package/fesm2022/ngneers-controls-radio.mjs +152 -0
  78. package/fesm2022/ngneers-controls-radio.mjs.map +1 -0
  79. package/fesm2022/ngneers-controls-roving-focus.mjs +323 -0
  80. package/fesm2022/ngneers-controls-roving-focus.mjs.map +1 -0
  81. package/fesm2022/ngneers-controls-scroll-shadow.mjs +65 -0
  82. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
  83. package/fesm2022/ngneers-controls-scroller.mjs +308 -0
  84. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -0
  85. package/fesm2022/ngneers-controls-select-button.mjs +58 -0
  86. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -0
  87. package/fesm2022/ngneers-controls-select.mjs +417 -0
  88. package/fesm2022/ngneers-controls-select.mjs.map +1 -0
  89. package/fesm2022/ngneers-controls-slider.mjs +167 -0
  90. package/fesm2022/ngneers-controls-slider.mjs.map +1 -0
  91. package/fesm2022/ngneers-controls-snackbar.mjs +414 -0
  92. package/fesm2022/ngneers-controls-snackbar.mjs.map +1 -0
  93. package/fesm2022/ngneers-controls-spin-buttons.mjs +126 -0
  94. package/fesm2022/ngneers-controls-spin-buttons.mjs.map +1 -0
  95. package/fesm2022/ngneers-controls-spinner.mjs +184 -0
  96. package/fesm2022/ngneers-controls-spinner.mjs.map +1 -0
  97. package/fesm2022/ngneers-controls-splitter.mjs +604 -0
  98. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -0
  99. package/fesm2022/ngneers-controls-state.mjs +185 -0
  100. package/fesm2022/ngneers-controls-state.mjs.map +1 -0
  101. package/fesm2022/ngneers-controls-switch.mjs +39 -0
  102. package/fesm2022/ngneers-controls-switch.mjs.map +1 -0
  103. package/fesm2022/ngneers-controls-table.mjs +2634 -0
  104. package/fesm2022/ngneers-controls-table.mjs.map +1 -0
  105. package/fesm2022/ngneers-controls-tabs.mjs +336 -0
  106. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -0
  107. package/fesm2022/ngneers-controls-tag.mjs +30 -0
  108. package/fesm2022/ngneers-controls-tag.mjs.map +1 -0
  109. package/fesm2022/ngneers-controls-toast.mjs +350 -0
  110. package/fesm2022/ngneers-controls-toast.mjs.map +1 -0
  111. package/fesm2022/ngneers-controls-toggle-button.mjs +70 -0
  112. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -0
  113. package/fesm2022/ngneers-controls-tooltip.mjs +506 -0
  114. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -0
  115. package/fesm2022/ngneers-controls-tree.mjs +599 -0
  116. package/fesm2022/ngneers-controls-tree.mjs.map +1 -0
  117. package/fesm2022/ngneers-controls-upload.mjs +377 -0
  118. package/fesm2022/ngneers-controls-upload.mjs.map +1 -0
  119. package/fesm2022/ngneers-controls-utils-ng.mjs +411 -0
  120. package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -0
  121. package/fesm2022/ngneers-controls-utils.mjs +261 -0
  122. package/fesm2022/ngneers-controls-utils.mjs.map +1 -0
  123. package/fesm2022/ngneers-controls.mjs +6 -0
  124. package/fesm2022/ngneers-controls.mjs.map +1 -0
  125. package/package.json +302 -0
  126. package/types/ngneers-controls-accordion.d.ts +139 -0
  127. package/types/ngneers-controls-api-ng.d.ts +364 -0
  128. package/types/ngneers-controls-api-resize.d.ts +318 -0
  129. package/types/ngneers-controls-api.d.ts +145 -0
  130. package/types/ngneers-controls-avatar.d.ts +54 -0
  131. package/types/ngneers-controls-base.d.ts +277 -0
  132. package/types/ngneers-controls-breadcrumb.d.ts +272 -0
  133. package/types/ngneers-controls-button-group.d.ts +46 -0
  134. package/types/ngneers-controls-button.d.ts +48 -0
  135. package/types/ngneers-controls-calendar.d.ts +466 -0
  136. package/types/ngneers-controls-checkbox.d.ts +40 -0
  137. package/types/ngneers-controls-chip.d.ts +202 -0
  138. package/types/ngneers-controls-default-icons.d.ts +5 -0
  139. package/types/ngneers-controls-defer.d.ts +48 -0
  140. package/types/ngneers-controls-dialog.d.ts +399 -0
  141. package/types/ngneers-controls-directives.d.ts +171 -0
  142. package/types/ngneers-controls-drawer.d.ts +308 -0
  143. package/types/ngneers-controls-edit-inplace.d.ts +263 -0
  144. package/types/ngneers-controls-errors.d.ts +155 -0
  145. package/types/ngneers-controls-filter.d.ts +609 -0
  146. package/types/ngneers-controls-hint.d.ts +88 -0
  147. package/types/ngneers-controls-i18n-translations-de.d.ts +164 -0
  148. package/types/ngneers-controls-i18n-translations-en.d.ts +164 -0
  149. package/types/ngneers-controls-i18n.d.ts +186 -0
  150. package/types/ngneers-controls-icon.d.ts +68 -0
  151. package/types/ngneers-controls-inplace.d.ts +98 -0
  152. package/types/ngneers-controls-input-field.d.ts +248 -0
  153. package/types/ngneers-controls-input.d.ts +30 -0
  154. package/types/ngneers-controls-item-view.d.ts +215 -0
  155. package/types/ngneers-controls-list-box.d.ts +334 -0
  156. package/types/ngneers-controls-mask-input.d.ts +434 -0
  157. package/types/ngneers-controls-menu.d.ts +192 -0
  158. package/types/ngneers-controls-message.d.ts +21 -0
  159. package/types/ngneers-controls-number-input.d.ts +146 -0
  160. package/types/ngneers-controls-paginator.d.ts +255 -0
  161. package/types/ngneers-controls-popover.d.ts +116 -0
  162. package/types/ngneers-controls-progress.d.ts +49 -0
  163. package/types/ngneers-controls-radio.d.ts +91 -0
  164. package/types/ngneers-controls-roving-focus.d.ts +107 -0
  165. package/types/ngneers-controls-scroll-shadow.d.ts +25 -0
  166. package/types/ngneers-controls-scroller.d.ts +151 -0
  167. package/types/ngneers-controls-select-button.d.ts +51 -0
  168. package/types/ngneers-controls-select.d.ts +409 -0
  169. package/types/ngneers-controls-slider.d.ts +57 -0
  170. package/types/ngneers-controls-snackbar.d.ts +330 -0
  171. package/types/ngneers-controls-spin-buttons.d.ts +69 -0
  172. package/types/ngneers-controls-spinner.d.ts +117 -0
  173. package/types/ngneers-controls-splitter.d.ts +465 -0
  174. package/types/ngneers-controls-state.d.ts +102 -0
  175. package/types/ngneers-controls-switch.d.ts +18 -0
  176. package/types/ngneers-controls-table.d.ts +3127 -0
  177. package/types/ngneers-controls-tabs.d.ts +118 -0
  178. package/types/ngneers-controls-tag.d.ts +21 -0
  179. package/types/ngneers-controls-toast.d.ts +298 -0
  180. package/types/ngneers-controls-toggle-button.d.ts +48 -0
  181. package/types/ngneers-controls-tooltip.d.ts +189 -0
  182. package/types/ngneers-controls-tree.d.ts +409 -0
  183. package/types/ngneers-controls-upload.d.ts +347 -0
  184. package/types/ngneers-controls-utils-ng.d.ts +129 -0
  185. package/types/ngneers-controls-utils.d.ts +130 -0
  186. package/types/ngneers-controls.d.ts +2 -0
@@ -0,0 +1,599 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { viewChild, contentChild, input, computed, Directive, inject, booleanAttribute, model, output, signal, effect, untracked, runInInjectionContext, Component } from '@angular/core';
4
+ import { treeControlTemplate } from '@ngneers/controls-themes/templates/tree';
5
+ import { filterOptions } from '@ngneers/controls/api';
6
+ import { templateTypesFn, NgnStorage, NgnTemplate } from '@ngneers/controls/api/ng';
7
+ import { ValueControlBase, NgnPt, provideSelf } from '@ngneers/controls/base';
8
+ import { NgnCheckbox } from '@ngneers/controls/checkbox';
9
+ import { I18n } from '@ngneers/controls/i18n';
10
+ import { NgnScroller, NgnScrollerItem } from '@ngneers/controls/scroller';
11
+ import { createConditionalSpinner, NgnSpinner } from '@ngneers/controls/spinner';
12
+ import { maybeCallback, Logger } from '@ngneers/controls/utils';
13
+ import { asyncComputed } from '@ngneers/controls/utils-ng';
14
+
15
+ class TreeTemplates extends ValueControlBase {
16
+ // Item template
17
+ _defaultItemTemplate = viewChild.required('defaultItemTemplate');
18
+ _userItemTemplate = contentChild('item', /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "_userItemTemplate" }] : /* istanbul ignore next */ []));
20
+ /**
21
+ * Set a custom template for a tree node.
22
+ * Can also be set via an `<ng-template #item>` element. A per-node
23
+ * `item.template` takes precedence over this global template.
24
+ */
25
+ templateItem = input(null, /* @ts-ignore */
26
+ ...(ngDevMode ? [{ debugName: "templateItem" }] : /* istanbul ignore next */ []));
27
+ itemTemplate = computed(() => this._userItemTemplate() ?? this.templateItem() ?? this._defaultItemTemplate(), /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "itemTemplate" }] : /* istanbul ignore next */ []));
29
+ // Empty template
30
+ _defaultEmptyTemplate = viewChild.required('defaultEmptyTemplate');
31
+ _userEmptyTemplate = contentChild('empty', /* @ts-ignore */
32
+ ...(ngDevMode ? [{ debugName: "_userEmptyTemplate" }] : /* istanbul ignore next */ []));
33
+ /**
34
+ * Set a custom template for the empty state.
35
+ * Can also be set via an `<ng-template #empty>` element.
36
+ */
37
+ templateEmpty = input(null, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "templateEmpty" }] : /* istanbul ignore next */ []));
39
+ emptyTemplate = computed(() => this._userEmptyTemplate() ?? this.templateEmpty() ?? this._defaultEmptyTemplate(), /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "emptyTemplate" }] : /* istanbul ignore next */ []));
41
+ templateTypes = templateTypesFn();
42
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: TreeTemplates, deps: null, target: i0.ɵɵFactoryTarget.Directive });
43
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: TreeTemplates, isStandalone: true, inputs: { templateItem: { classPropertyName: "templateItem", publicName: "templateItem", isSignal: true, isRequired: false, transformFunction: null }, templateEmpty: { classPropertyName: "templateEmpty", publicName: "templateEmpty", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "_userItemTemplate", first: true, predicate: ["item"], descendants: true, isSignal: true }, { propertyName: "_userEmptyTemplate", first: true, predicate: ["empty"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "_defaultItemTemplate", first: true, predicate: ["defaultItemTemplate"], descendants: true, isSignal: true }, { propertyName: "_defaultEmptyTemplate", first: true, predicate: ["defaultEmptyTemplate"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0 });
44
+ }
45
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: TreeTemplates, decorators: [{
46
+ type: Directive
47
+ }], propDecorators: { _defaultItemTemplate: [{ type: i0.ViewChild, args: ['defaultItemTemplate', { isSignal: true }] }], _userItemTemplate: [{ type: i0.ContentChild, args: ['item', { isSignal: true }] }], templateItem: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateItem", required: false }] }], _defaultEmptyTemplate: [{ type: i0.ViewChild, args: ['defaultEmptyTemplate', { isSignal: true }] }], _userEmptyTemplate: [{ type: i0.ContentChild, args: ['empty', { isSignal: true }] }], templateEmpty: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateEmpty", required: false }] }] } });
48
+
49
+ /** Walk the tree, emitting only expanded + visible nodes as a flat list. */
50
+ function flattenTree(items, expanded, level = 0, parentDisabled = false) {
51
+ const result = [];
52
+ const setSize = items.length;
53
+ items.forEach((item, index) => {
54
+ // A lazy branch shows a toggle before its children are loaded.
55
+ const hasChildren = (!!item.items && item.items.length > 0) || !!item.lazy;
56
+ const isExpanded = hasChildren && expanded.has(item.value);
57
+ const effectiveDisabled = parentDisabled || !!item.disabled;
58
+ result.push({
59
+ item,
60
+ level,
61
+ setSize,
62
+ posInSet: index + 1,
63
+ hasChildren,
64
+ expanded: isExpanded,
65
+ parentDisabled,
66
+ });
67
+ if (isExpanded && item.items) {
68
+ result.push(...flattenTree(item.items, expanded, level + 1, effectiveDisabled));
69
+ }
70
+ });
71
+ return result;
72
+ }
73
+ /**
74
+ * Inject lazily-loaded children (keyed by node value) into the tree, returning
75
+ * a new forest where resolved lazy nodes carry their loaded `items` and are no
76
+ * longer marked `lazy`. Returns the input unchanged when nothing is loaded.
77
+ */
78
+ function applyLoadedChildren(items, loaded) {
79
+ if (loaded.size === 0) {
80
+ return items;
81
+ }
82
+ return items.map(item => {
83
+ const cached = loaded.get(item.value);
84
+ if (cached !== undefined) {
85
+ return { ...item, items: [...applyLoadedChildren(cached, loaded)], lazy: false };
86
+ }
87
+ if (item.items && item.items.length > 0) {
88
+ return { ...item, items: [...applyLoadedChildren(item.items, loaded)] };
89
+ }
90
+ return item;
91
+ });
92
+ }
93
+ /** All selectable, enabled leaf values under (and including) an item. */
94
+ function collectLeafValues(item, parentDisabled = false) {
95
+ const disabled = parentDisabled || !!item.disabled;
96
+ if (item.items && item.items.length > 0) {
97
+ return item.items.flatMap(child => collectLeafValues(child, disabled));
98
+ }
99
+ if (disabled || item.selectable === false) {
100
+ return [];
101
+ }
102
+ return [item.value];
103
+ }
104
+ /** All branch values in the forest (used to auto-expand while filtering). */
105
+ function collectBranchValues(items) {
106
+ return items.flatMap(item => item.items && item.items.length > 0 ? [item.value, ...collectBranchValues(item.items)] : []);
107
+ }
108
+ /** Add/remove all of a node's enabled leaf values against the current value set. */
109
+ function cascadeCheck(node, shouldCheck, currentValue) {
110
+ const leaves = collectLeafValues(node);
111
+ const set = new Set(currentValue);
112
+ for (const value of leaves) {
113
+ if (shouldCheck) {
114
+ set.add(value);
115
+ }
116
+ else {
117
+ set.delete(value);
118
+ }
119
+ }
120
+ return [...set];
121
+ }
122
+ /** Derive a node's tri-state from which of its leaves are in the value set. */
123
+ function computeNodeState(item, valueSet) {
124
+ const leaves = collectLeafValues(item);
125
+ if (leaves.length === 0) {
126
+ return 'unchecked';
127
+ }
128
+ let checked = 0;
129
+ for (const value of leaves) {
130
+ if (valueSet.has(value)) {
131
+ checked++;
132
+ }
133
+ }
134
+ if (checked === 0)
135
+ return 'unchecked';
136
+ if (checked === leaves.length)
137
+ return 'checked';
138
+ return 'indeterminate';
139
+ }
140
+
141
+ /**
142
+ * @category control
143
+ */
144
+ class NgnTree extends TreeTemplates {
145
+ i18n = inject(I18n).translations;
146
+ theme = this.injectThemeTemplate(treeControlTemplate, {
147
+ root: true,
148
+ invalid: () => this.invalid(),
149
+ empty: () => !this.flatNodes().length,
150
+ });
151
+ _scroller = viewChild.required(NgnScroller);
152
+ /** The tree items to render, as a nested list of {@link NgnTreeItem} objects. */
153
+ items = input.required(/* @ts-ignore */
154
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
155
+ /**
156
+ * Whether nodes can be selected/checked.
157
+ * @default true
158
+ */
159
+ selectable = input(true, { ...(ngDevMode ? { debugName: "selectable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
160
+ /**
161
+ * Whether the tree is focusable and participates in keyboard navigation.
162
+ * @default true
163
+ */
164
+ focussable = input(true, { ...(ngDevMode ? { debugName: "focussable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
165
+ /**
166
+ * Whether the tree is virtualized. When enabled, provide {@link itemHeight}.
167
+ * @default false
168
+ */
169
+ virtual = input(false, { ...(ngDevMode ? { debugName: "virtual" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
170
+ /**
171
+ * When {@link virtual} is enabled, this defines the height of each node in the list.
172
+ */
173
+ itemHeight = input(/* @ts-ignore */
174
+ ...(ngDevMode ? [undefined, { debugName: "itemHeight" }] : /* istanbul ignore next */ []));
175
+ /**
176
+ * Enable this to allow selecting multiple nodes. The value then becomes an array of node values.
177
+ * @default false
178
+ */
179
+ multiple = input(/* @ts-ignore */
180
+ ...(ngDevMode ? [undefined, { debugName: "multiple" }] : /* istanbul ignore next */ []));
181
+ /**
182
+ * Whether to render checkboxes. Defaults to `multiple()`.
183
+ * @default multiple()
184
+ */
185
+ checkbox = input(/* @ts-ignore */
186
+ ...(ngDevMode ? [undefined, { debugName: "checkbox" }] : /* istanbul ignore next */ []));
187
+ /**
188
+ * Boolean to enable filtering, or a `FilterConfig` to customize it.
189
+ * @default false
190
+ */
191
+ filter = input(false, /* @ts-ignore */
192
+ ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
193
+ /** Manually set the filter text. */
194
+ filterText = input(null, /* @ts-ignore */
195
+ ...(ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []));
196
+ /** Values of expanded branch nodes (two-way). */
197
+ expandedValues = model([], /* @ts-ignore */
198
+ ...(ngDevMode ? [{ debugName: "expandedValues" }] : /* istanbul ignore next */ []));
199
+ /**
200
+ * Persist expansion + selection state across reloads. Provide a key and,
201
+ * optionally, an `NgnStorageKind` (defaults to `'localstorage'`).
202
+ */
203
+ storage = input(null, /* @ts-ignore */
204
+ ...(ngDevMode ? [{ debugName: "storage" }] : /* istanbul ignore next */ []));
205
+ _storage;
206
+ /**
207
+ * Loads the children of a `lazy` node the first time it is expanded.
208
+ * The returned items are cached internally and merged into the tree.
209
+ */
210
+ loadChildren = input(/* @ts-ignore */
211
+ ...(ngDevMode ? [undefined, { debugName: "loadChildren" }] : /* istanbul ignore next */ []));
212
+ /** Emitted with the node's value whenever a node is clicked/selected. */
213
+ itemClicked = output();
214
+ /** Emitted when a branch is expanded (fires before any lazy load resolves). */
215
+ nodeExpand = output();
216
+ maybeCallback = maybeCallback;
217
+ /** Lazily-loaded children, keyed by node value. */
218
+ _loadedChildren = signal(new Map(), /* @ts-ignore */
219
+ ...(ngDevMode ? [{ debugName: "_loadedChildren" }] : /* istanbul ignore next */ []));
220
+ /** Node values currently awaiting their lazy children. */
221
+ _loadingNodes = signal(new Set(), /* @ts-ignore */
222
+ ...(ngDevMode ? [{ debugName: "_loadingNodes" }] : /* istanbul ignore next */ []));
223
+ filteredItems = asyncComputed(async () => {
224
+ const filter = !!this.filter();
225
+ const appliedFilterOptions = this._appliedFilterOptions();
226
+ const filterText = this.filterText();
227
+ if (!filter || !filterText) {
228
+ return this.items();
229
+ }
230
+ return await filterOptions(this.items(), filterText, appliedFilterOptions);
231
+ }, []);
232
+ _appliedFilterOptions = computed(() => {
233
+ const filter = this.filter();
234
+ const providedFilterArgs = typeof filter === 'boolean' ? {} : filter;
235
+ const options = {
236
+ filterFieldsCallback: item => item.label,
237
+ fieldItems: 'items',
238
+ splitWords: true,
239
+ caseSensitive: false,
240
+ filterFn: 'contains',
241
+ ...providedFilterArgs,
242
+ };
243
+ return options;
244
+ }, /* @ts-ignore */
245
+ ...(ngDevMode ? [{ debugName: "_appliedFilterOptions" }] : /* istanbul ignore next */ []));
246
+ filterIsExecuting = this.filteredItems.isRunning;
247
+ /** While filtering, auto-expand every branch that survived the filter. */
248
+ _autoExpanded = computed(() => this.filterText() ? collectBranchValues(this.filteredItems()) : [], /* @ts-ignore */
249
+ ...(ngDevMode ? [{ debugName: "_autoExpanded" }] : /* istanbul ignore next */ []));
250
+ _expandedSet = computed(() => new Set([...this.expandedValues(), ...this._autoExpanded()]), /* @ts-ignore */
251
+ ...(ngDevMode ? [{ debugName: "_expandedSet" }] : /* istanbul ignore next */ []));
252
+ /** Filtered items with any lazily-loaded children merged in. */
253
+ _effectiveItems = computed(() => applyLoadedChildren(this.filteredItems(), this._loadedChildren()), /* @ts-ignore */
254
+ ...(ngDevMode ? [{ debugName: "_effectiveItems" }] : /* istanbul ignore next */ []));
255
+ flatNodes = computed(() => flattenTree(this._effectiveItems(), this._expandedSet()), /* @ts-ignore */
256
+ ...(ngDevMode ? [{ debugName: "flatNodes" }] : /* istanbul ignore next */ []));
257
+ valueArray = computed(() => {
258
+ const v = this.value();
259
+ return (Array.isArray(v) ? v : v != null ? [v] : []);
260
+ }, /* @ts-ignore */
261
+ ...(ngDevMode ? [{ debugName: "valueArray" }] : /* istanbul ignore next */ []));
262
+ _valueSet = computed(() => new Set(this.valueArray()), /* @ts-ignore */
263
+ ...(ngDevMode ? [{ debugName: "_valueSet" }] : /* istanbul ignore next */ []));
264
+ currentHighlightedValue = signal(null, /* @ts-ignore */
265
+ ...(ngDevMode ? [{ debugName: "currentHighlightedValue" }] : /* istanbul ignore next */ []));
266
+ showCheckboxes = computed(() => this.checkbox() ?? this.multiple() ?? false, /* @ts-ignore */
267
+ ...(ngDevMode ? [{ debugName: "showCheckboxes" }] : /* istanbul ignore next */ []));
268
+ constructor() {
269
+ super();
270
+ createConditionalSpinner(this.filterIsExecuting);
271
+ // Fallback init for when the `storage` config arrives after ngOnInit (e.g.
272
+ // an async binding). Idempotent with the ngOnInit path. Registered before
273
+ // the persistence effect so the store is restored before any write; the
274
+ // persistence effect no-ops until `_storage` exists anyway.
275
+ effect(() => {
276
+ const cfg = this.storage();
277
+ if (cfg) {
278
+ untracked(() => this._initStorage(cfg));
279
+ }
280
+ });
281
+ // Persist expansion + selection whenever they change.
282
+ effect(() => {
283
+ const expanded = this.expandedValues();
284
+ const value = this.value();
285
+ untracked(() => {
286
+ if (!this._storage) {
287
+ return;
288
+ }
289
+ if (this._persistsState('expanded')) {
290
+ this._storage.set('expanded', expanded);
291
+ }
292
+ if (this._persistsState('value')) {
293
+ this._storage.set('value', value ?? null);
294
+ }
295
+ });
296
+ });
297
+ // Drop cached lazy children when the source items change identity.
298
+ effect(() => {
299
+ this.items();
300
+ untracked(() => {
301
+ if (this._loadedChildren().size || this._loadingNodes().size) {
302
+ this._loadedChildren.set(new Map());
303
+ this._loadingNodes.set(new Set());
304
+ }
305
+ });
306
+ });
307
+ effect(() => {
308
+ const highlighted = this.currentHighlightedValue();
309
+ if (highlighted == null) {
310
+ return;
311
+ }
312
+ const index = this.flatNodes().findIndex(node => node.item.value === highlighted);
313
+ if (index >= 0) {
314
+ this._scroller().scrollToIndex(index);
315
+ }
316
+ });
317
+ }
318
+ /**
319
+ * Create the store and restore persisted state before the first view check
320
+ * (so it also feeds the SSR render for the `cookie` kind without triggering
321
+ * ExpressionChangedAfterItHasBeenChecked).
322
+ *
323
+ * This is the common path: the `storage` config is normally bound before
324
+ * init, so the restore happens pre-render. When the config only becomes
325
+ * available after init (e.g. an async binding, or a dynamic host that applies
326
+ * inputs after creating the component), the constructor effect below picks it
327
+ * up instead — `_initStorage` is idempotent, so at most one of them runs.
328
+ */
329
+ ngOnInit() {
330
+ const cfg = this.storage();
331
+ if (cfg) {
332
+ this._initStorage(cfg);
333
+ }
334
+ }
335
+ /** Create the store and restore persisted state. Runs at most once. */
336
+ _initStorage(cfg) {
337
+ if (this._storage) {
338
+ return;
339
+ }
340
+ // NgnStorage reads platform/document/request tokens, so it needs an
341
+ // injection context.
342
+ this._storage = runInInjectionContext(this.injector, () => new NgnStorage(cfg.key, cfg.kind ?? 'localstorage', {
343
+ expanded: this.expandedValues(),
344
+ value: this.value() ?? null,
345
+ }));
346
+ if (this._persistsState('expanded')) {
347
+ const savedExpanded = this._storage.get('expanded');
348
+ if (Array.isArray(savedExpanded)) {
349
+ this.expandedValues.set(savedExpanded);
350
+ }
351
+ }
352
+ if (this._persistsState('value')) {
353
+ const savedValue = this._storage.get('value');
354
+ if (savedValue !== undefined) {
355
+ this.value.set(savedValue);
356
+ }
357
+ }
358
+ }
359
+ /** Whether the given state should be persisted (all states when unset). */
360
+ _persistsState(state) {
361
+ const states = this.storage()?.states;
362
+ return !states || states.includes(state);
363
+ }
364
+ /** Derived tri-state for a node's checkbox / aria-checked. */
365
+ nodeState(item) {
366
+ return computeNodeState(item, this._valueSet());
367
+ }
368
+ /** Whether a node can be selected/checked right now. */
369
+ isSelectable(node) {
370
+ const disabled = node.parentDisabled || !!node.item.disabled;
371
+ return this.selectable() && node.item.selectable !== false && !disabled;
372
+ }
373
+ /** Whether a lazy node is currently loading its children. */
374
+ isLoading(item) {
375
+ return this._loadingNodes().has(item.value);
376
+ }
377
+ async toggleExpand(item) {
378
+ const value = item.value;
379
+ const willExpand = !this.expandedValues().includes(value);
380
+ this.expandedValues.update(values => values.includes(value) ? values.filter(v => v !== value) : [...values, value]);
381
+ if (!willExpand) {
382
+ return;
383
+ }
384
+ this.nodeExpand.emit(item);
385
+ const loader = this.loadChildren();
386
+ if (!item.lazy ||
387
+ !loader ||
388
+ this._loadedChildren().has(value) ||
389
+ this._loadingNodes().has(value)) {
390
+ return;
391
+ }
392
+ this._loadingNodes.update(set => new Set(set).add(value));
393
+ let children;
394
+ try {
395
+ children = await loader(item);
396
+ }
397
+ catch (error) {
398
+ Logger.warn(`ngn-tree: failed to load children for node "${String(value)}"`, error);
399
+ }
400
+ // Ignore stale results: if items() changed mid-load, the loading state was
401
+ // cleared and this result should be dropped.
402
+ if (children && this._loadingNodes().has(value)) {
403
+ this._loadedChildren.update(map => new Map(map).set(value, children));
404
+ }
405
+ this._loadingNodes.update(set => {
406
+ const next = new Set(set);
407
+ next.delete(value);
408
+ return next;
409
+ });
410
+ }
411
+ onRowClick(node) {
412
+ if (!this.isSelectable(node)) {
413
+ return;
414
+ }
415
+ this.onSelect(node.item);
416
+ }
417
+ onSelect(item) {
418
+ const value = item.value;
419
+ if (this.multiple()) {
420
+ if (this.showCheckboxes()) {
421
+ // Cascade tri-state checking; value stays a leaf-only array.
422
+ const shouldCheck = this.nodeState(item) !== 'checked';
423
+ this.value.set(cascadeCheck(item, shouldCheck, this.valueArray()));
424
+ }
425
+ else {
426
+ const current = this.value() ?? [];
427
+ this.value.set((current.includes(value)
428
+ ? current.filter(v => v !== value)
429
+ : [...current, value]));
430
+ }
431
+ }
432
+ else {
433
+ // Single-select always stores a single value (never an array), even when
434
+ // checkboxes are shown.
435
+ this.value.set(value);
436
+ }
437
+ this.itemClicked.emit(value);
438
+ this.touched.set(true);
439
+ }
440
+ scrollToIndex(index) {
441
+ this._scroller().scrollToIndex(index);
442
+ }
443
+ onKeyDown(event) {
444
+ const nodes = this.flatNodes();
445
+ if (!nodes.length) {
446
+ return;
447
+ }
448
+ const enabled = nodes.filter(n => !(n.parentDisabled || n.item.disabled));
449
+ const key = event.key;
450
+ if (key === 'ArrowDown' || key === 'ArrowUp') {
451
+ event.preventDefault();
452
+ event.stopPropagation();
453
+ this._moveHighlight(enabled, key === 'ArrowDown' ? 1 : -1);
454
+ }
455
+ else if (key === 'Home' || key === 'End') {
456
+ event.preventDefault();
457
+ event.stopPropagation();
458
+ const target = key === 'Home' ? enabled[0] : enabled[enabled.length - 1];
459
+ this.currentHighlightedValue.set((target?.item.value ?? null));
460
+ }
461
+ else if (key === 'ArrowRight') {
462
+ event.preventDefault();
463
+ event.stopPropagation();
464
+ this._expandOrEnter(nodes);
465
+ }
466
+ else if (key === 'ArrowLeft') {
467
+ event.preventDefault();
468
+ event.stopPropagation();
469
+ this._collapseOrLeave(nodes);
470
+ }
471
+ else if (key === 'Enter' || key === ' ') {
472
+ const node = this._currentNode(nodes);
473
+ if (node && this.isSelectable(node)) {
474
+ event.preventDefault();
475
+ event.stopImmediatePropagation();
476
+ this.onSelect(node.item);
477
+ }
478
+ }
479
+ else if (key.length === 1 && /\S/.test(key)) {
480
+ this._typeahead(enabled, key);
481
+ }
482
+ }
483
+ _currentNode(nodes) {
484
+ const current = this.currentHighlightedValue();
485
+ return nodes.find(n => n.item.value === current);
486
+ }
487
+ _moveHighlight(enabled, dir) {
488
+ if (!enabled.length) {
489
+ this.currentHighlightedValue.set(null);
490
+ return;
491
+ }
492
+ const current = this.currentHighlightedValue();
493
+ const idx = enabled.findIndex(n => n.item.value === current);
494
+ let next;
495
+ if (idx === -1) {
496
+ next = dir === 1 ? 0 : enabled.length - 1;
497
+ }
498
+ else {
499
+ next = (idx + dir + enabled.length) % enabled.length;
500
+ }
501
+ const target = enabled[next];
502
+ if (target) {
503
+ this.currentHighlightedValue.set(target.item.value);
504
+ }
505
+ }
506
+ _expandOrEnter(nodes) {
507
+ const node = this._currentNode(nodes);
508
+ if (!node) {
509
+ return;
510
+ }
511
+ if (node.hasChildren && !node.expanded) {
512
+ void this.toggleExpand(node.item);
513
+ }
514
+ else if (node.hasChildren && node.expanded) {
515
+ const idx = nodes.indexOf(node);
516
+ for (let i = idx + 1; i < nodes.length; i++) {
517
+ const candidate = nodes[i];
518
+ if (!candidate || candidate.level <= node.level) {
519
+ break; // left this node's subtree
520
+ }
521
+ if (!(candidate.parentDisabled || candidate.item.disabled)) {
522
+ this.currentHighlightedValue.set(candidate.item.value);
523
+ return;
524
+ }
525
+ }
526
+ }
527
+ }
528
+ _collapseOrLeave(nodes) {
529
+ const node = this._currentNode(nodes);
530
+ if (!node) {
531
+ return;
532
+ }
533
+ if (node.hasChildren && node.expanded) {
534
+ void this.toggleExpand(node.item);
535
+ return;
536
+ }
537
+ // move to parent: previous node with a smaller level
538
+ const idx = nodes.indexOf(node);
539
+ for (let i = idx - 1; i >= 0; i--) {
540
+ const ancestor = nodes[i];
541
+ if (!ancestor) {
542
+ continue;
543
+ }
544
+ if (ancestor.level < node.level) {
545
+ if (!(ancestor.parentDisabled || ancestor.item.disabled)) {
546
+ this.currentHighlightedValue.set(ancestor.item.value);
547
+ }
548
+ return;
549
+ }
550
+ }
551
+ }
552
+ _typeahead(enabled, char) {
553
+ const lower = char.toLowerCase();
554
+ const current = this.currentHighlightedValue();
555
+ const start = enabled.findIndex(n => n.item.value === current);
556
+ for (let offset = 1; offset <= enabled.length; offset++) {
557
+ const node = enabled[(start + offset + enabled.length) % enabled.length];
558
+ if (!node) {
559
+ continue;
560
+ }
561
+ const label = maybeCallback(node.item.label).toLowerCase();
562
+ if (label.startsWith(lower)) {
563
+ this.currentHighlightedValue.set(node.item.value);
564
+ return;
565
+ }
566
+ }
567
+ }
568
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
569
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: NgnTree, isStandalone: true, selector: "ngn-tree", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, focussable: { classPropertyName: "focussable", publicName: "focussable", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, itemHeight: { classPropertyName: "itemHeight", publicName: "itemHeight", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, checkbox: { classPropertyName: "checkbox", publicName: "checkbox", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, filterText: { classPropertyName: "filterText", publicName: "filterText", isSignal: true, isRequired: false, transformFunction: null }, expandedValues: { classPropertyName: "expandedValues", publicName: "expandedValues", isSignal: true, isRequired: false, transformFunction: null }, storage: { classPropertyName: "storage", publicName: "storage", isSignal: true, isRequired: false, transformFunction: null }, loadChildren: { classPropertyName: "loadChildren", publicName: "loadChildren", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expandedValues: "expandedValuesChange", itemClicked: "itemClicked", nodeExpand: "nodeExpand" }, host: { attributes: { "role": "tree" }, listeners: { "keydown": "onKeyDown($event)", "focusout": "currentHighlightedValue.set(null)" }, properties: { "attr.tabIndex": "focussable() ? 0 : null", "attr.aria-activedescendant": "currentHighlightedValue() != null ? inputId() + \"_node_\" + currentHighlightedValue() : null", "attr.aria-multiselectable": "!!multiple()", "attr.aria-label": "label()", "attr.aria-labelledby": "labelledBy()", "id": "inputId()" } }, providers: [provideSelf(NgnTree)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: NgnScroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ngn-scroller\n [ptInt]=\"this\"\n [ptDep]=\"'scroller'\"\n [virtual]=\"virtual()\"\n [itemHeight]=\"itemHeight()\"\n [items]=\"flatNodes()\"\n [templateItem]=\"usedItemTemplate\"\n #scroller\n/>\n\n@if (!flatNodes().length) {\n <div role=\"status\">\n <ng-container\n *ngTemplateOutlet=\"emptyTemplate() || defaultEmptyTemplate; context: {}\"\n ></ng-container>\n </div>\n}\n\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-node>\n @let item = node.item;\n @let disabled = node.parentDisabled || item.disabled;\n @let state = nodeState(item);\n @let selectableNode = isSelectable(node);\n <!--\n Virtual scrolling renders a flat list, so treeitems are not DOM-nested in\n role=\"group\" containers. Per WAI-ARIA, aria-level + aria-setsize +\n aria-posinset are the sanctioned substitute that convey the hierarchy to\n assistive tech. (The `group` CSS class below is visual styling only.)\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n [ngnScrollerItem]=\"node\"\n [id]=\"inputId() + '_node_' + item.value\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.level + 1\"\n [attr.aria-setsize]=\"node.setSize\"\n [attr.aria-posinset]=\"node.posInSet\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"\n !showCheckboxes() && selectableNode ? valueArray().includes(item.value) : null\n \"\n [attr.aria-checked]=\"\n showCheckboxes() && selectableNode\n ? state === 'indeterminate'\n ? 'mixed'\n : state === 'checked'\n ? 'true'\n : 'false'\n : null\n \"\n [attr.aria-disabled]=\"disabled ? 'true' : null\"\n [style.--ngn-tree-level]=\"node.level\"\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: !item.items,\n group: !!item.items,\n 'item-selectable': selectableNode,\n 'item-selected': valueArray().includes(item.value),\n 'item-highlighted': item.value === currentHighlightedValue(),\n 'item-disabled': !!disabled,\n 'item-expanded': node.expanded,\n }\"\n (click)=\"onRowClick(node)\"\n >\n @if (node.hasChildren) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [ptInt]=\"this\"\n [ptClass]=\"'toggle'\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggleExpand(item); $event.stopPropagation()\"\n >\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-icon'\">\n @if (isLoading(item)) {\n <ngn-spinner [size]=\"14\" />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-arrow'\"></span>\n }\n </span>\n </button>\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (showCheckboxes() && selectableNode) {\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes() && selectable() && item.selectable !== false) {\n <!-- Disabled node: show the checkbox for consistency, but disabled. -->\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n [disabled]=\"true\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes()) {\n <span [ptInt]=\"this\" [ptClass]=\"'item-checkbox-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (item.template) {\n <ng-container *ngTemplateOutlet=\"item.template; context: { $implicit: item }\"></ng-container>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: {\n $implicit: item,\n level: node.level,\n expanded: node.expanded,\n hasChildren: node.hasChildren,\n }\n \"\n ></ng-container>\n }\n </div>\n</ng-template>\n\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\n <span [attr.data-testid]=\"item?.testId\" [ptInt]=\"this\" [ptClass]=\"'default-item'\">\n @let label = item?.label;\n @if (label) {\n <span>{{ maybeCallback(label) }}</span>\n } @else {\n <span>&ZeroWidthSpace;</span>\n }\n </span>\n</ng-template>\n\n<ng-template #defaultEmptyTemplate>\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\" [attr.data-testid]=\"'empty'\">{{\n i18n['tree_noItemsFound']()\n }}</span>\n</ng-template>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnScroller, selector: "ngn-scroller, [ngn-scroller]", inputs: ["items", "fieldId", "virtual", "itemHeight", "virtualPadding", "focusable", "fieldSticky"] }, { kind: "directive", type: NgnScrollerItem, selector: "[ngnScrollerItem]", inputs: ["ngnScrollerItem"] }, { kind: "component", type: NgnCheckbox, selector: "ngn-checkbox", inputs: ["iconChecked", "iconUnchecked", "iconIndeterminate", "allowIndeterminate"] }, { kind: "component", type: NgnSpinner, selector: "ngn-spinner", inputs: ["size", "thickness", "centered"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass", "ptDep"] }] });
570
+ }
571
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NgnTree, decorators: [{
572
+ type: Component,
573
+ args: [{ selector: 'ngn-tree', imports: [
574
+ NgTemplateOutlet,
575
+ NgnScroller,
576
+ NgnScrollerItem,
577
+ NgnCheckbox,
578
+ NgnSpinner,
579
+ NgnTemplate,
580
+ NgnPt,
581
+ ], providers: [provideSelf(NgnTree)], host: {
582
+ '[attr.tabIndex]': 'focussable() ? 0 : null',
583
+ '(keydown)': 'onKeyDown($event)',
584
+ '(focusout)': 'currentHighlightedValue.set(null)',
585
+ role: 'tree',
586
+ '[attr.aria-activedescendant]': 'currentHighlightedValue() != null ? inputId() + "_node_" + currentHighlightedValue() : null',
587
+ '[attr.aria-multiselectable]': '!!multiple()',
588
+ '[attr.aria-label]': 'label()',
589
+ '[attr.aria-labelledby]': 'labelledBy()',
590
+ '[id]': 'inputId()',
591
+ }, template: "<ngn-scroller\n [ptInt]=\"this\"\n [ptDep]=\"'scroller'\"\n [virtual]=\"virtual()\"\n [itemHeight]=\"itemHeight()\"\n [items]=\"flatNodes()\"\n [templateItem]=\"usedItemTemplate\"\n #scroller\n/>\n\n@if (!flatNodes().length) {\n <div role=\"status\">\n <ng-container\n *ngTemplateOutlet=\"emptyTemplate() || defaultEmptyTemplate; context: {}\"\n ></ng-container>\n </div>\n}\n\n<ng-template #usedItemTemplate [ngnTemplate]=\"scroller.templateTypes.item\" let-node>\n @let item = node.item;\n @let disabled = node.parentDisabled || item.disabled;\n @let state = nodeState(item);\n @let selectableNode = isSelectable(node);\n <!--\n Virtual scrolling renders a flat list, so treeitems are not DOM-nested in\n role=\"group\" containers. Per WAI-ARIA, aria-level + aria-setsize +\n aria-posinset are the sanctioned substitute that convey the hierarchy to\n assistive tech. (The `group` CSS class below is visual styling only.)\n -->\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n [ngnScrollerItem]=\"node\"\n [id]=\"inputId() + '_node_' + item.value\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.level + 1\"\n [attr.aria-setsize]=\"node.setSize\"\n [attr.aria-posinset]=\"node.posInSet\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"\n !showCheckboxes() && selectableNode ? valueArray().includes(item.value) : null\n \"\n [attr.aria-checked]=\"\n showCheckboxes() && selectableNode\n ? state === 'indeterminate'\n ? 'mixed'\n : state === 'checked'\n ? 'true'\n : 'false'\n : null\n \"\n [attr.aria-disabled]=\"disabled ? 'true' : null\"\n [style.--ngn-tree-level]=\"node.level\"\n [ptInt]=\"this\"\n [ptClass]=\"{\n item: !item.items,\n group: !!item.items,\n 'item-selectable': selectableNode,\n 'item-selected': valueArray().includes(item.value),\n 'item-highlighted': item.value === currentHighlightedValue(),\n 'item-disabled': !!disabled,\n 'item-expanded': node.expanded,\n }\"\n (click)=\"onRowClick(node)\"\n >\n @if (node.hasChildren) {\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [ptInt]=\"this\"\n [ptClass]=\"'toggle'\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggleExpand(item); $event.stopPropagation()\"\n >\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-icon'\">\n @if (isLoading(item)) {\n <ngn-spinner [size]=\"14\" />\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-arrow'\"></span>\n }\n </span>\n </button>\n } @else {\n <span [ptInt]=\"this\" [ptClass]=\"'toggle-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (showCheckboxes() && selectableNode) {\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes() && selectable() && item.selectable !== false) {\n <!-- Disabled node: show the checkbox for consistency, but disabled. -->\n <ngn-checkbox\n [ptInt]=\"this\"\n [ptDep]=\"'item-checkbox'\"\n [allowIndeterminate]=\"true\"\n [value]=\"state === 'indeterminate' ? null : state === 'checked'\"\n [disabled]=\"true\"\n aria-hidden=\"true\"\n inert=\"true\"\n />\n } @else if (showCheckboxes()) {\n <span [ptInt]=\"this\" [ptClass]=\"'item-checkbox-placeholder'\" aria-hidden=\"true\"></span>\n }\n @if (item.template) {\n <ng-container *ngTemplateOutlet=\"item.template; context: { $implicit: item }\"></ng-container>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate();\n context: {\n $implicit: item,\n level: node.level,\n expanded: node.expanded,\n hasChildren: node.hasChildren,\n }\n \"\n ></ng-container>\n }\n </div>\n</ng-template>\n\n<ng-template #defaultItemTemplate [ngnTemplate]=\"templateTypes.item\" let-item>\n <span [attr.data-testid]=\"item?.testId\" [ptInt]=\"this\" [ptClass]=\"'default-item'\">\n @let label = item?.label;\n @if (label) {\n <span>{{ maybeCallback(label) }}</span>\n } @else {\n <span>&ZeroWidthSpace;</span>\n }\n </span>\n</ng-template>\n\n<ng-template #defaultEmptyTemplate>\n <span [ptInt]=\"this\" [ptClass]=\"'empty'\" [attr.data-testid]=\"'empty'\">{{\n i18n['tree_noItemsFound']()\n }}</span>\n</ng-template>\n" }]
592
+ }], ctorParameters: () => [], propDecorators: { _scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnScroller), { isSignal: true }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], focussable: [{ type: i0.Input, args: [{ isSignal: true, alias: "focussable", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], itemHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemHeight", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], checkbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkbox", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterText: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterText", required: false }] }], expandedValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedValues", required: false }] }, { type: i0.Output, args: ["expandedValuesChange"] }], storage: [{ type: i0.Input, args: [{ isSignal: true, alias: "storage", required: false }] }], loadChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadChildren", required: false }] }], itemClicked: [{ type: i0.Output, args: ["itemClicked"] }], nodeExpand: [{ type: i0.Output, args: ["nodeExpand"] }] } });
593
+
594
+ /**
595
+ * Generated bundle index. Do not edit.
596
+ */
597
+
598
+ export { NgnTree, TreeTemplates, applyLoadedChildren, cascadeCheck, collectBranchValues, collectLeafValues, computeNodeState, flattenTree };
599
+ //# sourceMappingURL=ngneers-controls-tree.mjs.map