@kirbydesign/extensions-angular 3.3.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/assets/spot-illustrations/airballoon.wind.ballon-part.cloud.medium.svg +8 -0
  2. package/assets/spot-illustrations/banknotes.arrow.dot.circle.medium.svg +16 -0
  3. package/assets/spot-illustrations/bell.checkmark.dot.bars.xlarge.svg +9 -0
  4. package/assets/spot-illustrations/bell.exclamation-mark.triangle.bars.medium.svg +9 -0
  5. package/assets/spot-illustrations/box.curve.box.half-circle.medium.svg +8 -0
  6. package/assets/spot-illustrations/box.none.plus-dot.bars.large.svg +9 -0
  7. package/assets/spot-illustrations/calculator.money.coin.rectangle.medium.svg +9 -0
  8. package/assets/spot-illustrations/calendar.money.coin.bars.medium.svg +9 -0
  9. package/assets/spot-illustrations/calender.loop-arrow.zoom.bars.large.svg +9 -0
  10. package/assets/spot-illustrations/calender.loop-arrow.zoom.bars.xlarge.svg +8 -0
  11. package/assets/spot-illustrations/calender.money-arrows.header.bars.large.svg +9 -0
  12. package/assets/spot-illustrations/calender.money-arrows.header.bars.medium.svg +9 -0
  13. package/assets/spot-illustrations/checkmark.circle.circle-part.shadow.large.svg +9 -0
  14. package/assets/spot-illustrations/computer.certificate.padlock.circle.large.svg +9 -0
  15. package/assets/spot-illustrations/cross.circle.circle-part.shadow.large.svg +9 -0
  16. package/assets/spot-illustrations/exclamation-mark.circle.circle-part.shadow.large.svg +9 -0
  17. package/assets/spot-illustrations/hand-money.line.money-dot.bars.medium.svg +8 -0
  18. package/assets/spot-illustrations/hand.balloon.balloon.circle.xlarge.svg +9 -0
  19. package/assets/spot-illustrations/invest-document.graph.bars.square.medium.svg +9 -0
  20. package/assets/spot-illustrations/invest-document.graph.bars.square.xlarge.svg +9 -0
  21. package/assets/spot-illustrations/invest-page.money.dot.circle.medium.svg +8 -0
  22. package/assets/spot-illustrations/megaphone.sound.megaphone-part.circle.xlarge.svg +9 -0
  23. package/assets/spot-illustrations/money.arrow.coin.bars.medium.svg +12 -0
  24. package/assets/spot-illustrations/money.none.coin.bars.medium.svg +16 -0
  25. package/assets/spot-illustrations/piggy-bank.money.money-dot.bars.medium.svg +8 -0
  26. package/assets/spot-illustrations/plant.bars.leaf.bars.medium.svg +9 -0
  27. package/assets/spot-illustrations/robot.none.antenna-dot.body.large.svg +8 -0
  28. package/assets/spot-illustrations/robot.none.antenna-dot.body.medium.svg +29 -0
  29. package/fesm2022/kirbydesign-extensions-angular-image-banner.mjs +10 -12
  30. package/fesm2022/kirbydesign-extensions-angular-image-banner.mjs.map +1 -1
  31. package/fesm2022/kirbydesign-extensions-angular-localization.mjs +30 -30
  32. package/fesm2022/kirbydesign-extensions-angular-localization.mjs.map +1 -1
  33. package/fesm2022/kirbydesign-extensions-angular-sidebar-menu.mjs +620 -0
  34. package/fesm2022/kirbydesign-extensions-angular-sidebar-menu.mjs.map +1 -0
  35. package/fesm2022/kirbydesign-extensions-angular-skeleton-loader.mjs +6 -6
  36. package/fesm2022/kirbydesign-extensions-angular-skeleton-loader.mjs.map +1 -1
  37. package/fesm2022/kirbydesign-extensions-angular-spot-illustration.mjs +10 -4
  38. package/fesm2022/kirbydesign-extensions-angular-spot-illustration.mjs.map +1 -1
  39. package/fesm2022/kirbydesign-extensions-angular.mjs.map +1 -1
  40. package/package.json +18 -8
  41. package/sidebar-menu/index.d.ts +86 -0
  42. package/spot-illustration/index.d.ts +6 -0
@@ -0,0 +1,620 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, Component, signal, Injectable, inject, ElementRef, forwardRef, ChangeDetectionStrategy, afterNextRender, output, Directive, model, effect, EventEmitter, Output, Input, NgModule } from '@angular/core';
3
+ import { DividerComponent } from '@kirbydesign/designsystem/divider';
4
+ import { RouterLink } from '@angular/router';
5
+ import { ToggleButtonComponent } from '@kirbydesign/designsystem/toggle-button';
6
+ import { ButtonComponent } from '@kirbydesign/designsystem/button';
7
+ import * as i1 from '@kirbydesign/designsystem/icon';
8
+ import { IconModule, IconComponent } from '@kirbydesign/designsystem/icon';
9
+ import { BadgeComponent } from '@kirbydesign/designsystem/badge';
10
+ import { trigger, transition, style, animate, AUTO_STYLE } from '@angular/animations';
11
+ import { Subject, debounceTime } from 'rxjs';
12
+ import { distinctUntilChanged } from 'rxjs/operators';
13
+
14
+ /**
15
+ * the HTMLElement.scrollIntoViewIfNecessary is non-standard and not supported in all browsers (e.g. firefox),
16
+ * so we implement a version based on standard
17
+ */
18
+ function scrollIntoViewIfNecessary(scrollContainer, child, position = 'center') {
19
+ const containerRect = scrollContainer.getBoundingClientRect();
20
+ const elementRect = child.getBoundingClientRect();
21
+ if (containerRect.top > elementRect.top || containerRect.bottom < elementRect.bottom) {
22
+ child.scrollIntoView({
23
+ behavior: 'instant',
24
+ block: position,
25
+ inline: position,
26
+ });
27
+ }
28
+ }
29
+
30
+ class MenuItemComponent {
31
+ constructor() {
32
+ this.id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
33
+ this.size = input.required(...(ngDevMode ? [{ debugName: "size" }] : []));
34
+ this.icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : []));
35
+ this.iconSize = computed(() => (this.size() === 'lg' ? 'sm' : 'xs'), ...(ngDevMode ? [{ debugName: "iconSize" }] : []));
36
+ }
37
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
38
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: MenuItemComponent, isStandalone: true, selector: "a[kirby-x-menu-item],button[kirby-x-menu-item]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.id": "\"item\" + id()", "class": "size()" } }, ngImport: i0, template: "<div class=\"menu-item-start {{ size() }}\">\n @if (icon(); as icon) {\n <kirby-icon [size]=\"iconSize()\" [name]=\"icon\"></kirby-icon>\n }\n</div>\n\n<div class=\"menu-item-content {{ size() }}\">\n <ng-content></ng-content>\n</div>\n\n<div class=\"menu-item-end\">\n <ng-content select=\"[slot='end']\"></ng-content>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-grow:1;align-items:center;align-self:stretch;box-sizing:border-box;min-height:32px;border-radius:8px;padding:4px 12px;gap:8px;border:none;background:inherit;font:inherit;color:inherit;text-decoration:none;cursor:pointer}:host.lg{min-height:40px}@media (pointer: coarse){:host.md{min-height:40px}}:host:hover{background:var(--menuitem-hover-background-color, var(--kirby-inputs-background-color-hover))}:host:active{background:var(--menuitem-active-background-color, var(--kirby-inputs-background-color-active))}:host:focus-visible{outline:none}@media (hover: hover) and (pointer: fine){:host:focus{transition:all 80ms linear 0ms;box-shadow:0 0 0 2px var(--kirby-background-color),0 0 0 4px #4d90fe}:host:focus:not(:focus-visible){box-shadow:0 0 0 0 transparent}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:0 0 0 2px var(--kirby-background-color),0 0 0 4px #4d90fe}}.menu-item-start{display:flex;align-items:center;justify-content:center;flex-shrink:0}.menu-item-start.lg,.menu-item-start.md{width:24px;height:24px}.menu-item-start.sm,.menu-item-start.xs{width:16px;height:16px}.menu-item-start.sm{padding-left:32px}.menu-item-start.xs{padding-left:56px}.menu-item-content{display:flex;flex-grow:1;align-items:center;text-align:left}.menu-item-content.lg,.menu-item-content.md{font-size:16px;line-height:24px}.menu-item-content.sm{font-size:14px;line-height:20px}.menu-item-content.xs{font-size:12px;line-height:16px}.menu-item-end{display:inline-flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: IconModule }, { kind: "component", type: i1.IconComponent, selector: "kirby-icon", inputs: ["size", "name"] }] }); }
39
+ }
40
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuItemComponent, decorators: [{
41
+ type: Component,
42
+ args: [{ selector: 'a[kirby-x-menu-item],button[kirby-x-menu-item]', host: {
43
+ '[attr.id]': '"item" + id()',
44
+ '[class]': 'size()',
45
+ }, imports: [IconModule, BadgeComponent], template: "<div class=\"menu-item-start {{ size() }}\">\n @if (icon(); as icon) {\n <kirby-icon [size]=\"iconSize()\" [name]=\"icon\"></kirby-icon>\n }\n</div>\n\n<div class=\"menu-item-content {{ size() }}\">\n <ng-content></ng-content>\n</div>\n\n<div class=\"menu-item-end\">\n <ng-content select=\"[slot='end']\"></ng-content>\n</div>\n", styles: [":host{display:flex;flex-direction:row;flex-grow:1;align-items:center;align-self:stretch;box-sizing:border-box;min-height:32px;border-radius:8px;padding:4px 12px;gap:8px;border:none;background:inherit;font:inherit;color:inherit;text-decoration:none;cursor:pointer}:host.lg{min-height:40px}@media (pointer: coarse){:host.md{min-height:40px}}:host:hover{background:var(--menuitem-hover-background-color, var(--kirby-inputs-background-color-hover))}:host:active{background:var(--menuitem-active-background-color, var(--kirby-inputs-background-color-active))}:host:focus-visible{outline:none}@media (hover: hover) and (pointer: fine){:host:focus{transition:all 80ms linear 0ms;box-shadow:0 0 0 2px var(--kirby-background-color),0 0 0 4px #4d90fe}:host:focus:not(:focus-visible){box-shadow:0 0 0 0 transparent}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:0 0 0 2px var(--kirby-background-color),0 0 0 4px #4d90fe}}.menu-item-start{display:flex;align-items:center;justify-content:center;flex-shrink:0}.menu-item-start.lg,.menu-item-start.md{width:24px;height:24px}.menu-item-start.sm,.menu-item-start.xs{width:16px;height:16px}.menu-item-start.sm{padding-left:32px}.menu-item-start.xs{padding-left:56px}.menu-item-content{display:flex;flex-grow:1;align-items:center;text-align:left}.menu-item-content.lg,.menu-item-content.md{font-size:16px;line-height:24px}.menu-item-content.sm{font-size:14px;line-height:20px}.menu-item-content.xs{font-size:12px;line-height:16px}.menu-item-end{display:inline-flex;align-items:center}\n"] }]
46
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
47
+
48
+ class MenuStateService {
49
+ #toggledSubmenu = signal(undefined, ...(ngDevMode ? [{ debugName: "#toggledSubmenu" }] : []));
50
+ #selectedItem = signal(undefined, ...(ngDevMode ? [{ debugName: "#selectedItem" }] : []));
51
+ #checkEvent = signal(undefined, ...(ngDevMode ? [{ debugName: "#checkEvent" }] : []));
52
+ get toggledSubmenu() {
53
+ return this.#toggledSubmenu;
54
+ }
55
+ set toggledSubmenu(item) {
56
+ this.#toggledSubmenu.set(item);
57
+ }
58
+ get selectedItem() {
59
+ return this.#selectedItem;
60
+ }
61
+ set selectedItem(item) {
62
+ if (item.selectable ?? true) {
63
+ this.#selectedItem.set(item);
64
+ }
65
+ }
66
+ get checkEvent() {
67
+ return this.#checkEvent.asReadonly();
68
+ }
69
+ set checkEvent(event) {
70
+ this.#checkEvent.set(event);
71
+ }
72
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
73
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuStateService, providedIn: 'root' }); }
74
+ }
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuStateService, decorators: [{
76
+ type: Injectable,
77
+ args: [{ providedIn: 'root' }]
78
+ }] });
79
+
80
+ class MenuAnchorItemComponent {
81
+ constructor() {
82
+ this.item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
83
+ this.size = input.required(...(ngDevMode ? [{ debugName: "size" }] : []));
84
+ this.#menuStateService = inject(MenuStateService);
85
+ this.vm = {
86
+ link: computed(() => determineLinkType(this.item())),
87
+ id: computed(() => this.item().id),
88
+ size: this.size,
89
+ badge: computed(() => this.item().badge),
90
+ toggle: computed(() => this.item().toggle),
91
+ icon: computed(() => this.item().icon),
92
+ isSelected: computed(() => this.item().selected ?? false),
93
+ title: computed(() => this.item().title ?? ''),
94
+ selectItem: this.#selectItem.bind(this),
95
+ checkItem: this.#checkItem.bind(this),
96
+ };
97
+ }
98
+ #menuStateService;
99
+ #selectItem() {
100
+ this.#menuStateService.selectedItem = this.item();
101
+ }
102
+ #checkItem(checked) {
103
+ this.#menuStateService.checkEvent = { item: this.item(), checked };
104
+ }
105
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuAnchorItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
106
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: MenuAnchorItemComponent, isStandalone: true, selector: "li[kirby-x-menu-anchor-item]", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.selected": "vm.isSelected()", "class.checked": "vm.toggle()?.isChecked", "class.has-toggle": "!!vm.toggle()", "class.has-badge": "!!vm.badge()" } }, ngImport: i0, template: "@let link = vm.link();\n@if (link) {\n @switch (link.type) {\n @case ('action') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.href]=\"link.url\"\n (click)=\"$event.preventDefault(); vm.selectItem()\"\n (keydown.enter)=\"$event.preventDefault(); vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n @case ('router-link') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [routerLink]=\"link.route\"\n [queryParams]=\"link.queryParams\"\n [skipLocationChange]=\"link.skipLocationChange\"\n [replaceUrl]=\"link.replaceUrl\"\n (click)=\"vm.selectItem()\"\n (keydown.enter)=\"vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n @case ('external-link') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.href]=\"link.url\"\n [attr.target]=\"link.target\"\n (click)=\"vm.selectItem()\"\n (keydown.enter)=\"vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n }\n\n @if (vm.toggle(); as toggle) {\n <kirby-toggle-button\n class=\"toggle-button-wrapper\"\n [checked]=\"toggle.isChecked\"\n (checkChanged)=\"vm.checkItem($event)\"\n >\n <button\n kirby-button\n unchecked\n size=\"xs\"\n attentionLevel=\"3\"\n tabindex=\"-1\"\n [noDecoration]=\"true\"\n class=\"toggle-button toggle-button-unchecked\"\n >\n <kirby-icon size=\"xs\" [name]=\"toggle.uncheckedIcon\"></kirby-icon>\n </button>\n <button\n kirby-button\n checked\n size=\"xs\"\n attentionLevel=\"3\"\n tabindex=\"-1\"\n [noDecoration]=\"true\"\n class=\"toggle-button toggle-button-checked\"\n >\n <kirby-icon size=\"xs\" [name]=\"toggle.checkedIcon\"></kirby-icon>\n </button>\n </kirby-toggle-button>\n }\n}\n", styles: [":host{display:flex;flex-direction:row;align-items:center;position:relative}:host.selected{color:var(--menuitem-selected-color, var(--kirby-primary-contrast))}:host:not(.selected):hover a{background:var(--menuitem-hover-background-color, var(--kirby-inputs-background-color-hover))}.selected,.selected:hover,.selected:active,.selected:focus-visible{background:var(--menuitem-selected-background-color, var(--kirby-primary));font-weight:700}:host:where(.has-toggle,.has-badge) .end-wrapper{display:inline-flex;justify-content:center;box-sizing:border-box;min-width:24px}:host.checked .end-wrapper{padding-right:32px}.item-badge{display:inline-flex}:host.has-toggle:not(.checked):hover .item-badge,:host.has-toggle:not(.checked):active .item-badge,:host.has-toggle:not(.checked):has(a:focus-visible) .item-badge,:host.has-toggle:not(.checked):has(.toggle-button-unchecked:focus-visible) .item-badge{display:none}.toggle-button-wrapper{display:inline-flex;position:absolute;right:12px}.toggle-button{margin:0;--kirby-button-color: inherit}.toggle-button-unchecked{transition:opacity 0ms}.toggle-button-unchecked:not(:focus-visible){opacity:0}:host:hover .toggle-button-unchecked,:host:active .toggle-button-unchecked,:host:has(a:focus-visible) .toggle-button-unchecked{opacity:1}\n"], dependencies: [{ kind: "component", type: MenuItemComponent, selector: "a[kirby-x-menu-item],button[kirby-x-menu-item]", inputs: ["id", "size", "icon"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: ToggleButtonComponent, selector: "kirby-toggle-button", inputs: ["checked"], outputs: ["checkChanged"] }, { kind: "component", type: ButtonComponent, selector: "button[kirby-button],Button[kirby-button],a[kirby-button]", inputs: ["attentionLevel", "noDecoration", "themeColor", "expand", "isFloating", "size", "showIconOnly"] }, { kind: "component", type: IconComponent, selector: "kirby-icon", inputs: ["size", "name"] }, { kind: "component", type: BadgeComponent, selector: "kirby-badge", inputs: ["text", "size"] }] }); }
107
+ }
108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuAnchorItemComponent, decorators: [{
109
+ type: Component,
110
+ args: [{ selector: 'li[kirby-x-menu-anchor-item]', host: {
111
+ '[class.selected]': 'vm.isSelected()',
112
+ '[class.checked]': 'vm.toggle()?.isChecked',
113
+ '[class.has-toggle]': '!!vm.toggle()',
114
+ '[class.has-badge]': '!!vm.badge()',
115
+ }, imports: [
116
+ MenuItemComponent,
117
+ RouterLink,
118
+ ToggleButtonComponent,
119
+ ButtonComponent,
120
+ IconComponent,
121
+ BadgeComponent,
122
+ ], template: "@let link = vm.link();\n@if (link) {\n @switch (link.type) {\n @case ('action') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.href]=\"link.url\"\n (click)=\"$event.preventDefault(); vm.selectItem()\"\n (keydown.enter)=\"$event.preventDefault(); vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n @case ('router-link') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [routerLink]=\"link.route\"\n [queryParams]=\"link.queryParams\"\n [skipLocationChange]=\"link.skipLocationChange\"\n [replaceUrl]=\"link.replaceUrl\"\n (click)=\"vm.selectItem()\"\n (keydown.enter)=\"vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n @case ('external-link') {\n <a\n kirby-x-menu-item\n [class.selected]=\"vm.isSelected()\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.href]=\"link.url\"\n [attr.target]=\"link.target\"\n (click)=\"vm.selectItem()\"\n (keydown.enter)=\"vm.selectItem()\"\n >\n {{ vm.title() }}\n <div class=\"end-wrapper\" slot=\"end\">\n @if (vm.badge(); as badge) {\n <kirby-badge class=\"item-badge\" [themeColor]=\"badge.themeColor ?? 'primary'\">\n {{ badge.value }}\n </kirby-badge>\n }\n </div>\n </a>\n }\n }\n\n @if (vm.toggle(); as toggle) {\n <kirby-toggle-button\n class=\"toggle-button-wrapper\"\n [checked]=\"toggle.isChecked\"\n (checkChanged)=\"vm.checkItem($event)\"\n >\n <button\n kirby-button\n unchecked\n size=\"xs\"\n attentionLevel=\"3\"\n tabindex=\"-1\"\n [noDecoration]=\"true\"\n class=\"toggle-button toggle-button-unchecked\"\n >\n <kirby-icon size=\"xs\" [name]=\"toggle.uncheckedIcon\"></kirby-icon>\n </button>\n <button\n kirby-button\n checked\n size=\"xs\"\n attentionLevel=\"3\"\n tabindex=\"-1\"\n [noDecoration]=\"true\"\n class=\"toggle-button toggle-button-checked\"\n >\n <kirby-icon size=\"xs\" [name]=\"toggle.checkedIcon\"></kirby-icon>\n </button>\n </kirby-toggle-button>\n }\n}\n", styles: [":host{display:flex;flex-direction:row;align-items:center;position:relative}:host.selected{color:var(--menuitem-selected-color, var(--kirby-primary-contrast))}:host:not(.selected):hover a{background:var(--menuitem-hover-background-color, var(--kirby-inputs-background-color-hover))}.selected,.selected:hover,.selected:active,.selected:focus-visible{background:var(--menuitem-selected-background-color, var(--kirby-primary));font-weight:700}:host:where(.has-toggle,.has-badge) .end-wrapper{display:inline-flex;justify-content:center;box-sizing:border-box;min-width:24px}:host.checked .end-wrapper{padding-right:32px}.item-badge{display:inline-flex}:host.has-toggle:not(.checked):hover .item-badge,:host.has-toggle:not(.checked):active .item-badge,:host.has-toggle:not(.checked):has(a:focus-visible) .item-badge,:host.has-toggle:not(.checked):has(.toggle-button-unchecked:focus-visible) .item-badge{display:none}.toggle-button-wrapper{display:inline-flex;position:absolute;right:12px}.toggle-button{margin:0;--kirby-button-color: inherit}.toggle-button-unchecked{transition:opacity 0ms}.toggle-button-unchecked:not(:focus-visible){opacity:0}:host:hover .toggle-button-unchecked,:host:active .toggle-button-unchecked,:host:has(a:focus-visible) .toggle-button-unchecked{opacity:1}\n"] }]
123
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }] } });
124
+ function determineLinkType(item) {
125
+ if (item.isAction) {
126
+ return {
127
+ type: 'action',
128
+ url: item.link?.url ?? '',
129
+ };
130
+ }
131
+ if (item.link?.relativeLink) {
132
+ return {
133
+ type: 'router-link',
134
+ target: item.link.target ?? '_self',
135
+ route: item.link.relativeLink,
136
+ queryParams: item.link.queryParams,
137
+ skipLocationChange: item.skipLocationChange ?? false,
138
+ replaceUrl: item.replaceUrl ?? false,
139
+ };
140
+ }
141
+ if (item.link?.url) {
142
+ return {
143
+ type: 'external-link',
144
+ url: item.link.url,
145
+ target: item.link.target ?? '_self',
146
+ };
147
+ }
148
+ return undefined;
149
+ }
150
+
151
+ /**
152
+ * Ensures the element top is not smaller than the scroll container top.
153
+ */
154
+ function ensureInView(scrollContainer, element, durationMs) {
155
+ let startTime = null;
156
+ const updatePosition = (timestamp) => {
157
+ if (!startTime) {
158
+ startTime = timestamp;
159
+ }
160
+ const elapsedTime = timestamp - startTime;
161
+ if (elapsedTime < durationMs) {
162
+ scrollIntoViewIfNecessary(scrollContainer, element, 'start');
163
+ requestAnimationFrame(updatePosition);
164
+ }
165
+ };
166
+ requestAnimationFrame(updatePosition);
167
+ }
168
+
169
+ const DropDownAnimation = trigger('dropDownMenu', [
170
+ transition(':enter', [
171
+ style({ height: 0, overflow: 'hidden' }),
172
+ animate('300ms ease', style({ height: AUTO_STYLE })),
173
+ ]),
174
+ transition(':leave', [
175
+ style({ height: AUTO_STYLE, overflow: 'hidden' }),
176
+ animate('300ms ease', style({ height: 0 })),
177
+ ]),
178
+ ]);
179
+
180
+ class MenuSubmenuItemComponent {
181
+ constructor() {
182
+ this.item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
183
+ this.size = input.required(...(ngDevMode ? [{ debugName: "size" }] : []));
184
+ this.disableAnimations = input.required(...(ngDevMode ? [{ debugName: "disableAnimations" }] : []));
185
+ this.#element = inject(ElementRef).nativeElement;
186
+ this.#menuStateService = inject(MenuStateService);
187
+ this.#isExpanded = computed(() => this.item().isExpanded ?? false, ...(ngDevMode ? [{ debugName: "#isExpanded" }] : []));
188
+ this.#submenuSize = computed(() => {
189
+ switch (this.size()) {
190
+ case 'lg':
191
+ return 'md';
192
+ case 'md':
193
+ return 'sm';
194
+ default:
195
+ return 'xs';
196
+ }
197
+ }, ...(ngDevMode ? [{ debugName: "#submenuSize" }] : []));
198
+ this.vm = {
199
+ id: computed(() => this.item().id),
200
+ submenuId: computed(() => `item-${this.item().id}-content`),
201
+ title: computed(() => this.item().title ?? ''),
202
+ size: this.size,
203
+ icon: computed(() => this.item().icon),
204
+ isExpanded: this.#isExpanded,
205
+ children: computed(() => this.item().children ?? []),
206
+ submenuSize: this.#submenuSize,
207
+ disableAnimations: this.disableAnimations,
208
+ toggleSubmenu: this.#toggleSubmenu.bind(this),
209
+ };
210
+ }
211
+ #element;
212
+ #menuStateService;
213
+ #isExpanded;
214
+ #submenuSize;
215
+ #toggleSubmenu() {
216
+ const button = this.#element.querySelector('button[kirby-x-menu-item]');
217
+ const scrollContainer = this.#element.closest('.sidebar-content');
218
+ if (button && scrollContainer) {
219
+ console.log('Ensuring submenu item is in view');
220
+ ensureInView(scrollContainer, button, 400);
221
+ }
222
+ this.#menuStateService.toggledSubmenu = this.item();
223
+ }
224
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuSubmenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
225
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: MenuSubmenuItemComponent, isStandalone: true, selector: "li[kirby-x-menu-submenu-item]", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: true, transformFunction: null }, disableAnimations: { classPropertyName: "disableAnimations", publicName: "disableAnimations", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<button\n kirby-x-menu-item\n type=\"button\"\n class=\"submenu-toggle\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.aria-controls]=\"vm.submenuId()\"\n [attr.aria-expanded]=\"vm.isExpanded()\"\n (click)=\"vm.toggleSubmenu()\"\n>\n {{ vm.title() }}\n <kirby-icon\n slot=\"end\"\n name=\"arrow-more\"\n class=\"disclosure\"\n [class.rotate]=\"vm.isExpanded()\"\n ></kirby-icon>\n</button>\n@if (vm.isExpanded()) {\n <ul\n kirby-x-menu-item-list\n [items]=\"vm.children()\"\n [size]=\"vm.submenuSize()\"\n [id]=\"vm.submenuId()\"\n [disableAnimations]=\"vm.disableAnimations()\"\n [@dropDownMenu]\n [@.disabled]=\"vm.disableAnimations()\"\n ></ul>\n}\n", styles: [":host{display:flex;flex-direction:column}.submenu-toggle{width:100%}.submenu-toggle[aria-expanded=true]{font-weight:700}.disclosure{transition:transform .2s}.disclosure.rotate{transform:rotate(90deg)}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => MenuItemComponent), selector: "a[kirby-x-menu-item],button[kirby-x-menu-item]", inputs: ["id", "size", "icon"] }, { kind: "component", type: i0.forwardRef(() => IconComponent), selector: "kirby-icon", inputs: ["size", "name"] }, { kind: "component", type: i0.forwardRef(() => MenuItemListComponent), selector: "ul[kirby-x-menu-item-list]", inputs: ["items", "size", "id", "disableAnimations"] }], animations: [DropDownAnimation] }); }
226
+ }
227
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuSubmenuItemComponent, decorators: [{
228
+ type: Component,
229
+ args: [{ selector: 'li[kirby-x-menu-submenu-item]', animations: [DropDownAnimation], imports: [MenuItemComponent, IconComponent, forwardRef(() => MenuItemListComponent)], template: "<button\n kirby-x-menu-item\n type=\"button\"\n class=\"submenu-toggle\"\n [id]=\"vm.id()\"\n [size]=\"vm.size()\"\n [icon]=\"vm.icon()\"\n [attr.aria-controls]=\"vm.submenuId()\"\n [attr.aria-expanded]=\"vm.isExpanded()\"\n (click)=\"vm.toggleSubmenu()\"\n>\n {{ vm.title() }}\n <kirby-icon\n slot=\"end\"\n name=\"arrow-more\"\n class=\"disclosure\"\n [class.rotate]=\"vm.isExpanded()\"\n ></kirby-icon>\n</button>\n@if (vm.isExpanded()) {\n <ul\n kirby-x-menu-item-list\n [items]=\"vm.children()\"\n [size]=\"vm.submenuSize()\"\n [id]=\"vm.submenuId()\"\n [disableAnimations]=\"vm.disableAnimations()\"\n [@dropDownMenu]\n [@.disabled]=\"vm.disableAnimations()\"\n ></ul>\n}\n", styles: [":host{display:flex;flex-direction:column}.submenu-toggle{width:100%}.submenu-toggle[aria-expanded=true]{font-weight:700}.disclosure{transition:transform .2s}.disclosure.rotate{transform:rotate(90deg)}\n"] }]
230
+ }], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: true }] }], disableAnimations: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableAnimations", required: true }] }] } });
231
+
232
+ class MenuItemListComponent {
233
+ constructor() {
234
+ this.items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
235
+ this.size = input('lg', ...(ngDevMode ? [{ debugName: "size" }] : []));
236
+ this.id = input(...(ngDevMode ? [undefined, { debugName: "id" }] : []));
237
+ this.disableAnimations = input(false, ...(ngDevMode ? [{ debugName: "disableAnimations" }] : []));
238
+ this.vm = {
239
+ id: this.id,
240
+ size: this.size,
241
+ items: computed(() => this.items().filter((item) => !item.hidden)),
242
+ disableAnimations: this.disableAnimations,
243
+ };
244
+ }
245
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuItemListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
246
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: MenuItemListComponent, isStandalone: true, selector: "ul[kirby-x-menu-item-list]", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disableAnimations: { classPropertyName: "disableAnimations", publicName: "disableAnimations", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.id": "vm.id()", "class": "vm.size()" } }, ngImport: i0, template: "@for (item of vm.items(); track item.id) {\n @if (item.isDivider) {\n <kirby-divider aria-hidden=\"true\"></kirby-divider>\n } @else if (item.children) {\n <li\n kirby-x-menu-submenu-item\n [item]=\"item\"\n [size]=\"vm.size()\"\n [disableAnimations]=\"vm.disableAnimations()\"\n ></li>\n } @else {\n <li kirby-x-menu-anchor-item [item]=\"item\" [size]=\"vm.size()\"></li>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;margin:0;padding-left:0;padding-right:0;list-style-type:none}kirby-divider{display:flex;background:var(--kirby-white-overlay-20);margin:8px 0}li{margin-block-start:0!important}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => DividerComponent), selector: "kirby-divider", inputs: ["hasMargin"] }, { kind: "component", type: i0.forwardRef(() => MenuAnchorItemComponent), selector: "li[kirby-x-menu-anchor-item]", inputs: ["item", "size"] }, { kind: "component", type: i0.forwardRef(() => MenuSubmenuItemComponent), selector: "li[kirby-x-menu-submenu-item]", inputs: ["item", "size", "disableAnimations"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
247
+ }
248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuItemListComponent, decorators: [{
249
+ type: Component,
250
+ args: [{ selector: 'ul[kirby-x-menu-item-list]', host: {
251
+ '[attr.id]': 'vm.id()',
252
+ '[class]': 'vm.size()',
253
+ }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [DividerComponent, MenuAnchorItemComponent, forwardRef(() => MenuSubmenuItemComponent)], template: "@for (item of vm.items(); track item.id) {\n @if (item.isDivider) {\n <kirby-divider aria-hidden=\"true\"></kirby-divider>\n } @else if (item.children) {\n <li\n kirby-x-menu-submenu-item\n [item]=\"item\"\n [size]=\"vm.size()\"\n [disableAnimations]=\"vm.disableAnimations()\"\n ></li>\n } @else {\n <li kirby-x-menu-anchor-item [item]=\"item\" [size]=\"vm.size()\"></li>\n }\n}\n", styles: [":host{display:flex;flex-direction:column;margin:0;padding-left:0;padding-right:0;list-style-type:none}kirby-divider{display:flex;background:var(--kirby-white-overlay-20);margin:8px 0}li{margin-block-start:0!important}\n"] }]
254
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disableAnimations: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableAnimations", required: false }] }] } });
255
+
256
+ class MenuContainerComponent {
257
+ #element;
258
+ #disableAnimations;
259
+ constructor() {
260
+ this.items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
261
+ this.#element = inject(ElementRef).nativeElement;
262
+ this.#disableAnimations = signal(true, ...(ngDevMode ? [{ debugName: "#disableAnimations" }] : []));
263
+ this.vm = {
264
+ items: this.items,
265
+ disableAnimations: this.#disableAnimations.asReadonly(),
266
+ };
267
+ afterNextRender({
268
+ read: () => {
269
+ this.#scrollSelectedItemIntoView();
270
+ this.#disableAnimations.set(false);
271
+ },
272
+ });
273
+ }
274
+ #scrollSelectedItemIntoView() {
275
+ const selectedItem = this.#element.querySelector('.menu-item.selected');
276
+ const scrollContainer = this.#element.closest('.sidebar-content');
277
+ if (selectedItem && scrollContainer) {
278
+ scrollIntoViewIfNecessary(scrollContainer, selectedItem);
279
+ }
280
+ }
281
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
282
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.15", type: MenuContainerComponent, isStandalone: true, selector: "kirby-x-menu-container", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
283
+ <nav aria-label="Sidebar Menu">
284
+ <ul
285
+ kirby-x-menu-item-list
286
+ [items]="vm.items()"
287
+ [disableAnimations]="vm.disableAnimations()"
288
+ ></ul>
289
+ </nav>
290
+ `, isInline: true, styles: ["nav{padding:0 var(--kirby-spacing-xxs)}\n"], dependencies: [{ kind: "component", type: MenuItemListComponent, selector: "ul[kirby-x-menu-item-list]", inputs: ["items", "size", "id", "disableAnimations"] }] }); }
291
+ }
292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MenuContainerComponent, decorators: [{
293
+ type: Component,
294
+ args: [{ selector: 'kirby-x-menu-container', template: `
295
+ <nav aria-label="Sidebar Menu">
296
+ <ul
297
+ kirby-x-menu-item-list
298
+ [items]="vm.items()"
299
+ [disableAnimations]="vm.disableAnimations()"
300
+ ></ul>
301
+ </nav>
302
+ `, imports: [MenuItemListComponent], styles: ["nav{padding:0 var(--kirby-spacing-xxs)}\n"] }]
303
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });
304
+
305
+ class HeightObserverDirective {
306
+ #element;
307
+ #heightChanges;
308
+ #observer;
309
+ constructor() {
310
+ this.heightChange = output();
311
+ this.#element = inject(ElementRef).nativeElement;
312
+ this.#heightChanges = new Subject();
313
+ this.#observer = new ResizeObserver((entries) => {
314
+ this.#heightChanges.next(entries[0].contentRect.height);
315
+ });
316
+ this.#observer.observe(this.#element, { box: 'content-box' });
317
+ this.#heightChanges
318
+ .pipe(debounceTime(50), distinctUntilChanged())
319
+ .subscribe(this.heightChange.emit.bind(this.heightChange));
320
+ }
321
+ ngOnDestroy() {
322
+ this.#observer.unobserve(this.#element);
323
+ this.#observer.disconnect();
324
+ }
325
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HeightObserverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
326
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: HeightObserverDirective, isStandalone: true, selector: "[kirbyXHeightObserver]", outputs: { heightChange: "heightChange" }, ngImport: i0 }); }
327
+ }
328
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: HeightObserverDirective, decorators: [{
329
+ type: Directive,
330
+ args: [{
331
+ selector: '[kirbyXHeightObserver]',
332
+ }]
333
+ }], ctorParameters: () => [], propDecorators: { heightChange: [{ type: i0.Output, args: ["heightChange"] }] } });
334
+
335
+ function selectMenuItem(itemId, items) {
336
+ return items.map((item) => {
337
+ if (item.id === itemId) {
338
+ return { ...item, selected: true };
339
+ }
340
+ if (item.children) {
341
+ return { ...item, children: selectMenuItem(itemId, item.children) };
342
+ }
343
+ return { ...item, selected: false };
344
+ });
345
+ }
346
+
347
+ function toggleSubmenu(itemId, items) {
348
+ return items.map((item) => {
349
+ if (item.id === itemId) {
350
+ return { ...item, isExpanded: !item.isExpanded };
351
+ }
352
+ if (item.children && item.isExpanded) {
353
+ return { ...item, children: toggleSubmenu(itemId, item.children) };
354
+ }
355
+ return item;
356
+ });
357
+ }
358
+
359
+ function toggleSubmenuAutoCollapsed(itemId, items) {
360
+ return toggleSubmenuAndCollapseNonAncestors(itemId, items).updatedItems;
361
+ }
362
+ function toggleSubmenuAndCollapseNonAncestors(itemId, items) {
363
+ let found = false;
364
+ const updatedItems = items.map((item) => {
365
+ if (found) {
366
+ return collapse(item);
367
+ }
368
+ if (item.id === itemId) {
369
+ found = true;
370
+ return item.isExpanded ? collapse(item) : { ...item, isExpanded: true };
371
+ }
372
+ if (item.children && item.isExpanded) {
373
+ const { updatedItems, isAncestor } = toggleSubmenuAndCollapseNonAncestors(itemId, item.children);
374
+ found = isAncestor;
375
+ return { ...item, isExpanded: isAncestor, children: updatedItems };
376
+ }
377
+ return item;
378
+ });
379
+ return { updatedItems, isAncestor: found };
380
+ }
381
+ function collapse(item) {
382
+ if (item.children && item.isExpanded) {
383
+ return { ...item, isExpanded: false, children: item.children.map(collapse) };
384
+ }
385
+ return item;
386
+ }
387
+
388
+ class SidebarService {
389
+ #menuHeight = signal(0, ...(ngDevMode ? [{ debugName: "#menuHeight" }] : []));
390
+ #containerHeight = signal(0, ...(ngDevMode ? [{ debugName: "#containerHeight" }] : []));
391
+ #scrollDistance = signal(0, ...(ngDevMode ? [{ debugName: "#scrollDistance" }] : []));
392
+ #scrollableDistance = computed(() => Math.max(0, this.#menuHeight() - this.#containerHeight()), ...(ngDevMode ? [{ debugName: "#scrollableDistance" }] : []));
393
+ #hasScrollbar = computed(() => this.#scrollableDistance() > 0, ...(ngDevMode ? [{ debugName: "#hasScrollbar" }] : []));
394
+ #notAtBottom = computed(() => this.#scrollDistance() < this.#scrollableDistance(), ...(ngDevMode ? [{ debugName: "#notAtBottom" }] : []));
395
+ #showHeaderBottomBorder = computed(() => this.#scrollDistance() > 0, ...(ngDevMode ? [{ debugName: "#showHeaderBottomBorder" }] : []));
396
+ #showFooterTopBorder = computed(() => this.#hasScrollbar() && this.#notAtBottom(), ...(ngDevMode ? [{ debugName: "#showFooterTopBorder" }] : []));
397
+ get showHeaderBottomBorder() {
398
+ return this.#showHeaderBottomBorder;
399
+ }
400
+ get showFooterTopBorder() {
401
+ return this.#showFooterTopBorder;
402
+ }
403
+ set menuHeight(height) {
404
+ this.#menuHeight.set(height);
405
+ }
406
+ set containerHeight(height) {
407
+ this.#containerHeight.set(height);
408
+ }
409
+ set scrollDistance(distance) {
410
+ this.#scrollDistance.set(distance);
411
+ }
412
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
413
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarService, providedIn: 'root' }); }
414
+ }
415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarService, decorators: [{
416
+ type: Injectable,
417
+ args: [{ providedIn: 'root' }]
418
+ }] });
419
+
420
+ function checkMenuItem(itemId, isChecked, items) {
421
+ return items.map((item) => {
422
+ if (item.id === itemId) {
423
+ return { ...item, toggle: { ...item.toggle, isChecked } };
424
+ }
425
+ if (item.children) {
426
+ return { ...item, children: checkMenuItem(itemId, isChecked, item.children) };
427
+ }
428
+ return item;
429
+ });
430
+ }
431
+
432
+ class SidebarMenuComponent {
433
+ #sidebarService;
434
+ #menuStateService;
435
+ constructor() {
436
+ this.menuItems = model.required(...(ngDevMode ? [{ debugName: "menuItems" }] : []));
437
+ this.autoCollapse = input(false, ...(ngDevMode ? [{ debugName: "autoCollapse" }] : []));
438
+ this.submenuToggle = output();
439
+ this.itemClick = output();
440
+ this.itemChecked = output();
441
+ this.#sidebarService = inject(SidebarService);
442
+ this.#menuStateService = inject((MenuStateService));
443
+ this.vm = {
444
+ items: this.menuItems.asReadonly(),
445
+ showHeaderBottomBorder: this.#sidebarService.showHeaderBottomBorder,
446
+ showFooterTopBorder: this.#sidebarService.showFooterTopBorder,
447
+ setContainerHeight: this.#setContainerHeight.bind(this),
448
+ setMenuHeight: this.#setMenuHeight.bind(this),
449
+ setScroll: this.#setScrollDistance.bind(this),
450
+ };
451
+ effect(() => {
452
+ const toggledSubmenu = this.#menuStateService.toggledSubmenu();
453
+ if (toggledSubmenu) {
454
+ this.submenuToggle.emit(toggledSubmenu);
455
+ this.menuItems.update((items) => this.autoCollapse()
456
+ ? toggleSubmenuAutoCollapsed(toggledSubmenu.id, items)
457
+ : toggleSubmenu(toggledSubmenu.id, items));
458
+ }
459
+ });
460
+ effect(() => {
461
+ const selectedItem = this.#menuStateService.selectedItem();
462
+ if (selectedItem) {
463
+ this.itemClick.emit(selectedItem);
464
+ this.menuItems.update((items) => selectMenuItem(selectedItem.id, items));
465
+ }
466
+ });
467
+ effect(() => {
468
+ const checkEvent = this.#menuStateService.checkEvent();
469
+ if (checkEvent) {
470
+ this.itemChecked.emit(checkEvent);
471
+ this.menuItems.update((items) => checkMenuItem(checkEvent.item.id, checkEvent.checked, items));
472
+ }
473
+ });
474
+ }
475
+ #setContainerHeight(height) {
476
+ this.#sidebarService.containerHeight = height;
477
+ }
478
+ #setMenuHeight(height) {
479
+ this.#sidebarService.menuHeight = height;
480
+ }
481
+ #setScrollDistance(event) {
482
+ const target = event.target;
483
+ if (target) {
484
+ this.#sidebarService.scrollDistance = target.scrollTop;
485
+ }
486
+ }
487
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
488
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.15", type: SidebarMenuComponent, isStandalone: true, selector: "aside[kirby-x-sidebar-menu]", inputs: { menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: true, transformFunction: null }, autoCollapse: { classPropertyName: "autoCollapse", publicName: "autoCollapse", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuItems: "menuItemsChange", submenuToggle: "submenuToggle", itemClick: "itemClick", itemChecked: "itemChecked" }, ngImport: i0, template: "<div class=\"sidebar-header\" [class.bottom-border]=\"vm.showHeaderBottomBorder()\">\n <ng-content select=\"[slot='header']\"></ng-content>\n</div>\n<div\n kirbyXHeightObserver\n class=\"sidebar-content\"\n (scroll)=\"vm.setScroll($event)\"\n (heightChange)=\"vm.setContainerHeight($event)\"\n>\n <kirby-x-menu-container\n kirbyXHeightObserver\n [items]=\"vm.items()\"\n (heightChange)=\"vm.setMenuHeight($event)\"\n ></kirby-x-menu-container>\n</div>\n<div class=\"sidebar-footer\" [class.top-border]=\"vm.showFooterTopBorder()\">\n <ng-content select=\"[slot='footer']\"></ng-content>\n</div>\n", styles: ["ion-item-option.primary{background-color:var(--kirby-primary);color:var(--kirby-primary-contrast)}ion-item-option.secondary{background-color:var(--kirby-secondary);color:var(--kirby-secondary-contrast)}ion-item-option.tertiary{background-color:var(--kirby-tertiary);color:var(--kirby-tertiary-contrast)}ion-item-option.success{background-color:var(--kirby-success);color:var(--kirby-success-contrast)}ion-item-option.warning{background-color:var(--kirby-warning);color:var(--kirby-warning-contrast)}ion-item-option.danger{background-color:var(--kirby-danger);color:var(--kirby-danger-contrast)}ion-item-option.white-overlay{background-color:var(--kirby-white-overlay);color:var(--kirby-white-overlay-contrast)}ion-item-option.light{background-color:var(--kirby-light);color:var(--kirby-light-contrast)}ion-item-option.medium{background-color:var(--kirby-medium);color:var(--kirby-medium-contrast)}ion-item-option.dark{background-color:var(--kirby-dark);color:var(--kirby-dark-contrast)}ion-item-option.dark-overlay{background-color:var(--kirby-dark-overlay);color:var(--kirby-dark-overlay-contrast)}:host{--kirby-inputs-background-color: var(--kirby-white-overlay);--kirby-inputs-background-color-hover: var(--kirby-white-overlay-30);--kirby-inputs-background-color-active: var(--kirby-white-overlay-40);--kirby-inputs-color: var(--kirby-white);--kirby-inputs-indicator-background-color: var(--kirby-white);--kirby-inputs-indicator-color: var(--kirby-black);--kirby-inputs-placeholder-color: var(--kirby-white-overlay-50);--kirby-inputs-elevation: none;height:100%;height:100dvh;background-color:var(--sidebar-background-color, var(--kirby-tertiary));color:var(--sidebar-color, var(--kirby-tertiary-contrast));display:flex;flex-direction:column;overflow:auto}:host *{scrollbar-width:thin;scrollbar-color:var(--kirby-semi-dark) var(--kirby-tertiary)}:host *::-webkit-scrollbar{width:16px}:host *::-webkit-scrollbar-track{background:transparent}:host *::-webkit-scrollbar-thumb{background-color:#8e8e8e80;border-radius:20px;border:4px solid transparent;background-clip:content-box}.sidebar-content{flex-grow:1;display:flex;flex-direction:column;overflow-y:auto}@supports (scrollbar-gutter: stable){.sidebar-content{overflow:hidden auto;scrollbar-gutter:auto}}.sidebar-header,.sidebar-footer{z-index:1;display:flex;align-items:center;justify-content:center;position:sticky}.sidebar-header.bottom-border{box-shadow:0 4px 10px 2px #1c1c1c4d;transition:opacity .3s}.sidebar-footer.top-border{box-shadow:0 -4px 10px 2px #1c1c1c4d;transition:opacity .3s}\n"], dependencies: [{ kind: "directive", type: HeightObserverDirective, selector: "[kirbyXHeightObserver]", outputs: ["heightChange"] }, { kind: "component", type: MenuContainerComponent, selector: "kirby-x-menu-container", inputs: ["items"] }] }); }
489
+ }
490
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarMenuComponent, decorators: [{
491
+ type: Component,
492
+ args: [{ selector: 'aside[kirby-x-sidebar-menu]', imports: [HeightObserverDirective, MenuContainerComponent], template: "<div class=\"sidebar-header\" [class.bottom-border]=\"vm.showHeaderBottomBorder()\">\n <ng-content select=\"[slot='header']\"></ng-content>\n</div>\n<div\n kirbyXHeightObserver\n class=\"sidebar-content\"\n (scroll)=\"vm.setScroll($event)\"\n (heightChange)=\"vm.setContainerHeight($event)\"\n>\n <kirby-x-menu-container\n kirbyXHeightObserver\n [items]=\"vm.items()\"\n (heightChange)=\"vm.setMenuHeight($event)\"\n ></kirby-x-menu-container>\n</div>\n<div class=\"sidebar-footer\" [class.top-border]=\"vm.showFooterTopBorder()\">\n <ng-content select=\"[slot='footer']\"></ng-content>\n</div>\n", styles: ["ion-item-option.primary{background-color:var(--kirby-primary);color:var(--kirby-primary-contrast)}ion-item-option.secondary{background-color:var(--kirby-secondary);color:var(--kirby-secondary-contrast)}ion-item-option.tertiary{background-color:var(--kirby-tertiary);color:var(--kirby-tertiary-contrast)}ion-item-option.success{background-color:var(--kirby-success);color:var(--kirby-success-contrast)}ion-item-option.warning{background-color:var(--kirby-warning);color:var(--kirby-warning-contrast)}ion-item-option.danger{background-color:var(--kirby-danger);color:var(--kirby-danger-contrast)}ion-item-option.white-overlay{background-color:var(--kirby-white-overlay);color:var(--kirby-white-overlay-contrast)}ion-item-option.light{background-color:var(--kirby-light);color:var(--kirby-light-contrast)}ion-item-option.medium{background-color:var(--kirby-medium);color:var(--kirby-medium-contrast)}ion-item-option.dark{background-color:var(--kirby-dark);color:var(--kirby-dark-contrast)}ion-item-option.dark-overlay{background-color:var(--kirby-dark-overlay);color:var(--kirby-dark-overlay-contrast)}:host{--kirby-inputs-background-color: var(--kirby-white-overlay);--kirby-inputs-background-color-hover: var(--kirby-white-overlay-30);--kirby-inputs-background-color-active: var(--kirby-white-overlay-40);--kirby-inputs-color: var(--kirby-white);--kirby-inputs-indicator-background-color: var(--kirby-white);--kirby-inputs-indicator-color: var(--kirby-black);--kirby-inputs-placeholder-color: var(--kirby-white-overlay-50);--kirby-inputs-elevation: none;height:100%;height:100dvh;background-color:var(--sidebar-background-color, var(--kirby-tertiary));color:var(--sidebar-color, var(--kirby-tertiary-contrast));display:flex;flex-direction:column;overflow:auto}:host *{scrollbar-width:thin;scrollbar-color:var(--kirby-semi-dark) var(--kirby-tertiary)}:host *::-webkit-scrollbar{width:16px}:host *::-webkit-scrollbar-track{background:transparent}:host *::-webkit-scrollbar-thumb{background-color:#8e8e8e80;border-radius:20px;border:4px solid transparent;background-clip:content-box}.sidebar-content{flex-grow:1;display:flex;flex-direction:column;overflow-y:auto}@supports (scrollbar-gutter: stable){.sidebar-content{overflow:hidden auto;scrollbar-gutter:auto}}.sidebar-header,.sidebar-footer{z-index:1;display:flex;align-items:center;justify-content:center;position:sticky}.sidebar-header.bottom-border{box-shadow:0 4px 10px 2px #1c1c1c4d;transition:opacity .3s}.sidebar-footer.top-border{box-shadow:0 -4px 10px 2px #1c1c1c4d;transition:opacity .3s}\n"] }]
493
+ }], ctorParameters: () => [], propDecorators: { menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: true }] }, { type: i0.Output, args: ["menuItemsChange"] }], autoCollapse: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCollapse", required: false }] }], submenuToggle: [{ type: i0.Output, args: ["submenuToggle"] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], itemChecked: [{ type: i0.Output, args: ["itemChecked"] }] } });
494
+
495
+ class SidebarComponent {
496
+ constructor() {
497
+ this.autoCollapse = false;
498
+ this.menuItemsChange = new EventEmitter();
499
+ this.afterMenuClicked = new EventEmitter();
500
+ this.afterMenuToggled = new EventEmitter();
501
+ this.itemChecked = new EventEmitter();
502
+ /**
503
+ * @deprecated has not had an effect for a long time
504
+ */
505
+ this.expandIconOnHover = false;
506
+ /**
507
+ * @deprecated should use menuItemsChange or two-way binding on menuItems instead
508
+ **/
509
+ this.menuItemsChanged = new EventEmitter();
510
+ }
511
+ changeMenuItems(event) {
512
+ this.menuItems = event;
513
+ this.menuItemsChange.emit(this.menuItems);
514
+ this.menuItemsChanged.emit(this.menuItems);
515
+ }
516
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
517
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SidebarComponent, isStandalone: true, selector: "kirby-x-sidebar", inputs: { menuItems: "menuItems", autoCollapse: "autoCollapse", expandIconOnHover: "expandIconOnHover" }, outputs: { menuItemsChange: "menuItemsChange", afterMenuClicked: "afterMenuClicked", afterMenuToggled: "afterMenuToggled", itemChecked: "itemChecked", menuItemsChanged: "menuItemsChanged" }, ngImport: i0, template: `
518
+ <aside
519
+ kirby-x-sidebar-menu
520
+ [menuItems]="menuItems"
521
+ [autoCollapse]="autoCollapse"
522
+ (menuItemsChange)="changeMenuItems($event)"
523
+ (itemClick)="afterMenuClicked.emit($event)"
524
+ (submenuToggle)="afterMenuToggled.emit($event)"
525
+ (itemChecked)="itemChecked.emit($event)"
526
+ >
527
+ <ng-content select="kirby-x-sidebar-header" slot="header"></ng-content>
528
+ <ng-content select="kirby-x-sidebar-footer" slot="footer"></ng-content>
529
+ </aside>
530
+ `, isInline: true, dependencies: [{ kind: "component", type: SidebarMenuComponent, selector: "aside[kirby-x-sidebar-menu]", inputs: ["menuItems", "autoCollapse"], outputs: ["menuItemsChange", "submenuToggle", "itemClick", "itemChecked"] }] }); }
531
+ }
532
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarComponent, decorators: [{
533
+ type: Component,
534
+ args: [{
535
+ selector: 'kirby-x-sidebar',
536
+ template: `
537
+ <aside
538
+ kirby-x-sidebar-menu
539
+ [menuItems]="menuItems"
540
+ [autoCollapse]="autoCollapse"
541
+ (menuItemsChange)="changeMenuItems($event)"
542
+ (itemClick)="afterMenuClicked.emit($event)"
543
+ (submenuToggle)="afterMenuToggled.emit($event)"
544
+ (itemChecked)="itemChecked.emit($event)"
545
+ >
546
+ <ng-content select="kirby-x-sidebar-header" slot="header"></ng-content>
547
+ <ng-content select="kirby-x-sidebar-footer" slot="footer"></ng-content>
548
+ </aside>
549
+ `,
550
+ imports: [SidebarMenuComponent],
551
+ }]
552
+ }], propDecorators: { menuItems: [{
553
+ type: Input
554
+ }], autoCollapse: [{
555
+ type: Input
556
+ }], menuItemsChange: [{
557
+ type: Output
558
+ }], afterMenuClicked: [{
559
+ type: Output
560
+ }], afterMenuToggled: [{
561
+ type: Output
562
+ }], itemChecked: [{
563
+ type: Output
564
+ }], expandIconOnHover: [{
565
+ type: Input
566
+ }], menuItemsChanged: [{
567
+ type: Output
568
+ }] } });
569
+
570
+ class SidebarFooterComponent {
571
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
572
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SidebarFooterComponent, isStandalone: true, selector: "kirby-x-sidebar-footer", ngImport: i0, template: '<footer><ng-content></ng-content></footer>', isInline: true, styles: [":host,footer{display:flex;flex-direction:column;align-items:center;justify-content:center}:host{min-height:80px}footer{width:100%}\n"] }); }
573
+ }
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarFooterComponent, decorators: [{
575
+ type: Component,
576
+ args: [{ selector: 'kirby-x-sidebar-footer', template: '<footer><ng-content></ng-content></footer>', styles: [":host,footer{display:flex;flex-direction:column;align-items:center;justify-content:center}:host{min-height:80px}footer{width:100%}\n"] }]
577
+ }] });
578
+
579
+ class SidebarHeaderComponent {
580
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
581
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SidebarHeaderComponent, isStandalone: true, selector: "kirby-x-sidebar-header", ngImport: i0, template: `
582
+ <header>
583
+ <span class="sidebar-logo"><ng-content slot="logo"></ng-content></span>
584
+ <div class="action-bar">
585
+ <ng-content select="[slot='action']"></ng-content>
586
+ </div>
587
+ </header>
588
+ `, isInline: true, styles: [":host,header{display:flex;flex-direction:column;align-items:center;justify-content:center}:host{flex-grow:1}header{width:100%}:host::ng-deep [slot=logo]{height:inherit}:host::ng-deep [slot=logo] img{object-fit:contain;width:100%;height:100%}.sidebar-logo{display:flex;align-items:center;height:50px;margin:var(--kirby-spacing-l) var(--kirby-spacing-m)}.action-bar{display:flex;flex-direction:row;align-items:center;justify-content:center;box-sizing:border-box;width:100%}.action-bar:has([slot=action]){padding:0 var(--kirby-spacing-xs) var(--kirby-spacing-xs)}\n"] }); }
589
+ }
590
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarHeaderComponent, decorators: [{
591
+ type: Component,
592
+ args: [{ selector: 'kirby-x-sidebar-header', template: `
593
+ <header>
594
+ <span class="sidebar-logo"><ng-content slot="logo"></ng-content></span>
595
+ <div class="action-bar">
596
+ <ng-content select="[slot='action']"></ng-content>
597
+ </div>
598
+ </header>
599
+ `, styles: [":host,header{display:flex;flex-direction:column;align-items:center;justify-content:center}:host{flex-grow:1}header{width:100%}:host::ng-deep [slot=logo]{height:inherit}:host::ng-deep [slot=logo] img{object-fit:contain;width:100%;height:100%}.sidebar-logo{display:flex;align-items:center;height:50px;margin:var(--kirby-spacing-l) var(--kirby-spacing-m)}.action-bar{display:flex;flex-direction:row;align-items:center;justify-content:center;box-sizing:border-box;width:100%}.action-bar:has([slot=action]){padding:0 var(--kirby-spacing-xs) var(--kirby-spacing-xs)}\n"] }]
600
+ }] });
601
+
602
+ class SidebarModule {
603
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
604
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: SidebarModule, imports: [SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent], exports: [SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent] }); }
605
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarModule, imports: [SidebarComponent] }); }
606
+ }
607
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SidebarModule, decorators: [{
608
+ type: NgModule,
609
+ args: [{
610
+ imports: [SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent],
611
+ exports: [SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent],
612
+ }]
613
+ }] });
614
+
615
+ /**
616
+ * Generated bundle index. Do not edit.
617
+ */
618
+
619
+ export { SidebarComponent, SidebarFooterComponent, SidebarHeaderComponent, SidebarModule };
620
+ //# sourceMappingURL=kirbydesign-extensions-angular-sidebar-menu.mjs.map