ng-hub-ui-panels 21.2.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.
@@ -0,0 +1,1080 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, inject, Renderer2, ElementRef, DestroyRef, viewChild, viewChildren, output, input, booleanAttribute, signal, computed, afterNextRender, RendererStyleFlags2, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component, model, effect, TemplateRef, Directive } from '@angular/core';
3
+ import { NgTemplateOutlet } from '@angular/common';
4
+ import { Router, NavigationEnd, RouterOutlet } from '@angular/router';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import { filter } from 'rxjs';
8
+
9
+ /**
10
+ * Injectable defaults for every `<hub-panels>` in the application.
11
+ *
12
+ * Override at any injector level to change the defaults globally or for a
13
+ * feature subtree:
14
+ *
15
+ * ```ts
16
+ * providers: [{ provide: PanelsConfig, useValue: { ...new PanelsConfig(), type: 'pills' } }]
17
+ * ```
18
+ */
19
+ class PanelsConfig {
20
+ /** Default navigation style — `'tabs'`, `'pills'` or `'accordion'`. */
21
+ type = 'tabs';
22
+ /** Whether keyboard navigation (arrows / Home / End / Delete) is enabled. */
23
+ isKeysAllowed = true;
24
+ /** Accessible label announced for the panel list. */
25
+ ariaLabel = 'Tabs';
26
+ /** Accessible label for the backward scroll button (scrollable mode). */
27
+ scrollBackwardAriaLabel = 'Scroll tabs backward';
28
+ /** Accessible label for the forward scroll button (scrollable mode). */
29
+ scrollForwardAriaLabel = 'Scroll tabs forward';
30
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelsConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
31
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelsConfig, providedIn: 'root' });
32
+ }
33
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelsConfig, decorators: [{
34
+ type: Injectable,
35
+ args: [{ providedIn: 'root' }]
36
+ }] });
37
+
38
+ /**
39
+ * Usable inner width of an element: `offsetWidth` minus horizontal paddings
40
+ * and borders. Used by the scrollable strip to size its scroll steps.
41
+ */
42
+ function contentBoxWidth(element) {
43
+ const style = getComputedStyle(element);
44
+ return (element.offsetWidth -
45
+ parseFloat(style.paddingLeft) -
46
+ parseFloat(style.paddingRight) -
47
+ parseFloat(style.borderLeftWidth) -
48
+ parseFloat(style.borderRightWidth));
49
+ }
50
+
51
+ /**
52
+ * Reads a nested value from an object using dot-notation path syntax
53
+ * (e.g. `id`, `meta.key`). Returns `undefined` when the path cannot be
54
+ * resolved. Used by the panels' `bindValue` form-value mapping.
55
+ */
56
+ function readByPath(source, path) {
57
+ if (source == null || !path) {
58
+ return undefined;
59
+ }
60
+ return path.split('.').reduce((currentValue, segment) => {
61
+ if (currentValue == null) {
62
+ return undefined;
63
+ }
64
+ return currentValue[segment];
65
+ }, source);
66
+ }
67
+
68
+ /**
69
+ * Content-panels container — the hub `panels` primitive.
70
+ *
71
+ * Projects `<hub-panel>` panes and renders them in one of three views:
72
+ *
73
+ * - **`tabs` / `pills`** — an accessible `tablist` of clickable headers
74
+ * (roving tabindex, arrow/Home/End/Delete keys) above the active pane.
75
+ * Panels with a `routerLink` switch the content area to a `<router-outlet>`
76
+ * and the active panel follows the URL. With `scrollable`, overflowing
77
+ * headers get backward/forward scroll buttons.
78
+ * - **`accordion`** — each panel renders as a stacked disclosure panel with an
79
+ * animated collapse: clicking an open header closes it, `multiple` allows
80
+ * several panels open at once and `flush` removes the outer chrome. All
81
+ * panels start collapsed unless a panel is marked `[active]` or a form value
82
+ * is bound. Routed panels are not supported in this view.
83
+ *
84
+ * The container implements `ControlValueAccessor`, so the active panel(s) can
85
+ * be bound as a form value (`ngModel` / `formControl`): each panel contributes
86
+ * its `value` (or its `id` by default), optionally narrowed with `bindValue`
87
+ * and compared with `compareWith`. With `multiple` the form value is an array.
88
+ *
89
+ * Theming is driven entirely by the `--hub-panels-*` CSS custom properties
90
+ * (see `panels.variables.scss`); the accordion view also honours the
91
+ * `--hub-accordion-*` ng-hub-ui contract as a compatibility fallback.
92
+ *
93
+ * @example
94
+ * ```html
95
+ * <hub-panels type="accordion" multiple [formControl]="openPanels">
96
+ * <hub-panel heading="Fields" value="fields">…</hub-panel>
97
+ * <hub-panel heading="Validations" value="validations">…</hub-panel>
98
+ * </hub-panels>
99
+ * ```
100
+ */
101
+ class PanelsComponent {
102
+ #router = inject(Router);
103
+ #renderer = inject(Renderer2);
104
+ #elementRef = inject(ElementRef);
105
+ #destroyRef = inject(DestroyRef);
106
+ /** Container-wide defaults (aria labels, default type, keyboard toggle). */
107
+ config = inject(PanelsConfig);
108
+ /** Scrollable container hosting the panel headers (`tabs` / `pills` views). */
109
+ navScroller = viewChild('navScroller', /* @ts-ignore */
110
+ ...(ngDevMode ? [{ debugName: "navScroller" }] : /* istanbul ignore next */ []));
111
+ /** Backward scroll button (only rendered while it can scroll back). */
112
+ prevBtn = viewChild('prevBtn', /* @ts-ignore */
113
+ ...(ngDevMode ? [{ debugName: "prevBtn" }] : /* istanbul ignore next */ []));
114
+ /** Forward scroll button (only rendered while it can scroll forward). */
115
+ nextBtn = viewChild('nextBtn', /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "nextBtn" }] : /* istanbul ignore next */ []));
117
+ /** Parking lot that owns the projected panel hosts before they are placed. */
118
+ contentRoot = viewChild('contentRoot', /* @ts-ignore */
119
+ ...(ngDevMode ? [{ debugName: "contentRoot" }] : /* istanbul ignore next */ []));
120
+ /** Visible block hosts used by `multiple` tabs / pills. */
121
+ multiplePaneHosts = viewChildren('multiplePaneHost', /* @ts-ignore */
122
+ ...(ngDevMode ? [{ debugName: "multiplePaneHosts" }] : /* istanbul ignore next */ []));
123
+ /** Visible grouped blocks used by `multiple` tabs / pills. */
124
+ multipleBlocks = viewChildren('multipleBlock', /* @ts-ignore */
125
+ ...(ngDevMode ? [{ debugName: "multipleBlocks" }] : /* istanbul ignore next */ []));
126
+ /** Emitted when the user activates (opens) a different panel. */
127
+ panelChange = output();
128
+ /** Whether the panel list is stacked beside the content (`tabs` / `pills`). */
129
+ vertical = input(false, { ...(ngDevMode ? { debugName: "vertical" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
130
+ /** Whether panel headers stretch to share the available width equally. */
131
+ justified = input(false, { ...(ngDevMode ? { debugName: "justified" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
132
+ /** Visualization — `'tabs'` (default), `'pills'` or `'accordion'`. */
133
+ type = input(this.config.type, /* @ts-ignore */
134
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
135
+ /** Whether keyboard navigation is enabled. */
136
+ isKeysAllowed = input(this.config.isKeysAllowed, { ...(ngDevMode ? { debugName: "isKeysAllowed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
137
+ /** Whether overflowing headers get backward/forward scroll buttons. */
138
+ scrollable = input(false, { ...(ngDevMode ? { debugName: "scrollable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
139
+ /** Accordion view: whether several panels may be expanded at once. */
140
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
141
+ /** Accordion view: edge-to-edge panels without outer borders or radius. */
142
+ flush = input(false, { ...(ngDevMode ? { debugName: "flush" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
143
+ /**
144
+ * Dot-notation path applied to each panel's `value` to obtain the emitted
145
+ * form value (e.g. `'meta.key'`). Empty by default — the raw value is used.
146
+ */
147
+ bindValue = input(undefined, /* @ts-ignore */
148
+ ...(ngDevMode ? [{ debugName: "bindValue" }] : /* istanbul ignore next */ []));
149
+ /** Equality used to match form values against panel values. `===` by default. */
150
+ compareWith = input((a, b) => a === b, /* @ts-ignore */
151
+ ...(ngDevMode ? [{ debugName: "compareWith" }] : /* istanbul ignore next */ []));
152
+ /** Registered panels, in projection order. */
153
+ panels = signal([], /* @ts-ignore */
154
+ ...(ngDevMode ? [{ debugName: "panels" }] : /* istanbul ignore next */ []));
155
+ /** Currently active panel, if any (the first one, under `multiple`). */
156
+ activePanel = computed(() => this.panels().find((panel) => panel.active()), /* @ts-ignore */
157
+ ...(ngDevMode ? [{ debugName: "activePanel" }] : /* istanbul ignore next */ []));
158
+ /** Panel index lookup used by the strip templates and keyboard navigation. */
159
+ panelIndexMap = computed(() => new Map(this.panels().map((panel, index) => [panel, index])), /* @ts-ignore */
160
+ ...(ngDevMode ? [{ debugName: "panelIndexMap" }] : /* istanbul ignore next */ []));
161
+ /** Whether the container renders the accordion visualization. */
162
+ isAccordionView = computed(() => this.type() === 'accordion', /* @ts-ignore */
163
+ ...(ngDevMode ? [{ debugName: "isAccordionView" }] : /* istanbul ignore next */ []));
164
+ /**
165
+ * Whether the container renders the chromeless `card` visualization: no
166
+ * navigation strip and every panel always visible, each styled as a card.
167
+ */
168
+ isCardView = computed(() => this.type() === 'card', /* @ts-ignore */
169
+ ...(ngDevMode ? [{ debugName: "isCardView" }] : /* istanbul ignore next */ []));
170
+ /**
171
+ * Whether more than one panel may be active at once. Driven by `multiple`
172
+ * for every view, and always enabled in the `card` view, where all panels
173
+ * stay visible at once and must not deactivate one another.
174
+ */
175
+ allowsMultipleActive = computed(() => this.multiple() || this.isCardView(), /* @ts-ignore */
176
+ ...(ngDevMode ? [{ debugName: "allowsMultipleActive" }] : /* istanbul ignore next */ []));
177
+ /** Whether the bound form control disabled the whole container. */
178
+ formDisabled = signal(false, /* @ts-ignore */
179
+ ...(ngDevMode ? [{ debugName: "formDisabled" }] : /* istanbul ignore next */ []));
180
+ /**
181
+ * Whether the active panel routes its content through a `<router-outlet>`.
182
+ * Never true in the accordion view, where routed panels are not supported.
183
+ */
184
+ activePanelHasRouter = computed(() => !this.isAccordionView() && !this.allowsMultipleActive() && !!this.activePanel()?.routerUrl(), /* @ts-ignore */
185
+ ...(ngDevMode ? [{ debugName: "activePanelHasRouter" }] : /* istanbul ignore next */ []));
186
+ /**
187
+ * In `multiple` tabs/pills, headers are partitioned into blocks. Every
188
+ * active panel starts a block; following inactive headers stay visible in
189
+ * that same block until the next active panel starts a new one.
190
+ */
191
+ multipleHeaderGroups = computed(() => {
192
+ if (!this.allowsMultipleActive() || this.isAccordionView()) {
193
+ return [];
194
+ }
195
+ const panels = this.panels();
196
+ if (!panels.length) {
197
+ return [];
198
+ }
199
+ const activeIndices = panels
200
+ .map((panel, index) => (panel.active() ? index : -1))
201
+ .filter((index) => index !== -1);
202
+ if (!activeIndices.length) {
203
+ return [{ headers: panels }];
204
+ }
205
+ return activeIndices.map((activeIndex, groupIndex) => {
206
+ const startIndex = groupIndex === 0 ? 0 : activeIndex;
207
+ const endIndex = activeIndices[groupIndex + 1] ?? panels.length;
208
+ return {
209
+ activePanel: panels[activeIndex],
210
+ headers: panels.slice(startIndex, endIndex)
211
+ };
212
+ });
213
+ }, /* @ts-ignore */
214
+ ...(ngDevMode ? [{ debugName: "multipleHeaderGroups" }] : /* istanbul ignore next */ []));
215
+ /** Whether the strip cannot scroll further backward. */
216
+ backwardIsDisabled = signal(true, /* @ts-ignore */
217
+ ...(ngDevMode ? [{ debugName: "backwardIsDisabled" }] : /* istanbul ignore next */ []));
218
+ /** Whether the strip cannot scroll further forward. */
219
+ forwardIsDisabled = signal(false, /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "forwardIsDisabled" }] : /* istanbul ignore next */ []));
221
+ #isDestroyed = false;
222
+ #syncScheduled = false;
223
+ #viewInitialised = false;
224
+ #placementScheduled = false;
225
+ #multipleBlockSizingScheduled = false;
226
+ /** Last value written by the bound form control; `null` when no form. */
227
+ #formValue = null;
228
+ #onChange = () => undefined;
229
+ #onTouched = () => undefined;
230
+ constructor() {
231
+ this.#destroyRef.onDestroy(() => {
232
+ this.#isDestroyed = true;
233
+ });
234
+ afterNextRender(() => {
235
+ this.#viewInitialised = true;
236
+ this.updateScrollButtons();
237
+ this.setActivePanel();
238
+ // Re-apply any form value written before the panels' inputs were bound.
239
+ this.#applyFormValue();
240
+ this.#ensureActivePanel();
241
+ this.#schedulePanelPlacement();
242
+ this.#scheduleMultipleBlockSizing();
243
+ this.#router.events
244
+ .pipe(filter((event) => event instanceof NavigationEnd), takeUntilDestroyed(this.#destroyRef))
245
+ .subscribe(() => this.setActivePanel());
246
+ });
247
+ }
248
+ /**
249
+ * Registers a panel in the container. Called by `PanelComponent` on
250
+ * construction — not meant for manual use.
251
+ */
252
+ registerPanel(panel) {
253
+ this.panels.update((panels) => [...panels, panel]);
254
+ this.#scheduleSync();
255
+ }
256
+ /**
257
+ * Removes a panel from the container. `reselect` activates the closest
258
+ * enabled neighbour when the removed panel was active; `emit` fires the
259
+ * panel's `removed` output.
260
+ */
261
+ removePanel(panel, options = {}) {
262
+ const { reselect = true, emit = true } = options;
263
+ const panels = this.panels();
264
+ const index = panels.indexOf(panel);
265
+ if (index === -1 || this.#isDestroyed) {
266
+ return;
267
+ }
268
+ if (reselect && panel.active() && !this.isAccordionView()) {
269
+ const closestIndex = this.#closestEnabledIndex(index);
270
+ if (closestIndex !== -1) {
271
+ panels[closestIndex].active.set(true);
272
+ }
273
+ }
274
+ if (emit) {
275
+ panel.removed.emit(panel);
276
+ }
277
+ this.panels.update((currentPanels) => currentPanels.filter((candidate) => candidate !== panel));
278
+ // Detach the projected pane: the consumer template still owns the node,
279
+ // so removing the header alone would leave the pane content behind.
280
+ const paneElement = panel.elementRef.nativeElement;
281
+ if (paneElement.parentNode) {
282
+ this.#renderer.removeChild(paneElement.parentNode, paneElement);
283
+ }
284
+ if (panel.active()) {
285
+ panel.active.set(false);
286
+ this.#emitFormValue();
287
+ }
288
+ this.#scheduleSync();
289
+ }
290
+ /**
291
+ * Activates a panel on behalf of the user (`tabs` / `pills` views): marks it
292
+ * active, navigates when routed, and emits `panelChange`. In `multiple` mode
293
+ * clicking an active panel toggles it off (the panes render side by side);
294
+ * otherwise an already-active panel no-ops. Disabled panels always no-op.
295
+ */
296
+ selectPanel(panel) {
297
+ if (panel.disabled() || this.formDisabled()) {
298
+ return;
299
+ }
300
+ if (this.allowsMultipleActive()) {
301
+ if (panel.active()) {
302
+ panel.active.set(false);
303
+ }
304
+ else {
305
+ panel.active.set(true);
306
+ panel.navigate();
307
+ this.panelChange.emit({ prev: undefined, current: panel });
308
+ }
309
+ this.#emitFormValue();
310
+ this.#onTouched();
311
+ this.#schedulePanelPlacement();
312
+ return;
313
+ }
314
+ if (panel.active()) {
315
+ return;
316
+ }
317
+ const previousPanel = this.activePanel();
318
+ // Deactivate the previously active panel up front so the emitted form
319
+ // value reflects only the newly selected panel. The per-panel effect that
320
+ // enforces single-active runs asynchronously, so relying on it here would
321
+ // emit a stale value that still includes the previous panel.
322
+ if (previousPanel && previousPanel !== panel) {
323
+ previousPanel.active.set(false);
324
+ }
325
+ panel.active.set(true);
326
+ panel.navigate();
327
+ this.panelChange.emit({ prev: previousPanel, current: panel });
328
+ this.#emitFormValue();
329
+ this.#onTouched();
330
+ this.#schedulePanelPlacement();
331
+ }
332
+ /**
333
+ * Toggles a panel on behalf of the user (accordion view): an open panel
334
+ * closes, a closed one opens — closing the others unless `multiple`.
335
+ * Emits `panelChange` when a panel opens.
336
+ */
337
+ togglePanel(panel) {
338
+ if (panel.disabled() || this.formDisabled()) {
339
+ return;
340
+ }
341
+ if (panel.active()) {
342
+ panel.active.set(false);
343
+ }
344
+ else {
345
+ const previousPanel = this.allowsMultipleActive() ? undefined : this.activePanel();
346
+ panel.active.set(true);
347
+ this.panelChange.emit({ prev: previousPanel, current: panel });
348
+ }
349
+ this.#emitFormValue();
350
+ this.#onTouched();
351
+ this.#schedulePanelPlacement();
352
+ }
353
+ /**
354
+ * Marks the routed panel matching the current URL as active. Does not
355
+ * navigate — activation through this path is URL-driven. No-op in the
356
+ * accordion view, where routed panels are not supported.
357
+ */
358
+ setActivePanel() {
359
+ if (this.isAccordionView()) {
360
+ return;
361
+ }
362
+ const [route, queryString] = this.#router.url.split('?');
363
+ for (const panel of this.panels()) {
364
+ if (!panel.routerUrl()) {
365
+ continue;
366
+ }
367
+ let matches;
368
+ if (panel.pathMatch() === 'full') {
369
+ const currentUrl = queryString?.trim() ? `${route}?${queryString.trim()}` : route;
370
+ matches = currentUrl === panel.getFullRoute();
371
+ }
372
+ else {
373
+ matches = route === panel.getRoute();
374
+ }
375
+ if (matches) {
376
+ if (!panel.active()) {
377
+ panel.active.set(true);
378
+ }
379
+ break;
380
+ }
381
+ }
382
+ }
383
+ // ── ControlValueAccessor ────────────────────────────────────────────────
384
+ /**
385
+ * Writes the form value into the container: panels whose (`bindValue`-mapped)
386
+ * value matches are activated, the rest are deactivated. With `multiple` an
387
+ * array is expected; otherwise a single value.
388
+ */
389
+ writeValue(value) {
390
+ if (value == null) {
391
+ this.#formValue = [];
392
+ }
393
+ else if (this.multiple()) {
394
+ this.#formValue = Array.isArray(value) ? [...value] : [value];
395
+ }
396
+ else {
397
+ this.#formValue = Array.isArray(value) ? value.slice(0, 1) : [value];
398
+ }
399
+ this.#applyFormValue();
400
+ }
401
+ /** Registers the form-control change callback. */
402
+ registerOnChange(onChange) {
403
+ this.#onChange = onChange;
404
+ }
405
+ /** Registers the form-control touched callback. */
406
+ registerOnTouched(onTouched) {
407
+ this.#onTouched = onTouched;
408
+ }
409
+ /** Enables/disables every panel header on behalf of the form control. */
410
+ setDisabledState(isDisabled) {
411
+ this.formDisabled.set(isDisabled);
412
+ }
413
+ /** Keyboard navigation for the panel strip (arrows / Home / End / Delete). */
414
+ onPanelKeydown(event, index) {
415
+ if (!this.isKeysAllowed()) {
416
+ return;
417
+ }
418
+ switch (event.key) {
419
+ case 'ArrowRight':
420
+ case 'ArrowDown':
421
+ if (event.key === 'ArrowDown' && !this.vertical()) {
422
+ return;
423
+ }
424
+ event.preventDefault();
425
+ this.#focusPanelAt(this.#stepEnabledIndex(index, 1));
426
+ return;
427
+ case 'ArrowLeft':
428
+ case 'ArrowUp':
429
+ if (event.key === 'ArrowUp' && !this.vertical()) {
430
+ return;
431
+ }
432
+ event.preventDefault();
433
+ this.#focusPanelAt(this.#stepEnabledIndex(index, -1));
434
+ return;
435
+ case 'Home':
436
+ event.preventDefault();
437
+ this.#focusPanelAt(this.panels().findIndex((panel) => !panel.disabled()));
438
+ return;
439
+ case 'End': {
440
+ event.preventDefault();
441
+ const panels = this.panels();
442
+ this.#focusPanelAt(panels.length - 1 - [...panels].reverse().findIndex((panel) => !panel.disabled()));
443
+ return;
444
+ }
445
+ case 'Delete': {
446
+ const panel = this.panels()[index];
447
+ if (panel?.removable()) {
448
+ this.removePanel(panel);
449
+ queueMicrotask(() => this.#focusPanelAt(Math.min(index, this.panels().length - 1)));
450
+ }
451
+ }
452
+ }
453
+ }
454
+ /**
455
+ * Keyboard navigation between accordion headers (arrows / Home / End /
456
+ * Delete). Enter and Space toggle natively through the button click.
457
+ */
458
+ onAccordionKeydown(event, panel) {
459
+ if (!this.isKeysAllowed()) {
460
+ return;
461
+ }
462
+ const index = this.panels().indexOf(panel);
463
+ switch (event.key) {
464
+ case 'ArrowDown':
465
+ event.preventDefault();
466
+ this.#focusPanelAt(this.#stepEnabledIndex(index, 1));
467
+ return;
468
+ case 'ArrowUp':
469
+ event.preventDefault();
470
+ this.#focusPanelAt(this.#stepEnabledIndex(index, -1));
471
+ return;
472
+ case 'Home':
473
+ event.preventDefault();
474
+ this.#focusPanelAt(this.panels().findIndex((candidate) => !candidate.disabled()));
475
+ return;
476
+ case 'End': {
477
+ event.preventDefault();
478
+ const panels = this.panels();
479
+ this.#focusPanelAt(panels.length - 1 - [...panels].reverse().findIndex((candidate) => !candidate.disabled()));
480
+ return;
481
+ }
482
+ case 'Delete': {
483
+ if (panel.removable()) {
484
+ this.removePanel(panel);
485
+ queueMicrotask(() => this.#focusPanelAt(Math.min(index, this.panels().length - 1)));
486
+ }
487
+ }
488
+ }
489
+ }
490
+ /** Recomputes the enabled/disabled state of the scroll buttons. */
491
+ updateScrollButtons() {
492
+ if (!this.scrollable() || this.isAccordionView()) {
493
+ return;
494
+ }
495
+ const scroller = this.navScroller()?.nativeElement;
496
+ if (!scroller) {
497
+ return;
498
+ }
499
+ const { scrollLeft, scrollWidth } = scroller;
500
+ this.backwardIsDisabled.set(scrollLeft <= 0);
501
+ this.forwardIsDisabled.set(scrollLeft + contentBoxWidth(scroller) + 1 >= scrollWidth);
502
+ }
503
+ /** Re-syncs scroll controls and multiple-block sizing on window resize. */
504
+ onWindowResize() {
505
+ this.updateScrollButtons();
506
+ this.#scheduleMultipleBlockSizing();
507
+ }
508
+ /** Updates the scroll buttons as the strip scrolls. */
509
+ onScroll() {
510
+ this.updateScrollButtons();
511
+ }
512
+ /** Global index of a panel within the projected strip order. */
513
+ panelIndex(panel) {
514
+ return this.panelIndexMap().get(panel) ?? -1;
515
+ }
516
+ /** Scrolls the panel headers one viewport backward. */
517
+ navBackward() {
518
+ const scroller = this.navScroller()?.nativeElement;
519
+ if (!scroller) {
520
+ return;
521
+ }
522
+ const step = contentBoxWidth(scroller) - this.#visibleScrollButtonsWidth();
523
+ scroller.scrollLeft = Math.max(scroller.scrollLeft - step, 0);
524
+ }
525
+ /** Scrolls the panel headers one viewport forward. */
526
+ navForward() {
527
+ const scroller = this.navScroller()?.nativeElement;
528
+ if (!scroller) {
529
+ return;
530
+ }
531
+ const step = contentBoxWidth(scroller) - this.#visibleScrollButtonsWidth();
532
+ const lastPosition = scroller.scrollWidth - step;
533
+ scroller.scrollLeft = Math.min(scroller.scrollLeft + step, lastPosition);
534
+ }
535
+ /** Width currently taken by the visible scroll buttons. */
536
+ #visibleScrollButtonsWidth() {
537
+ return [this.prevBtn()?.nativeElement, this.nextBtn()?.nativeElement].reduce((total, button) => (button ? total + contentBoxWidth(button) : total), 0);
538
+ }
539
+ /** Index of the enabled panel closest to `index`, or `-1` when none exists. */
540
+ #closestEnabledIndex(index) {
541
+ const panels = this.panels();
542
+ for (let step = 1; step <= panels.length; step += 1) {
543
+ const previous = panels[index - step];
544
+ if (previous && !previous.disabled()) {
545
+ return index - step;
546
+ }
547
+ const next = panels[index + step];
548
+ if (next && !next.disabled()) {
549
+ return index + step;
550
+ }
551
+ }
552
+ return -1;
553
+ }
554
+ /** Next enabled panel index from `index` in `direction`, wrapping around. */
555
+ #stepEnabledIndex(index, direction) {
556
+ const panels = this.panels();
557
+ for (let step = 1; step <= panels.length; step += 1) {
558
+ const candidate = (index + direction * step + panels.length * step) % panels.length;
559
+ if (!panels[candidate].disabled()) {
560
+ return candidate;
561
+ }
562
+ }
563
+ return index;
564
+ }
565
+ /** Moves DOM focus to the panel header at `index` (strip link or accordion button). */
566
+ #focusPanelAt(index) {
567
+ if (index < 0) {
568
+ return;
569
+ }
570
+ const selector = this.isAccordionView() ? '.hub-panels__accordion-btn' : '.hub-panels__nav-link';
571
+ const headers = this.#elementRef.nativeElement.querySelectorAll(selector);
572
+ headers[index]?.focus();
573
+ }
574
+ /**
575
+ * Coalesces post-registration/removal housekeeping into one microtask:
576
+ * re-applies the bound form value to the current panels, guarantees an
577
+ * active panel (non-routed `tabs` / `pills` containers without a bound
578
+ * form) and refreshes the scroll buttons after the strip changed size.
579
+ * Skipped until the first render — before it the projected panels' inputs
580
+ * are not bound yet, and the constructor's `afterNextRender` performs the
581
+ * initial pass instead.
582
+ */
583
+ #scheduleSync() {
584
+ if (this.#syncScheduled || this.#isDestroyed) {
585
+ return;
586
+ }
587
+ this.#syncScheduled = true;
588
+ queueMicrotask(() => {
589
+ this.#syncScheduled = false;
590
+ if (this.#isDestroyed || !this.#viewInitialised) {
591
+ return;
592
+ }
593
+ this.#applyFormValue();
594
+ this.#ensureActivePanel();
595
+ this.#schedulePanelPlacement();
596
+ this.updateScrollButtons();
597
+ });
598
+ }
599
+ /** Coalesces DOM moves for the projected `hub-panel` hosts. */
600
+ #schedulePanelPlacement() {
601
+ if (this.#placementScheduled || this.#isDestroyed) {
602
+ return;
603
+ }
604
+ this.#placementScheduled = true;
605
+ requestAnimationFrame(() => {
606
+ this.#placementScheduled = false;
607
+ if (this.#isDestroyed || !this.#viewInitialised) {
608
+ return;
609
+ }
610
+ this.#runPanelPlacementPass();
611
+ requestAnimationFrame(() => {
612
+ if (this.#isDestroyed || !this.#viewInitialised) {
613
+ return;
614
+ }
615
+ this.#runPanelPlacementPass();
616
+ });
617
+ });
618
+ }
619
+ /** Performs one projected-panel placement pass plus dependent sizing. */
620
+ #runPanelPlacementPass() {
621
+ this.#placeProjectedPanels();
622
+ this.#scheduleMultipleBlockSizing();
623
+ }
624
+ /** Coalesces the DOM measurements needed by `multiple + vertical`. */
625
+ #scheduleMultipleBlockSizing() {
626
+ if (this.#multipleBlockSizingScheduled || this.#isDestroyed) {
627
+ return;
628
+ }
629
+ this.#multipleBlockSizingScheduled = true;
630
+ requestAnimationFrame(() => {
631
+ this.#multipleBlockSizingScheduled = false;
632
+ if (this.#isDestroyed || !this.#viewInitialised) {
633
+ return;
634
+ }
635
+ this.#syncMultipleBlockSizing();
636
+ });
637
+ }
638
+ /** Moves projected panel hosts into their visible multiple-block container. */
639
+ #placeProjectedPanels() {
640
+ const contentRoot = this.contentRoot()?.nativeElement;
641
+ if (!contentRoot) {
642
+ return;
643
+ }
644
+ if (!this.allowsMultipleActive() || this.isAccordionView()) {
645
+ for (const panel of this.panels()) {
646
+ const panelElement = panel.elementRef.nativeElement;
647
+ if (panelElement.parentElement !== contentRoot) {
648
+ this.#renderer.appendChild(contentRoot, panelElement);
649
+ }
650
+ }
651
+ return;
652
+ }
653
+ const hostMap = new Map(Array.from(this.#elementRef.nativeElement.querySelectorAll('.hub-panels__multiple-layout .hub-panels__multiple-pane-host'))
654
+ .map((host) => [host.dataset['panelId'] ?? '', host]));
655
+ for (const panel of this.panels()) {
656
+ const panelElement = panel.elementRef.nativeElement;
657
+ const targetHost = hostMap.get(panel.id()) ?? contentRoot;
658
+ if (panelElement.parentElement !== targetHost) {
659
+ this.#renderer.appendChild(targetHost, panelElement);
660
+ }
661
+ }
662
+ }
663
+ /** Updates the minimum content width for each `multiple + vertical` block. */
664
+ #syncMultipleBlockSizing() {
665
+ for (const blockRef of this.multipleBlocks()) {
666
+ const block = blockRef.nativeElement;
667
+ this.#renderer.removeStyle(block, '--hub-panels-multiple-vertical-panel-min-width', RendererStyleFlags2.DashCase);
668
+ if (!this.vertical() || !this.allowsMultipleActive() || this.isAccordionView()) {
669
+ continue;
670
+ }
671
+ const header = block.querySelector(':scope > .hub-panels__header');
672
+ if (!(header instanceof HTMLElement)) {
673
+ continue;
674
+ }
675
+ const headerStackHeight = Math.ceil(header.getBoundingClientRect().height);
676
+ this.#renderer.setStyle(block, '--hub-panels-multiple-vertical-panel-min-width', `${headerStackHeight}px`, RendererStyleFlags2.DashCase);
677
+ }
678
+ }
679
+ /**
680
+ * Activates the first enabled panel when none is active. Skipped for the
681
+ * accordion view (panels start collapsed), for routed containers (the URL
682
+ * is the single source of truth) and when a form value is bound (the form
683
+ * is).
684
+ */
685
+ #ensureActivePanel() {
686
+ if (this.isAccordionView() || this.#formValue !== null) {
687
+ return;
688
+ }
689
+ if (this.allowsMultipleActive()) {
690
+ return;
691
+ }
692
+ const panels = this.panels();
693
+ if (!panels.length || panels.some((panel) => panel.active())) {
694
+ return;
695
+ }
696
+ if (panels.some((panel) => !!panel.routerUrl())) {
697
+ return;
698
+ }
699
+ panels.find((panel) => !panel.disabled())?.active.set(true);
700
+ }
701
+ /** Activates/deactivates panels so they mirror the bound form value. */
702
+ #applyFormValue() {
703
+ if (this.#formValue === null) {
704
+ return;
705
+ }
706
+ const compare = this.compareWith();
707
+ for (const panel of this.panels()) {
708
+ const panelValue = this.#comparableValue(panel);
709
+ const matches = this.#formValue.some((selectedValue) => compare(selectedValue, panelValue));
710
+ if (panel.active() !== matches) {
711
+ panel.active.set(matches);
712
+ }
713
+ }
714
+ }
715
+ /** Emits the current selection through the form-control callback. */
716
+ #emitFormValue() {
717
+ const compare = this.compareWith();
718
+ const activeValues = this.panels()
719
+ .filter((panel) => panel.active())
720
+ .map((panel) => this.#comparableValue(panel));
721
+ if (this.#formValue !== null) {
722
+ this.#formValue = [...activeValues];
723
+ }
724
+ // Deduplicate defensively when several panels share the same value.
725
+ const uniqueValues = activeValues.filter((value, index) => activeValues.findIndex((candidate) => compare(candidate, value)) === index);
726
+ this.#onChange(this.multiple() ? uniqueValues : (uniqueValues[0] ?? null));
727
+ }
728
+ /** Form value of a panel with the `bindValue` path applied. */
729
+ #comparableValue(panel) {
730
+ const rawValue = panel.formValue();
731
+ const bindPath = this.bindValue();
732
+ return bindPath ? readByPath(rawValue, bindPath) : rawValue;
733
+ }
734
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
735
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: PanelsComponent, isStandalone: true, selector: "hub-panels", inputs: { vertical: { classPropertyName: "vertical", publicName: "vertical", isSignal: true, isRequired: false, transformFunction: null }, justified: { classPropertyName: "justified", publicName: "justified", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, isKeysAllowed: { classPropertyName: "isKeysAllowed", publicName: "isKeysAllowed", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, bindValue: { classPropertyName: "bindValue", publicName: "bindValue", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { panelChange: "panelChange" }, host: { listeners: { "window:resize": "onWindowResize()" }, properties: { "class.hub-panels--tabs": "type() === 'tabs'", "class.hub-panels--pills": "type() === 'pills'", "class.hub-panels--card": "isCardView()", "class.hub-panels--vertical": "vertical() && !isAccordionView() && !isCardView()", "class.hub-panels--accordion": "isAccordionView()", "class.hub-panels--flush": "isAccordionView() && flush()", "class.hub-panels--multiple": "multiple()" }, classAttribute: "hub-panels" }, providers: [
736
+ {
737
+ provide: NG_VALUE_ACCESSOR,
738
+ useExisting: forwardRef(() => PanelsComponent),
739
+ multi: true
740
+ }
741
+ ], viewQueries: [{ propertyName: "navScroller", first: true, predicate: ["navScroller"], descendants: true, isSignal: true }, { propertyName: "prevBtn", first: true, predicate: ["prevBtn"], descendants: true, isSignal: true }, { propertyName: "nextBtn", first: true, predicate: ["nextBtn"], descendants: true, isSignal: true }, { propertyName: "contentRoot", first: true, predicate: ["contentRoot"], descendants: true, isSignal: true }, { propertyName: "multiplePaneHosts", predicate: ["multiplePaneHost"], descendants: true, isSignal: true }, { propertyName: "multipleBlocks", predicate: ["multipleBlock"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!isAccordionView() && !isCardView()) {\n\t@if (multiple()) {\n\t\t<div class=\"hub-panels__multiple-layout\">\n\t\t\t@for (group of multipleHeaderGroups(); track group.activePanel ?? group.headers[0]) {\n\t\t\t\t<div\n\t\t\t\t\t#multipleBlock\n\t\t\t\t\tclass=\"hub-panels hub-panels__multiple-block\"\n\t\t\t\t\t[class.hub-panels--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t[class.hub-panels--pills]=\"type() === 'pills'\"\n\t\t\t\t\t[class.hub-panels--vertical]=\"vertical()\"\n\t\t\t\t>\n\t\t\t\t\t<div class=\"hub-panels__header\">\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tclass=\"hub-panels__nav\"\n\t\t\t\t\t\t\trole=\"tablist\"\n\t\t\t\t\t\t\t[attr.aria-label]=\"config.ariaLabel\"\n\t\t\t\t\t\t\t[attr.aria-orientation]=\"vertical() ? 'vertical' : null\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--pills]=\"type() === 'pills'\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--vertical]=\"vertical()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--justified]=\"justified()\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (panel of group.headers; track panel) {\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-item\"\n\t\t\t\t\t\t\t\t\t[class]=\"panel.customClass()\"\n\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-item--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-item--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-link\"\n\t\t\t\t\t\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t\t\t\t\t\t[id]=\"panel.id() + '-link'\"\n\t\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t\t\t[disabled]=\"panel.disabled() || formDisabled()\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-controls]=\"panel.id()\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-selected]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t\t[attr.tabindex]=\"panel.active() || (!activePanel() && panelIndex(panel) === 0) ? null : -1\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"selectPanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t(keydown)=\"onPanelKeydown($event, panelIndex(panel))\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (panel.headingRef(); as headingRef) {\n\t\t\t\t\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingRef\" />\n\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t{{ panel.heading() }}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if (panel.removable()) {\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__remove-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation(); removePanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t\t\t>&times;</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t@if (!justified() && type() !== 'pills' && !vertical()) {\n\t\t\t\t\t\t\t<div class=\"hub-panels__spacer\"></div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t@if (group.activePanel) {\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"hub-panels__content\"\n\t\t\t\t\t\t\t[class.hub-panels__content--pills]=\"type() === 'pills'\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t#multiplePaneHost\n\t\t\t\t\t\t\t\tclass=\"hub-panels__multiple-pane-host\"\n\t\t\t\t\t\t\t\t[attr.data-panel-id]=\"group.activePanel.id()\"\n\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t} @else {\n\t\t<div class=\"hub-panels__header\">\n\t\t\t@if (scrollable() && !backwardIsDisabled()) {\n\t\t\t\t<button\n\t\t\t\t\t#prevBtn\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"hub-panels__scroll-btn hub-panels__scroll-btn--backward\"\n\t\t\t\t\t[attr.aria-label]=\"config.scrollBackwardAriaLabel\"\n\t\t\t\t\t(click)=\"navBackward()\"\n\t\t\t\t></button>\n\t\t\t}\n\n\t\t\t<div #navScroller class=\"hub-panels__nav-scroller\" (scroll)=\"onScroll()\">\n\t\t\t\t<ul\n\t\t\t\t\tclass=\"hub-panels__nav\"\n\t\t\t\t\trole=\"tablist\"\n\t\t\t\t\t[attr.aria-label]=\"config.ariaLabel\"\n\t\t\t\t\t[attr.aria-orientation]=\"vertical() ? 'vertical' : null\"\n\t\t\t\t\t[class.hub-panels__nav--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t[class.hub-panels__nav--pills]=\"type() === 'pills'\"\n\t\t\t\t\t[class.hub-panels__nav--vertical]=\"vertical()\"\n\t\t\t\t\t[class.hub-panels__nav--justified]=\"justified()\"\n\t\t\t\t>\n\t\t\t\t\t@for (panel of panels(); track panel; let index = $index) {\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tclass=\"hub-panels__nav-item\"\n\t\t\t\t\t\t\t[class]=\"panel.customClass()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav-item--active]=\"panel.active()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav-item--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-link\"\n\t\t\t\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t\t\t\t[id]=\"panel.id() + '-link'\"\n\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t[disabled]=\"panel.disabled() || formDisabled()\"\n\t\t\t\t\t\t\t\t[attr.aria-controls]=\"panel.id()\"\n\t\t\t\t\t\t\t\t[attr.aria-selected]=\"panel.active()\"\n\t\t\t\t\t\t\t\t[attr.tabindex]=\"panel.active() || (!activePanel() && index === 0) ? null : -1\"\n\t\t\t\t\t\t\t\t(click)=\"selectPanel(panel)\"\n\t\t\t\t\t\t\t\t(keydown)=\"onPanelKeydown($event, index)\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (panel.headingRef(); as headingRef) {\n\t\t\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingRef\" />\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t{{ panel.heading() }}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (panel.removable()) {\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__remove-btn\"\n\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation(); removePanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t>&times;</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t\t@if (!justified() && type() !== 'pills') {\n\t\t\t\t\t<div class=\"hub-panels__spacer\"></div>\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t@if (scrollable() && !forwardIsDisabled()) {\n\t\t\t\t<button\n\t\t\t\t\t#nextBtn\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"hub-panels__scroll-btn hub-panels__scroll-btn--forward\"\n\t\t\t\t\t[attr.aria-label]=\"config.scrollForwardAriaLabel\"\n\t\t\t\t\t(click)=\"navForward()\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t</div>\n\t}\n}\n\n<div\n\t#contentRoot\n\tclass=\"hub-panels__content\"\n\t[class.hub-panels__content--pills]=\"type() === 'pills'\"\n\t[class.hub-panels__content--accordion]=\"isAccordionView()\"\n\t[class.hub-panels__content--multiple-parking]=\"!isAccordionView() && multiple()\"\n>\n\t@if (activePanelHasRouter()) {\n\t\t<router-outlet />\n\t} @else {\n\t\t<ng-content />\n\t}\n</div>\n", styles: [":where(.hub-panels){--hub-panels-flex-direction: row;--hub-panels-border-width: var(--hub-ref-border-width, 1px);--hub-panels-border-color: var(--hub-sys-border-color-default, #dee2e6);--hub-panels-border-radius: var(--hub-ref-radius-md, .375rem);--hub-panels-content-bg: var(--hub-sys-surface-page, #fff);--hub-panels-header-bg: var(--hub-panels-content-bg);--hub-panels-content-box-shadow: none;--hub-panels-content-padding-x: var(--hub-ref-space-3, 1rem);--hub-panels-content-padding-y: var(--hub-ref-space-3, 1rem);--hub-panels-nav-btn-width: 1.5rem;--hub-panels-nav-btn-height: 1.5rem;--hub-panels-backward-btn-bg: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23212529' d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3E%3C/svg%3E\");--hub-panels-forward-btn-bg: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23212529' d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E\");--hub-panels-nav-link-padding-x: var(--hub-ref-space-3, 1rem);--hub-panels-nav-link-padding-y: var(--hub-ref-space-2, .5rem);--hub-panels-nav-link-color: var(--hub-sys-text-primary, #212529);--hub-panels-nav-link-hover-color: var(--hub-sys-color-primary-emphasis, #0a58ca);--hub-panels-nav-link-active-color: var(--hub-sys-color-primary, #0d6efd);--hub-panels-nav-link-active-bg: var(--hub-panels-content-bg);--hub-panels-nav-link-disabled-color: var(--hub-sys-text-muted, #6c757d);--hub-panels-tab-font-family: var(--hub-container-font-family, var(--hub-ref-font-family-base));--hub-panels-tab-font-size: var(--hub-container-font-size, var(--hub-ref-font-size-base, 1rem));--hub-panels-tab-font-weight: 500;--hub-panels-tab-line-height: var(--hub-ref-line-height-base, 1.5);--hub-panels-tab-padding-x: var(--hub-panels-nav-link-padding-x);--hub-panels-tab-padding-y: var(--hub-panels-nav-link-padding-y);--hub-panels-tab-gap: var(--hub-ref-space-2, .5rem);--hub-panels-tab-bg: transparent;--hub-panels-tab-color: var(--hub-panels-nav-link-color);--hub-panels-tab-border-width: var(--hub-panels-border-width);--hub-panels-tab-border-style: solid;--hub-panels-tab-border-color: transparent;--hub-panels-tab-border-radius: var(--hub-panels-border-radius);--hub-panels-tab-bg-hover: var(--hub-ref-surface-2, #f8f9fa);--hub-panels-tab-color-hover: var(--hub-panels-nav-link-hover-color);--hub-panels-tab-bg-active: var(--hub-panels-nav-link-active-bg);--hub-panels-tab-color-active: var(--hub-panels-nav-link-active-color);--hub-panels-tab-border-color-active: var(--hub-sys-color-primary, #0d6efd);--hub-panels-tab-bg-disabled: transparent;--hub-panels-tab-color-disabled: var(--hub-panels-nav-link-disabled-color);--hub-panels-tab-focus-ring-width: var(--hub-sys-focus-ring-width, .25rem);--hub-panels-tab-focus-ring-color: var(--hub-sys-focus-ring-color, rgba(13, 110, 253, .25));--hub-panels-tab-transition: var(--hub-sys-transition-base, all .2s ease-in-out);--hub-panels-tab-active-shadow: 0 -.25rem .5rem rgba(0, 0, 0, .06);--hub-panels-tab-active-shadow-vertical: -.25rem 0 .5rem rgba(0, 0, 0, .06);--hub-panels-strip-margin-top: var(--hub-ref-space-2, .5rem);--hub-panels-nav-gap: 0;--hub-panels-pill-border-radius: 50rem;--hub-panels-pill-bg-active: var(--hub-sys-color-primary, #0d6efd);--hub-panels-pill-color-active: var(--hub-ref-color-white, #fff);--hub-panels-pill-gap: var(--hub-ref-space-2, .5rem);--hub-panels-pill-content-border-width: 0;--hub-panels-remove-btn-opacity: .6;--hub-panels-remove-btn-opacity-hover: 1;--hub-panels-nav-content-gap: var(--hub-ref-space-2, .5rem);--hub-panels-pane-min-width: 16rem;--hub-panels-pane-min-height: 8rem;--hub-panels-pane-gap: 0;--hub-panels-card-gap: var(--hub-ref-space-3, 1rem);--hub-panels-accordion-color: var(--hub-accordion-color, var(--hub-sys-text-primary, #212529));--hub-panels-accordion-bg: var(--hub-accordion-bg, var(--hub-sys-surface-page, #fff));--hub-panels-accordion-border-width: var(--hub-accordion-border-width, var(--hub-ref-border-width, 1px));--hub-panels-accordion-border-color: var(--hub-accordion-border-color, var(--hub-sys-border-color-default, #dee2e6));--hub-panels-accordion-border-radius: var(--hub-accordion-border-radius, var(--hub-ref-radius-sm, .25rem));--hub-panels-accordion-inner-border-radius: var( --hub-accordion-inner-border-radius, calc(var(--hub-panels-accordion-border-radius) - var(--hub-panels-accordion-border-width)) );--hub-panels-accordion-btn-padding-x: var(--hub-accordion-btn-padding-x, 1.25rem);--hub-panels-accordion-btn-padding-y: var(--hub-accordion-btn-padding-y, var(--hub-ref-space-3, 1rem));--hub-panels-accordion-btn-color: var(--hub-accordion-btn-color, var(--hub-sys-text-primary, #212529));--hub-panels-accordion-btn-bg: var(--hub-accordion-btn-bg, var(--hub-sys-surface-page, #fff));--hub-panels-accordion-active-color: var(--hub-accordion-active-color, var(--hub-sys-color-primary, #0c63e4));--hub-panels-accordion-active-bg: var(--hub-accordion-active-bg, var(--hub-sys-color-primary-subtle, #e7f1ff));--hub-panels-accordion-icon-color: var(--hub-accordion-icon-color, var(--hub-panels-accordion-btn-color));--hub-panels-accordion-icon-active-color: var(--hub-accordion-icon-active-color, var(--hub-panels-accordion-active-color));--hub-panels-accordion-btn-icon-mask: var(--hub-accordion-btn-icon-mask, url(\"data:image/svg+xml;charset=UTF-8,%3Csvg viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E\"));--hub-panels-accordion-btn-icon-width: var(--hub-accordion-btn-icon-width, 1.25rem);--hub-panels-accordion-btn-icon-transform: var(--hub-accordion-btn-icon-transform, rotate(-180deg));--hub-panels-accordion-btn-icon-transition: var(--hub-accordion-btn-icon-transition, transform .2s ease-in-out);--hub-panels-accordion-btn-focus-box-shadow: var( --hub-accordion-btn-focus-box-shadow, 0 0 0 var(--hub-sys-focus-ring-width, .25rem) var(--hub-sys-focus-ring-color, rgba(13, 110, 253, .25)) );--hub-panels-accordion-transition: var( --hub-accordion-transition, color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, border-radius .15s ease );--hub-panels-accordion-collapse-transition-duration: var(--hub-accordion-collapse-transition-duration, .25s);--hub-panels-accordion-collapse-transition-easing: var( --hub-accordion-collapse-transition-easing, cubic-bezier(.4, 0, .2, 1) );--hub-panels-accordion-body-padding-x: var(--hub-accordion-body-padding-x, 1.25rem);--hub-panels-accordion-body-padding-y: var(--hub-accordion-body-padding-y, var(--hub-ref-space-3, 1rem))}\n", ".hub-panels{display:block;width:100%;box-sizing:border-box}.hub-panels,.hub-panels *,.hub-panels *:before,.hub-panels *:after{box-sizing:border-box}.hub-panels__header{display:flex;flex-direction:var(--hub-panels-flex-direction);align-items:stretch;background:var(--hub-panels-header-bg)}.hub-panels__scroll-btn{flex:0 0 auto;align-self:center;width:var(--hub-panels-nav-btn-width);height:var(--hub-panels-nav-btn-height);padding:0;border:0;cursor:pointer;background-color:transparent;background-position:center;background-repeat:no-repeat;background-size:1rem 1rem}.hub-panels__scroll-btn--backward{background-image:var(--hub-panels-backward-btn-bg)}.hub-panels__scroll-btn--forward{background-image:var(--hub-panels-forward-btn-bg)}.hub-panels__nav-scroller{display:flex;flex:1 1 auto;overflow-x:auto;background:var(--hub-panels-header-bg);scroll-behavior:smooth;scrollbar-width:none}.hub-panels__nav-scroller::-webkit-scrollbar{display:none}.hub-panels__nav{display:flex;flex:0 0 auto;flex-wrap:nowrap;gap:var(--hub-panels-nav-gap);margin:0;padding:0;list-style:none}.hub-panels__nav--tabs{border-bottom:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__nav--tabs .hub-panels__nav-link{margin-bottom:calc(-1 * var(--hub-panels-border-width))}.hub-panels__nav--pills{gap:var(--hub-panels-pill-gap)}.hub-panels__nav--pills .hub-panels__nav-link{border-radius:var(--hub-panels-pill-border-radius)}.hub-panels__nav--pills .hub-panels__nav-link--active{color:var(--hub-panels-pill-color-active);background:var(--hub-panels-pill-bg-active);border-color:transparent}.hub-panels__nav--justified{flex:1 1 auto}.hub-panels__nav--justified .hub-panels__nav-item{flex:1 1 0}.hub-panels__nav--justified .hub-panels__nav-link{width:100%;justify-content:center}.hub-panels__nav--vertical{flex-direction:column}.hub-panels__nav--vertical.hub-panels__nav--tabs{border-bottom:0;border-inline-end:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__nav--vertical.hub-panels__nav--tabs .hub-panels__nav-link{margin-bottom:0;margin-inline-end:calc(-1 * var(--hub-panels-border-width))}.hub-panels__nav-item{display:flex;flex:0 0 auto}.hub-panels__nav-link{position:relative;display:inline-flex;align-items:center;gap:var(--hub-panels-tab-gap);padding:var(--hub-panels-tab-padding-y) var(--hub-panels-tab-padding-x);font-family:var(--hub-panels-tab-font-family);font-size:var(--hub-panels-tab-font-size);font-weight:var(--hub-panels-tab-font-weight);line-height:var(--hub-panels-tab-line-height);white-space:nowrap;color:var(--hub-panels-tab-color);background:var(--hub-panels-tab-bg);border-style:var(--hub-panels-tab-border-style);border-width:var(--hub-panels-tab-border-width);border-color:var(--hub-panels-tab-border-color);border-start-start-radius:var(--hub-panels-tab-border-radius);border-start-end-radius:var(--hub-panels-tab-border-radius);cursor:pointer;transition:var(--hub-panels-tab-transition)}.hub-panels__nav-link:hover:not(:disabled):not(.hub-panels__nav-link--active){color:var(--hub-panels-tab-color-hover);background:var(--hub-panels-tab-bg-hover)}.hub-panels__nav-link:focus-visible{z-index:1;outline:0;box-shadow:0 0 0 var(--hub-panels-tab-focus-ring-width) var(--hub-panels-tab-focus-ring-color)}.hub-panels__nav-link--active{color:var(--hub-panels-tab-color-active);background:var(--hub-panels-tab-bg-active);border-color:var(--hub-panels-tab-border-color-active)}.hub-panels__nav-link:disabled{color:var(--hub-panels-tab-color-disabled);background:var(--hub-panels-tab-bg-disabled);cursor:not-allowed}.hub-panels__remove-btn{display:inline-flex;align-items:center;line-height:1;opacity:var(--hub-panels-remove-btn-opacity)}.hub-panels__remove-btn:hover{opacity:var(--hub-panels-remove-btn-opacity-hover)}.hub-panels__spacer{flex:1 1 auto;background:var(--hub-panels-header-bg);border-bottom:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__content{padding:var(--hub-panels-content-padding-y) var(--hub-panels-content-padding-x);background:var(--hub-panels-content-bg);box-shadow:var(--hub-panels-content-box-shadow)}.hub-panels__content--accordion{padding:0;background:transparent;box-shadow:none}.hub-panels__content--multiple-parking{display:none}.hub-panels__multiple-layout{display:flex;align-items:stretch;gap:var(--hub-panels-pane-gap);overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth}.hub-panels__multiple-block{display:flex;flex-direction:column;flex:1 0 var(--hub-panels-pane-min-width);align-self:stretch;min-width:var(--hub-panels-pane-min-width);min-height:100%}.hub-panels__multiple-block.hub-panels--vertical{flex-direction:row;flex:1 1 auto;width:100%;min-width:100%;min-height:var(--hub-panels-pane-min-height)}.hub-panels__multiple-block>.hub-panels__content{display:flex;flex:1 1 auto;min-height:0}.hub-panels__multiple-block.hub-panels--vertical>.hub-panels__content{flex:1 1 auto;width:auto;min-width:max(var(--hub-panels-pane-min-width),var(--hub-panels-multiple-vertical-panel-min-width, 0px))}.hub-panels__multiple-pane-host{display:flex;flex:1 1 auto;width:100%;min-height:0}.hub-panels__multiple-pane-host>.hub-panels__panel{flex:1 1 auto;width:100%;height:100%}.hub-panels__multiple-pane-host>.hub-panels__panel.hub-panels__panel--active{display:flex;flex-direction:column}.hub-panels__multiple-pane-host>.hub-panels__panel.hub-panels__panel--active>div{flex:1 1 auto;min-height:0}.hub-panels--vertical.hub-panels--multiple>.hub-panels__multiple-layout{flex-direction:column;align-items:stretch;width:100%}.hub-panels--pills.hub-panels--multiple.hub-panels--vertical>.hub-panels__multiple-layout>.hub-panels__multiple-block+.hub-panels__multiple-block{padding-top:var(--hub-panels-nav-content-gap);border-top:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__panel{display:none}.hub-panels__panel--active{display:block}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple)>.hub-panels__header{margin-top:var(--hub-panels-strip-margin-top)}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple)>.hub-panels__content{border:var(--hub-panels-border-width) solid var(--hub-panels-border-color);border-top:0;border-end-start-radius:var(--hub-panels-border-radius);border-end-end-radius:var(--hub-panels-border-radius)}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple) .hub-panels__nav--tabs .hub-panels__nav-link--active{border-color:var(--hub-panels-border-color);border-bottom-color:var(--hub-panels-tab-bg-active);box-shadow:var(--hub-panels-tab-active-shadow)}.hub-panels--pills>.hub-panels__header{margin-bottom:var(--hub-panels-nav-content-gap)}.hub-panels--pills>.hub-panels__content{border:var(--hub-panels-pill-content-border-width) solid var(--hub-panels-border-color);border-radius:var(--hub-panels-border-radius)}.hub-panels--vertical{display:flex;align-items:stretch}.hub-panels--vertical .hub-panels__header{flex-direction:column}.hub-panels--vertical .hub-panels__nav-scroller{overflow:visible}.hub-panels--vertical .hub-panels__content{flex:1 1 auto;min-width:0}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple)>.hub-panels__header{margin-inline-start:var(--hub-panels-strip-margin-top);align-self:stretch;min-height:0}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__spacer{display:none}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav-scroller{flex:1 1 auto;align-self:stretch;min-height:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical{flex:1 1 auto;align-self:stretch;min-height:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical .hub-panels__nav-item{width:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical .hub-panels__nav-link{width:calc(100% + var(--hub-panels-border-width));justify-content:flex-start;margin-bottom:0;border-start-start-radius:var(--hub-panels-tab-border-radius);border-start-end-radius:0;border-end-start-radius:var(--hub-panels-tab-border-radius);border-end-end-radius:0;margin-inline-end:calc(-1 * var(--hub-panels-border-width))}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple)>.hub-panels__content{border:var(--hub-panels-border-width) solid var(--hub-panels-border-color);border-inline-start:0;border-start-end-radius:var(--hub-panels-border-radius);border-end-end-radius:var(--hub-panels-border-radius)}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--tabs .hub-panels__nav-link--active{z-index:1;border-color:var(--hub-panels-border-color);border-inline-end-color:var(--hub-panels-tab-bg-active);box-shadow:var(--hub-panels-tab-active-shadow-vertical)}.hub-panels--pills.hub-panels--vertical>.hub-panels__header{margin-bottom:0;margin-inline-end:var(--hub-panels-nav-content-gap)}.hub-panels--card{display:flex}.hub-panels--card>.hub-panels__content{display:flex;flex-direction:column;gap:var(--hub-panels-card-gap, var(--hub-ref-space-3, 1rem));width:100%;padding:0;background:transparent;box-shadow:none}\n", ".hub-panels__panel--accordion{display:block;color:var(--hub-panels-accordion-color);background-color:var(--hub-panels-accordion-bg);border:var(--hub-panels-accordion-border-width) solid var(--hub-panels-accordion-border-color)}.hub-panels__panel--accordion:not(:first-of-type){border-top:0}.hub-panels__panel--accordion:first-of-type{border-start-start-radius:var(--hub-panels-accordion-border-radius);border-start-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__panel--accordion:first-of-type>.hub-panels__accordion-header .hub-panels__accordion-btn{border-start-start-radius:var(--hub-panels-accordion-inner-border-radius);border-start-end-radius:var(--hub-panels-accordion-inner-border-radius)}.hub-panels__panel--accordion:last-of-type{border-end-start-radius:var(--hub-panels-accordion-border-radius);border-end-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__panel--accordion:last-of-type>.hub-panels__accordion-header .hub-panels__accordion-btn--collapsed{border-end-start-radius:var(--hub-panels-accordion-inner-border-radius);border-end-end-radius:var(--hub-panels-accordion-inner-border-radius)}.hub-panels__panel--accordion:last-of-type>.hub-panels__accordion-collapse{border-end-start-radius:var(--hub-panels-accordion-border-radius);border-end-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__accordion-header{margin:0}.hub-panels__accordion-btn{position:relative;display:flex;align-items:center;width:100%;gap:var(--hub-panels-tab-gap);padding:var(--hub-panels-accordion-btn-padding-y) var(--hub-panels-accordion-btn-padding-x);font-family:var(--hub-panels-tab-font-family);font-size:var(--hub-panels-tab-font-size);line-height:var(--hub-panels-tab-line-height);text-align:left;color:var(--hub-panels-accordion-btn-color);background-color:var(--hub-panels-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--hub-panels-accordion-transition);cursor:pointer}.hub-panels__accordion-btn:after{flex-shrink:0;width:var(--hub-panels-accordion-btn-icon-width);height:var(--hub-panels-accordion-btn-icon-width);margin-left:auto;content:\"\";background-color:var(--hub-panels-accordion-icon-active-color);-webkit-mask-image:var(--hub-panels-accordion-btn-icon-mask);mask-image:var(--hub-panels-accordion-btn-icon-mask);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-position:center;mask-position:center;transform:var(--hub-panels-accordion-btn-icon-transform);transition:var(--hub-panels-accordion-btn-icon-transition)}.hub-panels__accordion-btn:not(.hub-panels__accordion-btn--collapsed){color:var(--hub-panels-accordion-active-color);background-color:var(--hub-panels-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--hub-panels-accordion-border-width)) 0 var(--hub-panels-accordion-border-color)}.hub-panels__accordion-btn--collapsed:after{background-color:var(--hub-panels-accordion-icon-color);transform:none}.hub-panels__accordion-btn:hover:not(:disabled){z-index:2}.hub-panels__accordion-btn:focus-visible{z-index:3;outline:0;box-shadow:var(--hub-panels-accordion-btn-focus-box-shadow)}.hub-panels__accordion-btn:disabled{color:var(--hub-panels-tab-color-disabled);cursor:not-allowed}.hub-panels__accordion-collapse{display:grid;grid-template-rows:1fr;transition:grid-template-rows var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing)}.hub-panels__accordion-collapse--collapsed{grid-template-rows:0fr}.hub-panels__accordion-collapse>.hub-panels__accordion-body{min-height:0;overflow:hidden;opacity:1;padding:var(--hub-panels-accordion-body-padding-y) var(--hub-panels-accordion-body-padding-x);transition:opacity var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing),padding var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing)}.hub-panels__accordion-collapse--collapsed>.hub-panels__accordion-body{opacity:0;padding-top:0;padding-bottom:0}.hub-panels--flush .hub-panels__panel--accordion{border-right:0;border-left:0;border-radius:0}.hub-panels--flush .hub-panels__panel--accordion:first-of-type{border-top:0}.hub-panels--flush .hub-panels__panel--accordion:last-of-type{border-bottom:0}.hub-panels--flush .hub-panels__panel--accordion>.hub-panels__accordion-collapse,.hub-panels--flush .hub-panels__panel--accordion>.hub-panels__accordion-header .hub-panels__accordion-btn{border-radius:0}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
742
+ }
743
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelsComponent, decorators: [{
744
+ type: Component,
745
+ args: [{ selector: 'hub-panels', imports: [NgTemplateOutlet, RouterOutlet], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
746
+ {
747
+ provide: NG_VALUE_ACCESSOR,
748
+ useExisting: forwardRef(() => PanelsComponent),
749
+ multi: true
750
+ }
751
+ ], host: {
752
+ class: 'hub-panels',
753
+ '[class.hub-panels--tabs]': "type() === 'tabs'",
754
+ '[class.hub-panels--pills]': "type() === 'pills'",
755
+ '[class.hub-panels--card]': 'isCardView()',
756
+ '[class.hub-panels--vertical]': 'vertical() && !isAccordionView() && !isCardView()',
757
+ '[class.hub-panels--accordion]': 'isAccordionView()',
758
+ '[class.hub-panels--flush]': 'isAccordionView() && flush()',
759
+ '[class.hub-panels--multiple]': 'multiple()',
760
+ '(window:resize)': 'onWindowResize()'
761
+ }, template: "@if (!isAccordionView() && !isCardView()) {\n\t@if (multiple()) {\n\t\t<div class=\"hub-panels__multiple-layout\">\n\t\t\t@for (group of multipleHeaderGroups(); track group.activePanel ?? group.headers[0]) {\n\t\t\t\t<div\n\t\t\t\t\t#multipleBlock\n\t\t\t\t\tclass=\"hub-panels hub-panels__multiple-block\"\n\t\t\t\t\t[class.hub-panels--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t[class.hub-panels--pills]=\"type() === 'pills'\"\n\t\t\t\t\t[class.hub-panels--vertical]=\"vertical()\"\n\t\t\t\t>\n\t\t\t\t\t<div class=\"hub-panels__header\">\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tclass=\"hub-panels__nav\"\n\t\t\t\t\t\t\trole=\"tablist\"\n\t\t\t\t\t\t\t[attr.aria-label]=\"config.ariaLabel\"\n\t\t\t\t\t\t\t[attr.aria-orientation]=\"vertical() ? 'vertical' : null\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--pills]=\"type() === 'pills'\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--vertical]=\"vertical()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav--justified]=\"justified()\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (panel of group.headers; track panel) {\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-item\"\n\t\t\t\t\t\t\t\t\t[class]=\"panel.customClass()\"\n\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-item--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-item--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-link\"\n\t\t\t\t\t\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t\t\t\t\t\t[id]=\"panel.id() + '-link'\"\n\t\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t\t\t[disabled]=\"panel.disabled() || formDisabled()\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-controls]=\"panel.id()\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-selected]=\"panel.active()\"\n\t\t\t\t\t\t\t\t\t\t[attr.tabindex]=\"panel.active() || (!activePanel() && panelIndex(panel) === 0) ? null : -1\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"selectPanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t(keydown)=\"onPanelKeydown($event, panelIndex(panel))\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (panel.headingRef(); as headingRef) {\n\t\t\t\t\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingRef\" />\n\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t{{ panel.heading() }}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if (panel.removable()) {\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__remove-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation(); removePanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t\t\t>&times;</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t@if (!justified() && type() !== 'pills' && !vertical()) {\n\t\t\t\t\t\t\t<div class=\"hub-panels__spacer\"></div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t@if (group.activePanel) {\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclass=\"hub-panels__content\"\n\t\t\t\t\t\t\t[class.hub-panels__content--pills]=\"type() === 'pills'\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t#multiplePaneHost\n\t\t\t\t\t\t\t\tclass=\"hub-panels__multiple-pane-host\"\n\t\t\t\t\t\t\t\t[attr.data-panel-id]=\"group.activePanel.id()\"\n\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t} @else {\n\t\t<div class=\"hub-panels__header\">\n\t\t\t@if (scrollable() && !backwardIsDisabled()) {\n\t\t\t\t<button\n\t\t\t\t\t#prevBtn\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"hub-panels__scroll-btn hub-panels__scroll-btn--backward\"\n\t\t\t\t\t[attr.aria-label]=\"config.scrollBackwardAriaLabel\"\n\t\t\t\t\t(click)=\"navBackward()\"\n\t\t\t\t></button>\n\t\t\t}\n\n\t\t\t<div #navScroller class=\"hub-panels__nav-scroller\" (scroll)=\"onScroll()\">\n\t\t\t\t<ul\n\t\t\t\t\tclass=\"hub-panels__nav\"\n\t\t\t\t\trole=\"tablist\"\n\t\t\t\t\t[attr.aria-label]=\"config.ariaLabel\"\n\t\t\t\t\t[attr.aria-orientation]=\"vertical() ? 'vertical' : null\"\n\t\t\t\t\t[class.hub-panels__nav--tabs]=\"type() === 'tabs'\"\n\t\t\t\t\t[class.hub-panels__nav--pills]=\"type() === 'pills'\"\n\t\t\t\t\t[class.hub-panels__nav--vertical]=\"vertical()\"\n\t\t\t\t\t[class.hub-panels__nav--justified]=\"justified()\"\n\t\t\t\t>\n\t\t\t\t\t@for (panel of panels(); track panel; let index = $index) {\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tclass=\"hub-panels__nav-item\"\n\t\t\t\t\t\t\t[class]=\"panel.customClass()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav-item--active]=\"panel.active()\"\n\t\t\t\t\t\t\t[class.hub-panels__nav-item--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"hub-panels__nav-link\"\n\t\t\t\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t\t\t\t[id]=\"panel.id() + '-link'\"\n\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--active]=\"panel.active()\"\n\t\t\t\t\t\t\t\t[class.hub-panels__nav-link--disabled]=\"panel.disabled()\"\n\t\t\t\t\t\t\t\t[disabled]=\"panel.disabled() || formDisabled()\"\n\t\t\t\t\t\t\t\t[attr.aria-controls]=\"panel.id()\"\n\t\t\t\t\t\t\t\t[attr.aria-selected]=\"panel.active()\"\n\t\t\t\t\t\t\t\t[attr.tabindex]=\"panel.active() || (!activePanel() && index === 0) ? null : -1\"\n\t\t\t\t\t\t\t\t(click)=\"selectPanel(panel)\"\n\t\t\t\t\t\t\t\t(keydown)=\"onPanelKeydown($event, index)\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (panel.headingRef(); as headingRef) {\n\t\t\t\t\t\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingRef\" />\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t{{ panel.heading() }}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (panel.removable()) {\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclass=\"hub-panels__remove-btn\"\n\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation(); removePanel(panel)\"\n\t\t\t\t\t\t\t\t\t\t>&times;</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t}\n\t\t\t\t</ul>\n\t\t\t\t@if (!justified() && type() !== 'pills') {\n\t\t\t\t\t<div class=\"hub-panels__spacer\"></div>\n\t\t\t\t}\n\t\t\t</div>\n\n\t\t\t@if (scrollable() && !forwardIsDisabled()) {\n\t\t\t\t<button\n\t\t\t\t\t#nextBtn\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclass=\"hub-panels__scroll-btn hub-panels__scroll-btn--forward\"\n\t\t\t\t\t[attr.aria-label]=\"config.scrollForwardAriaLabel\"\n\t\t\t\t\t(click)=\"navForward()\"\n\t\t\t\t></button>\n\t\t\t}\n\t\t</div>\n\t}\n}\n\n<div\n\t#contentRoot\n\tclass=\"hub-panels__content\"\n\t[class.hub-panels__content--pills]=\"type() === 'pills'\"\n\t[class.hub-panels__content--accordion]=\"isAccordionView()\"\n\t[class.hub-panels__content--multiple-parking]=\"!isAccordionView() && multiple()\"\n>\n\t@if (activePanelHasRouter()) {\n\t\t<router-outlet />\n\t} @else {\n\t\t<ng-content />\n\t}\n</div>\n", styles: [":where(.hub-panels){--hub-panels-flex-direction: row;--hub-panels-border-width: var(--hub-ref-border-width, 1px);--hub-panels-border-color: var(--hub-sys-border-color-default, #dee2e6);--hub-panels-border-radius: var(--hub-ref-radius-md, .375rem);--hub-panels-content-bg: var(--hub-sys-surface-page, #fff);--hub-panels-header-bg: var(--hub-panels-content-bg);--hub-panels-content-box-shadow: none;--hub-panels-content-padding-x: var(--hub-ref-space-3, 1rem);--hub-panels-content-padding-y: var(--hub-ref-space-3, 1rem);--hub-panels-nav-btn-width: 1.5rem;--hub-panels-nav-btn-height: 1.5rem;--hub-panels-backward-btn-bg: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23212529' d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3E%3C/svg%3E\");--hub-panels-forward-btn-bg: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23212529' d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E\");--hub-panels-nav-link-padding-x: var(--hub-ref-space-3, 1rem);--hub-panels-nav-link-padding-y: var(--hub-ref-space-2, .5rem);--hub-panels-nav-link-color: var(--hub-sys-text-primary, #212529);--hub-panels-nav-link-hover-color: var(--hub-sys-color-primary-emphasis, #0a58ca);--hub-panels-nav-link-active-color: var(--hub-sys-color-primary, #0d6efd);--hub-panels-nav-link-active-bg: var(--hub-panels-content-bg);--hub-panels-nav-link-disabled-color: var(--hub-sys-text-muted, #6c757d);--hub-panels-tab-font-family: var(--hub-container-font-family, var(--hub-ref-font-family-base));--hub-panels-tab-font-size: var(--hub-container-font-size, var(--hub-ref-font-size-base, 1rem));--hub-panels-tab-font-weight: 500;--hub-panels-tab-line-height: var(--hub-ref-line-height-base, 1.5);--hub-panels-tab-padding-x: var(--hub-panels-nav-link-padding-x);--hub-panels-tab-padding-y: var(--hub-panels-nav-link-padding-y);--hub-panels-tab-gap: var(--hub-ref-space-2, .5rem);--hub-panels-tab-bg: transparent;--hub-panels-tab-color: var(--hub-panels-nav-link-color);--hub-panels-tab-border-width: var(--hub-panels-border-width);--hub-panels-tab-border-style: solid;--hub-panels-tab-border-color: transparent;--hub-panels-tab-border-radius: var(--hub-panels-border-radius);--hub-panels-tab-bg-hover: var(--hub-ref-surface-2, #f8f9fa);--hub-panels-tab-color-hover: var(--hub-panels-nav-link-hover-color);--hub-panels-tab-bg-active: var(--hub-panels-nav-link-active-bg);--hub-panels-tab-color-active: var(--hub-panels-nav-link-active-color);--hub-panels-tab-border-color-active: var(--hub-sys-color-primary, #0d6efd);--hub-panels-tab-bg-disabled: transparent;--hub-panels-tab-color-disabled: var(--hub-panels-nav-link-disabled-color);--hub-panels-tab-focus-ring-width: var(--hub-sys-focus-ring-width, .25rem);--hub-panels-tab-focus-ring-color: var(--hub-sys-focus-ring-color, rgba(13, 110, 253, .25));--hub-panels-tab-transition: var(--hub-sys-transition-base, all .2s ease-in-out);--hub-panels-tab-active-shadow: 0 -.25rem .5rem rgba(0, 0, 0, .06);--hub-panels-tab-active-shadow-vertical: -.25rem 0 .5rem rgba(0, 0, 0, .06);--hub-panels-strip-margin-top: var(--hub-ref-space-2, .5rem);--hub-panels-nav-gap: 0;--hub-panels-pill-border-radius: 50rem;--hub-panels-pill-bg-active: var(--hub-sys-color-primary, #0d6efd);--hub-panels-pill-color-active: var(--hub-ref-color-white, #fff);--hub-panels-pill-gap: var(--hub-ref-space-2, .5rem);--hub-panels-pill-content-border-width: 0;--hub-panels-remove-btn-opacity: .6;--hub-panels-remove-btn-opacity-hover: 1;--hub-panels-nav-content-gap: var(--hub-ref-space-2, .5rem);--hub-panels-pane-min-width: 16rem;--hub-panels-pane-min-height: 8rem;--hub-panels-pane-gap: 0;--hub-panels-card-gap: var(--hub-ref-space-3, 1rem);--hub-panels-accordion-color: var(--hub-accordion-color, var(--hub-sys-text-primary, #212529));--hub-panels-accordion-bg: var(--hub-accordion-bg, var(--hub-sys-surface-page, #fff));--hub-panels-accordion-border-width: var(--hub-accordion-border-width, var(--hub-ref-border-width, 1px));--hub-panels-accordion-border-color: var(--hub-accordion-border-color, var(--hub-sys-border-color-default, #dee2e6));--hub-panels-accordion-border-radius: var(--hub-accordion-border-radius, var(--hub-ref-radius-sm, .25rem));--hub-panels-accordion-inner-border-radius: var( --hub-accordion-inner-border-radius, calc(var(--hub-panels-accordion-border-radius) - var(--hub-panels-accordion-border-width)) );--hub-panels-accordion-btn-padding-x: var(--hub-accordion-btn-padding-x, 1.25rem);--hub-panels-accordion-btn-padding-y: var(--hub-accordion-btn-padding-y, var(--hub-ref-space-3, 1rem));--hub-panels-accordion-btn-color: var(--hub-accordion-btn-color, var(--hub-sys-text-primary, #212529));--hub-panels-accordion-btn-bg: var(--hub-accordion-btn-bg, var(--hub-sys-surface-page, #fff));--hub-panels-accordion-active-color: var(--hub-accordion-active-color, var(--hub-sys-color-primary, #0c63e4));--hub-panels-accordion-active-bg: var(--hub-accordion-active-bg, var(--hub-sys-color-primary-subtle, #e7f1ff));--hub-panels-accordion-icon-color: var(--hub-accordion-icon-color, var(--hub-panels-accordion-btn-color));--hub-panels-accordion-icon-active-color: var(--hub-accordion-icon-active-color, var(--hub-panels-accordion-active-color));--hub-panels-accordion-btn-icon-mask: var(--hub-accordion-btn-icon-mask, url(\"data:image/svg+xml;charset=UTF-8,%3Csvg viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E\"));--hub-panels-accordion-btn-icon-width: var(--hub-accordion-btn-icon-width, 1.25rem);--hub-panels-accordion-btn-icon-transform: var(--hub-accordion-btn-icon-transform, rotate(-180deg));--hub-panels-accordion-btn-icon-transition: var(--hub-accordion-btn-icon-transition, transform .2s ease-in-out);--hub-panels-accordion-btn-focus-box-shadow: var( --hub-accordion-btn-focus-box-shadow, 0 0 0 var(--hub-sys-focus-ring-width, .25rem) var(--hub-sys-focus-ring-color, rgba(13, 110, 253, .25)) );--hub-panels-accordion-transition: var( --hub-accordion-transition, color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, border-radius .15s ease );--hub-panels-accordion-collapse-transition-duration: var(--hub-accordion-collapse-transition-duration, .25s);--hub-panels-accordion-collapse-transition-easing: var( --hub-accordion-collapse-transition-easing, cubic-bezier(.4, 0, .2, 1) );--hub-panels-accordion-body-padding-x: var(--hub-accordion-body-padding-x, 1.25rem);--hub-panels-accordion-body-padding-y: var(--hub-accordion-body-padding-y, var(--hub-ref-space-3, 1rem))}\n", ".hub-panels{display:block;width:100%;box-sizing:border-box}.hub-panels,.hub-panels *,.hub-panels *:before,.hub-panels *:after{box-sizing:border-box}.hub-panels__header{display:flex;flex-direction:var(--hub-panels-flex-direction);align-items:stretch;background:var(--hub-panels-header-bg)}.hub-panels__scroll-btn{flex:0 0 auto;align-self:center;width:var(--hub-panels-nav-btn-width);height:var(--hub-panels-nav-btn-height);padding:0;border:0;cursor:pointer;background-color:transparent;background-position:center;background-repeat:no-repeat;background-size:1rem 1rem}.hub-panels__scroll-btn--backward{background-image:var(--hub-panels-backward-btn-bg)}.hub-panels__scroll-btn--forward{background-image:var(--hub-panels-forward-btn-bg)}.hub-panels__nav-scroller{display:flex;flex:1 1 auto;overflow-x:auto;background:var(--hub-panels-header-bg);scroll-behavior:smooth;scrollbar-width:none}.hub-panels__nav-scroller::-webkit-scrollbar{display:none}.hub-panels__nav{display:flex;flex:0 0 auto;flex-wrap:nowrap;gap:var(--hub-panels-nav-gap);margin:0;padding:0;list-style:none}.hub-panels__nav--tabs{border-bottom:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__nav--tabs .hub-panels__nav-link{margin-bottom:calc(-1 * var(--hub-panels-border-width))}.hub-panels__nav--pills{gap:var(--hub-panels-pill-gap)}.hub-panels__nav--pills .hub-panels__nav-link{border-radius:var(--hub-panels-pill-border-radius)}.hub-panels__nav--pills .hub-panels__nav-link--active{color:var(--hub-panels-pill-color-active);background:var(--hub-panels-pill-bg-active);border-color:transparent}.hub-panels__nav--justified{flex:1 1 auto}.hub-panels__nav--justified .hub-panels__nav-item{flex:1 1 0}.hub-panels__nav--justified .hub-panels__nav-link{width:100%;justify-content:center}.hub-panels__nav--vertical{flex-direction:column}.hub-panels__nav--vertical.hub-panels__nav--tabs{border-bottom:0;border-inline-end:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__nav--vertical.hub-panels__nav--tabs .hub-panels__nav-link{margin-bottom:0;margin-inline-end:calc(-1 * var(--hub-panels-border-width))}.hub-panels__nav-item{display:flex;flex:0 0 auto}.hub-panels__nav-link{position:relative;display:inline-flex;align-items:center;gap:var(--hub-panels-tab-gap);padding:var(--hub-panels-tab-padding-y) var(--hub-panels-tab-padding-x);font-family:var(--hub-panels-tab-font-family);font-size:var(--hub-panels-tab-font-size);font-weight:var(--hub-panels-tab-font-weight);line-height:var(--hub-panels-tab-line-height);white-space:nowrap;color:var(--hub-panels-tab-color);background:var(--hub-panels-tab-bg);border-style:var(--hub-panels-tab-border-style);border-width:var(--hub-panels-tab-border-width);border-color:var(--hub-panels-tab-border-color);border-start-start-radius:var(--hub-panels-tab-border-radius);border-start-end-radius:var(--hub-panels-tab-border-radius);cursor:pointer;transition:var(--hub-panels-tab-transition)}.hub-panels__nav-link:hover:not(:disabled):not(.hub-panels__nav-link--active){color:var(--hub-panels-tab-color-hover);background:var(--hub-panels-tab-bg-hover)}.hub-panels__nav-link:focus-visible{z-index:1;outline:0;box-shadow:0 0 0 var(--hub-panels-tab-focus-ring-width) var(--hub-panels-tab-focus-ring-color)}.hub-panels__nav-link--active{color:var(--hub-panels-tab-color-active);background:var(--hub-panels-tab-bg-active);border-color:var(--hub-panels-tab-border-color-active)}.hub-panels__nav-link:disabled{color:var(--hub-panels-tab-color-disabled);background:var(--hub-panels-tab-bg-disabled);cursor:not-allowed}.hub-panels__remove-btn{display:inline-flex;align-items:center;line-height:1;opacity:var(--hub-panels-remove-btn-opacity)}.hub-panels__remove-btn:hover{opacity:var(--hub-panels-remove-btn-opacity-hover)}.hub-panels__spacer{flex:1 1 auto;background:var(--hub-panels-header-bg);border-bottom:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__content{padding:var(--hub-panels-content-padding-y) var(--hub-panels-content-padding-x);background:var(--hub-panels-content-bg);box-shadow:var(--hub-panels-content-box-shadow)}.hub-panels__content--accordion{padding:0;background:transparent;box-shadow:none}.hub-panels__content--multiple-parking{display:none}.hub-panels__multiple-layout{display:flex;align-items:stretch;gap:var(--hub-panels-pane-gap);overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth}.hub-panels__multiple-block{display:flex;flex-direction:column;flex:1 0 var(--hub-panels-pane-min-width);align-self:stretch;min-width:var(--hub-panels-pane-min-width);min-height:100%}.hub-panels__multiple-block.hub-panels--vertical{flex-direction:row;flex:1 1 auto;width:100%;min-width:100%;min-height:var(--hub-panels-pane-min-height)}.hub-panels__multiple-block>.hub-panels__content{display:flex;flex:1 1 auto;min-height:0}.hub-panels__multiple-block.hub-panels--vertical>.hub-panels__content{flex:1 1 auto;width:auto;min-width:max(var(--hub-panels-pane-min-width),var(--hub-panels-multiple-vertical-panel-min-width, 0px))}.hub-panels__multiple-pane-host{display:flex;flex:1 1 auto;width:100%;min-height:0}.hub-panels__multiple-pane-host>.hub-panels__panel{flex:1 1 auto;width:100%;height:100%}.hub-panels__multiple-pane-host>.hub-panels__panel.hub-panels__panel--active{display:flex;flex-direction:column}.hub-panels__multiple-pane-host>.hub-panels__panel.hub-panels__panel--active>div{flex:1 1 auto;min-height:0}.hub-panels--vertical.hub-panels--multiple>.hub-panels__multiple-layout{flex-direction:column;align-items:stretch;width:100%}.hub-panels--pills.hub-panels--multiple.hub-panels--vertical>.hub-panels__multiple-layout>.hub-panels__multiple-block+.hub-panels__multiple-block{padding-top:var(--hub-panels-nav-content-gap);border-top:var(--hub-panels-border-width) solid var(--hub-panels-border-color)}.hub-panels__panel{display:none}.hub-panels__panel--active{display:block}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple)>.hub-panels__header{margin-top:var(--hub-panels-strip-margin-top)}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple)>.hub-panels__content{border:var(--hub-panels-border-width) solid var(--hub-panels-border-color);border-top:0;border-end-start-radius:var(--hub-panels-border-radius);border-end-end-radius:var(--hub-panels-border-radius)}.hub-panels--tabs:not(.hub-panels--vertical):not(.hub-panels--multiple) .hub-panels__nav--tabs .hub-panels__nav-link--active{border-color:var(--hub-panels-border-color);border-bottom-color:var(--hub-panels-tab-bg-active);box-shadow:var(--hub-panels-tab-active-shadow)}.hub-panels--pills>.hub-panels__header{margin-bottom:var(--hub-panels-nav-content-gap)}.hub-panels--pills>.hub-panels__content{border:var(--hub-panels-pill-content-border-width) solid var(--hub-panels-border-color);border-radius:var(--hub-panels-border-radius)}.hub-panels--vertical{display:flex;align-items:stretch}.hub-panels--vertical .hub-panels__header{flex-direction:column}.hub-panels--vertical .hub-panels__nav-scroller{overflow:visible}.hub-panels--vertical .hub-panels__content{flex:1 1 auto;min-width:0}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple)>.hub-panels__header{margin-inline-start:var(--hub-panels-strip-margin-top);align-self:stretch;min-height:0}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__spacer{display:none}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav-scroller{flex:1 1 auto;align-self:stretch;min-height:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical{flex:1 1 auto;align-self:stretch;min-height:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical .hub-panels__nav-item{width:100%}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--vertical .hub-panels__nav-link{width:calc(100% + var(--hub-panels-border-width));justify-content:flex-start;margin-bottom:0;border-start-start-radius:var(--hub-panels-tab-border-radius);border-start-end-radius:0;border-end-start-radius:var(--hub-panels-tab-border-radius);border-end-end-radius:0;margin-inline-end:calc(-1 * var(--hub-panels-border-width))}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple)>.hub-panels__content{border:var(--hub-panels-border-width) solid var(--hub-panels-border-color);border-inline-start:0;border-start-end-radius:var(--hub-panels-border-radius);border-end-end-radius:var(--hub-panels-border-radius)}.hub-panels--tabs.hub-panels--vertical:not(.hub-panels--multiple) .hub-panels__nav--tabs .hub-panels__nav-link--active{z-index:1;border-color:var(--hub-panels-border-color);border-inline-end-color:var(--hub-panels-tab-bg-active);box-shadow:var(--hub-panels-tab-active-shadow-vertical)}.hub-panels--pills.hub-panels--vertical>.hub-panels__header{margin-bottom:0;margin-inline-end:var(--hub-panels-nav-content-gap)}.hub-panels--card{display:flex}.hub-panels--card>.hub-panels__content{display:flex;flex-direction:column;gap:var(--hub-panels-card-gap, var(--hub-ref-space-3, 1rem));width:100%;padding:0;background:transparent;box-shadow:none}\n", ".hub-panels__panel--accordion{display:block;color:var(--hub-panels-accordion-color);background-color:var(--hub-panels-accordion-bg);border:var(--hub-panels-accordion-border-width) solid var(--hub-panels-accordion-border-color)}.hub-panels__panel--accordion:not(:first-of-type){border-top:0}.hub-panels__panel--accordion:first-of-type{border-start-start-radius:var(--hub-panels-accordion-border-radius);border-start-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__panel--accordion:first-of-type>.hub-panels__accordion-header .hub-panels__accordion-btn{border-start-start-radius:var(--hub-panels-accordion-inner-border-radius);border-start-end-radius:var(--hub-panels-accordion-inner-border-radius)}.hub-panels__panel--accordion:last-of-type{border-end-start-radius:var(--hub-panels-accordion-border-radius);border-end-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__panel--accordion:last-of-type>.hub-panels__accordion-header .hub-panels__accordion-btn--collapsed{border-end-start-radius:var(--hub-panels-accordion-inner-border-radius);border-end-end-radius:var(--hub-panels-accordion-inner-border-radius)}.hub-panels__panel--accordion:last-of-type>.hub-panels__accordion-collapse{border-end-start-radius:var(--hub-panels-accordion-border-radius);border-end-end-radius:var(--hub-panels-accordion-border-radius)}.hub-panels__accordion-header{margin:0}.hub-panels__accordion-btn{position:relative;display:flex;align-items:center;width:100%;gap:var(--hub-panels-tab-gap);padding:var(--hub-panels-accordion-btn-padding-y) var(--hub-panels-accordion-btn-padding-x);font-family:var(--hub-panels-tab-font-family);font-size:var(--hub-panels-tab-font-size);line-height:var(--hub-panels-tab-line-height);text-align:left;color:var(--hub-panels-accordion-btn-color);background-color:var(--hub-panels-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--hub-panels-accordion-transition);cursor:pointer}.hub-panels__accordion-btn:after{flex-shrink:0;width:var(--hub-panels-accordion-btn-icon-width);height:var(--hub-panels-accordion-btn-icon-width);margin-left:auto;content:\"\";background-color:var(--hub-panels-accordion-icon-active-color);-webkit-mask-image:var(--hub-panels-accordion-btn-icon-mask);mask-image:var(--hub-panels-accordion-btn-icon-mask);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;-webkit-mask-position:center;mask-position:center;transform:var(--hub-panels-accordion-btn-icon-transform);transition:var(--hub-panels-accordion-btn-icon-transition)}.hub-panels__accordion-btn:not(.hub-panels__accordion-btn--collapsed){color:var(--hub-panels-accordion-active-color);background-color:var(--hub-panels-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--hub-panels-accordion-border-width)) 0 var(--hub-panels-accordion-border-color)}.hub-panels__accordion-btn--collapsed:after{background-color:var(--hub-panels-accordion-icon-color);transform:none}.hub-panels__accordion-btn:hover:not(:disabled){z-index:2}.hub-panels__accordion-btn:focus-visible{z-index:3;outline:0;box-shadow:var(--hub-panels-accordion-btn-focus-box-shadow)}.hub-panels__accordion-btn:disabled{color:var(--hub-panels-tab-color-disabled);cursor:not-allowed}.hub-panels__accordion-collapse{display:grid;grid-template-rows:1fr;transition:grid-template-rows var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing)}.hub-panels__accordion-collapse--collapsed{grid-template-rows:0fr}.hub-panels__accordion-collapse>.hub-panels__accordion-body{min-height:0;overflow:hidden;opacity:1;padding:var(--hub-panels-accordion-body-padding-y) var(--hub-panels-accordion-body-padding-x);transition:opacity var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing),padding var(--hub-panels-accordion-collapse-transition-duration) var(--hub-panels-accordion-collapse-transition-easing)}.hub-panels__accordion-collapse--collapsed>.hub-panels__accordion-body{opacity:0;padding-top:0;padding-bottom:0}.hub-panels--flush .hub-panels__panel--accordion{border-right:0;border-left:0;border-radius:0}.hub-panels--flush .hub-panels__panel--accordion:first-of-type{border-top:0}.hub-panels--flush .hub-panels__panel--accordion:last-of-type{border-bottom:0}.hub-panels--flush .hub-panels__panel--accordion>.hub-panels__accordion-collapse,.hub-panels--flush .hub-panels__panel--accordion>.hub-panels__accordion-header .hub-panels__accordion-btn{border-radius:0}\n"] }]
762
+ }], ctorParameters: () => [], propDecorators: { navScroller: [{ type: i0.ViewChild, args: ['navScroller', { isSignal: true }] }], prevBtn: [{ type: i0.ViewChild, args: ['prevBtn', { isSignal: true }] }], nextBtn: [{ type: i0.ViewChild, args: ['nextBtn', { isSignal: true }] }], contentRoot: [{ type: i0.ViewChild, args: ['contentRoot', { isSignal: true }] }], multiplePaneHosts: [{ type: i0.ViewChildren, args: ['multiplePaneHost', { isSignal: true }] }], multipleBlocks: [{ type: i0.ViewChildren, args: ['multipleBlock', { isSignal: true }] }], panelChange: [{ type: i0.Output, args: ["panelChange"] }], vertical: [{ type: i0.Input, args: [{ isSignal: true, alias: "vertical", required: false }] }], justified: [{ type: i0.Input, args: [{ isSignal: true, alias: "justified", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], isKeysAllowed: [{ type: i0.Input, args: [{ isSignal: true, alias: "isKeysAllowed", required: false }] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], bindValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "bindValue", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }] } });
763
+
764
+ /** Monotonic counter backing the auto-generated accessibility ids. */
765
+ let nextPanelId = 0;
766
+ /**
767
+ * One content panel inside a `<hub-panels>` container.
768
+ *
769
+ * In the `tabs` / `pills` views the host element is the tab *panel*
770
+ * (`role="tabpanel"`) and the clickable header is rendered by
771
+ * {@link PanelsComponent} in the strip. In the `accordion` view this component
772
+ * renders its own disclosure header plus an animated collapse wrapper around
773
+ * the projected content. In every view the header comes from `heading` or from
774
+ * a `<ng-template hubPanelHeading>` projected inside this element.
775
+ *
776
+ * Panels can be plain content panes or routed: when `routerLink` is set, the
777
+ * container renders a `<router-outlet>` instead of the projected panels and
778
+ * the active panel follows the current URL (`tabs` / `pills` views only).
779
+ *
780
+ * @example
781
+ * ```html
782
+ * <hub-panels>
783
+ * <hub-panel heading="Fields">…</hub-panel>
784
+ * <hub-panel>
785
+ * <ng-template hubPanelHeading>Validations <span class="badge">3</span></ng-template>
786
+ * …
787
+ * </hub-panel>
788
+ * </hub-panels>
789
+ * ```
790
+ */
791
+ class PanelComponent {
792
+ /** Host element reference — exposed so the container can detach removed panes. */
793
+ elementRef = inject(ElementRef);
794
+ #renderer = inject(Renderer2);
795
+ #router = inject(Router);
796
+ /**
797
+ * Owning panels container, or `null` when the panel is used standalone
798
+ * (outside any `<hub-panels>`), in which case it renders as a card.
799
+ * Exposed to the template for the accordion header.
800
+ */
801
+ tabset = inject(PanelsComponent, { optional: true });
802
+ /** Plain-text panel header. Ignored when a `hubPanelHeading` template exists. */
803
+ heading = input(undefined, /* @ts-ignore */
804
+ ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
805
+ /**
806
+ * Unique id used for the `tab` / `tabpanel` (or accordion header / region)
807
+ * ARIA pairing. Auto-generated (`hub-panel-<n>`) when not provided.
808
+ */
809
+ id = input(`hub-panel-${nextPanelId++}`, /* @ts-ignore */
810
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
811
+ /** Whether the panel cannot be activated. */
812
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
813
+ /** Whether the panel shows a remove affordance (✕ and the Delete key). */
814
+ removable = input(false, { ...(ngDevMode ? { debugName: "removable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
815
+ /**
816
+ * URL comparison used to mark routed panels active: `'route'` compares the
817
+ * path only, `'full'` also compares query params.
818
+ */
819
+ pathMatch = input('route', /* @ts-ignore */
820
+ ...(ngDevMode ? [{ debugName: "pathMatch" }] : /* istanbul ignore next */ []));
821
+ /** Navigation target that turns this panel into a routed panel. */
822
+ routerLink = input(undefined, /* @ts-ignore */
823
+ ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
824
+ /** Query params appended when navigating to `routerLink`. */
825
+ queryParams = input(undefined, /* @ts-ignore */
826
+ ...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
827
+ /** Extra CSS classes applied to both the nav item and this pane. */
828
+ customClass = input(undefined, /* @ts-ignore */
829
+ ...(ngDevMode ? [{ debugName: "customClass" }] : /* istanbul ignore next */ []));
830
+ /**
831
+ * Value this panel contributes when the container is used as a form control
832
+ * (see {@link PanelsComponent} `ControlValueAccessor`). Defaults to `id`.
833
+ */
834
+ value = input(undefined, /* @ts-ignore */
835
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
836
+ /** Whether the panel is currently active (expanded, in the accordion view). */
837
+ active = model(false, /* @ts-ignore */
838
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
839
+ /** Emitted when the panel becomes active. */
840
+ selectPanel = output();
841
+ /** Emitted when the panel stops being active. */
842
+ deselectPanel = output();
843
+ /** Emitted when the panel is removed through the ✕ button or the Delete key. */
844
+ removed = output();
845
+ /** Custom header template registered by `PanelHeadingDirective`. */
846
+ headingRef = signal(undefined, /* @ts-ignore */
847
+ ...(ngDevMode ? [{ debugName: "headingRef" }] : /* istanbul ignore next */ []));
848
+ /** Whether the owning container renders the accordion visualization. */
849
+ accordionView = computed(() => this.tabset?.isAccordionView() ?? false, /* @ts-ignore */
850
+ ...(ngDevMode ? [{ debugName: "accordionView" }] : /* istanbul ignore next */ []));
851
+ /**
852
+ * Whether this panel renders as a card: either inside a `<hub-panels
853
+ * type="card">` container or standalone (no owning container at all).
854
+ */
855
+ cardView = computed(() => !this.tabset || this.tabset.isCardView(), /* @ts-ignore */
856
+ ...(ngDevMode ? [{ debugName: "cardView" }] : /* istanbul ignore next */ []));
857
+ /** Form value resolved for this panel: the explicit `value` or the panel id. */
858
+ formValue = computed(() => (this.value() !== undefined ? this.value() : this.id()), /* @ts-ignore */
859
+ ...(ngDevMode ? [{ debugName: "formValue" }] : /* istanbul ignore next */ []));
860
+ /** `routerLink` normalised to a segments array, or `null` when not routed. */
861
+ routerUrl = computed(() => {
862
+ const link = this.routerLink();
863
+ if (!link) {
864
+ return null;
865
+ }
866
+ return Array.isArray(link) ? link : [link];
867
+ }, /* @ts-ignore */
868
+ ...(ngDevMode ? [{ debugName: "routerUrl" }] : /* istanbul ignore next */ []));
869
+ /** Last activation state seen by the effect — avoids emitting on init. */
870
+ #wasActive = false;
871
+ constructor() {
872
+ // Standalone panels (no container) render as a card and skip registration.
873
+ this.tabset?.registerPanel(this);
874
+ // Mirror `customClass` onto the pane host element.
875
+ effect((onCleanup) => {
876
+ const classes = (this.customClass() ?? '')
877
+ .split(' ')
878
+ .map((cssClass) => cssClass.trim())
879
+ .filter((cssClass) => cssClass.length > 0);
880
+ for (const cssClass of classes) {
881
+ this.#renderer.addClass(this.elementRef.nativeElement, cssClass);
882
+ }
883
+ onCleanup(() => {
884
+ for (const cssClass of classes) {
885
+ this.#renderer.removeClass(this.elementRef.nativeElement, cssClass);
886
+ }
887
+ });
888
+ });
889
+ // Activation side effects: emit outputs and enforce single-active-panel
890
+ // (unless the container allows multiple expanded panels — accordion mode).
891
+ effect(() => {
892
+ const isActive = this.active();
893
+ if (isActive && this.disabled()) {
894
+ this.active.set(false);
895
+ return;
896
+ }
897
+ if (isActive === this.#wasActive) {
898
+ return;
899
+ }
900
+ this.#wasActive = isActive;
901
+ if (isActive) {
902
+ this.selectPanel.emit(this);
903
+ if (this.tabset && !this.tabset.allowsMultipleActive()) {
904
+ for (const panel of this.tabset.panels()) {
905
+ if (panel !== this) {
906
+ panel.active.set(false);
907
+ }
908
+ }
909
+ }
910
+ }
911
+ else {
912
+ this.deselectPanel.emit(this);
913
+ }
914
+ });
915
+ }
916
+ ngOnDestroy() {
917
+ this.tabset?.removePanel(this, { reselect: false, emit: false });
918
+ }
919
+ /** Route path plus query string, or `null` when the panel is not routed. */
920
+ getFullRoute() {
921
+ const route = this.getRoute();
922
+ if (!route) {
923
+ return null;
924
+ }
925
+ const queryString = this.getQueryParamsString();
926
+ return queryString ? `${route}?${queryString}` : route;
927
+ }
928
+ /** Normalised absolute route path, or `null` when the panel is not routed. */
929
+ getRoute() {
930
+ const routerUrl = this.routerUrl();
931
+ if (!routerUrl) {
932
+ return null;
933
+ }
934
+ let route = routerUrl.join('/').replace(/\/\/+/g, '/');
935
+ if (route.charAt(0) === '.') {
936
+ route = route.substring(1);
937
+ }
938
+ if (route.charAt(0) !== '/') {
939
+ route = `/${route}`;
940
+ }
941
+ return route;
942
+ }
943
+ /** `queryParams` serialised as `key=value&…`, or `null` when empty. */
944
+ getQueryParamsString() {
945
+ const params = this.queryParams();
946
+ if (!params || !Object.keys(params).length) {
947
+ return null;
948
+ }
949
+ return Object.entries(params)
950
+ .map(([key, value]) => `${key}=${value}`)
951
+ .join('&');
952
+ }
953
+ /** Navigates to the panel's route. No-op for non-routed panels. */
954
+ navigate() {
955
+ const routerUrl = this.routerUrl();
956
+ if (routerUrl) {
957
+ this.#router.navigate(routerUrl, { queryParams: this.queryParams() ?? {} });
958
+ }
959
+ }
960
+ /** Accordion header click — toggles this panel through the container. */
961
+ onAccordionHeaderClick() {
962
+ this.tabset?.togglePanel(this);
963
+ }
964
+ /** Accordion header keyboard navigation, delegated to the container. */
965
+ onAccordionHeaderKeydown(event) {
966
+ this.tabset?.onAccordionKeydown(event, this);
967
+ }
968
+ /** Removes this panel through the accordion header's ✕ affordance. */
969
+ removeSelf() {
970
+ this.tabset?.removePanel(this);
971
+ }
972
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
973
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: PanelComponent, isStandalone: true, selector: "hub-panel, [hub-panel]", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, pathMatch: { classPropertyName: "pathMatch", publicName: "pathMatch", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", isSignal: true, isRequired: false, transformFunction: null }, customClass: { classPropertyName: "customClass", publicName: "customClass", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { active: "activeChange", selectPanel: "selectPanel", deselectPanel: "deselectPanel", removed: "removed" }, host: { properties: { "attr.id": "accordionView() || cardView() ? null : id()", "attr.role": "accordionView() || cardView() ? null : 'tabpanel'", "attr.aria-labelledby": "accordionView() || cardView() ? null : id() + '-link'", "class.hub-panels__panel--active": "active()", "class.hub-panels__panel--accordion": "accordionView()", "class.hub-panels__panel--card": "cardView()" }, classAttribute: "hub-panels__panel" }, exportAs: ["hubPanel"], ngImport: i0, template: "@if (accordionView()) {\n\t<h2 class=\"hub-panels__accordion-header\">\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclass=\"hub-panels__accordion-btn\"\n\t\t\t[class.hub-panels__accordion-btn--collapsed]=\"!active()\"\n\t\t\t[id]=\"id() + '-link'\"\n\t\t\t[disabled]=\"disabled() || tabset?.formDisabled()\"\n\t\t\t[attr.aria-expanded]=\"active()\"\n\t\t\t[attr.aria-controls]=\"id()\"\n\t\t\t(click)=\"onAccordionHeaderClick()\"\n\t\t\t(keydown)=\"onAccordionHeaderKeydown($event)\"\n\t\t>\n\t\t\t@if (headingRef(); as headingTemplate) {\n\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingTemplate\" />\n\t\t\t} @else {\n\t\t\t\t{{ heading() }}\n\t\t\t}\n\t\t\t@if (removable()) {\n\t\t\t\t<span class=\"hub-panels__remove-btn\" aria-hidden=\"true\" (click)=\"$event.stopPropagation(); removeSelf()\">&times;</span>\n\t\t\t}\n\t\t</button>\n\t</h2>\n}\n\n<!-- Projection slots for every view. In the strip views the wrappers are plain\n\t pass-throughs; in the accordion view they become the animated collapse\n\t region. The header/footer `select` slots and the default `<ng-content>` are\n\t all UNCONDITIONAL \u2014 never wrapped in `@if` \u2014 to avoid the lost-projection\n\t bug that conditional slots cause. Header/footer render in every view; the\n\t `card` view styles them as the card's header and footer bands. -->\n<div\n\t[class.hub-panels__accordion-collapse]=\"accordionView()\"\n\t[class.hub-panels__accordion-collapse--collapsed]=\"accordionView() && !active()\"\n\t[attr.role]=\"accordionView() ? 'region' : null\"\n\t[attr.id]=\"accordionView() ? id() : null\"\n\t[attr.aria-labelledby]=\"accordionView() ? id() + '-link' : null\"\n\t[attr.aria-hidden]=\"accordionView() ? !active() : null\"\n\t[attr.inert]=\"accordionView() && !active() ? '' : null\"\n>\n\t<div [class.hub-panels__accordion-body]=\"accordionView()\">\n\t\t<ng-content select=\"[hubPanelHeader]\" />\n\t\t<div class=\"hub-panels__panel-body\">\n\t\t\t<ng-content />\n\t\t</div>\n\t\t<ng-content select=\"[hubPanelFooter]\" />\n\t</div>\n</div>\n", styles: [":where(.hub-panels__panel--card){--hub-panels-card-bg: var(--hub-sys-surface-page, #fff);--hub-panels-card-color: var(--hub-sys-text-primary, #212529);--hub-panels-card-border-width: var(--hub-ref-border-width, 1px);--hub-panels-card-border-color: var(--hub-sys-border-color-default, #dee2e6);--hub-panels-card-border-radius: var(--hub-ref-radius-md, .375rem);--hub-panels-card-box-shadow: var(--hub-sys-shadow-sm, 0 .125rem .25rem rgba(0, 0, 0, .075));--hub-panels-card-padding-x: var(--hub-ref-space-4, 1.25rem);--hub-panels-card-padding-y: var(--hub-ref-space-3, 1rem)}:where(.hub-panels__panel-header,.hub-panels__panel-footer){--hub-panels-panel-header-bg: var(--hub-ref-surface-2, #f8f9fa);--hub-panels-panel-header-color: var(--hub-sys-text-primary, #212529);--hub-panels-panel-header-padding-x: var(--hub-ref-space-4, 1.25rem);--hub-panels-panel-header-padding-y: var(--hub-ref-space-3, 1rem);--hub-panels-panel-header-font-weight: 600;--hub-panels-panel-header-border-width: var(--hub-ref-border-width, 1px);--hub-panels-panel-header-border-color: var(--hub-sys-border-color-default, #dee2e6)}.hub-panels__panel-header{display:block;padding:var(--hub-panels-panel-header-padding-y) var(--hub-panels-panel-header-padding-x);color:var(--hub-panels-panel-header-color);background:var(--hub-panels-panel-header-bg);font-weight:var(--hub-panels-panel-header-font-weight);border-bottom:var(--hub-panels-panel-header-border-width) solid var(--hub-panels-panel-header-border-color)}.hub-panels__panel-footer{display:block;padding:var(--hub-panels-panel-header-padding-y) var(--hub-panels-panel-header-padding-x);color:var(--hub-panels-panel-header-color);background:var(--hub-panels-panel-header-bg);border-top:var(--hub-panels-panel-header-border-width) solid var(--hub-panels-panel-header-border-color)}.hub-panels__panel.hub-panels__panel--card{display:flex;flex-direction:column;color:var(--hub-panels-card-color);background:var(--hub-panels-card-bg);border:var(--hub-panels-card-border-width) solid var(--hub-panels-card-border-color);border-radius:var(--hub-panels-card-border-radius);box-shadow:var(--hub-panels-card-box-shadow);overflow:hidden}.hub-panels__panel.hub-panels__panel--card .hub-panels__panel-body{padding:var(--hub-panels-card-padding-y) var(--hub-panels-card-padding-x)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
974
+ }
975
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelComponent, decorators: [{
976
+ type: Component,
977
+ args: [{ selector: 'hub-panel, [hub-panel]', exportAs: 'hubPanel', imports: [NgTemplateOutlet], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
978
+ class: 'hub-panels__panel',
979
+ '[attr.id]': 'accordionView() || cardView() ? null : id()',
980
+ '[attr.role]': "accordionView() || cardView() ? null : 'tabpanel'",
981
+ '[attr.aria-labelledby]': "accordionView() || cardView() ? null : id() + '-link'",
982
+ '[class.hub-panels__panel--active]': 'active()',
983
+ '[class.hub-panels__panel--accordion]': 'accordionView()',
984
+ '[class.hub-panels__panel--card]': 'cardView()'
985
+ }, template: "@if (accordionView()) {\n\t<h2 class=\"hub-panels__accordion-header\">\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tclass=\"hub-panels__accordion-btn\"\n\t\t\t[class.hub-panels__accordion-btn--collapsed]=\"!active()\"\n\t\t\t[id]=\"id() + '-link'\"\n\t\t\t[disabled]=\"disabled() || tabset?.formDisabled()\"\n\t\t\t[attr.aria-expanded]=\"active()\"\n\t\t\t[attr.aria-controls]=\"id()\"\n\t\t\t(click)=\"onAccordionHeaderClick()\"\n\t\t\t(keydown)=\"onAccordionHeaderKeydown($event)\"\n\t\t>\n\t\t\t@if (headingRef(); as headingTemplate) {\n\t\t\t\t<ng-container [ngTemplateOutlet]=\"headingTemplate\" />\n\t\t\t} @else {\n\t\t\t\t{{ heading() }}\n\t\t\t}\n\t\t\t@if (removable()) {\n\t\t\t\t<span class=\"hub-panels__remove-btn\" aria-hidden=\"true\" (click)=\"$event.stopPropagation(); removeSelf()\">&times;</span>\n\t\t\t}\n\t\t</button>\n\t</h2>\n}\n\n<!-- Projection slots for every view. In the strip views the wrappers are plain\n\t pass-throughs; in the accordion view they become the animated collapse\n\t region. The header/footer `select` slots and the default `<ng-content>` are\n\t all UNCONDITIONAL \u2014 never wrapped in `@if` \u2014 to avoid the lost-projection\n\t bug that conditional slots cause. Header/footer render in every view; the\n\t `card` view styles them as the card's header and footer bands. -->\n<div\n\t[class.hub-panels__accordion-collapse]=\"accordionView()\"\n\t[class.hub-panels__accordion-collapse--collapsed]=\"accordionView() && !active()\"\n\t[attr.role]=\"accordionView() ? 'region' : null\"\n\t[attr.id]=\"accordionView() ? id() : null\"\n\t[attr.aria-labelledby]=\"accordionView() ? id() + '-link' : null\"\n\t[attr.aria-hidden]=\"accordionView() ? !active() : null\"\n\t[attr.inert]=\"accordionView() && !active() ? '' : null\"\n>\n\t<div [class.hub-panels__accordion-body]=\"accordionView()\">\n\t\t<ng-content select=\"[hubPanelHeader]\" />\n\t\t<div class=\"hub-panels__panel-body\">\n\t\t\t<ng-content />\n\t\t</div>\n\t\t<ng-content select=\"[hubPanelFooter]\" />\n\t</div>\n</div>\n", styles: [":where(.hub-panels__panel--card){--hub-panels-card-bg: var(--hub-sys-surface-page, #fff);--hub-panels-card-color: var(--hub-sys-text-primary, #212529);--hub-panels-card-border-width: var(--hub-ref-border-width, 1px);--hub-panels-card-border-color: var(--hub-sys-border-color-default, #dee2e6);--hub-panels-card-border-radius: var(--hub-ref-radius-md, .375rem);--hub-panels-card-box-shadow: var(--hub-sys-shadow-sm, 0 .125rem .25rem rgba(0, 0, 0, .075));--hub-panels-card-padding-x: var(--hub-ref-space-4, 1.25rem);--hub-panels-card-padding-y: var(--hub-ref-space-3, 1rem)}:where(.hub-panels__panel-header,.hub-panels__panel-footer){--hub-panels-panel-header-bg: var(--hub-ref-surface-2, #f8f9fa);--hub-panels-panel-header-color: var(--hub-sys-text-primary, #212529);--hub-panels-panel-header-padding-x: var(--hub-ref-space-4, 1.25rem);--hub-panels-panel-header-padding-y: var(--hub-ref-space-3, 1rem);--hub-panels-panel-header-font-weight: 600;--hub-panels-panel-header-border-width: var(--hub-ref-border-width, 1px);--hub-panels-panel-header-border-color: var(--hub-sys-border-color-default, #dee2e6)}.hub-panels__panel-header{display:block;padding:var(--hub-panels-panel-header-padding-y) var(--hub-panels-panel-header-padding-x);color:var(--hub-panels-panel-header-color);background:var(--hub-panels-panel-header-bg);font-weight:var(--hub-panels-panel-header-font-weight);border-bottom:var(--hub-panels-panel-header-border-width) solid var(--hub-panels-panel-header-border-color)}.hub-panels__panel-footer{display:block;padding:var(--hub-panels-panel-header-padding-y) var(--hub-panels-panel-header-padding-x);color:var(--hub-panels-panel-header-color);background:var(--hub-panels-panel-header-bg);border-top:var(--hub-panels-panel-header-border-width) solid var(--hub-panels-panel-header-border-color)}.hub-panels__panel.hub-panels__panel--card{display:flex;flex-direction:column;color:var(--hub-panels-card-color);background:var(--hub-panels-card-bg);border:var(--hub-panels-card-border-width) solid var(--hub-panels-card-border-color);border-radius:var(--hub-panels-card-border-radius);box-shadow:var(--hub-panels-card-box-shadow);overflow:hidden}.hub-panels__panel.hub-panels__panel--card .hub-panels__panel-body{padding:var(--hub-panels-card-padding-y) var(--hub-panels-card-padding-x)}\n"] }]
986
+ }], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], pathMatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "pathMatch", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }, { type: i0.Output, args: ["activeChange"] }], selectPanel: [{ type: i0.Output, args: ["selectPanel"] }], deselectPanel: [{ type: i0.Output, args: ["deselectPanel"] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
987
+
988
+ /**
989
+ * Marks an `<ng-template>` inside a `hub-panel` as that panel's custom header
990
+ * (strip link in `tabs`/`pills` view, disclosure button in `accordion` view),
991
+ * replacing the plain-text `heading` input.
992
+ *
993
+ * @example
994
+ * ```html
995
+ * <hub-panel>
996
+ * <ng-template hubPanelHeading><em>Rich</em> heading</ng-template>
997
+ * Panel content
998
+ * </hub-panel>
999
+ * ```
1000
+ */
1001
+ class PanelHeadingDirective {
1002
+ constructor() {
1003
+ inject(PanelComponent).headingRef.set(inject(TemplateRef));
1004
+ }
1005
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelHeadingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1006
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: PanelHeadingDirective, isStandalone: true, selector: "[hubPanelHeading]", ngImport: i0 });
1007
+ }
1008
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelHeadingDirective, decorators: [{
1009
+ type: Directive,
1010
+ args: [{ selector: '[hubPanelHeading]' }]
1011
+ }], ctorParameters: () => [] });
1012
+
1013
+ /**
1014
+ * Marks an element inside a `hub-panel` as the panel's content **header** band,
1015
+ * projected at the top of the panel body (above `<ng-content>`).
1016
+ *
1017
+ * Unlike `hubPanelHeading` — which renders the navigational label in the
1018
+ * `tabs`/`pills` strip or the `accordion` disclosure button — this header lives
1019
+ * inside the panel body and renders in **every** view (`tabs`, `pills`,
1020
+ * `accordion`, `card`). It is the natural title slot for the `card` format.
1021
+ *
1022
+ * @example
1023
+ * ```html
1024
+ * <hub-panel>
1025
+ * <div hubPanelHeader>Card title</div>
1026
+ * Card content
1027
+ * <div hubPanelFooter>Actions</div>
1028
+ * </hub-panel>
1029
+ * ```
1030
+ */
1031
+ class PanelHeaderDirective {
1032
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1033
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: PanelHeaderDirective, isStandalone: true, selector: "[hubPanelHeader]", host: { classAttribute: "hub-panels__panel-header" }, ngImport: i0 });
1034
+ }
1035
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelHeaderDirective, decorators: [{
1036
+ type: Directive,
1037
+ args: [{
1038
+ selector: '[hubPanelHeader]',
1039
+ host: { class: 'hub-panels__panel-header' }
1040
+ }]
1041
+ }] });
1042
+
1043
+ /**
1044
+ * Marks an element inside a `hub-panel` as the panel's content **footer** band,
1045
+ * projected at the bottom of the panel body (below `<ng-content>`).
1046
+ *
1047
+ * Renders in **every** view (`tabs`, `pills`, `accordion`, `card`); it is the
1048
+ * natural actions/footer slot for the `card` format.
1049
+ *
1050
+ * @example
1051
+ * ```html
1052
+ * <hub-panel>
1053
+ * <div hubPanelHeader>Card title</div>
1054
+ * Card content
1055
+ * <div hubPanelFooter>Actions</div>
1056
+ * </hub-panel>
1057
+ * ```
1058
+ */
1059
+ class PanelFooterDirective {
1060
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1061
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.1", type: PanelFooterDirective, isStandalone: true, selector: "[hubPanelFooter]", host: { classAttribute: "hub-panels__panel-footer" }, ngImport: i0 });
1062
+ }
1063
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: PanelFooterDirective, decorators: [{
1064
+ type: Directive,
1065
+ args: [{
1066
+ selector: '[hubPanelFooter]',
1067
+ host: { class: 'hub-panels__panel-footer' }
1068
+ }]
1069
+ }] });
1070
+
1071
+ /*
1072
+ * Public API Surface of ng-hub-ui-panels
1073
+ */
1074
+
1075
+ /**
1076
+ * Generated bundle index. Do not edit.
1077
+ */
1078
+
1079
+ export { PanelComponent, PanelFooterDirective, PanelHeaderDirective, PanelHeadingDirective, PanelsComponent, PanelsConfig };
1080
+ //# sourceMappingURL=ng-hub-ui-panels.mjs.map