@koobiq/components 20.1.0 → 20.3.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 (249) hide show
  1. package/accordion/_accordion-theme.scss +6 -2
  2. package/accordion/accordion-tokens.scss +3 -0
  3. package/accordion/{accordion-trigger.component.scss → accordion-trigger.scss} +10 -0
  4. package/accordion/accordion.scss +71 -0
  5. package/accordion/index.d.ts +264 -121
  6. package/actions-panel/index.d.ts +25 -13
  7. package/alert/_alert-theme.scss +14 -0
  8. package/alert/alert.component.scss +24 -13
  9. package/alert/index.d.ts +63 -29
  10. package/app-switcher/app-switcher-dropdown-app.scss +7 -5
  11. package/app-switcher/app-switcher-dropdown-site.scss +6 -4
  12. package/app-switcher/index.d.ts +41 -4
  13. package/app-switcher/kbq-app-switcher-list-item.scss +6 -8
  14. package/autocomplete/autocomplete.scss +2 -1
  15. package/autocomplete/index.d.ts +58 -37
  16. package/badge/index.d.ts +2 -3
  17. package/breadcrumbs/index.d.ts +1 -1
  18. package/button/index.d.ts +2 -2
  19. package/button-toggle/button-toggle-tokens.scss +2 -0
  20. package/button-toggle/button-toggle.scss +9 -0
  21. package/button-toggle/index.d.ts +5 -0
  22. package/checkbox/index.d.ts +33 -29
  23. package/clamped-text/index.d.ts +3 -2
  24. package/code-block/index.d.ts +24 -19
  25. package/content-panel/index.d.ts +1 -1
  26. package/core/index.d.ts +1217 -186
  27. package/core/option/_action-container-mixins.scss +28 -0
  28. package/core/option/_option-theme.scss +24 -0
  29. package/core/option/action.scss +7 -0
  30. package/core/styles/_core.scss +2 -0
  31. package/core/styles/_koobiq-theme.scss +2 -0
  32. package/core/styles/_tokens.scss +1 -0
  33. package/core/styles/common/_animation.scss +31 -24
  34. package/core/styles/common/_connected-overlay.scss +39 -0
  35. package/core/styles/common/_select.scss +19 -2
  36. package/core/styles/common/_tokens.scss +0 -9
  37. package/core/styles/theming/_index.scss +0 -1
  38. package/core/styles/theming/_theming.scss +34 -5
  39. package/datepicker/index.d.ts +46 -15
  40. package/dl/dl.scss +26 -0
  41. package/dl/index.d.ts +8 -1
  42. package/dropdown/_dropdown-theme.scss +28 -2
  43. package/dropdown/dropdown-item.scss +29 -0
  44. package/dropdown/dropdown-tokens.scss +2 -1
  45. package/dropdown/dropdown.scss +16 -1
  46. package/dropdown/index.d.ts +331 -17
  47. package/dynamic-translation/index.d.ts +1 -1
  48. package/fesm2022/koobiq-components-accordion.mjs +480 -317
  49. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  50. package/fesm2022/koobiq-components-actions-panel.mjs +57 -46
  51. package/fesm2022/koobiq-components-actions-panel.mjs.map +1 -1
  52. package/fesm2022/koobiq-components-alert.mjs +98 -58
  53. package/fesm2022/koobiq-components-alert.mjs.map +1 -1
  54. package/fesm2022/koobiq-components-app-switcher.mjs +233 -38
  55. package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
  56. package/fesm2022/koobiq-components-autocomplete.mjs +89 -52
  57. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  58. package/fesm2022/koobiq-components-badge.mjs +18 -17
  59. package/fesm2022/koobiq-components-badge.mjs.map +1 -1
  60. package/fesm2022/koobiq-components-breadcrumbs.mjs +25 -25
  61. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  62. package/fesm2022/koobiq-components-button-toggle.mjs +17 -12
  63. package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
  64. package/fesm2022/koobiq-components-button.mjs +25 -25
  65. package/fesm2022/koobiq-components-checkbox.mjs +69 -92
  66. package/fesm2022/koobiq-components-checkbox.mjs.map +1 -1
  67. package/fesm2022/koobiq-components-clamped-text.mjs +18 -13
  68. package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
  69. package/fesm2022/koobiq-components-code-block.mjs +56 -69
  70. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  71. package/fesm2022/koobiq-components-content-panel.mjs +29 -29
  72. package/fesm2022/koobiq-components-core.mjs +2485 -735
  73. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  74. package/fesm2022/koobiq-components-datepicker.mjs +168 -126
  75. package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
  76. package/fesm2022/koobiq-components-divider.mjs +7 -7
  77. package/fesm2022/koobiq-components-dl.mjs +23 -15
  78. package/fesm2022/koobiq-components-dl.mjs.map +1 -1
  79. package/fesm2022/koobiq-components-dropdown.mjs +814 -136
  80. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  81. package/fesm2022/koobiq-components-dynamic-translation.mjs +13 -13
  82. package/fesm2022/koobiq-components-ellipsis-center.mjs +7 -7
  83. package/fesm2022/koobiq-components-empty-state.mjs +19 -19
  84. package/fesm2022/koobiq-components-file-upload.mjs +74 -42
  85. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  86. package/fesm2022/koobiq-components-filter-bar.mjs +783 -199
  87. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  88. package/fesm2022/koobiq-components-flag.mjs +7 -7
  89. package/fesm2022/koobiq-components-form-field.mjs +350 -162
  90. package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
  91. package/fesm2022/koobiq-components-icon.mjs +52 -25
  92. package/fesm2022/koobiq-components-icon.mjs.map +1 -1
  93. package/fesm2022/koobiq-components-inline-edit.mjs +175 -28
  94. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  95. package/fesm2022/koobiq-components-input.mjs +41 -24
  96. package/fesm2022/koobiq-components-input.mjs.map +1 -1
  97. package/fesm2022/koobiq-components-link.mjs +8 -8
  98. package/fesm2022/koobiq-components-list.mjs +657 -143
  99. package/fesm2022/koobiq-components-list.mjs.map +1 -1
  100. package/fesm2022/koobiq-components-loader-overlay.mjs +16 -16
  101. package/fesm2022/koobiq-components-markdown.mjs +14 -11
  102. package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
  103. package/fesm2022/koobiq-components-modal.mjs +59 -35
  104. package/fesm2022/koobiq-components-modal.mjs.map +1 -1
  105. package/fesm2022/koobiq-components-navbar.mjs +177 -67
  106. package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
  107. package/fesm2022/koobiq-components-notification-center.mjs +40 -24
  108. package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
  109. package/fesm2022/koobiq-components-overflow-items.mjs +16 -17
  110. package/fesm2022/koobiq-components-overflow-items.mjs.map +1 -1
  111. package/fesm2022/koobiq-components-popover.mjs +31 -21
  112. package/fesm2022/koobiq-components-popover.mjs.map +1 -1
  113. package/fesm2022/koobiq-components-progress-bar.mjs +13 -13
  114. package/fesm2022/koobiq-components-progress-spinner.mjs +13 -13
  115. package/fesm2022/koobiq-components-radio.mjs +10 -10
  116. package/fesm2022/koobiq-components-resizer.mjs +33 -14
  117. package/fesm2022/koobiq-components-resizer.mjs.map +1 -1
  118. package/fesm2022/koobiq-components-scrollbar.mjs +10 -10
  119. package/fesm2022/koobiq-components-search-expandable.mjs +49 -14
  120. package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
  121. package/fesm2022/koobiq-components-select.mjs +450 -146
  122. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  123. package/fesm2022/koobiq-components-sidebar.mjs +14 -14
  124. package/fesm2022/koobiq-components-sidepanel.mjs +26 -26
  125. package/fesm2022/koobiq-components-skeleton.mjs +10 -10
  126. package/fesm2022/koobiq-components-split-button.mjs +10 -17
  127. package/fesm2022/koobiq-components-split-button.mjs.map +1 -1
  128. package/fesm2022/koobiq-components-splitter.mjs +17 -17
  129. package/fesm2022/koobiq-components-table.mjs +15 -13
  130. package/fesm2022/koobiq-components-table.mjs.map +1 -1
  131. package/fesm2022/koobiq-components-tabs.mjs +80 -68
  132. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  133. package/fesm2022/koobiq-components-tags.mjs +283 -122
  134. package/fesm2022/koobiq-components-tags.mjs.map +1 -1
  135. package/fesm2022/koobiq-components-textarea.mjs +13 -8
  136. package/fesm2022/koobiq-components-textarea.mjs.map +1 -1
  137. package/fesm2022/koobiq-components-time-range.mjs +29 -24
  138. package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
  139. package/fesm2022/koobiq-components-timepicker.mjs +61 -30
  140. package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
  141. package/fesm2022/koobiq-components-timezone.mjs +77 -42
  142. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  143. package/fesm2022/koobiq-components-title.mjs +7 -7
  144. package/fesm2022/koobiq-components-toast.mjs +51 -26
  145. package/fesm2022/koobiq-components-toast.mjs.map +1 -1
  146. package/fesm2022/koobiq-components-toggle.mjs +72 -64
  147. package/fesm2022/koobiq-components-toggle.mjs.map +1 -1
  148. package/fesm2022/koobiq-components-tooltip.mjs +219 -28
  149. package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
  150. package/fesm2022/koobiq-components-top-bar.mjs +13 -13
  151. package/fesm2022/koobiq-components-tree-select.mjs +222 -80
  152. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  153. package/fesm2022/koobiq-components-tree.mjs +302 -81
  154. package/fesm2022/koobiq-components-tree.mjs.map +1 -1
  155. package/fesm2022/koobiq-components-username.mjs +27 -27
  156. package/file-upload/index.d.ts +25 -6
  157. package/file-upload/multiple-file-upload.component.scss +14 -0
  158. package/filter-bar/_filter-bar-theme.scss +0 -10
  159. package/filter-bar/filter-bar-tokens.scss +3 -0
  160. package/filter-bar/filters.scss +3 -2
  161. package/filter-bar/index.d.ts +281 -9
  162. package/filter-bar/pipes/base-pipe.scss +0 -11
  163. package/filter-bar/pipes/pipe-input.scss +10 -0
  164. package/filter-bar/pipes/pipe-multi-tree-select.scss +0 -6
  165. package/filter-bar/pipes/pipe-multiselect.scss +0 -6
  166. package/filter-bar/pipes/pipe-tree-select.scss +0 -6
  167. package/form-field/_form-field-theme.scss +76 -20
  168. package/form-field/fieldset.scss +10 -0
  169. package/form-field/form-field-tokens.scss +15 -1
  170. package/form-field/form-field.scss +15 -19
  171. package/form-field/index.d.ts +147 -65
  172. package/icon/icon-button.scss +3 -1
  173. package/icon/index.d.ts +45 -7
  174. package/inline-edit/index.d.ts +57 -4
  175. package/inline-edit/inline-edit.scss +10 -0
  176. package/input/index.d.ts +17 -2
  177. package/list/_list-theme.scss +42 -10
  178. package/list/index.d.ts +326 -79
  179. package/list/list-tokens.scss +43 -8
  180. package/list/list.scss +186 -12
  181. package/markdown/index.d.ts +1 -1
  182. package/modal/index.d.ts +21 -4
  183. package/modal/modal-tokens.scss +1 -1
  184. package/navbar/_navbar-item_vertical.scss +7 -5
  185. package/navbar/index.d.ts +54 -5
  186. package/navbar/navbar-brand.scss +23 -7
  187. package/navbar/vertical-navbar.scss +6 -0
  188. package/notification-center/index.d.ts +2 -1
  189. package/notification-center/notification-item.scss +6 -0
  190. package/package.json +3 -6
  191. package/popover/index.d.ts +2 -2
  192. package/prebuilt-themes/dark-theme.css +1 -1
  193. package/prebuilt-themes/light-theme.css +1 -1
  194. package/prebuilt-themes/theme.css +1 -1
  195. package/resizer/index.d.ts +12 -0
  196. package/schematics/collection.json +10 -0
  197. package/schematics/migrations/alert-signals/README.md +64 -0
  198. package/schematics/migrations/alert-signals/data.js +70 -0
  199. package/schematics/migrations/alert-signals/index.js +350 -0
  200. package/schematics/migrations/alert-signals/schema.json +20 -0
  201. package/schematics/migrations/autocomplete-panel-width-auto/README.md +68 -0
  202. package/schematics/migrations/autocomplete-panel-width-auto/index.js +70 -0
  203. package/schematics/migrations/autocomplete-panel-width-auto/schema.json +15 -0
  204. package/schematics/migrations/deprecated-icons/README.md +29 -4
  205. package/schematics/migrations/deprecated-icons/data.js +7 -0
  206. package/schematics/migrations/deprecated-icons/index.js +62 -35
  207. package/schematics/migrations/filter-bar-signals/README.md +1 -1
  208. package/schematics/migrations/new-icons-pack/README.md +37 -2
  209. package/schematics/migrations/new-icons-pack/data.js +8 -0
  210. package/schematics/migrations/new-icons-pack/index.js +144 -53
  211. package/schematics/migrations.json +11 -1
  212. package/schematics/ng-add/index.js +3 -3
  213. package/schematics/utils/angular-parsing.js +41 -14
  214. package/schematics/utils/ast.js +31 -0
  215. package/schematics/utils/icon-migration.js +530 -0
  216. package/schematics/utils/typescript.js +44 -0
  217. package/scrollbar/index.d.ts +1 -1
  218. package/search-expandable/index.d.ts +3 -0
  219. package/select/index.d.ts +212 -40
  220. package/select/select-tokens.scss +0 -1
  221. package/select/select.scss +0 -2
  222. package/splitter/index.d.ts +1 -1
  223. package/table/_table-theme.scss +4 -0
  224. package/table/index.d.ts +2 -1
  225. package/table/table.scss +8 -0
  226. package/tabs/_tabs-common.scss +60 -31
  227. package/tabs/_tabs-theme.scss +12 -0
  228. package/tabs/index.d.ts +2 -2
  229. package/tabs/tab-header.scss +14 -0
  230. package/tabs/tab-nav-bar.scss +4 -0
  231. package/tabs/tabs-tokens.scss +1 -0
  232. package/tags/index.d.ts +128 -29
  233. package/textarea/index.d.ts +5 -0
  234. package/time-range/index.d.ts +1 -1
  235. package/timepicker/index.d.ts +4 -0
  236. package/timezone/index.d.ts +10 -8
  237. package/toast/index.d.ts +26 -3
  238. package/toggle/index.d.ts +34 -14
  239. package/tooltip/index.d.ts +119 -7
  240. package/tree/_tree-theme.scss +25 -5
  241. package/tree/index.d.ts +71 -3
  242. package/tree/tree-option.scss +2 -8
  243. package/tree-select/_tree-select-theme.scss +22 -0
  244. package/tree-select/index.d.ts +127 -36
  245. package/accordion/accordion.component.scss +0 -50
  246. package/core/styles/_tokens.kbq.import.scss +0 -4
  247. package/core/styles/theming/_components-theming.scss +0 -1927
  248. package/filter-bar/pipes/pipe-select.scss +0 -5
  249. package/overflow-items/overflow-items.scss +0 -20
@@ -1,200 +1,96 @@
1
- import * as i0 from '@angular/core';
2
- import { inject, ElementRef, ChangeDetectorRef, contentChildren, forwardRef, HostAttributeToken, input, booleanAttribute, output, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, Directive, viewChild, afterNextRender, contentChild, EventEmitter, Renderer2, signal, NgModule } from '@angular/core';
3
- import { Platform } from '@angular/cdk/platform';
4
- import { KBQ_WINDOW, kbqInjectNativeElement } from '@koobiq/components/core';
5
- import { UniqueSelectionDispatcher } from '@angular/cdk/collections';
6
- import { Subject, merge, Subscription } from 'rxjs';
7
- import { KbqIcon } from '@koobiq/components/icon';
8
1
  import { FocusMonitor, FocusKeyManager } from '@angular/cdk/a11y';
9
2
  import { Directionality } from '@angular/cdk/bidi';
10
- import { ENTER, SPACE, TAB } from '@angular/cdk/keycodes';
11
- import { outputToObservable } from '@angular/core/rxjs-interop';
3
+ import { UniqueSelectionDispatcher } from '@angular/cdk/collections';
4
+ import { ENTER, SPACE } from '@angular/cdk/keycodes';
5
+ import * as i0 from '@angular/core';
6
+ import { inject, Renderer2, signal, afterNextRender, RendererStyleFlags2, Directive, viewChild, ViewEncapsulation, Component, ChangeDetectorRef, contentChild, forwardRef, input, computed, output, booleanAttribute, Input, Injectable, InjectionToken, ElementRef, Injector, contentChildren, numberAttribute, effect, untracked, isDevMode, ChangeDetectionStrategy, NgModule } from '@angular/core';
7
+ import { Subscription, Subject } from 'rxjs';
8
+ import { kbqInjectNativeElement, KBQ_WINDOW } from '@koobiq/components/core';
9
+ import { KbqIcon } from '@koobiq/components/icon';
10
+ import { Platform } from '@angular/cdk/platform';
12
11
 
13
- var KbqAccordionVariant;
14
- (function (KbqAccordionVariant) {
15
- KbqAccordionVariant["fill"] = "fill";
16
- KbqAccordionVariant["hug"] = "hug";
17
- KbqAccordionVariant["hugSpaceBetween"] = "hugSpaceBetween";
18
- })(KbqAccordionVariant || (KbqAccordionVariant = {}));
19
- let uniqueIdCounter$1 = 0;
20
- class KbqAccordion {
21
- /**
22
- * The value of the item to expand when initially rendered and type is "single". Use when you do not need to control the state of the items.
23
- */
24
- // TODO: Skipped for migration because:
25
- // Accessor inputs cannot be migrated as they are too complex.
26
- get defaultValue() {
27
- return this.isMultiple ? this._defaultValue : this._defaultValue[0];
28
- }
29
- set defaultValue(value) {
30
- if (value !== this._defaultValue) {
31
- this._defaultValue = Array.isArray(value) ? value : [value];
32
- }
33
- }
34
- /** The controlled value of the item to expand */
35
- // TODO: Skipped for migration because:
36
- // Accessor inputs cannot be migrated as they are too complex.
37
- get value() {
38
- if (this._value === undefined) {
39
- return this.defaultValue;
40
- }
41
- return this.isMultiple ? this._value : this._value[0];
42
- }
43
- set value(value) {
44
- if (value !== this._value) {
45
- this._value = Array.isArray(value) ? value : [value];
46
- this.selectionDispatcher.notify(this.value, this.id);
47
- }
48
- }
49
- get id() {
50
- return this._id;
51
- }
52
- get isMultiple() {
53
- return this.type() === 'multiple';
12
+ class KbqAccordionContentDirective {
13
+ /** @docs-private */
14
+ get contentId() {
15
+ return `${this.item.id}-content`;
54
16
  }
55
- get hasSavedState() {
56
- return typeof this.state === 'object' && this.state !== null;
17
+ /** @docs-private */
18
+ get triggerId() {
19
+ return `${this.item.id}-trigger`;
57
20
  }
58
21
  constructor() {
59
- this.isBrowser = inject(Platform).isBrowser;
60
- this.localStorage = inject(KBQ_WINDOW)?.localStorage;
61
- /** @docs-private */
62
- this.focusMonitor = inject(FocusMonitor);
63
- /** @docs-private */
64
- this.elementRef = inject(ElementRef);
65
- /** @docs-private */
66
- this.changeDetectorRef = inject(ChangeDetectorRef);
22
+ this.renderer = inject(Renderer2);
67
23
  /** @docs-private */
68
- this.selectionDispatcher = inject(UniqueSelectionDispatcher);
24
+ this.nativeElement = kbqInjectNativeElement();
69
25
  /** @docs-private */
70
- this.dir = inject(Directionality, { optional: true });
26
+ this.item = inject(KbqAccordionItem);
71
27
  /** @docs-private */
72
- this.openCloseAllActions = new Subject();
73
- /** @docs-private */
74
- this.items = contentChildren(forwardRef(() => KbqAccordionItem), ...(ngDevMode ? [{ debugName: "items", descendants: true }] : [{ descendants: true }]));
75
- /** Specifies whether the accordion saves its states. Default is false */
76
- this.useStateSaving = inject(new HostAttributeToken('useStateSaving'), { optional: true }) !== null;
77
- this.variant = input(KbqAccordionVariant.fill, ...(ngDevMode ? [{ debugName: "variant" }] : []));
78
- /** Whether the Accordion is disabled. */
79
- this.disabled = input(undefined, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
80
- /** The orientation of the accordion. */
81
- this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
82
- /** Determines whether one or multiple items can be opened at the same time. */
83
- this.type = input('single', ...(ngDevMode ? [{ debugName: "type" }] : []));
84
- this.collapsible = input(true, ...(ngDevMode ? [{ debugName: "collapsible" }] : []));
85
- this.onValueChange = output();
86
- this._defaultValue = [];
87
- this._id = `kbq-accordion-${uniqueIdCounter$1++}`;
88
- if (this.useStateSaving) {
89
- this.getSavedState();
90
- }
91
- }
92
- ngAfterContentInit() {
93
- if (this._value) {
94
- this.selectionDispatcher.notify(this._value, this.id);
95
- }
96
- else if (this.useStateSaving && this.hasSavedState) {
97
- Object.keys(this.state).forEach((key) => {
98
- if (this.state && this.state[key].expanded) {
99
- this.selectionDispatcher.notify(this.state[key].value, this.id);
100
- }
101
- });
102
- }
103
- else {
104
- this.selectionDispatcher.notify(this._defaultValue, this.id);
105
- }
106
- this.keyManager = new FocusKeyManager(this.items()).withHomeAndEnd();
107
- if (this.orientation() === 'horizontal') {
108
- this.keyManager.withHorizontalOrientation(this.dir?.value || 'ltr');
109
- }
110
- else {
111
- this.keyManager.withVerticalOrientation();
112
- }
113
- this.onValueChangeSubscription = merge(...this.items().map((item) => outputToObservable(item.expandedChange))).subscribe(() => this.onValueChange.emit());
28
+ this.hidden = signal(true, ...(ngDevMode ? [{ debugName: "hidden" }] : []));
29
+ /** Whether the first browser render has happened, i.e. whether the content can be measured. */
30
+ this.rendered = false;
31
+ // `afterNextRender` never runs on the server, so no platform check is needed here.
32
+ this.afterRenderRef = afterNextRender(() => {
33
+ this.rendered = true;
34
+ this.updateHeight();
35
+ this.enableAnimation();
36
+ this.afterRenderRef?.destroy();
37
+ });
114
38
  }
115
39
  ngAfterViewInit() {
116
- this.focusMonitor.monitor(this.elementRef, true);
117
- }
118
- ngOnDestroy() {
119
- this.focusMonitor.stopMonitoring(this.elementRef);
120
- this.openCloseAllActions.complete();
121
- this.onValueChangeSubscription.unsubscribe();
122
- }
123
- /** @docs-private */
124
- keydownHandler(event) {
125
- if (!this.keyManager.activeItem) {
126
- this.keyManager.setFirstItemActive();
127
- }
128
- const activeItem = this.keyManager.activeItem;
129
- if ((event.keyCode === ENTER || event.keyCode === SPACE) &&
130
- !this.keyManager.isTyping() &&
131
- activeItem &&
132
- !activeItem.disabled) {
133
- event.preventDefault();
134
- activeItem.toggle();
135
- }
136
- else if (event.keyCode === TAB && event.shiftKey) {
137
- if (this.keyManager.activeItemIndex === 0)
138
- return;
139
- this.keyManager.setPreviousItemActive();
140
- event.preventDefault();
141
- }
142
- else if (event.keyCode === TAB) {
143
- if (this.keyManager.activeItemIndex === this.items().length - 1)
144
- return;
145
- this.keyManager.setNextItemActive();
146
- event.preventDefault();
147
- }
148
- else {
149
- this.keyManager.onKeydown(event);
150
- }
40
+ this.disableAnimation();
151
41
  }
152
- /** Opens all enabled accordion items in an accordion where multi is enabled. */
153
- openAll() {
154
- if (this.isMultiple) {
155
- this.openCloseAllActions.next(true);
42
+ toggle() {
43
+ // Re-measure while the content is still collapsed. Its natural height depends on the width
44
+ // it is finally laid out at, and that width is not known at first render when the accordion
45
+ // lives in a container sized afterwards (sidepanel, overlay, responsive layout).
46
+ if (this.rendered && this.item.expanded && this.hidden()) {
47
+ this.updateHeight();
156
48
  }
49
+ this.hidden.set(!this.item.expanded);
157
50
  }
158
- /** Closes all enabled accordion items. */
159
- closeAll() {
160
- this.openCloseAllActions.next(false);
161
- }
162
- /** @docs-private */
163
- setActiveItem(item) {
164
- this.keyManager.setActiveItem(item);
51
+ disableAnimation() {
52
+ this.savedTransition = this.nativeElement.style.transition;
53
+ this.nativeElement.style.transition = 'none';
165
54
  }
166
- /** @docs-private */
167
- saveItemState(item, force = true) {
168
- if (!this.isBrowser || (!force && !!this.state && !!this.state[item.id]))
169
- return;
170
- this.state = this.state || {};
171
- this.state[item.id] = item.getState();
172
- this.localStorage.setItem(this.id, JSON.stringify(this.state));
55
+ enableAnimation() {
56
+ this.nativeElement.style.transition = this.savedTransition;
173
57
  }
174
- getSavedState() {
175
- if (!this.isBrowser)
176
- return;
177
- this.state = JSON.parse(this.localStorage.getItem(this.id));
58
+ /**
59
+ * Publishes the natural height of the projected content as `--kbq-accordion-content-height`,
60
+ * which the open state animates towards.
61
+ *
62
+ * `scrollHeight` is used rather than `getBoundingClientRect()`: the host is `overflow: hidden`
63
+ * and collapsed to `height: 0` while closed, so its box reports `0`, whereas `scrollHeight`
64
+ * still reports the height of the content it clips.
65
+ */
66
+ updateHeight() {
67
+ this.renderer.setStyle(this.nativeElement, '--kbq-accordion-content-height', `${this.nativeElement.scrollHeight}px`, RendererStyleFlags2.DashCase);
178
68
  }
179
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
180
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.25", type: KbqAccordion, isStandalone: true, selector: "kbq-accordion, [kbq-accordion]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: false, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onValueChange: "onValueChange" }, host: { listeners: { "keydown": "keydownHandler($event)" }, properties: { "attr.data-orientation": "orientation()" }, classAttribute: "kbq-accordion" }, providers: [
181
- { provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }
182
- ], queries: [{ propertyName: "items", predicate: i0.forwardRef(() => KbqAccordionItem), descendants: true, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true, styles: [".kbq-accordion{display:block}.kbq-accordion-item{box-sizing:border-box;display:block;border-width:2px;border-style:solid;border-color:transparent}.kbq-accordion-header{display:flex;height:var(--kbq-accordion-size-item-header-height)}.kbq-accordion-content{display:block;overflow:hidden}.kbq-accordion-content>p{margin:unset;padding:var(--kbq-accordion-size-item-content-padding)}.kbq-accordion-content[data-state=open]{transition:height .3s ease-out;height:var(--radix-accordion-content-height)}.kbq-accordion-content[data-state=closed]{transition:height .3s ease-out;height:0}@media(prefers-reduced-motion:reduce){.kbq-accordion-content[data-state=open],.kbq-accordion-content[data-state=closed]{transition:none}}.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:focus-within{border-color:var(--kbq-accordion-item-states-focus-border-color)}.kbq-accordion-item{background:var(--kbq-accordion-item-default-background);color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-item .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-default-icon-color)}.kbq-accordion-trigger{width:100%;background:transparent;border:none;outline:none;color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-trigger:hover:not([data-disabled=true]){cursor:pointer}.kbq-accordion-trigger:hover:not([data-disabled=true]) .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-hover-icon-color)}.kbq-accordion-trigger[data-disabled=true]{color:var(--kbq-accordion-item-states-disabled-text-color)}.kbq-accordion-trigger[data-disabled=true] .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-disabled-icon-color)!important}.kbq-accordion-header,.kbq-accordion-trigger{font-size:var(--kbq-typography-text-big-font-size);font-style:var(--kbq-typography-text-big-font-style);font-weight:var(--kbq-typography-text-big-font-weight);line-height:var(--kbq-typography-text-big-line-height);font-family:var(--kbq-typography-text-big-font-family);text-transform:var(--kbq-typography-text-big-text-transform);font-feature-settings:var(--kbq-typography-text-big-font-feature-settings);letter-spacing:var(--kbq-typography-text-big-letter-spacing)}.kbq-accordion-trigger__caption{font-size:var(--kbq-typography-text-compact-font-size);font-style:var(--kbq-typography-text-compact-font-style);font-weight:var(--kbq-typography-text-compact-font-weight);line-height:var(--kbq-typography-text-compact-line-height);font-family:var(--kbq-typography-text-compact-font-family);text-transform:var(--kbq-typography-text-compact-text-transform);font-feature-settings:var(--kbq-typography-text-compact-font-feature-settings);letter-spacing:var(--kbq-typography-text-compact-letter-spacing)}.kbq-accordion-content{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-accordion{--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
69
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
70
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqAccordionContentDirective, isStandalone: true, selector: "[kbqAccordionContent]", host: { listeners: { "transitionend": "toggle()" }, properties: { "attr.id": "contentId", "attr.role": "\"region\"", "attr.hidden": "hidden() ? \"\" : null", "attr.inert": "hidden() ? \"\" : null", "attr.aria-labelledby": "triggerId", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" } }, exportAs: ["kbqAccordionContent"], ngImport: i0 }); }
183
71
  }
184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordion, decorators: [{
185
- type: Component,
186
- args: [{ selector: 'kbq-accordion, [kbq-accordion]', template: '<ng-content />', providers: [
187
- { provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }
188
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
189
- class: 'kbq-accordion',
190
- '[attr.data-orientation]': 'orientation()',
191
- '(keydown)': 'keydownHandler($event)'
192
- }, styles: [".kbq-accordion{display:block}.kbq-accordion-item{box-sizing:border-box;display:block;border-width:2px;border-style:solid;border-color:transparent}.kbq-accordion-header{display:flex;height:var(--kbq-accordion-size-item-header-height)}.kbq-accordion-content{display:block;overflow:hidden}.kbq-accordion-content>p{margin:unset;padding:var(--kbq-accordion-size-item-content-padding)}.kbq-accordion-content[data-state=open]{transition:height .3s ease-out;height:var(--radix-accordion-content-height)}.kbq-accordion-content[data-state=closed]{transition:height .3s ease-out;height:0}@media(prefers-reduced-motion:reduce){.kbq-accordion-content[data-state=open],.kbq-accordion-content[data-state=closed]{transition:none}}.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:focus-within{border-color:var(--kbq-accordion-item-states-focus-border-color)}.kbq-accordion-item{background:var(--kbq-accordion-item-default-background);color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-item .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-default-icon-color)}.kbq-accordion-trigger{width:100%;background:transparent;border:none;outline:none;color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-trigger:hover:not([data-disabled=true]){cursor:pointer}.kbq-accordion-trigger:hover:not([data-disabled=true]) .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-hover-icon-color)}.kbq-accordion-trigger[data-disabled=true]{color:var(--kbq-accordion-item-states-disabled-text-color)}.kbq-accordion-trigger[data-disabled=true] .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-disabled-icon-color)!important}.kbq-accordion-header,.kbq-accordion-trigger{font-size:var(--kbq-typography-text-big-font-size);font-style:var(--kbq-typography-text-big-font-style);font-weight:var(--kbq-typography-text-big-font-weight);line-height:var(--kbq-typography-text-big-line-height);font-family:var(--kbq-typography-text-big-font-family);text-transform:var(--kbq-typography-text-big-text-transform);font-feature-settings:var(--kbq-typography-text-big-font-feature-settings);letter-spacing:var(--kbq-typography-text-big-letter-spacing)}.kbq-accordion-trigger__caption{font-size:var(--kbq-typography-text-compact-font-size);font-style:var(--kbq-typography-text-compact-font-style);font-weight:var(--kbq-typography-text-compact-font-weight);line-height:var(--kbq-typography-text-compact-line-height);font-family:var(--kbq-typography-text-compact-font-family);text-transform:var(--kbq-typography-text-compact-text-transform);font-feature-settings:var(--kbq-typography-text-compact-font-feature-settings);letter-spacing:var(--kbq-typography-text-compact-letter-spacing)}.kbq-accordion-content{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-accordion{--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"] }]
193
- }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.ContentChildren, args: [forwardRef(() => KbqAccordionItem), { ...{ descendants: true }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], defaultValue: [{
194
- type: Input
195
- }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], value: [{
196
- type: Input
197
- }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
72
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionContentDirective, decorators: [{
73
+ type: Directive,
74
+ args: [{
75
+ selector: '[kbqAccordionContent]',
76
+ host: {
77
+ '[attr.id]': 'contentId',
78
+ '[attr.role]': '"region"',
79
+ '[attr.hidden]': 'hidden() ? "" : null',
80
+ // `hidden` alone does not take the collapsed content out of the tab order: the host keeps an
81
+ // explicit `display: block` so the height can animate, which overrides `[hidden]`'s
82
+ // `display: none`. Without `inert`, every control inside a closed section stays focusable in
83
+ // a zero-height, clipped box.
84
+ '[attr.inert]': 'hidden() ? "" : null',
85
+ '[attr.aria-labelledby]': 'triggerId',
86
+ '[attr.data-state]': 'item.dataState',
87
+ '[attr.data-disabled]': 'item.disabled',
88
+ '[attr.data-orientation]': 'item.orientation',
89
+ '(transitionend)': 'toggle()'
90
+ },
91
+ exportAs: 'kbqAccordionContent'
92
+ }]
93
+ }], ctorParameters: () => [] });
198
94
 
199
95
  class KbqAccordionTriggerDirective {
200
96
  constructor() {
@@ -220,14 +116,38 @@ class KbqAccordionTriggerDirective {
220
116
  this.item.toggle();
221
117
  this.accordion.setActiveItem(this.item);
222
118
  }
119
+ /**
120
+ * Keeps the accordion's key manager in sync with native focus so that arrow-key navigation
121
+ * continues from the header the user has just tabbed/focused into.
122
+ * @docs-private
123
+ */
124
+ onFocus() {
125
+ this.accordion.setActiveItem(this.item);
126
+ }
127
+ /**
128
+ * Routes keyboard interaction to the accordion, but only while the trigger itself is focused.
129
+ *
130
+ * A `keydown` targets the focused element, so header actions placed next to the trigger and
131
+ * controls inside the expanded content keep their own keys — the accordion no longer toggles the
132
+ * section on Enter/Space nor moves focus to another header on the arrow keys. The `target` check
133
+ * additionally covers focusable content mistakenly nested inside the trigger.
134
+ */
135
+ onKeydown(event) {
136
+ if (event.target !== this.nativeElement)
137
+ return;
138
+ // The key manager can lag behind when focus was moved programmatically rather than by the
139
+ // user, in which case `onFocus` never ran.
140
+ this.accordion.setActiveItem(this.item);
141
+ this.accordion.keydownHandler(event);
142
+ }
223
143
  /** @docs-private */
224
144
  focus() {
225
145
  this.nativeElement.focus();
226
146
  }
227
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
228
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqAccordionTriggerDirective, isStandalone: true, selector: "[kbqAccordionTrigger]", host: { listeners: { "click": "onClick()" }, properties: { "attr.id": "triggerId", "attr.role": "\"button\"", "attr.aria-expanded": "item.expanded", "attr.aria-controls": "contentId", "attr.aria-disabled": "item.disabled", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" } }, ngImport: i0 }); }
147
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
148
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqAccordionTriggerDirective, isStandalone: true, selector: "[kbqAccordionTrigger]", host: { listeners: { "click": "onClick()", "focus": "onFocus()", "keydown": "onKeydown($event)" }, properties: { "attr.id": "triggerId", "attr.role": "\"button\"", "attr.aria-expanded": "item.expanded", "attr.aria-controls": "contentId", "attr.aria-disabled": "item.disabled", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" } }, ngImport: i0 }); }
229
149
  }
230
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionTriggerDirective, decorators: [{
150
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionTriggerDirective, decorators: [{
231
151
  type: Directive,
232
152
  args: [{
233
153
  selector: '[kbqAccordionTrigger]',
@@ -240,20 +160,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
240
160
  '[attr.data-state]': 'item.dataState',
241
161
  '[attr.data-disabled]': 'item.disabled',
242
162
  '[attr.data-orientation]': 'item.orientation',
243
- '(click)': 'onClick()'
163
+ '(click)': 'onClick()',
164
+ '(focus)': 'onFocus()',
165
+ '(keydown)': 'onKeydown($event)'
244
166
  }
245
167
  }]
246
168
  }] });
247
169
 
248
170
  class KbqAccordionTrigger {
249
171
  get isFill() {
250
- return this.accordion.variant() === KbqAccordionVariant.fill;
172
+ return this.accordion.variant() === 'fill';
251
173
  }
252
174
  get isHug() {
253
- return this.accordion.variant() === KbqAccordionVariant.hug;
175
+ return this.accordion.variant() === 'hug';
254
176
  }
255
177
  get isHugSpaceBetween() {
256
- return this.accordion.variant() === KbqAccordionVariant.hugSpaceBetween;
178
+ return this.accordion.variant() === 'hugSpaceBetween';
257
179
  }
258
180
  constructor() {
259
181
  /** @docs-private */
@@ -263,13 +185,16 @@ class KbqAccordionTrigger {
263
185
  /** @docs-private */
264
186
  this.icon = viewChild.required(KbqIcon);
265
187
  this.afterRenderRef = afterNextRender(() => {
266
- setTimeout(() => this.enableAnimation());
188
+ this.animationTimerId = setTimeout(() => this.enableAnimation());
267
189
  this.afterRenderRef?.destroy();
268
190
  });
269
191
  }
270
192
  ngAfterViewInit() {
271
193
  this.disableAnimation();
272
194
  }
195
+ ngOnDestroy() {
196
+ clearTimeout(this.animationTimerId);
197
+ }
273
198
  disableAnimation() {
274
199
  this.savedTransition = this.icon().elementRef.nativeElement.style.transition;
275
200
  this.icon().getHostElement().style.transition = 'none';
@@ -277,17 +202,17 @@ class KbqAccordionTrigger {
277
202
  enableAnimation() {
278
203
  this.icon().getHostElement().style.transition = this.savedTransition;
279
204
  }
280
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionTrigger, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
281
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.25", type: KbqAccordionTrigger, isStandalone: true, selector: "kbq-accordion-trigger, [kbq-accordion-trigger]", host: { properties: { "class.kbq-accordion-trigger_fill": "isFill", "class.kbq-accordion-trigger_hug": "isHug", "class.kbq-accordion-trigger_hug-space-between": "isHugSpaceBetween" }, classAttribute: "kbq-accordion-trigger" }, viewQueries: [{ propertyName: "icon", first: true, predicate: KbqIcon, descendants: true, isSignal: true }], hostDirectives: [{ directive: KbqAccordionTriggerDirective }], ngImport: i0, template: `
205
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionTrigger, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
206
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.30", type: KbqAccordionTrigger, isStandalone: true, selector: "kbq-accordion-trigger, [kbq-accordion-trigger]", host: { properties: { "class.kbq-accordion-trigger_fill": "isFill", "class.kbq-accordion-trigger_hug": "isHug", "class.kbq-accordion-trigger_hug-space-between": "isHugSpaceBetween" }, classAttribute: "kbq-accordion-trigger" }, viewQueries: [{ propertyName: "icon", first: true, predicate: KbqIcon, descendants: true, isSignal: true }], hostDirectives: [{ directive: KbqAccordionTriggerDirective }], ngImport: i0, template: `
282
207
  <i
283
208
  kbq-icon="{{ isHugSpaceBetween ? 'kbq-chevron-down-s_16' : 'kbq-chevron-right-s_16' }}"
284
209
  class="kbq-accordion-trigger__icon"
285
210
  ></i>
286
211
 
287
212
  <ng-content />
288
- `, isInline: true, styles: [".kbq-accordion-trigger{display:flex;flex-direction:row;align-items:center}.kbq-accordion-trigger .kbq-accordion-trigger__icon{box-sizing:initial;align-self:flex-start;padding:var(--kbq-size-xxs);transition:transform .3s ease-out}.kbq-accordion-trigger .kbq-accordion-trigger__badge,.kbq-accordion-trigger .kbq-accordion-trigger__caption{margin-top:var(--kbq-size-xxs);margin-right:var(--kbq-size-s)}.kbq-accordion-trigger .kbq-accordion-trigger__action{padding:var(--kbq-size-xxs) var(--kbq-size-xxs) var(--kbq-size-3xs);margin-bottom:calc(-1 * var(--kbq-size-3xs))}.kbq-accordion-trigger.kbq-accordion-trigger_fill{padding:var(--kbq-accordion-size-item-header-variant-fill-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_fill[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug{flex-direction:row-reverse;justify-content:flex-end;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between{flex-direction:row-reverse;justify-content:space-between;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(-180deg)}button.kbq-accordion-trigger{flex:1}\n", ".kbq-accordion{--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"], dependencies: [{ kind: "component", type: KbqIcon, selector: "[kbq-icon]", inputs: ["small", "autoColor", "kbq-icon"] }], encapsulation: i0.ViewEncapsulation.None }); }
213
+ `, isInline: true, styles: [".kbq-accordion-trigger{display:flex;flex-direction:row;align-items:center}.kbq-accordion-trigger .kbq-accordion-trigger__icon{box-sizing:initial;align-self:flex-start;padding:var(--kbq-size-xxs);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.kbq-accordion-trigger .kbq-accordion-trigger__icon{transition:none}}.kbq-accordion-trigger .kbq-accordion-trigger__badge,.kbq-accordion-trigger .kbq-accordion-trigger__caption{margin-top:var(--kbq-size-xxs);margin-right:var(--kbq-size-s)}.kbq-accordion-trigger .kbq-accordion-trigger__action{padding:var(--kbq-size-xxs) var(--kbq-size-xxs) var(--kbq-size-3xs);margin-bottom:calc(-1 * var(--kbq-size-3xs))}.kbq-accordion-trigger.kbq-accordion-trigger_fill{padding:var(--kbq-accordion-size-item-header-variant-fill-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_fill[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug{flex-direction:row-reverse;justify-content:flex-end;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between{flex-direction:row-reverse;justify-content:space-between;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(-180deg)}button.kbq-accordion-trigger{flex:1;min-width:0}\n", ".kbq-accordion{--kbq-accordion-size-item-border-width: 2px;--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-size-item-header-actions-gap: var(--kbq-size-3xs);--kbq-accordion-size-item-header-actions-padding-right: var(--kbq-size-s);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"], dependencies: [{ kind: "component", type: KbqIcon, selector: "[kbq-icon]", inputs: ["small", "autoColor", "kbq-icon"] }], encapsulation: i0.ViewEncapsulation.None }); }
289
214
  }
290
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionTrigger, decorators: [{
215
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionTrigger, decorators: [{
291
216
  type: Component,
292
217
  args: [{ selector: 'kbq-accordion-trigger, [kbq-accordion-trigger]', imports: [KbqIcon], template: `
293
218
  <i
@@ -301,24 +226,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
301
226
  '[class.kbq-accordion-trigger_fill]': 'isFill',
302
227
  '[class.kbq-accordion-trigger_hug]': 'isHug',
303
228
  '[class.kbq-accordion-trigger_hug-space-between]': 'isHugSpaceBetween'
304
- }, hostDirectives: [KbqAccordionTriggerDirective], styles: [".kbq-accordion-trigger{display:flex;flex-direction:row;align-items:center}.kbq-accordion-trigger .kbq-accordion-trigger__icon{box-sizing:initial;align-self:flex-start;padding:var(--kbq-size-xxs);transition:transform .3s ease-out}.kbq-accordion-trigger .kbq-accordion-trigger__badge,.kbq-accordion-trigger .kbq-accordion-trigger__caption{margin-top:var(--kbq-size-xxs);margin-right:var(--kbq-size-s)}.kbq-accordion-trigger .kbq-accordion-trigger__action{padding:var(--kbq-size-xxs) var(--kbq-size-xxs) var(--kbq-size-3xs);margin-bottom:calc(-1 * var(--kbq-size-3xs))}.kbq-accordion-trigger.kbq-accordion-trigger_fill{padding:var(--kbq-accordion-size-item-header-variant-fill-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_fill[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug{flex-direction:row-reverse;justify-content:flex-end;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between{flex-direction:row-reverse;justify-content:space-between;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(-180deg)}button.kbq-accordion-trigger{flex:1}\n", ".kbq-accordion{--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"] }]
229
+ }, hostDirectives: [KbqAccordionTriggerDirective], styles: [".kbq-accordion-trigger{display:flex;flex-direction:row;align-items:center}.kbq-accordion-trigger .kbq-accordion-trigger__icon{box-sizing:initial;align-self:flex-start;padding:var(--kbq-size-xxs);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.kbq-accordion-trigger .kbq-accordion-trigger__icon{transition:none}}.kbq-accordion-trigger .kbq-accordion-trigger__badge,.kbq-accordion-trigger .kbq-accordion-trigger__caption{margin-top:var(--kbq-size-xxs);margin-right:var(--kbq-size-s)}.kbq-accordion-trigger .kbq-accordion-trigger__action{padding:var(--kbq-size-xxs) var(--kbq-size-xxs) var(--kbq-size-3xs);margin-bottom:calc(-1 * var(--kbq-size-3xs))}.kbq-accordion-trigger.kbq-accordion-trigger_fill{padding:var(--kbq-accordion-size-item-header-variant-fill-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_fill[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug{flex-direction:row-reverse;justify-content:flex-end;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(90deg)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between{flex-direction:row-reverse;justify-content:space-between;padding:var(--kbq-accordion-size-item-header-variant-hug-padding)}.kbq-accordion-trigger.kbq-accordion-trigger_hug-space-between[data-state=open]>.kbq-accordion-trigger__icon{transform:rotate(-180deg)}button.kbq-accordion-trigger{flex:1;min-width:0}\n", ".kbq-accordion{--kbq-accordion-size-item-border-width: 2px;--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-size-item-header-actions-gap: var(--kbq-size-3xs);--kbq-accordion-size-item-header-actions-padding-right: var(--kbq-size-s);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"] }]
305
230
  }], ctorParameters: () => [], propDecorators: { icon: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqIcon), { isSignal: true }] }] } });
306
231
 
307
- let uniqueIdCounter = 0;
232
+ let uniqueIdCounter$1 = 0;
308
233
  class KbqAccordionItem {
309
234
  /** The unique AccordionItem id. */
310
235
  get id() {
311
236
  return this._id;
312
237
  }
238
+ /** The current open/closed state of the item. @docs-private */
313
239
  get dataState() {
314
240
  return this.expanded ? 'open' : 'closed';
315
241
  }
242
+ /** The layout orientation inherited from the parent accordion. @docs-private */
316
243
  get orientation() {
317
244
  return this.accordion.orientation();
318
245
  }
319
246
  /** Whether the AccordionItem is expanded. */
320
- // TODO: Skipped for migration because:
321
- // Accessor inputs cannot be migrated as they are too complex.
247
+ // Kept as an `@Input` accessor (not `model()`): the setter runs a synchronous side-effect
248
+ // cascade (emits opened/closed/expandedChange, notifies the selection dispatcher, toggles the
249
+ // content and persists state) that sibling items rely on within the same change-detection tick.
322
250
  get expanded() {
323
251
  return this._expanded;
324
252
  }
@@ -334,39 +262,35 @@ class KbqAccordionItem {
334
262
  * In the unique selection dispatcher, the id parameter is the id of the KbqAccordionItem,
335
263
  * the name value is the id of the accordion.
336
264
  */
337
- const accordionId = this.accordion ? this.accordion.id : this.value;
338
- this.expansionDispatcher.notify(this.value, accordionId);
265
+ this.expansionDispatcher.notify(this.value(), this.accordion.id);
339
266
  }
340
267
  else {
341
268
  // TODO: The 'emit' function requires a mandatory void argument
342
269
  this.closed.emit();
343
270
  }
344
- this.content().toggle();
271
+ this.content()?.toggle();
345
272
  this.accordion.saveItemState(this);
346
273
  // Ensures that the animation will run when the value is set outside of an `@Input`.
347
274
  // This includes cases like the open, close and toggle methods.
348
275
  this.changeDetectorRef.markForCheck();
349
276
  }
350
277
  }
351
- // TODO: Skipped for migration because:
352
- // Accessor inputs cannot be migrated as they are too complex.
353
- set value(value) {
354
- this._value = value;
355
- }
356
- get value() {
357
- return this._value || this.id;
358
- }
359
278
  /** Whether the AccordionItem is disabled. */
360
- // TODO: Skipped for migration because:
361
- // Accessor inputs cannot be migrated as they are too complex.
279
+ // Kept as an `@Input` accessor (not a signal): `KbqAccordionItem` is a `FocusKeyManager` option,
280
+ // and `ListKeyManagerOption.disabled` (core/a11y/key-manager) is read as a plain `boolean` — a
281
+ // signal function is always truthy, which makes the key manager treat every item as disabled.
282
+ // The getter also merges the item's own state with the parent accordion's (an effective value).
362
283
  get disabled() {
363
- return this.accordion.disabled() ?? this._disabled;
284
+ return this.accordion.disabled() || this._disabled;
364
285
  }
365
286
  set disabled(value) {
366
287
  this._disabled = value;
367
288
  }
368
289
  constructor() {
369
- /** @docs-private */
290
+ /**
291
+ * The accordion this item belongs to — always the nearest one, because it is injected.
292
+ * The accordion reads it back to tell its own items apart from a nested accordion's.
293
+ */
370
294
  this.accordion = inject(KbqAccordion);
371
295
  /** @docs-private */
372
296
  this.changeDetectorRef = inject(ChangeDetectorRef);
@@ -378,20 +302,23 @@ class KbqAccordionItem {
378
302
  this.triggerComponent = contentChild(KbqAccordionTrigger, ...(ngDevMode ? [{ debugName: "triggerComponent" }] : []));
379
303
  /** @docs-private */
380
304
  this.content = contentChild(forwardRef(() => KbqAccordionContentDirective), ...(ngDevMode ? [{ debugName: "content" }] : []));
381
- this._id = `kbq-accordion-item-${uniqueIdCounter++}`;
305
+ this._id = `kbq-accordion-item-${uniqueIdCounter$1++}`;
382
306
  this._expanded = false;
307
+ /**
308
+ * The item's own `value` input. Must stay `public`: a `protected` input cannot be bound from a
309
+ * consumer template (TS2445 under full AOT template type-checking).
310
+ * @docs-private
311
+ */
312
+ this.valueInput = input('', ...(ngDevMode ? [{ debugName: "valueInput", alias: 'value' }] : [{ alias: 'value' }]));
313
+ /** The item's effective value — its own value, or the item id when none is provided. */
314
+ this.value = computed(() => this.valueInput() || this.id, ...(ngDevMode ? [{ debugName: "value" }] : []));
383
315
  this._disabled = false;
384
316
  /** Event emitted every time the AccordionItem is closed. */
385
317
  this.closed = output();
386
318
  /** Event emitted every time the AccordionItem is opened. */
387
319
  this.opened = output();
388
320
  /**
389
- * Event emitted when the AccordionItem is destroyed.
390
- * @docs-private
391
- */
392
- this.destroyed = new EventEmitter();
393
- /**
394
- * Emits whenever the expanded state of the accordion changes.
321
+ * Emits whenever the expanded state of the accordion item changes.
395
322
  * Primarily used to facilitate two-way binding.
396
323
  * @docs-private
397
324
  */
@@ -399,28 +326,36 @@ class KbqAccordionItem {
399
326
  /** Subscription to openAll/closeAll events. */
400
327
  this.openCloseAllSubscription = Subscription.EMPTY;
401
328
  this.removeUniqueSelectionListener = this.expansionDispatcher.listen((id, accordionId) => {
402
- if (this.accordion.isMultiple) {
403
- if (this.accordion.id === accordionId && id.includes(this.value)) {
329
+ if (this.accordion.id !== accordionId)
330
+ return;
331
+ // The dispatcher payload is a single value (individual toggle / single-mode value) or an
332
+ // array (controlled `[value]`, default value and restored state). Match exactly to avoid
333
+ // substring collisions (e.g. `item-10` must not open `item-1`).
334
+ const value = id;
335
+ const ownValue = this.value();
336
+ if (Array.isArray(value)) {
337
+ // Sync to array membership: opens members, closes non-members.
338
+ this.expanded = value.includes(ownValue);
339
+ }
340
+ else if (this.accordion.isMultiple) {
341
+ // Individual toggle in multiple mode: only the matching item opens; others stay independent.
342
+ if (value === ownValue) {
404
343
  this.expanded = true;
405
344
  }
406
345
  }
407
346
  else {
408
- this.expanded = this.accordion.id === accordionId && id.includes(this.value);
347
+ // Individual toggle / controlled value in single mode: exactly one item stays open.
348
+ this.expanded = value === ownValue;
409
349
  }
410
350
  });
411
351
  // When an accordion item is hosted in an accordion, subscribe to open/close events.
412
- if (this.accordion) {
413
- this.openCloseAllSubscription = this.subscribeToOpenCloseAllActions();
414
- }
415
- this.accordion.saveItemState(this, false);
352
+ this.openCloseAllSubscription = this.subscribeToOpenCloseAllActions();
416
353
  }
417
- /** Emits an event for the accordion item being destroyed. */
418
354
  ngOnDestroy() {
419
- this.destroyed.emit();
420
- this.destroyed.complete();
421
355
  this.removeUniqueSelectionListener();
422
356
  this.openCloseAllSubscription.unsubscribe();
423
357
  }
358
+ /** Moves focus to the item's trigger. */
424
359
  focus() {
425
360
  this.trigger()?.focus();
426
361
  }
@@ -442,16 +377,19 @@ class KbqAccordionItem {
442
377
  this.expanded = true;
443
378
  }
444
379
  }
380
+ /** Returns a snapshot of the item's current persisted state. @docs-private */
445
381
  getState() {
446
382
  return {
447
383
  expanded: this.expanded,
448
- value: this.value
384
+ value: this.value()
449
385
  };
450
386
  }
387
+ /** Disables the open/close animation of the item's content and chevron. @docs-private */
451
388
  disableAnimation() {
452
389
  this.content()?.disableAnimation();
453
390
  this.triggerComponent()?.disableAnimation();
454
391
  }
392
+ /** Restores the open/close animation of the item's content and chevron. @docs-private */
455
393
  enableAnimation() {
456
394
  this.content()?.enableAnimation();
457
395
  this.triggerComponent()?.enableAnimation();
@@ -464,10 +402,10 @@ class KbqAccordionItem {
464
402
  }
465
403
  });
466
404
  }
467
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
468
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.25", type: KbqAccordionItem, isStandalone: true, selector: "kbq-accordion-item, [kbq-accordion-item]", inputs: { expanded: ["expanded", "expanded", booleanAttribute], value: "value", disabled: ["disabled", "disabled", booleanAttribute] }, outputs: { closed: "closed", opened: "opened", expandedChange: "expandedChange" }, host: { properties: { "attr.data-state": "dataState", "attr.data-disabled": "disabled", "attr.data-orientation": "orientation" }, classAttribute: "kbq-accordion-item" }, queries: [{ propertyName: "trigger", first: true, predicate: KbqAccordionTriggerDirective, descendants: true, isSignal: true }, { propertyName: "triggerComponent", first: true, predicate: KbqAccordionTrigger, descendants: true, isSignal: true }, { propertyName: "content", first: true, predicate: i0.forwardRef(() => KbqAccordionContentDirective), descendants: true, isSignal: true }], ngImport: i0 }); }
405
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
406
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.3.30", type: KbqAccordionItem, isStandalone: true, selector: "kbq-accordion-item, [kbq-accordion-item]", inputs: { expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: false, isRequired: false, transformFunction: booleanAttribute }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: booleanAttribute } }, outputs: { closed: "closed", opened: "opened", expandedChange: "expandedChange" }, host: { properties: { "attr.data-state": "dataState", "attr.data-disabled": "disabled", "attr.data-orientation": "orientation" }, classAttribute: "kbq-accordion-item" }, queries: [{ propertyName: "trigger", first: true, predicate: KbqAccordionTriggerDirective, descendants: true, isSignal: true }, { propertyName: "triggerComponent", first: true, predicate: KbqAccordionTrigger, descendants: true, isSignal: true }, { propertyName: "content", first: true, predicate: i0.forwardRef(() => KbqAccordionContentDirective), descendants: true, isSignal: true }], ngImport: i0 }); }
469
407
  }
470
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionItem, decorators: [{
408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionItem, decorators: [{
471
409
  type: Directive,
472
410
  args: [{
473
411
  selector: 'kbq-accordion-item, [kbq-accordion-item]',
@@ -481,92 +419,314 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
481
419
  }], ctorParameters: () => [], propDecorators: { trigger: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KbqAccordionTriggerDirective), { isSignal: true }] }], triggerComponent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => KbqAccordionTrigger), { isSignal: true }] }], content: [{ type: i0.ContentChild, args: [forwardRef(() => KbqAccordionContentDirective), { isSignal: true }] }], expanded: [{
482
420
  type: Input,
483
421
  args: [{ transform: booleanAttribute }]
484
- }], value: [{
485
- type: Input
486
- }], disabled: [{
422
+ }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{
487
423
  type: Input,
488
424
  args: [{ transform: booleanAttribute }]
489
425
  }], closed: [{ type: i0.Output, args: ["closed"] }], opened: [{ type: i0.Output, args: ["opened"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }] } });
490
426
 
491
- class KbqAccordionContentDirective {
492
- /** @docs-private */
493
- get contentId() {
494
- return `${this.item.id}-content`;
427
+ /**
428
+ * Default `KbqAccordionStateStore` implementation backed by `localStorage`.
429
+ *
430
+ * All access is guarded so it is safe on the server (SSR) and in environments where storage
431
+ * throws on access (private mode, sandboxed iframes) or contains corrupt JSON.
432
+ */
433
+ class KbqAccordionLocalStorageStateStore {
434
+ constructor() {
435
+ this.isBrowser = inject(Platform).isBrowser;
436
+ this.window = inject(KBQ_WINDOW);
495
437
  }
496
- /** @docs-private */
497
- get triggerId() {
498
- return `${this.item.id}-trigger`;
438
+ getState(key) {
439
+ if (!this.isBrowser)
440
+ return null;
441
+ try {
442
+ const raw = this.window.localStorage.getItem(key);
443
+ return raw ? JSON.parse(raw) : null;
444
+ }
445
+ catch {
446
+ return null;
447
+ }
448
+ }
449
+ setState(key, state) {
450
+ if (!this.isBrowser)
451
+ return;
452
+ try {
453
+ this.window.localStorage.setItem(key, JSON.stringify(state));
454
+ }
455
+ catch {
456
+ // Ignore storage write failures (quota exceeded, disabled/blocked storage, etc.).
457
+ }
458
+ }
459
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionLocalStorageStateStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
460
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionLocalStorageStateStore, providedIn: 'root' }); }
461
+ }
462
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionLocalStorageStateStore, decorators: [{
463
+ type: Injectable,
464
+ args: [{ providedIn: 'root' }]
465
+ }] });
466
+ /**
467
+ * Injection token for the store used to persist accordion state while `useStateSaving` is enabled.
468
+ * Defaults to a `localStorage`-backed implementation (`KbqAccordionLocalStorageStateStore`).
469
+ */
470
+ const KBQ_ACCORDION_STATE_STORE = new InjectionToken('KBQ_ACCORDION_STATE_STORE', {
471
+ providedIn: 'root',
472
+ factory: () => inject(KbqAccordionLocalStorageStateStore)
473
+ });
474
+
475
+ let uniqueIdCounter = 0;
476
+ class KbqAccordion {
477
+ /** The unique id of this accordion. */
478
+ get id() {
479
+ return this._id;
480
+ }
481
+ /** Whether multiple items can be expanded at the same time. */
482
+ get isMultiple() {
483
+ return this.type() === 'multiple';
484
+ }
485
+ /** Whether a persisted state exists for this accordion. */
486
+ get hasSavedState() {
487
+ return typeof this.state === 'object' && this.state !== null;
499
488
  }
500
489
  constructor() {
501
- this.renderer = inject(Renderer2);
502
- this.platform = inject(Platform);
503
490
  /** @docs-private */
504
- this.nativeElement = kbqInjectNativeElement();
491
+ this.focusMonitor = inject(FocusMonitor);
505
492
  /** @docs-private */
506
- this.item = inject(KbqAccordionItem);
493
+ this.elementRef = inject(ElementRef);
507
494
  /** @docs-private */
508
- this.hidden = signal(true, ...(ngDevMode ? [{ debugName: "hidden" }] : []));
509
- this.afterRenderRef = afterNextRender(() => {
510
- this.enableAnimation();
511
- this.afterRenderRef?.destroy();
495
+ this.changeDetectorRef = inject(ChangeDetectorRef);
496
+ /** @docs-private */
497
+ this.selectionDispatcher = inject(UniqueSelectionDispatcher);
498
+ /** @docs-private */
499
+ this.dir = inject(Directionality, { optional: true });
500
+ this.injector = inject(Injector);
501
+ this.stateStore = inject(KBQ_ACCORDION_STATE_STORE);
502
+ /** Emits every time `openAll()` or `closeAll()` is called. @docs-private */
503
+ this.openCloseAllActions = new Subject();
504
+ /**
505
+ * Every item the content query matches, including those of a nested accordion.
506
+ * Use `items` instead — this one is the raw, unfiltered query.
507
+ * @docs-private
508
+ */
509
+ // The generic is explicit because `forwardRef` erases the locator type, which would leave both
510
+ // this query and `items` as `Signal<any[]>` in the public API report.
511
+ this.allItems = contentChildren(forwardRef(() => KbqAccordionItem), ...(ngDevMode ? [{ debugName: "allItems", descendants: true }] : [{ descendants: true }]));
512
+ /**
513
+ * The accordion items that belong to this accordion.
514
+ *
515
+ * A descendant content query resolves against the template the tag is authored in and does not
516
+ * stop at a nested component of the same type, so `allItems` of an outer accordion also matches
517
+ * the items of an accordion rendered inside an item's content. Each item's own `accordion` is
518
+ * injected and therefore always its nearest one, which makes ownership the reliable filter.
519
+ * Without it the key manager would move focus into the nested accordion's headers, `valueChange`
520
+ * would report a nested item's value and state saving would persist it under the outer key.
521
+ * @docs-private
522
+ */
523
+ this.items = computed(() => this.allItems().filter((item) => item.accordion === this), ...(ngDevMode ? [{ debugName: "items" }] : []));
524
+ /** Whether the accordion persists the expanded state of its items across reloads. Defaults to `false`. */
525
+ this.useStateSaving = input(false, ...(ngDevMode ? [{ debugName: "useStateSaving", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
526
+ /**
527
+ * A stable, consumer-provided key used to persist state when `useStateSaving` is enabled.
528
+ * Strongly recommended: without it the accordion falls back to an auto-generated,
529
+ * instantiation-order-dependent id that is unreliable under lazy/conditional/reordered rendering.
530
+ */
531
+ this.stateSavingKey = input('', ...(ngDevMode ? [{ debugName: "stateSavingKey" }] : []));
532
+ /** The visual variant of the accordion. Defaults to `fill`. */
533
+ this.variant = input('fill', ...(ngDevMode ? [{ debugName: "variant" }] : []));
534
+ /** Whether the whole accordion is disabled. */
535
+ this.disabled = input(undefined, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
536
+ /** The layout orientation of the accordion. Defaults to `vertical`. */
537
+ this.orientation = input('vertical', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
538
+ /**
539
+ * The `aria-level` applied to the item headings. Defaults to `2`.
540
+ * Set it to match the surrounding document outline (e.g. `3` when nested under an `<h2>` section).
541
+ */
542
+ this.level = input(2, ...(ngDevMode ? [{ debugName: "level", transform: numberAttribute }] : [{ transform: numberAttribute }]));
543
+ /**
544
+ * The value of the item(s) to expand when initially rendered.
545
+ * Use when you do not need to control the state of the items.
546
+ */
547
+ this.defaultValue = input([], ...(ngDevMode ? [{ debugName: "defaultValue" }] : []));
548
+ /** Determines whether one or multiple items can be expanded at the same time. */
549
+ this.type = input('single', ...(ngDevMode ? [{ debugName: "type" }] : []));
550
+ /** Whether an expanded item can be collapsed by the user. Defaults to `true`. */
551
+ this.collapsible = input(true, ...(ngDevMode ? [{ debugName: "collapsible" }] : []));
552
+ /**
553
+ * The raw controlled `value` input. Must stay `public`: a `protected` input cannot be bound from
554
+ * a consumer template (TS2445 under full AOT template type-checking).
555
+ * @docs-private
556
+ */
557
+ // `input()` (not `model()`): a `model()` would auto-create a colliding `valueChange` output.
558
+ this.valueInput = input(undefined, ...(ngDevMode ? [{ debugName: "valueInput", alias: 'value' }] : [{ alias: 'value' }]));
559
+ /**
560
+ * The controlled value of the item(s) to expand, reshaped by mode. Supports `[(value)]`.
561
+ * Falls back to `defaultValue` while `value` is unbound.
562
+ *
563
+ * Always an array in `multiple` mode and always a string in `single` mode — an empty string
564
+ * when nothing is expanded, matching what `valueChange` emits.
565
+ */
566
+ this.value = computed(() => {
567
+ const value = this.valueInput() ?? this.defaultValue();
568
+ const array = Array.isArray(value) ? value : [value];
569
+ return this.isMultiple ? array : (array[0] ?? '');
570
+ }, ...(ngDevMode ? [{ debugName: "value" }] : []));
571
+ /** Emits the current value whenever the expanded state of the accordion changes. */
572
+ this.valueChange = output();
573
+ /** `defaultValue` normalized to an array for internal coordination. */
574
+ this.defaultValueArray = computed(() => {
575
+ const value = this.defaultValue();
576
+ return Array.isArray(value) ? value : [value];
577
+ }, ...(ngDevMode ? [{ debugName: "defaultValueArray" }] : []));
578
+ this._id = `kbq-accordion-${uniqueIdCounter++}`;
579
+ this.state = null;
580
+ // Re-emit `valueChange` whenever any (current or future) item toggles its expanded state.
581
+ // Reading `items()` inside the effect keeps the subscriptions in sync with dynamically
582
+ // added/removed items.
583
+ effect((onCleanup) => {
584
+ const subscriptions = this.items().map((item) => item.expandedChange.subscribe(() => this.valueChange.emit(this.getCurrentValue())));
585
+ onCleanup(() => subscriptions.forEach((subscription) => subscription.unsubscribe()));
512
586
  });
587
+ // Notify the selection dispatcher when the controlled `[value]` changes (the old setter's
588
+ // side-effect). The `undefined` sentinel keeps the mandatory first run a no-op when unbound;
589
+ // when bound it re-notifies the value `ngAfterContentInit` already sent (idempotent).
590
+ effect(() => {
591
+ const value = this.valueInput();
592
+ if (value === undefined)
593
+ return;
594
+ untracked(() => this.selectionDispatcher.notify(this.value(), this.id));
595
+ });
596
+ }
597
+ ngAfterContentInit() {
598
+ if (this.useStateSaving()) {
599
+ if (isDevMode() && !this.stateSavingKey()) {
600
+ // eslint-disable-next-line no-console
601
+ console.warn('KbqAccordion: `useStateSaving` is enabled without a `stateSavingKey`. Falling back to an ' +
602
+ 'auto-generated id, which is unreliable across lazy/conditional/reordered rendering. ' +
603
+ 'Provide a stable `stateSavingKey`.');
604
+ }
605
+ this.state = this.stateStore.getState(this.stateStorageKey);
606
+ }
607
+ if (this.valueInput() !== undefined) {
608
+ this.selectionDispatcher.notify(this.value(), this.id);
609
+ }
610
+ else if (this.useStateSaving() && this.hasSavedState) {
611
+ const expandedValues = Object.keys(this.state)
612
+ .filter((key) => this.state[key].expanded)
613
+ .map((key) => this.state[key].value);
614
+ if (expandedValues.length) {
615
+ this.selectionDispatcher.notify((this.isMultiple ? expandedValues : expandedValues[0]), this.id);
616
+ }
617
+ }
618
+ else {
619
+ this.selectionDispatcher.notify(this.defaultValueArray(), this.id);
620
+ }
621
+ // Seed initial state for any items missing from the store (without overwriting existing entries).
622
+ if (this.useStateSaving()) {
623
+ this.items().forEach((item) => this.saveItemState(item, false));
624
+ }
625
+ this.keyManager = new FocusKeyManager(this.items, this.injector).withHomeAndEnd();
626
+ if (this.orientation() === 'horizontal') {
627
+ this.keyManager.withHorizontalOrientation(this.dir?.value || 'ltr');
628
+ }
629
+ else {
630
+ this.keyManager.withVerticalOrientation();
631
+ }
513
632
  }
514
633
  ngAfterViewInit() {
515
- this.disableAnimation();
634
+ this.focusMonitor.monitor(this.elementRef, true);
516
635
  }
517
- ngAfterContentInit() {
518
- if (!this.platform.isBrowser)
636
+ ngOnDestroy() {
637
+ this.focusMonitor.stopMonitoring(this.elementRef);
638
+ this.openCloseAllActions.complete();
639
+ this.keyManager?.destroy();
640
+ }
641
+ /**
642
+ * Handles a key pressed on an item's trigger.
643
+ *
644
+ * Invoked by `KbqAccordionTriggerDirective`, not bound on the accordion host: a root listener
645
+ * also receives keys bubbling from the section content and from controls placed next to the
646
+ * trigger, and would swallow their Enter/Space and hijack their arrow keys. It would also
647
+ * activate the first item — stealing focus — the first time any key was pressed anywhere inside.
648
+ * @docs-private
649
+ */
650
+ keydownHandler(event) {
651
+ const activeItem = this.keyManager?.activeItem;
652
+ if (!activeItem)
519
653
  return;
520
- const { height, width } = this.nativeElement.getBoundingClientRect();
521
- this.renderer.setProperty(this.nativeElement, 'style', `
522
- --radix-accordion-content-height: ${height}px;
523
- --radix-accordion-content-width: ${width}px;
524
- `);
654
+ if ((event.keyCode === ENTER || event.keyCode === SPACE) &&
655
+ !this.keyManager.isTyping() &&
656
+ !activeItem.disabled) {
657
+ event.preventDefault();
658
+ activeItem.toggle();
659
+ }
660
+ else {
661
+ // Arrow/Home/End move between headers; Tab falls through natively (WAI-ARIA APG pattern).
662
+ this.keyManager.onKeydown(event);
663
+ }
525
664
  }
526
- toggle() {
527
- this.hidden.set(!this.item.expanded);
665
+ /** Opens all enabled accordion items in an accordion where `type` is `multiple`. */
666
+ openAll() {
667
+ if (this.isMultiple) {
668
+ this.openCloseAllActions.next(true);
669
+ }
528
670
  }
529
- disableAnimation() {
530
- this.savedTransition = this.nativeElement.style.transition;
531
- this.nativeElement.style.transition = 'none';
671
+ /** Closes all enabled accordion items. */
672
+ closeAll() {
673
+ this.openCloseAllActions.next(false);
532
674
  }
533
- enableAnimation() {
534
- this.nativeElement.style.transition = this.savedTransition;
675
+ /** @docs-private */
676
+ setActiveItem(item) {
677
+ this.keyManager?.setActiveItem(item);
678
+ }
679
+ /**
680
+ * Persists the current state of the given item when `useStateSaving` is enabled.
681
+ * @docs-private
682
+ */
683
+ saveItemState(item, force = true) {
684
+ if (!this.useStateSaving())
685
+ return;
686
+ if (!force && this.state?.[item.id])
687
+ return;
688
+ this.state = this.state ?? {};
689
+ this.state[item.id] = item.getState();
690
+ this.stateStore.setState(this.stateStorageKey, this.state);
535
691
  }
536
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionContentDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
537
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqAccordionContentDirective, isStandalone: true, selector: "[kbqAccordionContent]", host: { listeners: { "transitionend": "toggle()" }, properties: { "attr.id": "contentId", "attr.role": "\"region\"", "attr.hidden": "hidden() ? \"\" : null", "attr.aria-labelledby": "triggerId", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" } }, exportAs: ["kbqAccordionContent"], ngImport: i0 }); }
692
+ /** The current expanded value(s) derived from the live item state. */
693
+ getCurrentValue() {
694
+ const expandedValues = this.items()
695
+ .filter((item) => item.expanded)
696
+ .map((item) => item.value());
697
+ return this.isMultiple ? expandedValues : (expandedValues[0] ?? '');
698
+ }
699
+ /** The key used to persist/restore state. */
700
+ get stateStorageKey() {
701
+ return this.stateSavingKey() || this.id;
702
+ }
703
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
704
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.30", type: KbqAccordion, isStandalone: true, selector: "kbq-accordion, [kbq-accordion]", inputs: { useStateSaving: { classPropertyName: "useStateSaving", publicName: "useStateSaving", isSignal: true, isRequired: false, transformFunction: null }, stateSavingKey: { classPropertyName: "stateSavingKey", publicName: "stateSavingKey", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "attr.data-orientation": "orientation()" }, classAttribute: "kbq-accordion" }, providers: [
705
+ { provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }
706
+ ], queries: [{ propertyName: "allItems", predicate: i0.forwardRef(() => KbqAccordionItem), descendants: true, isSignal: true }], ngImport: i0, template: '<ng-content />', isInline: true, styles: [".kbq-accordion{display:block}.kbq-accordion-item{box-sizing:border-box;display:block;border-width:var(--kbq-accordion-size-item-border-width);border-style:solid;border-color:transparent}.kbq-accordion-header{display:flex;height:var(--kbq-accordion-size-item-header-height);overflow:hidden}.kbq-accordion-header>.kbq-accordion-header__actions{display:flex;align-items:center;gap:var(--kbq-accordion-size-item-header-actions-gap);flex-shrink:0;padding-right:var(--kbq-accordion-size-item-header-actions-padding-right)}.kbq-accordion-content{display:block;overflow:hidden}.kbq-accordion-content>.kbq-accordion-content__body{margin:unset;padding:var(--kbq-accordion-size-item-content-padding)}.kbq-accordion-content[data-state=open]{transition:height .3s ease-out;height:var(--kbq-accordion-content-height)}.kbq-accordion-content[data-state=closed]{transition:height .3s ease-out;height:0}@media(prefers-reduced-motion:reduce){.kbq-accordion-content[data-state=open],.kbq-accordion-content[data-state=closed]{transition:none}}.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:has(>.kbq-accordion-trigger:focus),.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:has(>.kbq-accordion-header .kbq-accordion-trigger:focus){border-color:var(--kbq-accordion-item-states-focus-border-color)}.kbq-accordion-item{background:var(--kbq-accordion-item-default-background);color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-item .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-default-icon-color)}.kbq-accordion-trigger{width:100%;background:transparent;border:none;outline:none;color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-trigger:hover:not([data-disabled=true]){cursor:pointer}.kbq-accordion-trigger:hover:not([data-disabled=true]) .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-hover-icon-color)}.kbq-accordion-trigger[data-disabled=true]{color:var(--kbq-accordion-item-states-disabled-text-color)}.kbq-accordion-trigger[data-disabled=true] .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-disabled-icon-color)}.kbq-accordion-header,.kbq-accordion-trigger{font-size:var(--kbq-typography-text-big-font-size);font-style:var(--kbq-typography-text-big-font-style);font-weight:var(--kbq-typography-text-big-font-weight);line-height:var(--kbq-typography-text-big-line-height);font-family:var(--kbq-typography-text-big-font-family);text-transform:var(--kbq-typography-text-big-text-transform);font-feature-settings:var(--kbq-typography-text-big-font-feature-settings);letter-spacing:var(--kbq-typography-text-big-letter-spacing)}.kbq-accordion-trigger__caption{font-size:var(--kbq-typography-text-compact-font-size);font-style:var(--kbq-typography-text-compact-font-style);font-weight:var(--kbq-typography-text-compact-font-weight);line-height:var(--kbq-typography-text-compact-line-height);font-family:var(--kbq-typography-text-compact-font-family);text-transform:var(--kbq-typography-text-compact-text-transform);font-feature-settings:var(--kbq-typography-text-compact-font-feature-settings);letter-spacing:var(--kbq-typography-text-compact-letter-spacing)}.kbq-accordion-content{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-accordion{--kbq-accordion-size-item-border-width: 2px;--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-size-item-header-actions-gap: var(--kbq-size-3xs);--kbq-accordion-size-item-header-actions-padding-right: var(--kbq-size-s);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
538
707
  }
539
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionContentDirective, decorators: [{
540
- type: Directive,
541
- args: [{
542
- selector: '[kbqAccordionContent]',
543
- host: {
544
- '[attr.id]': 'contentId',
545
- '[attr.role]': '"region"',
546
- '[attr.hidden]': 'hidden() ? "" : null',
547
- '[attr.aria-labelledby]': 'triggerId',
548
- '[attr.data-state]': 'item.dataState',
549
- '[attr.data-disabled]': 'item.disabled',
550
- '[attr.data-orientation]': 'item.orientation',
551
- '(transitionend)': 'toggle()'
552
- },
553
- exportAs: 'kbqAccordionContent'
554
- }]
555
- }], ctorParameters: () => [] });
708
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordion, decorators: [{
709
+ type: Component,
710
+ args: [{ selector: 'kbq-accordion, [kbq-accordion]', template: '<ng-content />', providers: [
711
+ { provide: UniqueSelectionDispatcher, useClass: UniqueSelectionDispatcher }
712
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
713
+ class: 'kbq-accordion',
714
+ '[attr.data-orientation]': 'orientation()'
715
+ }, styles: [".kbq-accordion{display:block}.kbq-accordion-item{box-sizing:border-box;display:block;border-width:var(--kbq-accordion-size-item-border-width);border-style:solid;border-color:transparent}.kbq-accordion-header{display:flex;height:var(--kbq-accordion-size-item-header-height);overflow:hidden}.kbq-accordion-header>.kbq-accordion-header__actions{display:flex;align-items:center;gap:var(--kbq-accordion-size-item-header-actions-gap);flex-shrink:0;padding-right:var(--kbq-accordion-size-item-header-actions-padding-right)}.kbq-accordion-content{display:block;overflow:hidden}.kbq-accordion-content>.kbq-accordion-content__body{margin:unset;padding:var(--kbq-accordion-size-item-content-padding)}.kbq-accordion-content[data-state=open]{transition:height .3s ease-out;height:var(--kbq-accordion-content-height)}.kbq-accordion-content[data-state=closed]{transition:height .3s ease-out;height:0}@media(prefers-reduced-motion:reduce){.kbq-accordion-content[data-state=open],.kbq-accordion-content[data-state=closed]{transition:none}}.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:has(>.kbq-accordion-trigger:focus),.kbq-accordion.cdk-keyboard-focused .kbq-accordion-item:has(>.kbq-accordion-header .kbq-accordion-trigger:focus){border-color:var(--kbq-accordion-item-states-focus-border-color)}.kbq-accordion-item{background:var(--kbq-accordion-item-default-background);color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-item .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-default-icon-color)}.kbq-accordion-trigger{width:100%;background:transparent;border:none;outline:none;color:var(--kbq-accordion-item-default-text-color)}.kbq-accordion-trigger:hover:not([data-disabled=true]){cursor:pointer}.kbq-accordion-trigger:hover:not([data-disabled=true]) .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-hover-icon-color)}.kbq-accordion-trigger[data-disabled=true]{color:var(--kbq-accordion-item-states-disabled-text-color)}.kbq-accordion-trigger[data-disabled=true] .kbq-accordion-trigger__icon{color:var(--kbq-accordion-item-states-disabled-icon-color)}.kbq-accordion-header,.kbq-accordion-trigger{font-size:var(--kbq-typography-text-big-font-size);font-style:var(--kbq-typography-text-big-font-style);font-weight:var(--kbq-typography-text-big-font-weight);line-height:var(--kbq-typography-text-big-line-height);font-family:var(--kbq-typography-text-big-font-family);text-transform:var(--kbq-typography-text-big-text-transform);font-feature-settings:var(--kbq-typography-text-big-font-feature-settings);letter-spacing:var(--kbq-typography-text-big-letter-spacing)}.kbq-accordion-trigger__caption{font-size:var(--kbq-typography-text-compact-font-size);font-style:var(--kbq-typography-text-compact-font-style);font-weight:var(--kbq-typography-text-compact-font-weight);line-height:var(--kbq-typography-text-compact-line-height);font-family:var(--kbq-typography-text-compact-font-family);text-transform:var(--kbq-typography-text-compact-text-transform);font-feature-settings:var(--kbq-typography-text-compact-font-feature-settings);letter-spacing:var(--kbq-typography-text-compact-letter-spacing)}.kbq-accordion-content{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-accordion{--kbq-accordion-size-item-border-width: 2px;--kbq-accordion-size-item-header-height: 36px;--kbq-accordion-size-item-header-variant-fill-padding: var(--kbq-size-xs) var(--kbq-size-s);--kbq-accordion-size-item-header-variant-hug-padding: var(--kbq-size-xs) var(--kbq-size-s) var(--kbq-size-xs) var(--kbq-size-m);--kbq-accordion-size-item-content-padding: 0px var(--kbq-size-m) var(--kbq-size-s) var(--kbq-size-m);--kbq-accordion-size-item-header-actions-gap: var(--kbq-size-3xs);--kbq-accordion-size-item-header-actions-padding-right: var(--kbq-size-s);--kbq-accordion-item-default-background: transparent;--kbq-accordion-item-default-text-color: var(--kbq-foreground-contrast);--kbq-accordion-item-default-icon-color: var(--kbq-icon-contrast-fade);--kbq-accordion-item-states-hover-icon-color: var(--kbq-icon-contrast);--kbq-accordion-item-states-focus-border-color: var(--kbq-line-theme);--kbq-accordion-item-states-disabled-text-color: var(--kbq-foreground-contrast-secondary);--kbq-accordion-item-states-disabled-icon-color: var(--kbq-states-icon-disabled)}\n"] }]
716
+ }], ctorParameters: () => [], propDecorators: { allItems: [{ type: i0.ContentChildren, args: [forwardRef(() => KbqAccordionItem), { ...{ descendants: true }, isSignal: true }] }], useStateSaving: [{ type: i0.Input, args: [{ isSignal: true, alias: "useStateSaving", required: false }] }], stateSavingKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateSavingKey", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
556
717
 
557
718
  class KbqAccordionContent {
558
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionContent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
559
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: KbqAccordionContent, isStandalone: true, selector: "kbq-accordion-content, [kbq-accordion-content]", host: { classAttribute: "kbq-accordion-content" }, hostDirectives: [{ directive: KbqAccordionContentDirective }], ngImport: i0, template: `
560
- <p><ng-content /></p>
561
- `, isInline: true }); }
719
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionContent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
720
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.30", type: KbqAccordionContent, isStandalone: true, selector: "kbq-accordion-content, [kbq-accordion-content]", host: { classAttribute: "kbq-accordion-content" }, hostDirectives: [{ directive: KbqAccordionContentDirective }], ngImport: i0, template: '<div class="kbq-accordion-content__body"><ng-content /></div>', isInline: true }); }
562
721
  }
563
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionContent, decorators: [{
722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionContent, decorators: [{
564
723
  type: Component,
565
724
  args: [{
566
725
  selector: 'kbq-accordion-content, [kbq-accordion-content]',
567
- template: `
568
- <p><ng-content /></p>
569
- `,
726
+ // A `<div>` wrapper (not `<p>`) carries the content padding: `<p>` is invalid for projected
727
+ // block content (a `<div>` would auto-close it), and keeping padding off the animated
728
+ // `.kbq-accordion-content` host lets it collapse cleanly to `height: 0`.
729
+ template: '<div class="kbq-accordion-content__body"><ng-content /></div>',
570
730
  host: {
571
731
  class: 'kbq-accordion-content'
572
732
  },
@@ -574,20 +734,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
574
734
  }]
575
735
  }] });
576
736
 
577
- class KbqAccordionHeaderDirective {
737
+ class KbqAccordionHeader {
578
738
  constructor() {
739
+ /** @docs-private */
579
740
  this.item = inject(KbqAccordionItem);
741
+ /** @docs-private */
742
+ this.accordion = inject(KbqAccordion);
580
743
  }
581
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
582
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqAccordionHeaderDirective, isStandalone: true, selector: "[kbqAccordionHeader]", host: { properties: { "attr.role": "\"heading\"", "attr.aria-level": "2", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" } }, ngImport: i0 }); }
744
+ /**
745
+ * Names the heading after the trigger alone.
746
+ *
747
+ * A `role="heading"` takes its name from its content, so the header actions sitting beside the
748
+ * trigger would otherwise append their own labels to it — a section titled "Profile" would be
749
+ * announced as "Profile Run More" when navigating by heading. Pointing at the trigger yields the
750
+ * announcement the WAI-ARIA APG gets by keeping the button the only element in the heading,
751
+ * which this component cannot do because the header is also the row that lays the actions out.
752
+ * @docs-private
753
+ */
754
+ get labelledBy() {
755
+ return this.item.trigger()?.triggerId ?? null;
756
+ }
757
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
758
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqAccordionHeader, isStandalone: true, selector: "kbq-accordion-header, [kbq-accordion-header]", host: { properties: { "attr.role": "\"heading\"", "attr.aria-level": "accordion.level()", "attr.aria-labelledby": "labelledBy", "attr.data-state": "item.dataState", "attr.data-disabled": "item.disabled", "attr.data-orientation": "item.orientation" }, classAttribute: "kbq-accordion-header" }, ngImport: i0 }); }
583
759
  }
584
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionHeaderDirective, decorators: [{
760
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionHeader, decorators: [{
585
761
  type: Directive,
586
762
  args: [{
587
- selector: '[kbqAccordionHeader]',
763
+ selector: 'kbq-accordion-header, [kbq-accordion-header]',
588
764
  host: {
765
+ class: 'kbq-accordion-header',
589
766
  '[attr.role]': '"heading"',
590
- '[attr.aria-level]': '2',
767
+ '[attr.aria-level]': 'accordion.level()',
768
+ '[attr.aria-labelledby]': 'labelledBy',
591
769
  '[attr.data-state]': 'item.dataState',
592
770
  '[attr.data-disabled]': 'item.disabled',
593
771
  '[attr.data-orientation]': 'item.orientation'
@@ -595,24 +773,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
595
773
  }]
596
774
  }] });
597
775
 
598
- class KbqAccordionHeader {
599
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
600
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: KbqAccordionHeader, isStandalone: true, selector: "kbq-accordion-header, [kbq-accordion-header]", host: { classAttribute: "kbq-accordion-header" }, hostDirectives: [{ directive: KbqAccordionHeaderDirective }], ngImport: i0 }); }
601
- }
602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionHeader, decorators: [{
603
- type: Directive,
604
- args: [{
605
- selector: 'kbq-accordion-header, [kbq-accordion-header]',
606
- host: {
607
- class: 'kbq-accordion-header'
608
- },
609
- hostDirectives: [KbqAccordionHeaderDirective]
610
- }]
611
- }] });
612
-
613
776
  class KbqAccordionModule {
614
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
615
- /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionModule, imports: [KbqIcon,
777
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
778
+ /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionModule, imports: [KbqIcon,
616
779
  KbqAccordion,
617
780
  KbqAccordionContent,
618
781
  KbqAccordionHeader,
@@ -622,10 +785,10 @@ class KbqAccordionModule {
622
785
  KbqAccordionHeader,
623
786
  KbqAccordionItem,
624
787
  KbqAccordionTrigger] }); }
625
- /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionModule, imports: [KbqIcon,
788
+ /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionModule, imports: [KbqIcon,
626
789
  KbqAccordionTrigger] }); }
627
790
  }
628
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: KbqAccordionModule, decorators: [{
791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqAccordionModule, decorators: [{
629
792
  type: NgModule,
630
793
  args: [{
631
794
  imports: [
@@ -650,5 +813,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
650
813
  * Generated bundle index. Do not edit.
651
814
  */
652
815
 
653
- export { KbqAccordion, KbqAccordionContent, KbqAccordionContentDirective, KbqAccordionHeader, KbqAccordionHeaderDirective, KbqAccordionItem, KbqAccordionModule, KbqAccordionTrigger, KbqAccordionTriggerDirective, KbqAccordionVariant };
816
+ export { KBQ_ACCORDION_STATE_STORE, KbqAccordion, KbqAccordionContent, KbqAccordionContentDirective, KbqAccordionHeader, KbqAccordionItem, KbqAccordionLocalStorageStateStore, KbqAccordionModule, KbqAccordionTrigger, KbqAccordionTriggerDirective };
654
817
  //# sourceMappingURL=koobiq-components-accordion.mjs.map