@propbinder/mobile-design 0.2.37 → 0.2.40

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.
@@ -5,16 +5,17 @@ import { CommonModule, isPlatformBrowser } from '@angular/common';
5
5
  import * as i1$4 from '@angular/router';
6
6
  import { Router, NavigationEnd } from '@angular/router';
7
7
  import * as i1 from '@ionic/angular/standalone';
8
- import { ModalController, IonHeader, IonToolbar, IonTitle, IonButtons, IonContent, Platform, IonRefresher, IonRefresherContent, IonPopover, IonTabBar, IonTabButton, IonLabel, IonTabs, IonTab, IonSpinner, IonInfiniteScroll, IonInfiniteScrollContent } from '@ionic/angular/standalone';
8
+ import { ModalController, IonHeader, IonToolbar, IonTitle, IonButtons, IonContent, Platform, IonRefresher, IonRefresherContent, IonPopover, IonTabBar, IonTabButton, IonLabel, IonTabs, IonTab, IonSpinner, IonPicker, IonPickerColumn, IonPickerColumnOption, IonInfiniteScroll, IonInfiniteScrollContent } from '@ionic/angular/standalone';
9
9
  import { ImpactStyle, Haptics } from '@capacitor/haptics';
10
- import { DsIconButtonComponent, DsIconComponent, DsButtonComponent, DsAvatarComponent, DsShapeIndicatorComponent, DsTextareaComponent, DsCheckboxComponent, DsInputTimeComponent, DsLabelComponent, DsInputComponent, DsBadgeComponent } from '@propbinder/design-system';
10
+ import { DsIconButtonComponent, DsIconComponent, DsButtonComponent, DsAvatarComponent, DsShapeIndicatorComponent, DsTextareaComponent, DsDatepickerComponent, DsCheckboxComponent, DsInputTimeComponent, DsLabelComponent, DsInputComponent, DsBadgeComponent } from '@propbinder/design-system';
11
11
  import { StatusBar, Style } from '@capacitor/status-bar';
12
12
  import { Network } from '@capacitor/network';
13
13
  import { Keyboard } from '@capacitor/keyboard';
14
14
  import * as i1$1 from '@angular/platform-browser';
15
15
  import * as i1$3 from '@angular/forms';
16
16
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
17
- import { Camera, CameraSource, CameraResultType } from '@capacitor/camera';
17
+ import { Capacitor } from '@capacitor/core';
18
+ import { FilePicker } from '@capawesome/capacitor-file-picker';
18
19
  import { Subject } from 'rxjs';
19
20
  import { createAnimation } from '@ionic/core';
20
21
  import { filter } from 'rxjs/operators';
@@ -23,7 +24,7 @@ import { Pagination } from 'swiper/modules';
23
24
  import { Share } from '@capacitor/share';
24
25
  import { Filesystem, Directory } from '@capacitor/filesystem';
25
26
  import { Browser } from '@capacitor/browser';
26
- import { Capacitor } from '@capacitor/core';
27
+ import { AppTrackingTransparency } from 'capacitor-plugin-app-tracking-transparency';
27
28
 
28
29
  const DEFAULT_CONFIG = {
29
30
  logoUrl: '/Assets/logos/propbinder-logomark.svg',
@@ -2498,17 +2499,18 @@ class DsMobilePostCreateBottomSheetComponent {
2498
2499
  this.applySafeAreaToToolbar();
2499
2500
  try {
2500
2501
  console.log('Requesting photo from library...');
2501
- const image = await Camera.getPhoto({
2502
- quality: 90,
2503
- allowEditing: false,
2504
- resultType: CameraResultType.Uri,
2505
- source: CameraSource.Photos, // Only show photo library, not camera
2502
+ const result = await FilePicker.pickImages({
2503
+ limit: 1,
2506
2504
  });
2505
+ const image = result.files?.[0];
2507
2506
  console.log('Photo selected successfully:', image);
2508
2507
  // Add the image path to the array
2509
- if (image.webPath) {
2510
- this.selectedImages.update(images => [...images, image.webPath]);
2511
- console.log('Image added to preview:', image.webPath);
2508
+ if (image) {
2509
+ const imageSrc = image.path ? Capacitor.convertFileSrc(image.path) : (image.blob ? URL.createObjectURL(image.blob) : '');
2510
+ if (imageSrc) {
2511
+ this.selectedImages.update(images => [...images, imageSrc]);
2512
+ console.log('Image added to preview:', imageSrc);
2513
+ }
2512
2514
  }
2513
2515
  // Re-apply safe area padding immediately after returning
2514
2516
  // Since we're using fixed values, this won't cause flickering
@@ -5726,6 +5728,7 @@ class DsMobileSectionComponent {
5726
5728
  <ds-icon name="remixArrowRightSLine" size="16px" />
5727
5729
  </a>
5728
5730
  }
5731
+ <ng-content select="[header-action]" />
5729
5732
  </header>
5730
5733
  }
5731
5734
 
@@ -5757,6 +5760,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
5757
5760
  <ds-icon name="remixArrowRightSLine" size="16px" />
5758
5761
  </a>
5759
5762
  }
5763
+ <ng-content select="[header-action]" />
5760
5764
  </header>
5761
5765
  }
5762
5766
 
@@ -6862,9 +6866,15 @@ class DsMobileDropdownComponent {
6862
6866
  });
6863
6867
  }
6864
6868
  /**
6865
- * Content projection for custom item template
6869
+ * Content projection for custom item template (per-item renderer)
6866
6870
  */
6867
6871
  customItemTemplate;
6872
+ /**
6873
+ * Content projection for fully custom popover content.
6874
+ * When provided, the item list is bypassed entirely and this template
6875
+ * is rendered directly inside the popover — use for pickers, forms, etc.
6876
+ */
6877
+ customContent;
6868
6878
  /**
6869
6879
  * Optional trigger element ID for Ionic Popover positioning
6870
6880
  */
@@ -6875,9 +6885,10 @@ class DsMobileDropdownComponent {
6875
6885
  */
6876
6886
  keepFocusOn = input(...(ngDevMode ? [undefined, { debugName: "keepFocusOn" }] : []));
6877
6887
  /**
6878
- * Array of dropdown items to display
6888
+ * Array of dropdown items to display.
6889
+ * Not required when using the #customContent slot.
6879
6890
  */
6880
- items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
6891
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : []));
6881
6892
  /**
6882
6893
  * Whether the dropdown is open
6883
6894
  */
@@ -6906,6 +6917,12 @@ class DsMobileDropdownComponent {
6906
6917
  * ARIA label for the dropdown menu
6907
6918
  */
6908
6919
  ariaLabel = input('Dropdown menu', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
6920
+ /**
6921
+ * Maximum width of the popover.
6922
+ * Defaults to '192px' (standard dropdown width).
6923
+ * Override when using #customContent that requires more space (e.g. '280px' for a picker).
6924
+ */
6925
+ maxWidth = input('192px', ...(ngDevMode ? [{ debugName: "maxWidth" }] : []));
6909
6926
  /**
6910
6927
  * Emits when an item is selected
6911
6928
  */
@@ -6967,7 +6984,7 @@ class DsMobileDropdownComponent {
6967
6984
  this.itemSelected.emit(item);
6968
6985
  }
6969
6986
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6970
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileDropdownComponent, isStandalone: true, selector: "ds-mobile-dropdown", inputs: { trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, keepFocusOn: { classPropertyName: "keepFocusOn", publicName: "keepFocusOn", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected", closed: "closed" }, queries: [{ propertyName: "customItemTemplate", first: true, predicate: ["itemTemplate"], descendants: true }], ngImport: i0, template: `
6987
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileDropdownComponent, isStandalone: true, selector: "ds-mobile-dropdown", inputs: { trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, keepFocusOn: { classPropertyName: "keepFocusOn", publicName: "keepFocusOn", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelected: "itemSelected", closed: "closed" }, queries: [{ propertyName: "customItemTemplate", first: true, predicate: ["itemTemplate"], descendants: true }, { propertyName: "customContent", first: true, predicate: ["customContent"], descendants: true }], ngImport: i0, template: `
6971
6988
  <ion-popover
6972
6989
  [isOpen]="isOpen()"
6973
6990
  [trigger]="trigger()"
@@ -6981,71 +6998,77 @@ class DsMobileDropdownComponent {
6981
6998
  (didDismiss)="closed.emit()"
6982
6999
  [style.--offset-y]="offsetY()"
6983
7000
  [style.--offset-x]="offsetX()"
6984
- [style.--max-width]="'192px'"
7001
+ [style.--max-width]="maxWidth()"
6985
7002
  [style.--background]="'transparent'"
6986
7003
  [style.--box-shadow]="'none'"
6987
7004
  [style.--backdrop-opacity]="'0'">
6988
7005
 
6989
7006
  <ng-template>
6990
- <div
6991
- [class]="dropdownClasses()"
6992
- [style.max-height.px]="maxHeight()"
6993
- (mousedown)="$event.stopPropagation()"
6994
- (click)="$event.stopPropagation()"
6995
- role="menu"
6996
- [attr.aria-label]="ariaLabel()">
6997
-
6998
- @if (customItemTemplate) {
6999
- <!-- Custom template for each item -->
7000
- @for (item of items(); track item.id) {
7001
- <div
7002
- [class]="getItemClass(item)"
7003
- [attr.data-disabled]="item.disabled ? '' : null"
7004
- [attr.aria-disabled]="item.disabled"
7005
- (mousedown)="handleItemClick(item, $event)"
7006
- role="menuitem">
7007
- <ng-container
7008
- *ngTemplateOutlet="customItemTemplate; context: { $implicit: item }" />
7009
- </div>
7007
+ @if (customContent) {
7008
+ <!-- Fully custom popover content (e.g. picker, form) -->
7009
+ <ng-container *ngTemplateOutlet="customContent" />
7010
+ } @else {
7011
+ <!-- Standard item list -->
7012
+ <div
7013
+ [class]="dropdownClasses()"
7014
+ [style.max-height.px]="maxHeight()"
7015
+ (mousedown)="$event.stopPropagation()"
7016
+ (click)="$event.stopPropagation()"
7017
+ role="menu"
7018
+ [attr.aria-label]="ariaLabel()">
7019
+
7020
+ @if (customItemTemplate) {
7021
+ <!-- Custom template for each item -->
7022
+ @for (item of items(); track item.id) {
7023
+ <div
7024
+ [class]="getItemClass(item)"
7025
+ [attr.data-disabled]="item.disabled ? '' : null"
7026
+ [attr.aria-disabled]="item.disabled"
7027
+ (mousedown)="handleItemClick(item, $event)"
7028
+ role="menuitem">
7029
+ <ng-container
7030
+ *ngTemplateOutlet="customItemTemplate; context: { $implicit: item }" />
7031
+ </div>
7032
+ }
7033
+ } @else {
7034
+ <!-- Default three-slot template: leading - main - trailing -->
7035
+ @for (item of items(); track item.id) {
7036
+ <div
7037
+ [class]="getItemClass(item)"
7038
+ [attr.data-disabled]="item.disabled ? '' : null"
7039
+ [attr.aria-disabled]="item.disabled"
7040
+ (mousedown)="handleItemClick(item, $event)"
7041
+ role="menuitem">
7042
+
7043
+ <!-- Leading slot -->
7044
+ @if (item.leadingIcon) {
7045
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--leading">
7046
+ <ds-icon [name]="item.leadingIcon" size="16px" />
7047
+ </div>
7048
+ }
7049
+
7050
+ <!-- Main slot -->
7051
+ @if (item.label) {
7052
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--main">
7053
+ <span class="ds-mobile-dropdown__label">{{ item.label }}</span>
7054
+ </div>
7055
+ }
7056
+
7057
+ <!-- Trailing slot -->
7058
+ @if (item.trailingIcon) {
7059
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--trailing">
7060
+ <ds-icon [name]="item.trailingIcon" size="20px" />
7061
+ </div>
7062
+ }
7063
+ </div>
7064
+ }
7010
7065
  }
7011
- } @else {
7012
- <!-- Default three-slot template: leading - main - trailing -->
7013
- @for (item of items(); track item.id) {
7014
- <div
7015
- [class]="getItemClass(item)"
7016
- [attr.data-disabled]="item.disabled ? '' : null"
7017
- [attr.aria-disabled]="item.disabled"
7018
- (mousedown)="handleItemClick(item, $event)"
7019
- role="menuitem">
7020
-
7021
- <!-- Leading slot -->
7022
- @if (item.leadingIcon) {
7023
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--leading">
7024
- <ds-icon [name]="item.leadingIcon" size="16px" />
7025
- </div>
7026
- }
7027
-
7028
- <!-- Main slot -->
7029
- @if (item.label) {
7030
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--main">
7031
- <span class="ds-mobile-dropdown__label">{{ item.label }}</span>
7032
- </div>
7033
- }
7034
-
7035
- <!-- Trailing slot -->
7036
- @if (item.trailingIcon) {
7037
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--trailing">
7038
- <ds-icon [name]="item.trailingIcon" size="20px" />
7039
- </div>
7040
- }
7041
- </div>
7066
+
7067
+ @if (items().length === 0 && emptyMessage()) {
7068
+ <div class="ds-mobile-dropdown__empty">{{ emptyMessage() }}</div>
7042
7069
  }
7043
- }
7044
-
7045
- @if (items().length === 0 && emptyMessage()) {
7046
- <div class="ds-mobile-dropdown__empty">{{ emptyMessage() }}</div>
7047
- }
7048
- </div>
7070
+ </div>
7071
+ }
7049
7072
  </ng-template>
7050
7073
  </ion-popover>
7051
7074
  `, isInline: true, styles: ["ion-popover{--background: transparent;--box-shadow: none;--backdrop-opacity: 0;--max-width: 192px}ion-popover::part(content){background:#ffffff80!important;backdrop-filter:blur(12px)!important;-webkit-backdrop-filter:blur(12px)!important;box-shadow:0 2px 8px #00000014,0 4px 16px #0000001f!important;border:1px solid rgba(255,255,255,1)!important;border-radius:16px!important;padding:4px!important;outline:none!important;max-width:192px!important}ion-popover::part(backdrop){background:transparent!important}.ds-mobile-dropdown{position:relative;width:100%;background:transparent!important;border:none!important;box-shadow:none!important;overflow-y:auto;animation:slideIn var(--spring-duration-medium) var(--spring-curve-bouncy);padding:0}.ds-mobile-dropdown__item{display:flex;align-items:center;gap:12px;padding:4px 12px;background:none;width:100%;text-align:left;cursor:pointer;transition:background .15s ease;min-height:40px;justify-content:flex-start;border-radius:14px}.ds-mobile-dropdown__item:last-child{border-bottom:none}.ds-mobile-dropdown__item:hover:not(.ds-mobile-dropdown__item--disabled){background:#0000000a}.ds-mobile-dropdown__item:active:not(.ds-mobile-dropdown__item--disabled){background:#00000014;transition:background 0s}.ds-mobile-dropdown__item--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ds-mobile-dropdown__slot{display:flex;align-items:center}.ds-mobile-dropdown__slot--leading{flex-shrink:0;justify-content:center}.ds-mobile-dropdown__slot--main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left;align-items:flex-start}.ds-mobile-dropdown__slot--trailing{flex-shrink:0;margin-left:auto}.ds-mobile-dropdown__label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}.ds-mobile-dropdown__empty{padding:24px 16px;text-align:center;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373)}@keyframes slideIn{0%{opacity:0;transform:translateY(8px) scale(.75)}to{opacity:1;transform:translateY(0) scale(1)}}.ds-mobile-dropdown--above{animation:slideUp var(--spring-duration-medium) var(--spring-curve-bouncy)}@keyframes slideOut{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(8px) scale(.75)}}@keyframes slideDown{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(-8px) scale(.75)}}@keyframes slideUp{0%{opacity:0;transform:translateY(-8px) scale(.75)}to{opacity:1;transform:translateY(0) scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: IonPopover, selector: "ion-popover" }] });
@@ -7066,78 +7089,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7066
7089
  (didDismiss)="closed.emit()"
7067
7090
  [style.--offset-y]="offsetY()"
7068
7091
  [style.--offset-x]="offsetX()"
7069
- [style.--max-width]="'192px'"
7092
+ [style.--max-width]="maxWidth()"
7070
7093
  [style.--background]="'transparent'"
7071
7094
  [style.--box-shadow]="'none'"
7072
7095
  [style.--backdrop-opacity]="'0'">
7073
7096
 
7074
7097
  <ng-template>
7075
- <div
7076
- [class]="dropdownClasses()"
7077
- [style.max-height.px]="maxHeight()"
7078
- (mousedown)="$event.stopPropagation()"
7079
- (click)="$event.stopPropagation()"
7080
- role="menu"
7081
- [attr.aria-label]="ariaLabel()">
7082
-
7083
- @if (customItemTemplate) {
7084
- <!-- Custom template for each item -->
7085
- @for (item of items(); track item.id) {
7086
- <div
7087
- [class]="getItemClass(item)"
7088
- [attr.data-disabled]="item.disabled ? '' : null"
7089
- [attr.aria-disabled]="item.disabled"
7090
- (mousedown)="handleItemClick(item, $event)"
7091
- role="menuitem">
7092
- <ng-container
7093
- *ngTemplateOutlet="customItemTemplate; context: { $implicit: item }" />
7094
- </div>
7098
+ @if (customContent) {
7099
+ <!-- Fully custom popover content (e.g. picker, form) -->
7100
+ <ng-container *ngTemplateOutlet="customContent" />
7101
+ } @else {
7102
+ <!-- Standard item list -->
7103
+ <div
7104
+ [class]="dropdownClasses()"
7105
+ [style.max-height.px]="maxHeight()"
7106
+ (mousedown)="$event.stopPropagation()"
7107
+ (click)="$event.stopPropagation()"
7108
+ role="menu"
7109
+ [attr.aria-label]="ariaLabel()">
7110
+
7111
+ @if (customItemTemplate) {
7112
+ <!-- Custom template for each item -->
7113
+ @for (item of items(); track item.id) {
7114
+ <div
7115
+ [class]="getItemClass(item)"
7116
+ [attr.data-disabled]="item.disabled ? '' : null"
7117
+ [attr.aria-disabled]="item.disabled"
7118
+ (mousedown)="handleItemClick(item, $event)"
7119
+ role="menuitem">
7120
+ <ng-container
7121
+ *ngTemplateOutlet="customItemTemplate; context: { $implicit: item }" />
7122
+ </div>
7123
+ }
7124
+ } @else {
7125
+ <!-- Default three-slot template: leading - main - trailing -->
7126
+ @for (item of items(); track item.id) {
7127
+ <div
7128
+ [class]="getItemClass(item)"
7129
+ [attr.data-disabled]="item.disabled ? '' : null"
7130
+ [attr.aria-disabled]="item.disabled"
7131
+ (mousedown)="handleItemClick(item, $event)"
7132
+ role="menuitem">
7133
+
7134
+ <!-- Leading slot -->
7135
+ @if (item.leadingIcon) {
7136
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--leading">
7137
+ <ds-icon [name]="item.leadingIcon" size="16px" />
7138
+ </div>
7139
+ }
7140
+
7141
+ <!-- Main slot -->
7142
+ @if (item.label) {
7143
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--main">
7144
+ <span class="ds-mobile-dropdown__label">{{ item.label }}</span>
7145
+ </div>
7146
+ }
7147
+
7148
+ <!-- Trailing slot -->
7149
+ @if (item.trailingIcon) {
7150
+ <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--trailing">
7151
+ <ds-icon [name]="item.trailingIcon" size="20px" />
7152
+ </div>
7153
+ }
7154
+ </div>
7155
+ }
7095
7156
  }
7096
- } @else {
7097
- <!-- Default three-slot template: leading - main - trailing -->
7098
- @for (item of items(); track item.id) {
7099
- <div
7100
- [class]="getItemClass(item)"
7101
- [attr.data-disabled]="item.disabled ? '' : null"
7102
- [attr.aria-disabled]="item.disabled"
7103
- (mousedown)="handleItemClick(item, $event)"
7104
- role="menuitem">
7105
-
7106
- <!-- Leading slot -->
7107
- @if (item.leadingIcon) {
7108
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--leading">
7109
- <ds-icon [name]="item.leadingIcon" size="16px" />
7110
- </div>
7111
- }
7112
-
7113
- <!-- Main slot -->
7114
- @if (item.label) {
7115
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--main">
7116
- <span class="ds-mobile-dropdown__label">{{ item.label }}</span>
7117
- </div>
7118
- }
7119
-
7120
- <!-- Trailing slot -->
7121
- @if (item.trailingIcon) {
7122
- <div class="ds-mobile-dropdown__slot ds-mobile-dropdown__slot--trailing">
7123
- <ds-icon [name]="item.trailingIcon" size="20px" />
7124
- </div>
7125
- }
7126
- </div>
7157
+
7158
+ @if (items().length === 0 && emptyMessage()) {
7159
+ <div class="ds-mobile-dropdown__empty">{{ emptyMessage() }}</div>
7127
7160
  }
7128
- }
7129
-
7130
- @if (items().length === 0 && emptyMessage()) {
7131
- <div class="ds-mobile-dropdown__empty">{{ emptyMessage() }}</div>
7132
- }
7133
- </div>
7161
+ </div>
7162
+ }
7134
7163
  </ng-template>
7135
7164
  </ion-popover>
7136
7165
  `, styles: ["ion-popover{--background: transparent;--box-shadow: none;--backdrop-opacity: 0;--max-width: 192px}ion-popover::part(content){background:#ffffff80!important;backdrop-filter:blur(12px)!important;-webkit-backdrop-filter:blur(12px)!important;box-shadow:0 2px 8px #00000014,0 4px 16px #0000001f!important;border:1px solid rgba(255,255,255,1)!important;border-radius:16px!important;padding:4px!important;outline:none!important;max-width:192px!important}ion-popover::part(backdrop){background:transparent!important}.ds-mobile-dropdown{position:relative;width:100%;background:transparent!important;border:none!important;box-shadow:none!important;overflow-y:auto;animation:slideIn var(--spring-duration-medium) var(--spring-curve-bouncy);padding:0}.ds-mobile-dropdown__item{display:flex;align-items:center;gap:12px;padding:4px 12px;background:none;width:100%;text-align:left;cursor:pointer;transition:background .15s ease;min-height:40px;justify-content:flex-start;border-radius:14px}.ds-mobile-dropdown__item:last-child{border-bottom:none}.ds-mobile-dropdown__item:hover:not(.ds-mobile-dropdown__item--disabled){background:#0000000a}.ds-mobile-dropdown__item:active:not(.ds-mobile-dropdown__item--disabled){background:#00000014;transition:background 0s}.ds-mobile-dropdown__item--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ds-mobile-dropdown__slot{display:flex;align-items:center}.ds-mobile-dropdown__slot--leading{flex-shrink:0;justify-content:center}.ds-mobile-dropdown__slot--main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;text-align:left;align-items:flex-start}.ds-mobile-dropdown__slot--trailing{flex-shrink:0;margin-left:auto}.ds-mobile-dropdown__label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:left}.ds-mobile-dropdown__empty{padding:24px 16px;text-align:center;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373)}@keyframes slideIn{0%{opacity:0;transform:translateY(8px) scale(.75)}to{opacity:1;transform:translateY(0) scale(1)}}.ds-mobile-dropdown--above{animation:slideUp var(--spring-duration-medium) var(--spring-curve-bouncy)}@keyframes slideOut{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(8px) scale(.75)}}@keyframes slideDown{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(-8px) scale(.75)}}@keyframes slideUp{0%{opacity:0;transform:translateY(-8px) scale(.75)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
7137
7166
  }], ctorParameters: () => [], propDecorators: { customItemTemplate: [{
7138
7167
  type: ContentChild,
7139
7168
  args: ['itemTemplate']
7140
- }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], keepFocusOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepFocusOn", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
7169
+ }], customContent: [{
7170
+ type: ContentChild,
7171
+ args: ['customContent']
7172
+ }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], keepFocusOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepFocusOn", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], itemSelected: [{ type: i0.Output, args: ["itemSelected"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
7141
7173
 
7142
7174
  /**
7143
7175
  * DsMobileMessageComposerComponent
@@ -7546,7 +7578,7 @@ class DsMobileMessageComposerComponent {
7546
7578
  }
7547
7579
  /**
7548
7580
  * Handle add photo button click from menu
7549
- * Uses Capacitor Camera API to open photo library directly
7581
+ * Uses Capawesome File Picker API to open photo library directly
7550
7582
  * Allows multiple photo selection
7551
7583
  */
7552
7584
  async handleAddPhoto(event) {
@@ -7558,26 +7590,25 @@ class DsMobileMessageComposerComponent {
7558
7590
  return;
7559
7591
  }
7560
7592
  try {
7561
- console.log('[MessageComposer] Opening photo library');
7593
+ //console.log('[MessageComposer] Opening photo library');
7562
7594
  // Calculate remaining slots
7563
7595
  const remainingSlots = 6 - this.attachments().length;
7564
7596
  // Open photo library with multiple selection using pickImages
7565
- const result = await Camera.pickImages({
7566
- quality: 90,
7597
+ const result = await FilePicker.pickImages({
7567
7598
  limit: remainingSlots, // Limit to remaining slots
7568
7599
  });
7569
- if (result.photos && result.photos.length > 0) {
7570
- console.log(`[MessageComposer] ${result.photos.length} photo(s) selected`);
7600
+ if (result.files && result.files.length > 0) {
7601
+ //console.log(`[MessageComposer] ${result.files.length} photo(s) selected`);
7571
7602
  // Process each selected photo
7572
- for (const photo of result.photos) {
7603
+ for (const photo of result.files) {
7573
7604
  const attachmentId = `photo-${Date.now()}-${Math.random()}`;
7574
7605
  // Add attachment with loading state
7575
7606
  const loadingAttachment = {
7576
7607
  id: attachmentId,
7577
- src: photo.webPath || '',
7608
+ src: photo.path ? Capacitor.convertFileSrc(photo.path) : (photo.blob ? URL.createObjectURL(photo.blob) : ''),
7578
7609
  type: 'image',
7579
- name: `Photo ${this.attachments().length + 1}`,
7580
- size: '',
7610
+ name: photo.name,
7611
+ size: this.formatFileSize(photo.size ?? 0),
7581
7612
  isLoading: true,
7582
7613
  };
7583
7614
  this.attachments.update((attachments) => [...attachments, loadingAttachment]);
@@ -7587,7 +7618,7 @@ class DsMobileMessageComposerComponent {
7587
7618
  this.attachments.update((attachments) => attachments.map((a) => (a.id === attachmentId ? { ...a, isLoading: false } : a)));
7588
7619
  }, 1500); // 1.5s for testing - shows loading overlay clearly
7589
7620
  }
7590
- console.log('[MessageComposer] All photos added successfully');
7621
+ //console.log('[MessageComposer] All photos added successfully');
7591
7622
  // Notify parent that attachments changed so it can scroll
7592
7623
  this.attachmentsChanged.emit();
7593
7624
  // ResizeObserver in MobileModalBase automatically handles layout adjustments
@@ -7595,7 +7626,7 @@ class DsMobileMessageComposerComponent {
7595
7626
  }
7596
7627
  catch (error) {
7597
7628
  if (error.message && !error.message.includes('cancel')) {
7598
- console.error('[MessageComposer] Error adding photo:', error);
7629
+ //console.error('[MessageComposer] Error adding photo:', error);
7599
7630
  }
7600
7631
  // User cancelled - that's fine
7601
7632
  }
@@ -7894,7 +7925,7 @@ class DsMobileMessageComposerComponent {
7894
7925
  (change)="handleFileSelect($event)"
7895
7926
  />
7896
7927
  </div>
7897
- `, isInline: true, styles: [":host{display:block}.message-composer{background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);border-bottom-left-radius:0;border-bottom-right-radius:0;padding:12px 16px;width:100%;display:flex;flex-direction:column;gap:8px}.edit-indicator{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--color-background-brand-subtle, #f0edfe);border-radius:8px;animation:slideDown .2s ease-out}.edit-indicator-content{display:flex;align-items:center;gap:8px;color:var(--color-accent, #6b5ff5);flex:1;min-width:0}.edit-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:18px;color:var(--color-accent, #6b5ff5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-accent, #6b5ff5);border-radius:4px;transition:background .2s ease;flex-shrink:0}.cancel-edit:active{background:var(--color-brand-subtle, #e0dbfe)}.reply-indicator{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:8px;animation:slideDown .2s ease-out}.reply-indicator-content{display:flex;align-items:center;gap:4px;color:var(--color-text-secondary, #737373);flex:1;min-width:0}.reply-to-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.reply-author{color:var(--color-accent, #6b5ff5);font-weight:600}.cancel-reply{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary, #737373);border-radius:4px;transition:background .2s ease;flex-shrink:0}.cancel-reply:active{background:var(--color-background-neutral-secondary, #f5f5f5)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.attachment-previews-section{padding:0 0 8px;animation:slideDown .2s ease-out}.attachment-previews{display:flex;flex-wrap:wrap;gap:8px}.composer-content{display:flex;align-items:center;gap:12px;width:100%;position:relative}.composer-leading-button{flex-shrink:0}.composer-leading-button::ng-deep button{width:40px!important;height:40px!important;min-width:40px!important;min-height:40px!important;padding:0!important;border-radius:50%!important;transition:transform .3s ease}.composer-leading-button--open::ng-deep button{transform:rotate(45deg)}.composer-input-wrapper{flex:1;display:flex;align-items:flex-start;gap:8px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:24px;padding:12px 16px;min-height:44px;position:relative}.mention-user-info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.mention-user-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:20px;color:var(--color-text-primary, #1a1a1a)}.mention-user-role{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373)}.composer-input{flex:1;border:none;background:transparent;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:20px;color:var(--color-text-primary, #1a1a1a);outline:none;resize:none;min-height:20px;max-height:120px;overflow-y:auto;padding:0;margin:0}.composer-input::placeholder{color:var(--color-text-tertiary, #a0a0a0);font-size:var(--font-size-sm)}.send-button-inline{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;opacity:0;transform:translate(20px) scale(.8);pointer-events:none;transition:opacity .15s ease-in,transform .15s ease-in}.send-button-inline.show{opacity:1;transform:translate(0) scale(1);pointer-events:auto;animation:slideInFromRight var(--spring-bouncy)}.send-button-inline::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}@keyframes slideInFromRight{0%{opacity:0;transform:translate(20px) scale(.8)}to{opacity:1;transform:translate(0) scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileAttachmentPreviewComponent, selector: "ds-mobile-attachment-preview", inputs: ["attachment"], outputs: ["remove"] }, { kind: "component", type: DsMobileDropdownComponent, selector: "ds-mobile-dropdown", inputs: ["trigger", "keepFocusOn", "items", "isOpen", "position", "align", "maxHeight", "emptyMessage", "ariaLabel"], outputs: ["itemSelected", "closed"] }] });
7928
+ `, isInline: true, styles: [":host{display:block}.message-composer{background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);border-bottom-left-radius:0;border-bottom-right-radius:0;padding:12px 16px;width:100%;display:flex;flex-direction:column;gap:8px}.edit-indicator{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--color-background-brand-subtle, #f0edfe);border-radius:8px;animation:slideDown .2s ease-out}.edit-indicator-content{display:flex;align-items:center;gap:8px;color:var(--color-accent, #6b5ff5);flex:1;min-width:0}.edit-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:18px;color:var(--color-accent, #6b5ff5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-accent, #6b5ff5);border-radius:4px;transition:background .2s ease;flex-shrink:0}.cancel-edit:active{background:var(--color-brand-subtle, #e0dbfe)}.reply-indicator{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:8px;animation:slideDown .2s ease-out}.reply-indicator-content{display:flex;align-items:center;gap:4px;color:var(--color-text-secondary, #737373);flex:1;min-width:0}.reply-to-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.reply-author{color:var(--color-accent, #6b5ff5);font-weight:600}.cancel-reply{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-text-secondary, #737373);border-radius:4px;transition:background .2s ease;flex-shrink:0}.cancel-reply:active{background:var(--color-background-neutral-secondary, #f5f5f5)}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.attachment-previews-section{padding:0 0 8px;animation:slideDown .2s ease-out}.attachment-previews{display:flex;flex-wrap:wrap;gap:8px}.composer-content{display:flex;align-items:center;gap:12px;width:100%;position:relative}.composer-leading-button{flex-shrink:0}.composer-leading-button::ng-deep button{width:40px!important;height:40px!important;min-width:40px!important;min-height:40px!important;padding:0!important;border-radius:50%!important;transition:transform .3s ease}.composer-leading-button--open::ng-deep button{transform:rotate(45deg)}.composer-input-wrapper{flex:1;display:flex;align-items:flex-start;gap:8px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:24px;padding:12px 16px;min-height:44px;position:relative}.mention-user-info{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.mention-user-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:20px;color:var(--color-text-primary, #1a1a1a)}.mention-user-role{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:18px;color:var(--color-text-secondary, #737373)}.composer-input{flex:1;border:none;background:transparent;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);line-height:20px;color:var(--color-text-primary, #1a1a1a);outline:none;resize:none;min-height:20px;max-height:120px;overflow-y:auto;padding:0;margin:0}.composer-input::placeholder{color:var(--color-text-tertiary, #a0a0a0);font-size:var(--font-size-sm)}.send-button-inline{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;opacity:0;transform:translate(20px) scale(.8);pointer-events:none;transition:opacity .15s ease-in,transform .15s ease-in}.send-button-inline.show{opacity:1;transform:translate(0) scale(1);pointer-events:auto;animation:slideInFromRight var(--spring-bouncy)}.send-button-inline::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}@keyframes slideInFromRight{0%{opacity:0;transform:translate(20px) scale(.8)}to{opacity:1;transform:translate(0) scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileAttachmentPreviewComponent, selector: "ds-mobile-attachment-preview", inputs: ["attachment"], outputs: ["remove"] }, { kind: "component", type: DsMobileDropdownComponent, selector: "ds-mobile-dropdown", inputs: ["trigger", "keepFocusOn", "items", "isOpen", "position", "align", "maxHeight", "emptyMessage", "ariaLabel", "maxWidth"], outputs: ["itemSelected", "closed"] }] });
7898
7929
  }
7899
7930
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileMessageComposerComponent, decorators: [{
7900
7931
  type: Component,
@@ -10366,7 +10397,7 @@ class DsMobileTabBarComponent {
10366
10397
  <ds-avatar [size]="'md'" [type]="avatarType" [initials]="avatarInitials" [src]="avatarSrc" [iconName]="avatarIconName" (click)="handleAvatarClick()" />
10367
10398
  </div>
10368
10399
  </ion-tab-bar>
10369
- `, isInline: true, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--ion-safe-area-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:var(--font-size-xs);font-weight:400;letter-spacing:-.3px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabBar, selector: "ion-tab-bar", inputs: ["color", "mode", "selectedTab", "translucent"] }, { kind: "component", type: IonTabButton, selector: "ion-tab-button", inputs: ["disabled", "download", "href", "layout", "mode", "rel", "selected", "tab", "target"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }] });
10400
+ `, isInline: true, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--ion-safe-area-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:11px;font-weight:500;letter-spacing:-.5px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IonTabBar, selector: "ion-tab-bar", inputs: ["color", "mode", "selectedTab", "translucent"] }, { kind: "component", type: IonTabButton, selector: "ion-tab-button", inputs: ["disabled", "download", "href", "layout", "mode", "rel", "selected", "tab", "target"] }, { kind: "component", type: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }] });
10370
10401
  }
10371
10402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileTabBarComponent, decorators: [{
10372
10403
  type: Component,
@@ -10402,7 +10433,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10402
10433
  <ds-avatar [size]="'md'" [type]="avatarType" [initials]="avatarInitials" [src]="avatarSrc" [iconName]="avatarIconName" (click)="handleAvatarClick()" />
10403
10434
  </div>
10404
10435
  </ion-tab-bar>
10405
- `, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--ion-safe-area-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:var(--font-size-xs);font-weight:400;letter-spacing:-.3px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"] }]
10436
+ `, styles: [":host{--ds-tab-bar-height: 64px}@media (min-width: 768px){:host{display:block;--ds-tab-bar-height: 64px}}@media (max-width: 767px){:host{display:contents}}ion-tabs.ds-tabs-wrapper{height:100%;background:var(--color-header-surface)}ion-tab-button:before,ion-tab-button:after{content:none!important;display:none!important}ion-tab-button[title]:before,ion-tab-button[title]:after{display:none!important}ion-tab-button::part(native):before,ion-tab-button::part(native):after{display:none!important}.ds-tab-bar{--background: var(--color-background-neutral-primary);transition:transform .2s ease-in-out}ion-tab-bar[slot=bottom]{border-top:1px solid var(--border-color-default);padding-top:8px;padding-bottom:max(8px,calc(var(--ion-safe-area-bottom, 0px) - 16px));padding-left:12px;padding-right:12px}@media (max-width: 767px){ion-tab-bar[slot=bottom]{position:fixed;bottom:0;left:0;right:0;z-index:100}}@media (max-width: 767px){:host-context(ion-tabs:has(ds-mobile-page-details)) .ds-tab-bar{transform:translateY(100%);transition:transform .3s ease}}.ds-tab-bar__logo,.ds-tab-bar__actions{display:none}.ds-tab-bar__tabs{display:flex;width:100%;justify-content:space-around;align-items:center}@media (min-width: 769px){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)}}@media (display-mode: standalone){ion-tab-bar[slot=bottom]{padding-bottom:env(safe-area-inset-bottom,0px)!important}}@media (min-width: 768px){:host[slot=top]{order:-1!important}:host ion-tab-bar{position:relative!important;bottom:auto!important;top:0!important}ion-tab-bar[slot=top]{--background: var(--color-header-surface);position:relative!important;display:flex!important;align-items:center;padding:12px 24px;height:64px;max-width:none;bottom:auto!important;top:0!important}ion-tab-bar[slot=bottom]{position:relative!important;bottom:auto!important}ion-tabs>div:not([slot]){order:1!important}.ds-tab-bar__logo{display:flex;position:absolute;left:24px;align-items:center;color:var(--header-content-color, white)}.ds-tab-bar__actions{display:flex;position:absolute;right:24px;align-items:center;gap:12px}.ds-tab-bar__tabs{display:flex;gap:8px;align-items:center;max-width:640px;width:100%;margin:0 auto;justify-content:center;padding-left:var(--content-padding-md);padding-right:var(--content-padding-md)}.logomark{height:28px;width:auto;flex-shrink:0}}@media (min-width: 992px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg);justify-content:center}}@media (min-width: 1440px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.ds-tab-bar__tabs{max-width:640px;padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-tab-button{--color: var(--text-color-default-tertiary);--color-selected: var(--color-accent);display:flex;flex-direction:column;align-items:center;justify-content:center;position:relative;overflow:visible;pointer-events:auto}ion-tab-button[title]:before{content:attr(title);position:absolute;opacity:0;pointer-events:none}.tab-icon-ripple{position:absolute;left:50%;top:50%;width:40px;height:40px;border-radius:50%;background:var(--color-accent);transform:translate(-50%,-50%) scale(0);opacity:0;pointer-events:none;transition:all .6s cubic-bezier(.36,1.2,.04,1.4);z-index:0}.tab-selected .tab-icon-ripple{transform:translate(-50%,-50%) scale(2);opacity:.05;animation:ripple-fade .6s cubic-bezier(.36,.5,.04,1.8) forwards}@keyframes ripple-fade{0%{opacity:0;transform:translate(-50%,-50%) scale(0)}30%{opacity:.1}to{opacity:0;transform:translate(-50%,-50%) scale(2)}}ion-tab-button::part(native){overflow:visible}ion-tab-button ion-ripple-effect{color:var(--color-accent);border-radius:1000px}ion-tab-button ion-label{font-size:11px;font-weight:500;letter-spacing:-.5px;margin-top:0}.tab-icon-wrapper{position:relative;width:24px;height:24px;display:flex;align-items:center;justify-content:center;z-index:1;margin-bottom:4px}.tab-icon-inactive,.tab-icon-active{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);transition:all .8s cubic-bezier(.36,1,.04,1)}.tab-icon-inactive{opacity:1;transform:translate(-50%,-50%) scale(1)}.tab-icon-active{opacity:0;transform:translate(-50%,calc(-50% - 12px)) scale(.5)}.tab-selected .tab-icon-inactive{opacity:0;transform:translate(-50%,-50%) scale(.5)}.tab-selected .tab-icon-active{opacity:1;transform:translate(-50%,-50%) scale(1)}@media (min-width: 768px){.ds-tab-button{flex-direction:row;height:40px;padding:0 16px!important;border-radius:40px;transition:all .2s ease;width:-moz-fit-content;width:fit-content;min-width:-moz-fit-content;min-width:fit-content;flex:0 0 auto;--color: rgba(var(--color-header-content-rgb, 255, 255, 255), .7);--color-selected: var(--color-header-content, white);color:rgba(var(--color-header-content-rgb, 255, 255, 255),.7);background:transparent;position:relative;overflow:hidden}.tab-icon-wrapper,.tab-icon-ripple{width:20px;height:20px}.ds-tab-button::part(native){border-radius:40px}.ds-tab-button:hover:not(.tab-selected){--color: var(--color-header-content, white);--color-selected: var(--color-header-content, white);color:var(--color-header-content, white);background:rgba(var(--color-header-content-rgb, 255, 255, 255),.1)}.ds-tab-button:hover:not(.tab-selected) ion-label{color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon{--icon-color: var(--color-header-content, white);color:var(--color-header-content, white)}.ds-tab-button:hover:not(.tab-selected) ds-icon svg{fill:var(--color-header-content, white)}.ds-tab-button.tab-selected,.ds-tab-button.tab-selected:hover{background:var(--color-header-accent);--color-selected: var(--color-on-header-accent, white);--color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected:hover .tab-icon-active{opacity:1!important;visibility:visible!important}.ds-tab-button.tab-selected:hover .tab-icon-inactive{opacity:0!important}.ds-tab-button.tab-selected ion-label{color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon{--icon-color: var(--color-on-header-accent, white);color:var(--color-on-header-accent, white)}.ds-tab-button.tab-selected ds-icon svg{fill:var(--color-on-header-accent, white)}.ds-tab-button .button-native{width:auto;padding:0}.ds-tab-button ion-label{font-size:var(--font-size-sm);font-weight:500;margin:0;color:inherit}.ds-tab-button .tab-icon-wrapper{margin-right:4px;margin-bottom:0}.ds-tab-button ion-ripple-effect{color:rgba(var(--header-content-color-rgb, 255, 255, 255),.3);border-radius:1000px;transform:scale(1.5)}}@media (min-width: 768px){.ds-tab-bar__actions ds-avatar{cursor:pointer;transition:transform .2s ease}.ds-tab-bar__actions ds-avatar:hover{transform:scale(1.05)}}\n"] }]
10406
10437
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { tabs: [{
10407
10438
  type: Input
10408
10439
  }], avatarType: [{
@@ -10697,6 +10728,12 @@ class DsMobileSwiperComponent {
10697
10728
  slideNext() {
10698
10729
  this.swiperInstance?.slideNext();
10699
10730
  }
10731
+ /**
10732
+ * Navigate to a specific slide by index
10733
+ */
10734
+ slideTo(index, speed = 300) {
10735
+ this.swiperInstance?.slideTo(index, speed);
10736
+ }
10700
10737
  /**
10701
10738
  * Check if at the beginning
10702
10739
  */
@@ -15984,18 +16021,17 @@ class DsMobileNewInquiryModalComponent {
15984
16021
  return;
15985
16022
  }
15986
16023
  try {
15987
- const image = await Camera.getPhoto({
15988
- quality: 90,
15989
- allowEditing: false,
15990
- resultType: CameraResultType.Uri,
15991
- source: CameraSource.Photos, // Only show photo library
16024
+ const result = await FilePicker.pickImages({
16025
+ limit: 1,
15992
16026
  });
15993
- if (image.webPath) {
16027
+ const image = result.files?.[0];
16028
+ if (image) {
15994
16029
  const newAttachment = {
15995
16030
  id: `photo-${Date.now()}`,
15996
- src: image.webPath,
16031
+ src: image.path ? Capacitor.convertFileSrc(image.path) : (image.blob ? URL.createObjectURL(image.blob) : ''),
15997
16032
  type: 'image',
15998
- name: `Photo ${this.attachments().length + 1}`,
16033
+ name: image.name || `Photo ${this.attachments().length + 1}`,
16034
+ size: this.formatFileSize(image.size ?? 0),
15999
16035
  };
16000
16036
  this.attachments.update((attachments) => [...attachments, newAttachment]);
16001
16037
  }
@@ -16389,6 +16425,11 @@ class DsMobileBookingModalComponent {
16389
16425
  modalController;
16390
16426
  facilityId;
16391
16427
  facilityTitle;
16428
+ /**
16429
+ * Number of days ahead available for booking selection.
16430
+ * Defaults to 60 (2 months). Override via componentProps when opening the modal.
16431
+ */
16432
+ daysAhead = 60;
16392
16433
  swiperComponent;
16393
16434
  // Signals for reactive state management
16394
16435
  dateOptions = signal([], ...(ngDevMode ? [{ debugName: "dateOptions" }] : []));
@@ -16419,22 +16460,50 @@ class DsMobileBookingModalComponent {
16419
16460
  }
16420
16461
  }, 150);
16421
16462
  }
16463
+ /**
16464
+ * Returns true if the given date should be disabled in both the swiper and the datepicker.
16465
+ * Weekends are disabled. Index-based mock disabling (i===3, i===7) is swiper-only and
16466
+ * not representable as a date rule, so it is intentionally excluded here.
16467
+ */
16468
+ isDateUnavailable(date) {
16469
+ return date.getDay() === 0 || date.getDay() === 6;
16470
+ }
16471
+ /**
16472
+ * Computed signal that returns a fresh disabled-date function whenever dateOptions()
16473
+ * changes. Returning a new function reference causes the datepicker's own isDateDisabled
16474
+ * input to update, which triggers its internal computed to re-run and re-render all
16475
+ * calendar cells with the correct disabled state.
16476
+ * The disabledSet is pre-built once per computation for O(1) per-cell lookups.
16477
+ */
16478
+ dateDisabledFn = computed(() => {
16479
+ const disabledSet = new Set(this.dateOptions()
16480
+ .filter(opt => opt.state === 'disabled')
16481
+ .map(opt => opt.fullDate.toDateString()));
16482
+ return (date) => {
16483
+ const today = new Date();
16484
+ today.setHours(0, 0, 0, 0);
16485
+ const max = new Date(today);
16486
+ max.setDate(today.getDate() + this.daysAhead - 1);
16487
+ const d = new Date(date);
16488
+ d.setHours(0, 0, 0, 0);
16489
+ if (d < today || d > max)
16490
+ return true;
16491
+ return disabledSet.has(date.toDateString());
16492
+ };
16493
+ }, ...(ngDevMode ? [{ debugName: "dateDisabledFn" }] : []));
16422
16494
  /**
16423
16495
  * Generate mock date and time data
16424
16496
  */
16425
16497
  generateMockData() {
16426
- // Generate 14 days starting from today
16427
16498
  const dates = [];
16428
16499
  const today = new Date();
16429
16500
  const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
16430
16501
  const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
16431
16502
  let firstAvailableDate = null;
16432
- for (let i = 0; i < 14; i++) {
16503
+ for (let i = 0; i < this.daysAhead; i++) {
16433
16504
  const date = new Date(today);
16434
16505
  date.setDate(today.getDate() + i);
16435
- // Mark some dates as disabled (e.g., weekends or specific dates)
16436
- const isWeekend = date.getDay() === 0 || date.getDay() === 6;
16437
- const isDisabled = isWeekend || (i === 3) || (i === 7); // Example: disable some dates
16506
+ const isDisabled = this.isDateUnavailable(date) || (i === 3) || (i === 7);
16438
16507
  const dateOption = {
16439
16508
  id: `date-${i}`,
16440
16509
  dayName: dayNames[date.getDay()],
@@ -16522,6 +16591,23 @@ class DsMobileBookingModalComponent {
16522
16591
  this.timeSlots.set(updatedSlots);
16523
16592
  this.selectedTimeSlot.set(selectedSlot);
16524
16593
  }
16594
+ /**
16595
+ * Called when the datepicker overlay emits a date selection.
16596
+ * Finds the matching DateOption in the swiper, selects it, and auto-scrolls to it.
16597
+ */
16598
+ jumpToDate(date) {
16599
+ if (!date)
16600
+ return;
16601
+ const options = this.dateOptions();
16602
+ const idx = options.findIndex(opt => opt.fullDate.toDateString() === date.toDateString());
16603
+ if (idx === -1)
16604
+ return;
16605
+ const option = options[idx];
16606
+ if (option.state === 'disabled')
16607
+ return;
16608
+ this.selectDate(option);
16609
+ this.swiperComponent?.slideTo(idx);
16610
+ }
16525
16611
  /**
16526
16612
  * Handle confirm button click
16527
16613
  */
@@ -16548,7 +16634,7 @@ class DsMobileBookingModalComponent {
16548
16634
  await this.modalController.dismiss(null, 'cancel');
16549
16635
  }
16550
16636
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingModalComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component });
16551
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileBookingModalComponent, isStandalone: true, selector: "ds-mobile-booking-modal", inputs: { facilityId: "facilityId", facilityTitle: "facilityTitle" }, viewQueries: [{ propertyName: "swiperComponent", first: true, predicate: DsMobileSwiperComponent, descendants: true }], ngImport: i0, template: `
16637
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileBookingModalComponent, isStandalone: true, selector: "ds-mobile-booking-modal", inputs: { facilityId: "facilityId", facilityTitle: "facilityTitle", daysAhead: "daysAhead" }, viewQueries: [{ propertyName: "swiperComponent", first: true, predicate: DsMobileSwiperComponent, descendants: true }], ngImport: i0, template: `
16552
16638
  <ds-mobile-modal-base
16553
16639
  headerTitle="Hvornår skal det være?"
16554
16640
  [showCloseButton]="true"
@@ -16557,6 +16643,16 @@ class DsMobileBookingModalComponent {
16557
16643
 
16558
16644
  <!-- Date Section -->
16559
16645
  <ds-mobile-section headline="Vælg dato">
16646
+ <ds-datepicker
16647
+ header-action
16648
+ [isDateDisabled]="dateDisabledFn()"
16649
+ (dateChange)="jumpToDate($event)">
16650
+ <span class="calendar-link">
16651
+ Vælg fra kalender
16652
+ <ds-icon name="remixArrowRightSLine" size="16px" />
16653
+ </span>
16654
+ </ds-datepicker>
16655
+
16560
16656
  <ds-mobile-swiper
16561
16657
  class="date-swiper"
16562
16658
  [slideWidth]="'auto'"
@@ -16610,7 +16706,7 @@ class DsMobileBookingModalComponent {
16610
16706
  </ds-button>
16611
16707
  </div>
16612
16708
  </ds-mobile-modal-base>
16613
- `, isInline: true, styles: [".date-item{display:flex;flex-direction:column;align-items:center;justify-content:center;width:80px;padding:12px 8px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent;gap:4px}.date-item .day-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:uppercase}.date-item .date-number{font-family:Brockmann,sans-serif;font-size:var(--font-size-xl, 20px);font-weight:600;color:var(--text-color-default-primary, #202227)}.date-item .month-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:capitalize}.date-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.date-item.selected .day-name,.date-item.selected .date-number,.date-item.selected .month-name{color:#fff}.date-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.date-item.disabled .day-name,.date-item.disabled .date-number,.date-item.disabled .month-name{color:var(--text-color-default-tertiary, #9a9aa2)}.date-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.time-slots-list{display:flex;flex-direction:column;gap:8px}.time-slot-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:16px 20px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.time-slot-item span{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:400;color:var(--text-color-default-primary, #202227)}.time-slot-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.time-slot-item.selected span{color:#fff;font-weight:500}.time-slot-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.time-slot-item.disabled span{color:var(--text-color-default-tertiary, #9a9aa2)}.time-slot-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.booking-confirm-action{width:100%;padding:16px 20px;background:var(--color-surface-primary, #ffffff);border-top:1px solid var(--color-border, #e5e5e5);box-sizing:border-box}.booking-confirm-action ::ng-deep ds-button{display:block;width:100%}.booking-confirm-action ::ng-deep ds-button button{width:100%;border-radius:100px;height:48px}::ng-deep .date-swiper .swiper-slide{width:auto!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }] });
16709
+ `, isInline: true, styles: [".date-item{display:flex;flex-direction:column;align-items:center;justify-content:center;width:80px;padding:12px 8px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent;gap:4px}.date-item .day-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:uppercase}.date-item .date-number{font-family:Brockmann,sans-serif;font-size:var(--font-size-xl, 20px);font-weight:600;color:var(--text-color-default-primary, #202227)}.date-item .month-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:capitalize}.date-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.date-item.selected .day-name,.date-item.selected .date-number,.date-item.selected .month-name{color:#fff}.date-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.date-item.disabled .day-name,.date-item.disabled .date-number,.date-item.disabled .month-name{color:var(--text-color-default-tertiary, #9a9aa2)}.date-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.time-slots-list{display:flex;flex-direction:column;gap:8px}.time-slot-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:16px 20px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.time-slot-item span{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:400;color:var(--text-color-default-primary, #202227)}.time-slot-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.time-slot-item.selected span{color:#fff;font-weight:500}.time-slot-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.time-slot-item.disabled span{color:var(--text-color-default-tertiary, #9a9aa2)}.time-slot-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.booking-confirm-action{width:100%;padding:16px 20px;background:var(--color-surface-primary, #ffffff);border-top:1px solid var(--color-border, #e5e5e5);box-sizing:border-box}.booking-confirm-action ::ng-deep ds-button{display:block;width:100%}.booking-confirm-action ::ng-deep ds-button button{width:100%;border-radius:100px;height:48px}::ng-deep .date-swiper .swiper-slide{width:auto!important}.calendar-link{display:flex;align-items:center;gap:2px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--color-accent, #5d5fef);cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;line-height:1}::ng-deep .cdk-overlay-container{z-index:20001}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsMobileModalBaseComponent, selector: "ds-mobile-modal-base", inputs: ["showHeader"] }, { kind: "component", type: DsMobileSectionComponent, selector: "ds-mobile-section", inputs: ["headline", "icon", "linkText", "padding", "gap", "contentGap", "showBorder", "overflow"], outputs: ["linkClick"] }, { kind: "component", type: DsMobileSwiperComponent, selector: "ds-mobile-swiper", inputs: ["slideWidth", "gap", "pagination", "autoHeight", "progressiveOpacity", "progressiveScale"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsButtonComponent, selector: "ds-button", inputs: ["variant", "size", "disabled", "loading", "pressed", "expanded", "leadingIcon", "trailingIcon", "ariaLabel", "iconOnly"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsDatepickerComponent, selector: "ds-datepicker", inputs: ["variant", "disabled", "placeholder", "ariaLabel", "ariaDescribedBy", "disableFutureDates", "disablePastDates", "isDateDisabled", "locale"], outputs: ["dateChange", "opened", "closed"] }] });
16614
16710
  }
16615
16711
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileBookingModalComponent, decorators: [{
16616
16712
  type: Component,
@@ -16620,7 +16716,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
16620
16716
  DsMobileSectionComponent,
16621
16717
  DsMobileSwiperComponent,
16622
16718
  DsIconComponent,
16623
- DsButtonComponent
16719
+ DsButtonComponent,
16720
+ DsDatepickerComponent
16624
16721
  ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
16625
16722
  <ds-mobile-modal-base
16626
16723
  headerTitle="Hvornår skal det være?"
@@ -16630,6 +16727,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
16630
16727
 
16631
16728
  <!-- Date Section -->
16632
16729
  <ds-mobile-section headline="Vælg dato">
16730
+ <ds-datepicker
16731
+ header-action
16732
+ [isDateDisabled]="dateDisabledFn()"
16733
+ (dateChange)="jumpToDate($event)">
16734
+ <span class="calendar-link">
16735
+ Vælg fra kalender
16736
+ <ds-icon name="remixArrowRightSLine" size="16px" />
16737
+ </span>
16738
+ </ds-datepicker>
16739
+
16633
16740
  <ds-mobile-swiper
16634
16741
  class="date-swiper"
16635
16742
  [slideWidth]="'auto'"
@@ -16683,11 +16790,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
16683
16790
  </ds-button>
16684
16791
  </div>
16685
16792
  </ds-mobile-modal-base>
16686
- `, styles: [".date-item{display:flex;flex-direction:column;align-items:center;justify-content:center;width:80px;padding:12px 8px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent;gap:4px}.date-item .day-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:uppercase}.date-item .date-number{font-family:Brockmann,sans-serif;font-size:var(--font-size-xl, 20px);font-weight:600;color:var(--text-color-default-primary, #202227)}.date-item .month-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:capitalize}.date-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.date-item.selected .day-name,.date-item.selected .date-number,.date-item.selected .month-name{color:#fff}.date-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.date-item.disabled .day-name,.date-item.disabled .date-number,.date-item.disabled .month-name{color:var(--text-color-default-tertiary, #9a9aa2)}.date-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.time-slots-list{display:flex;flex-direction:column;gap:8px}.time-slot-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:16px 20px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.time-slot-item span{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:400;color:var(--text-color-default-primary, #202227)}.time-slot-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.time-slot-item.selected span{color:#fff;font-weight:500}.time-slot-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.time-slot-item.disabled span{color:var(--text-color-default-tertiary, #9a9aa2)}.time-slot-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.booking-confirm-action{width:100%;padding:16px 20px;background:var(--color-surface-primary, #ffffff);border-top:1px solid var(--color-border, #e5e5e5);box-sizing:border-box}.booking-confirm-action ::ng-deep ds-button{display:block;width:100%}.booking-confirm-action ::ng-deep ds-button button{width:100%;border-radius:100px;height:48px}::ng-deep .date-swiper .swiper-slide{width:auto!important}\n"] }]
16793
+ `, styles: [".date-item{display:flex;flex-direction:column;align-items:center;justify-content:center;width:80px;padding:12px 8px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent;gap:4px}.date-item .day-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:uppercase}.date-item .date-number{font-family:Brockmann,sans-serif;font-size:var(--font-size-xl, 20px);font-weight:600;color:var(--text-color-default-primary, #202227)}.date-item .month-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs, 12px);font-weight:400;color:var(--text-color-default-secondary, #71727a);text-transform:capitalize}.date-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.date-item.selected .day-name,.date-item.selected .date-number,.date-item.selected .month-name{color:#fff}.date-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.date-item.disabled .day-name,.date-item.disabled .date-number,.date-item.disabled .month-name{color:var(--text-color-default-tertiary, #9a9aa2)}.date-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.time-slots-list{display:flex;flex-direction:column;gap:8px}.time-slot-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:16px 20px;border-radius:12px;border:1px solid var(--color-border, #e5e5e5);background:var(--color-surface-primary, #ffffff);cursor:pointer;transition:all .2s ease;-webkit-tap-highlight-color:transparent}.time-slot-item span{font-family:Brockmann,sans-serif;font-size:var(--font-size-base, 16px);font-weight:400;color:var(--text-color-default-primary, #202227)}.time-slot-item.selected{background:var(--color-accent, #5d5fef);border-color:var(--color-accent, #5d5fef)}.time-slot-item.selected span{color:#fff;font-weight:500}.time-slot-item.disabled{background:var(--color-background-neutral-secondary, #f5f5f5);border-color:var(--color-border, #e5e5e5);cursor:not-allowed;opacity:.6}.time-slot-item.disabled span{color:var(--text-color-default-tertiary, #9a9aa2)}.time-slot-item:not(.disabled):not(.selected):hover{border-color:var(--color-accent, #5d5fef);background:var(--color-background-neutral-secondary, #f5f5f5)}.booking-confirm-action{width:100%;padding:16px 20px;background:var(--color-surface-primary, #ffffff);border-top:1px solid var(--color-border, #e5e5e5);box-sizing:border-box}.booking-confirm-action ::ng-deep ds-button{display:block;width:100%}.booking-confirm-action ::ng-deep ds-button button{width:100%;border-radius:100px;height:48px}::ng-deep .date-swiper .swiper-slide{width:auto!important}.calendar-link{display:flex;align-items:center;gap:2px;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--color-accent, #5d5fef);cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;line-height:1}::ng-deep .cdk-overlay-container{z-index:20001}\n"] }]
16687
16794
  }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { facilityId: [{
16688
16795
  type: Input
16689
16796
  }], facilityTitle: [{
16690
16797
  type: Input
16798
+ }], daysAhead: [{
16799
+ type: Input
16691
16800
  }], swiperComponent: [{
16692
16801
  type: ViewChild,
16693
16802
  args: [DsMobileSwiperComponent]
@@ -16937,12 +17046,14 @@ class DsMobileBookingModalService extends BaseModalService {
16937
17046
  * @param facilityId The ID of the facility to book
16938
17047
  * @param facilityTitle The display name of the facility
16939
17048
  * @param facilityThumbnail Optional thumbnail image URL
17049
+ * @param daysAhead Number of days ahead available for selection (defaults to 14)
16940
17050
  * @returns Promise that resolves when the booking flow is complete
16941
17051
  */
16942
- async open(facilityId, facilityTitle, facilityThumbnail) {
17052
+ async open(facilityId, facilityTitle, facilityThumbnail, daysAhead) {
16943
17053
  const modal = await this.createModal(DsMobileBookingModalComponent, {
16944
17054
  facilityId,
16945
- facilityTitle
17055
+ facilityTitle,
17056
+ ...(daysAhead !== undefined && { daysAhead })
16946
17057
  }, { keyboardClose: true });
16947
17058
  await modal.present();
16948
17059
  const result = await modal.onWillDismiss();
@@ -17249,18 +17360,17 @@ class DsMobileFacilityCreationModalComponent {
17249
17360
  return;
17250
17361
  }
17251
17362
  try {
17252
- const image = await Camera.getPhoto({
17253
- quality: 90,
17254
- allowEditing: false,
17255
- resultType: CameraResultType.Uri,
17256
- source: CameraSource.Photos, // Only show photo library
17363
+ const result = await FilePicker.pickImages({
17364
+ limit: 1,
17257
17365
  });
17258
- if (image.webPath) {
17366
+ const image = result.files?.[0];
17367
+ if (image) {
17259
17368
  const newAttachment = {
17260
17369
  id: `photo-${Date.now()}`,
17261
- src: image.webPath,
17370
+ src: image.path ? Capacitor.convertFileSrc(image.path) : (image.blob ? URL.createObjectURL(image.blob) : ''),
17262
17371
  type: 'image',
17263
- name: `Photo ${this.attachments().length + 1}`,
17372
+ name: image.name || `Photo ${this.attachments().length + 1}`,
17373
+ size: this.formatFileSize(image.size ?? 0),
17264
17374
  };
17265
17375
  this.attachments.update((attachments) => [...attachments, newAttachment]);
17266
17376
  }
@@ -17431,8 +17541,8 @@ class DsMobileFacilityCreationModalComponent {
17431
17541
 
17432
17542
  <!-- List Items Section -->
17433
17543
  <ds-mobile-section [contentGap]="'0'">
17434
- <!-- Who can book -->
17435
- <ds-mobile-list-item
17544
+ <!-- Who can book - hidden until Propbinder API supports this field -->
17545
+ <!-- <ds-mobile-list-item
17436
17546
  [leadingSize]="'32px'"
17437
17547
  [showDivider]="true"
17438
17548
  [interactive]="true"
@@ -17445,7 +17555,7 @@ class DsMobileFacilityCreationModalComponent {
17445
17555
  <div class="detail-value">{{ whoCanBook() }}</div>
17446
17556
  </div>
17447
17557
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17448
- </ds-mobile-list-item>
17558
+ </ds-mobile-list-item> -->
17449
17559
 
17450
17560
  <!-- When can it be booked -->
17451
17561
  <ds-mobile-list-item
@@ -17466,7 +17576,7 @@ class DsMobileFacilityCreationModalComponent {
17466
17576
  <!-- Price -->
17467
17577
  <ds-mobile-list-item
17468
17578
  [leadingSize]="'32px'"
17469
- [showDivider]="true"
17579
+ [showDivider]="false"
17470
17580
  [interactive]="true"
17471
17581
  [showDesktopMoreButton]="false"
17472
17582
  [align]="'center'"
@@ -17479,8 +17589,8 @@ class DsMobileFacilityCreationModalComponent {
17479
17589
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17480
17590
  </ds-mobile-list-item>
17481
17591
 
17482
- <!-- Access requirements -->
17483
- <ds-mobile-list-item
17592
+ <!-- Access requirements - hidden until Propbinder API supports this field -->
17593
+ <!-- <ds-mobile-list-item
17484
17594
  [leadingSize]="'32px'"
17485
17595
  [showDivider]="false"
17486
17596
  [interactive]="true"
@@ -17493,7 +17603,7 @@ class DsMobileFacilityCreationModalComponent {
17493
17603
  <div class="detail-value">{{ accessRequirements() }}</div>
17494
17604
  </div>
17495
17605
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17496
- </ds-mobile-list-item>
17606
+ </ds-mobile-list-item> -->
17497
17607
  </ds-mobile-section>
17498
17608
 
17499
17609
  <!-- Fixed Bottom Container (Slides with keyboard) -->
@@ -17587,8 +17697,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
17587
17697
 
17588
17698
  <!-- List Items Section -->
17589
17699
  <ds-mobile-section [contentGap]="'0'">
17590
- <!-- Who can book -->
17591
- <ds-mobile-list-item
17700
+ <!-- Who can book - hidden until Propbinder API supports this field -->
17701
+ <!-- <ds-mobile-list-item
17592
17702
  [leadingSize]="'32px'"
17593
17703
  [showDivider]="true"
17594
17704
  [interactive]="true"
@@ -17601,7 +17711,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
17601
17711
  <div class="detail-value">{{ whoCanBook() }}</div>
17602
17712
  </div>
17603
17713
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17604
- </ds-mobile-list-item>
17714
+ </ds-mobile-list-item> -->
17605
17715
 
17606
17716
  <!-- When can it be booked -->
17607
17717
  <ds-mobile-list-item
@@ -17622,7 +17732,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
17622
17732
  <!-- Price -->
17623
17733
  <ds-mobile-list-item
17624
17734
  [leadingSize]="'32px'"
17625
- [showDivider]="true"
17735
+ [showDivider]="false"
17626
17736
  [interactive]="true"
17627
17737
  [showDesktopMoreButton]="false"
17628
17738
  [align]="'center'"
@@ -17635,8 +17745,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
17635
17745
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17636
17746
  </ds-mobile-list-item>
17637
17747
 
17638
- <!-- Access requirements -->
17639
- <ds-mobile-list-item
17748
+ <!-- Access requirements - hidden until Propbinder API supports this field -->
17749
+ <!-- <ds-mobile-list-item
17640
17750
  [leadingSize]="'32px'"
17641
17751
  [showDivider]="false"
17642
17752
  [interactive]="true"
@@ -17649,7 +17759,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
17649
17759
  <div class="detail-value">{{ accessRequirements() }}</div>
17650
17760
  </div>
17651
17761
  <ds-icon content-trailing name="remixArrowRightSLine" size="20px" color="tertiary" />
17652
- </ds-mobile-list-item>
17762
+ </ds-mobile-list-item> -->
17653
17763
  </ds-mobile-section>
17654
17764
 
17655
17765
  <!-- Fixed Bottom Container (Slides with keyboard) -->
@@ -18068,24 +18178,59 @@ var dsMobileWhoCanBookSheet = /*#__PURE__*/Object.freeze({
18068
18178
  * DsMobileWhenCanBookSheetComponent
18069
18179
  *
18070
18180
  * Bottom sheet for selecting when a facility can be booked (days, time range, duration).
18181
+ * "Vælg selv" opens a drum-roll IonPopover on mobile, or inline number inputs on desktop.
18071
18182
  */
18072
18183
  class DsMobileWhenCanBookSheetComponent {
18073
18184
  modalController;
18185
+ platformId = inject(PLATFORM_ID);
18186
+ // Platform
18187
+ isDesktop = signal(false, ...(ngDevMode ? [{ debugName: "isDesktop" }] : []));
18074
18188
  // State
18075
- selectedDays = signal(new Set(['Fre', 'Lør', 'Søn']), ...(ngDevMode ? [{ debugName: "selectedDays" }] : [])); // default to weekend
18189
+ selectedDays = signal(new Set(['Fre', 'Lør', 'Søn']), ...(ngDevMode ? [{ debugName: "selectedDays" }] : []));
18076
18190
  startTime = signal('09:00', ...(ngDevMode ? [{ debugName: "startTime" }] : []));
18077
18191
  endTime = signal('17:30', ...(ngDevMode ? [{ debugName: "endTime" }] : []));
18078
18192
  selectedDuration = signal('1 time', ...(ngDevMode ? [{ debugName: "selectedDuration" }] : []));
18193
+ /**
18194
+ * Maximum number of days available in the "Vælg selv" picker/inputs.
18195
+ * Defaults to 30 (one full month). Override via componentProps when opening the sheet.
18196
+ */
18197
+ maxDays = input(30, ...(ngDevMode ? [{ debugName: "maxDays" }] : []));
18198
+ // Custom duration state
18199
+ pickerOpen = signal(false, ...(ngDevMode ? [{ debugName: "pickerOpen" }] : []));
18200
+ customDays = signal(0, ...(ngDevMode ? [{ debugName: "customDays" }] : []));
18201
+ customHours = signal(1, ...(ngDevMode ? [{ debugName: "customHours" }] : []));
18202
+ customMinutes = signal(0, ...(ngDevMode ? [{ debugName: "customMinutes" }] : []));
18203
+ customDurationLabel = computed(() => {
18204
+ const d = this.customDays();
18205
+ const h = this.customHours();
18206
+ const m = this.customMinutes();
18207
+ const parts = [];
18208
+ if (d > 0)
18209
+ parts.push(`${d} dage`);
18210
+ if (h > 0)
18211
+ parts.push(`${h}t`);
18212
+ if (m > 0)
18213
+ parts.push(`${m}min`);
18214
+ return parts.join(' ');
18215
+ }, ...(ngDevMode ? [{ debugName: "customDurationLabel" }] : []));
18216
+ showInlineInputs = computed(() => this.selectedDuration() === 'Vælg selv' && this.isDesktop(), ...(ngDevMode ? [{ debugName: "showInlineInputs" }] : []));
18079
18217
  // Options
18080
18218
  days = ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn'];
18081
18219
  durations = [
18082
18220
  { value: '30 min', label: '30 min' },
18083
18221
  { value: '1 time', label: '1 time' },
18084
18222
  { value: '2 timer', label: '2 timer' },
18223
+ { value: 'Hele dagen', label: 'Hele dagen' },
18085
18224
  { value: 'Vælg selv', label: 'Vælg selv' }
18086
18225
  ];
18226
+ daysOptions = computed(() => Array.from({ length: this.maxDays() + 1 }, (_, i) => i), ...(ngDevMode ? [{ debugName: "daysOptions" }] : []));
18227
+ hours = Array.from({ length: 24 }, (_, i) => i);
18228
+ minuteOptions = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
18087
18229
  // Validation
18088
18230
  isValid = computed(() => {
18231
+ if (this.selectedDuration() === 'Vælg selv' && this.customDurationLabel() === '') {
18232
+ return false;
18233
+ }
18089
18234
  return this.selectedDays().size > 0 &&
18090
18235
  this.startTime() &&
18091
18236
  this.endTime();
@@ -18093,6 +18238,50 @@ class DsMobileWhenCanBookSheetComponent {
18093
18238
  constructor(modalController) {
18094
18239
  this.modalController = modalController;
18095
18240
  }
18241
+ ngOnInit() {
18242
+ if (isPlatformBrowser(this.platformId)) {
18243
+ this.isDesktop.set(window.innerWidth >= 768);
18244
+ window.addEventListener('resize', () => {
18245
+ this.isDesktop.set(window.innerWidth >= 768);
18246
+ });
18247
+ }
18248
+ }
18249
+ /**
18250
+ * Handle duration chip selection.
18251
+ * On mobile, opening "Vælg selv" triggers the IonPopover via its trigger id.
18252
+ * On desktop, it shows the inline inputs.
18253
+ */
18254
+ selectDuration(value) {
18255
+ this.selectedDuration.set(value);
18256
+ if (value === 'Vælg selv' && !this.isDesktop()) {
18257
+ this.pickerOpen.set(true);
18258
+ }
18259
+ }
18260
+ /**
18261
+ * Normalizes days/hours/minutes so values never overflow their units.
18262
+ * e.g. 0d 48t 0min → 2d 0t 0min; 0d 0t 75min → 0d 1t 15min.
18263
+ * Caps days at maxDays().
18264
+ */
18265
+ normalize() {
18266
+ const totalMinutes = this.customDays() * 24 * 60
18267
+ + this.customHours() * 60
18268
+ + this.customMinutes();
18269
+ const maxTotalMinutes = this.maxDays() * 24 * 60 + 23 * 60 + 59;
18270
+ const clamped = Math.max(0, Math.min(totalMinutes, maxTotalMinutes));
18271
+ const d = Math.floor(clamped / (24 * 60));
18272
+ const h = Math.floor((clamped % (24 * 60)) / 60);
18273
+ const m = clamped % 60;
18274
+ this.customDays.set(d);
18275
+ this.customHours.set(h);
18276
+ this.customMinutes.set(m);
18277
+ }
18278
+ /**
18279
+ * Called when the mobile picker popover is dismissed — normalize and commit.
18280
+ */
18281
+ onPickerDismiss() {
18282
+ this.normalize();
18283
+ this.pickerOpen.set(false);
18284
+ }
18096
18285
  /**
18097
18286
  * Toggle day selection
18098
18287
  */
@@ -18105,10 +18294,13 @@ class DsMobileWhenCanBookSheetComponent {
18105
18294
  * Confirm selection and dismiss with data
18106
18295
  */
18107
18296
  confirmSelection() {
18297
+ const duration = this.selectedDuration() === 'Vælg selv'
18298
+ ? this.customDurationLabel()
18299
+ : this.selectedDuration();
18108
18300
  const data = {
18109
18301
  days: Array.from(this.selectedDays()),
18110
18302
  timeRange: { start: this.startTime(), end: this.endTime() },
18111
- duration: this.selectedDuration()
18303
+ duration
18112
18304
  };
18113
18305
  this.modalController.dismiss({ value: data }, 'select');
18114
18306
  }
@@ -18119,7 +18311,7 @@ class DsMobileWhenCanBookSheetComponent {
18119
18311
  this.modalController.dismiss(null, 'cancel');
18120
18312
  }
18121
18313
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileWhenCanBookSheetComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component });
18122
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileWhenCanBookSheetComponent, isStandalone: true, selector: "ds-mobile-when-can-book-sheet", ngImport: i0, template: `
18314
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileWhenCanBookSheetComponent, isStandalone: true, selector: "ds-mobile-when-can-book-sheet", inputs: { maxDays: { classPropertyName: "maxDays", publicName: "maxDays", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
18123
18315
  <ds-mobile-bottom-sheet-wrapper>
18124
18316
  <!-- Header with back and done buttons -->
18125
18317
  <ds-mobile-bottom-sheet-header
@@ -18152,9 +18344,9 @@ class DsMobileWhenCanBookSheetComponent {
18152
18344
  <div class="section">
18153
18345
  <ds-label size="md" className="form-section-label">I tidsrummet</ds-label>
18154
18346
  <div class="time-inputs">
18155
- <ds-input-time [(ngModel)]="startTime"></ds-input-time>
18347
+ <ds-input-time size="lg" [(ngModel)]="startTime"></ds-input-time>
18156
18348
  <span class="separator">til</span>
18157
- <ds-input-time [(ngModel)]="endTime"></ds-input-time>
18349
+ <ds-input-time size="lg" [(ngModel)]="endTime"></ds-input-time>
18158
18350
  </div>
18159
18351
  </div>
18160
18352
 
@@ -18166,16 +18358,79 @@ class DsMobileWhenCanBookSheetComponent {
18166
18358
  <button
18167
18359
  type="button"
18168
18360
  class="duration-chip"
18361
+ [id]="duration.value === 'Vælg selv' ? 'vaelg-selv-chip' : null"
18169
18362
  [class.active]="selectedDuration() === duration.value"
18170
- (click)="selectedDuration.set(duration.value)">
18171
- {{ duration.label }}
18363
+ (click)="selectDuration(duration.value)">
18364
+ @if (duration.value === 'Vælg selv' && selectedDuration() === 'Vælg selv') {
18365
+ {{ customDurationLabel() || 'Vælg selv' }}
18366
+ } @else {
18367
+ {{ duration.label }}
18368
+ }
18172
18369
  </button>
18173
18370
  }
18174
18371
  </div>
18372
+
18373
+ <!-- Desktop: inline inputs shown when "Vælg selv" is active -->
18374
+ @if (showInlineInputs()) {
18375
+ <div class="custom-duration-inputs">
18376
+ <input
18377
+ type="number"
18378
+ class="duration-number-input"
18379
+ min="0"
18380
+ [ngModel]="customHours()"
18381
+ (ngModelChange)="customHours.set(+$event)"
18382
+ (blur)="normalize()"
18383
+ />
18384
+ <span class="duration-unit">timer</span>
18385
+ <input
18386
+ type="number"
18387
+ class="duration-number-input"
18388
+ min="0"
18389
+ max="59"
18390
+ [ngModel]="customMinutes()"
18391
+ (ngModelChange)="customMinutes.set(+$event)"
18392
+ (blur)="normalize()"
18393
+ />
18394
+ <span class="duration-unit">min</span>
18395
+ </div>
18396
+ }
18397
+
18398
+ <!-- Mobile: drum-roll popover anchored to the "Vælg selv" chip -->
18399
+ @if (!isDesktop()) {
18400
+ <ds-mobile-dropdown
18401
+ trigger="vaelg-selv-chip"
18402
+ [isOpen]="pickerOpen()"
18403
+ maxWidth="240px"
18404
+ position="above"
18405
+ (closed)="onPickerDismiss()">
18406
+ <ng-template #customContent>
18407
+ <ion-picker style="--background: transparent; --fade-background-rgb: transparent; --highlight-background: rgba(0, 0, 0, 0.05); --highlight-border-radius: 9999px;">
18408
+ <ion-picker-column
18409
+ [value]="customHours()"
18410
+ style="--padding-start: 4px; --padding-end: 4px;"
18411
+ (ionChange)="customHours.set(+($event.detail.value ?? customHours()))">
18412
+ <div slot="suffix" class="picker-suffix">t</div>
18413
+ @for (h of hours; track h) {
18414
+ <ion-picker-column-option [value]="h">{{ h }}</ion-picker-column-option>
18415
+ }
18416
+ </ion-picker-column>
18417
+ <ion-picker-column
18418
+ [value]="customMinutes()"
18419
+ style="--padding-start: 4px; --padding-end: 4px;"
18420
+ (ionChange)="customMinutes.set(+($event.detail.value ?? customMinutes()))">
18421
+ <div slot="suffix" class="picker-suffix">min</div>
18422
+ @for (m of minuteOptions; track m) {
18423
+ <ion-picker-column-option [value]="m">{{ m }}</ion-picker-column-option>
18424
+ }
18425
+ </ion-picker-column>
18426
+ </ion-picker>
18427
+ </ng-template>
18428
+ </ds-mobile-dropdown>
18429
+ }
18175
18430
  </div>
18176
18431
  </div>
18177
18432
  </ds-mobile-bottom-sheet-wrapper>
18178
- `, isInline: true, styles: [".form-content{padding:16px}.section{margin-bottom:24px}.section:last-child{margin-bottom:0}::ng-deep ds-label.form-section-label{display:block;margin-bottom:12px}::ng-deep .form-section-label{font-weight:500}.day-chips{display:flex;gap:8px;flex-wrap:wrap;width:100%}.day-chip{flex:1;min-width:0;padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.day-chip:hover{background:var(--color-bg-secondary)}.day-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.time-inputs{display:flex;align-items:center;gap:12px}.separator{color:var(--color-text-secondary);font-size:14px}.duration-chips{display:flex;gap:8px;flex-wrap:wrap}.duration-chip{padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.duration-chip:hover{background:var(--color-bg-secondary)}.duration-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsInputTimeComponent, selector: "ds-input-time", inputs: ["variant", "disabled", "readonly", "required", "clearable", "ghost", "ariaLabel", "ariaDescribedBy", "ariaLabelledBy"], outputs: ["valueChange"] }, { kind: "component", type: DsLabelComponent, selector: "ds-label", inputs: ["className", "for", "id", "size"] }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper" }, { kind: "component", type: DsMobileBottomSheetHeaderComponent, selector: "ds-mobile-bottom-sheet-header", inputs: ["title", "leftButtonLabel", "rightButtonLabel", "rightButtonDisabled"], outputs: ["leftButtonClick", "rightButtonClick"] }] });
18433
+ `, isInline: true, styles: [".form-content{padding:16px}.section{display:flex;flex-direction:column;gap:8px;margin-bottom:24px}.section:last-child{margin-bottom:0}::ng-deep ds-label.form-section-label{display:block;margin-bottom:12px}::ng-deep .form-section-label{font-weight:500}.day-chips{display:flex;gap:8px;flex-wrap:wrap;width:100%}.day-chip{flex:none;width:64px;padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.day-chip:hover{background:var(--color-bg-secondary)}.day-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.time-inputs{display:flex;align-items:center;gap:12px}.separator{color:var(--color-text-secondary);font-size:14px}.duration-chips{display:flex;gap:8px;flex-wrap:wrap}.duration-chip{padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.duration-chip:hover{background:var(--color-bg-secondary)}.duration-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.custom-duration-inputs{display:flex;align-items:center;gap:8px;margin-top:12px;animation:fadeSlideIn .15s ease}@keyframes fadeSlideIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.duration-number-input{width:64px;padding:8px 10px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;text-align:center;outline:none;transition:border-color .2s ease}.duration-number-input:focus{border-color:var(--color-accent)}.duration-number-input::-webkit-inner-spin-button,.duration-number-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.duration-number-input[type=number]{-moz-appearance:textfield}.duration-unit{color:var(--color-text-secondary);font-size:14px}.picker-suffix{margin-left:-24px;font-size:13px;color:var(--color-text-secondary)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsInputTimeComponent, selector: "ds-input-time", inputs: ["variant", "disabled", "readonly", "required", "clearable", "ghost", "size", "ariaLabel", "ariaDescribedBy", "ariaLabelledBy"], outputs: ["valueChange"] }, { kind: "component", type: DsLabelComponent, selector: "ds-label", inputs: ["className", "for", "id", "size"] }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper" }, { kind: "component", type: DsMobileBottomSheetHeaderComponent, selector: "ds-mobile-bottom-sheet-header", inputs: ["title", "leftButtonLabel", "rightButtonLabel", "rightButtonDisabled"], outputs: ["leftButtonClick", "rightButtonClick"] }, { kind: "component", type: DsMobileDropdownComponent, selector: "ds-mobile-dropdown", inputs: ["trigger", "keepFocusOn", "items", "isOpen", "position", "align", "maxHeight", "emptyMessage", "ariaLabel", "maxWidth"], outputs: ["itemSelected", "closed"] }, { kind: "component", type: IonPicker, selector: "ion-picker", inputs: ["mode"] }, { kind: "component", type: IonPickerColumn, selector: "ion-picker-column", inputs: ["color", "disabled", "mode", "value"] }, { kind: "component", type: IonPickerColumnOption, selector: "ion-picker-column-option", inputs: ["color", "disabled", "value"] }] });
18179
18434
  }
18180
18435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileWhenCanBookSheetComponent, decorators: [{
18181
18436
  type: Component,
@@ -18185,7 +18440,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
18185
18440
  DsInputTimeComponent,
18186
18441
  DsLabelComponent,
18187
18442
  DsMobileBottomSheetWrapperComponent,
18188
- DsMobileBottomSheetHeaderComponent
18443
+ DsMobileBottomSheetHeaderComponent,
18444
+ DsMobileDropdownComponent,
18445
+ IonPicker,
18446
+ IonPickerColumn,
18447
+ IonPickerColumnOption,
18189
18448
  ], template: `
18190
18449
  <ds-mobile-bottom-sheet-wrapper>
18191
18450
  <!-- Header with back and done buttons -->
@@ -18219,9 +18478,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
18219
18478
  <div class="section">
18220
18479
  <ds-label size="md" className="form-section-label">I tidsrummet</ds-label>
18221
18480
  <div class="time-inputs">
18222
- <ds-input-time [(ngModel)]="startTime"></ds-input-time>
18481
+ <ds-input-time size="lg" [(ngModel)]="startTime"></ds-input-time>
18223
18482
  <span class="separator">til</span>
18224
- <ds-input-time [(ngModel)]="endTime"></ds-input-time>
18483
+ <ds-input-time size="lg" [(ngModel)]="endTime"></ds-input-time>
18225
18484
  </div>
18226
18485
  </div>
18227
18486
 
@@ -18233,17 +18492,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
18233
18492
  <button
18234
18493
  type="button"
18235
18494
  class="duration-chip"
18495
+ [id]="duration.value === 'Vælg selv' ? 'vaelg-selv-chip' : null"
18236
18496
  [class.active]="selectedDuration() === duration.value"
18237
- (click)="selectedDuration.set(duration.value)">
18238
- {{ duration.label }}
18497
+ (click)="selectDuration(duration.value)">
18498
+ @if (duration.value === 'Vælg selv' && selectedDuration() === 'Vælg selv') {
18499
+ {{ customDurationLabel() || 'Vælg selv' }}
18500
+ } @else {
18501
+ {{ duration.label }}
18502
+ }
18239
18503
  </button>
18240
18504
  }
18241
18505
  </div>
18506
+
18507
+ <!-- Desktop: inline inputs shown when "Vælg selv" is active -->
18508
+ @if (showInlineInputs()) {
18509
+ <div class="custom-duration-inputs">
18510
+ <input
18511
+ type="number"
18512
+ class="duration-number-input"
18513
+ min="0"
18514
+ [ngModel]="customHours()"
18515
+ (ngModelChange)="customHours.set(+$event)"
18516
+ (blur)="normalize()"
18517
+ />
18518
+ <span class="duration-unit">timer</span>
18519
+ <input
18520
+ type="number"
18521
+ class="duration-number-input"
18522
+ min="0"
18523
+ max="59"
18524
+ [ngModel]="customMinutes()"
18525
+ (ngModelChange)="customMinutes.set(+$event)"
18526
+ (blur)="normalize()"
18527
+ />
18528
+ <span class="duration-unit">min</span>
18529
+ </div>
18530
+ }
18531
+
18532
+ <!-- Mobile: drum-roll popover anchored to the "Vælg selv" chip -->
18533
+ @if (!isDesktop()) {
18534
+ <ds-mobile-dropdown
18535
+ trigger="vaelg-selv-chip"
18536
+ [isOpen]="pickerOpen()"
18537
+ maxWidth="240px"
18538
+ position="above"
18539
+ (closed)="onPickerDismiss()">
18540
+ <ng-template #customContent>
18541
+ <ion-picker style="--background: transparent; --fade-background-rgb: transparent; --highlight-background: rgba(0, 0, 0, 0.05); --highlight-border-radius: 9999px;">
18542
+ <ion-picker-column
18543
+ [value]="customHours()"
18544
+ style="--padding-start: 4px; --padding-end: 4px;"
18545
+ (ionChange)="customHours.set(+($event.detail.value ?? customHours()))">
18546
+ <div slot="suffix" class="picker-suffix">t</div>
18547
+ @for (h of hours; track h) {
18548
+ <ion-picker-column-option [value]="h">{{ h }}</ion-picker-column-option>
18549
+ }
18550
+ </ion-picker-column>
18551
+ <ion-picker-column
18552
+ [value]="customMinutes()"
18553
+ style="--padding-start: 4px; --padding-end: 4px;"
18554
+ (ionChange)="customMinutes.set(+($event.detail.value ?? customMinutes()))">
18555
+ <div slot="suffix" class="picker-suffix">min</div>
18556
+ @for (m of minuteOptions; track m) {
18557
+ <ion-picker-column-option [value]="m">{{ m }}</ion-picker-column-option>
18558
+ }
18559
+ </ion-picker-column>
18560
+ </ion-picker>
18561
+ </ng-template>
18562
+ </ds-mobile-dropdown>
18563
+ }
18242
18564
  </div>
18243
18565
  </div>
18244
18566
  </ds-mobile-bottom-sheet-wrapper>
18245
- `, styles: [".form-content{padding:16px}.section{margin-bottom:24px}.section:last-child{margin-bottom:0}::ng-deep ds-label.form-section-label{display:block;margin-bottom:12px}::ng-deep .form-section-label{font-weight:500}.day-chips{display:flex;gap:8px;flex-wrap:wrap;width:100%}.day-chip{flex:1;min-width:0;padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.day-chip:hover{background:var(--color-bg-secondary)}.day-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.time-inputs{display:flex;align-items:center;gap:12px}.separator{color:var(--color-text-secondary);font-size:14px}.duration-chips{display:flex;gap:8px;flex-wrap:wrap}.duration-chip{padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.duration-chip:hover{background:var(--color-bg-secondary)}.duration-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}\n"] }]
18246
- }], ctorParameters: () => [{ type: i1.ModalController }] });
18567
+ `, styles: [".form-content{padding:16px}.section{display:flex;flex-direction:column;gap:8px;margin-bottom:24px}.section:last-child{margin-bottom:0}::ng-deep ds-label.form-section-label{display:block;margin-bottom:12px}::ng-deep .form-section-label{font-weight:500}.day-chips{display:flex;gap:8px;flex-wrap:wrap;width:100%}.day-chip{flex:none;width:64px;padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.day-chip:hover{background:var(--color-bg-secondary)}.day-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.time-inputs{display:flex;align-items:center;gap:12px}.separator{color:var(--color-text-secondary);font-size:14px}.duration-chips{display:flex;gap:8px;flex-wrap:wrap}.duration-chip{padding:10px 16px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;outline:none}.duration-chip:hover{background:var(--color-bg-secondary)}.duration-chip.active{background:var(--color-accent);color:#fff;border-color:var(--color-accent)}.custom-duration-inputs{display:flex;align-items:center;gap:8px;margin-top:12px;animation:fadeSlideIn .15s ease}@keyframes fadeSlideIn{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.duration-number-input{width:64px;padding:8px 10px;border:1px solid var(--border-color-default, #d1d5db);border-radius:8px;background:transparent;color:var(--color-text-primary);font-size:14px;font-weight:500;text-align:center;outline:none;transition:border-color .2s ease}.duration-number-input:focus{border-color:var(--color-accent)}.duration-number-input::-webkit-inner-spin-button,.duration-number-input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.duration-number-input[type=number]{-moz-appearance:textfield}.duration-unit{color:var(--color-text-secondary);font-size:14px}.picker-suffix{margin-left:-24px;font-size:13px;color:var(--color-text-secondary)}\n"] }]
18568
+ }], ctorParameters: () => [{ type: i1.ModalController }], propDecorators: { maxDays: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDays", required: false }] }] } });
18247
18569
 
18248
18570
  var dsMobileWhenCanBookSheet = /*#__PURE__*/Object.freeze({
18249
18571
  __proto__: null,
@@ -18294,7 +18616,7 @@ class DsMobilePriceSheetComponent {
18294
18616
  this.modalController.dismiss(null, 'cancel');
18295
18617
  }
18296
18618
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobilePriceSheetComponent, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component });
18297
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DsMobilePriceSheetComponent, isStandalone: true, selector: "ds-mobile-price-sheet", ngImport: i0, template: `
18619
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobilePriceSheetComponent, isStandalone: true, selector: "ds-mobile-price-sheet", ngImport: i0, template: `
18298
18620
  <ds-mobile-bottom-sheet-wrapper>
18299
18621
  <!-- Header with back and done buttons -->
18300
18622
  <ds-mobile-bottom-sheet-header
@@ -18307,29 +18629,33 @@ class DsMobilePriceSheetComponent {
18307
18629
  </ds-mobile-bottom-sheet-header>
18308
18630
 
18309
18631
  <div class="form-content">
18310
- <!-- Toggle for Gratis -->
18311
- <div class="toggle-row" (click)="toggleFree()">
18312
- <ds-label size="md" className="form-section-label">Gratis</ds-label>
18313
- <div class="toggle-switch" [class.active]="isFree()">
18314
- <div class="toggle-knob"></div>
18632
+ <div class="price-toggle-container">
18633
+ <!-- Toggle for Gratis -->
18634
+ <div class="toggle-row" (click)="toggleFree()">
18635
+ <ds-label size="md" className="form-section-label">Gratis</ds-label>
18636
+ <div class="toggle-switch" [class.active]="isFree()">
18637
+ <div class="toggle-knob"></div>
18638
+ </div>
18315
18639
  </div>
18316
- </div>
18317
18640
 
18318
- <!-- Custom price input -->
18319
- <div class="section">
18320
- <ds-label size="md" className="form-section-label">Hvad skal det koste?</ds-label>
18321
- <ds-input
18322
- type="number"
18323
- [(ngModel)]="customPrice"
18324
- [disabled]="isFree()"
18325
- placeholder="75"
18326
- [suffix]="'kr. per booking'"
18327
- class="price-input">
18328
- </ds-input>
18641
+ <!-- Custom price input inside same container -->
18642
+ @if (!isFree()) {
18643
+ <div class="price-input-row" (click)="$event.stopPropagation()">
18644
+ <ds-label size="md" className="form-section-label">Hvad skal det koste?</ds-label>
18645
+ <ds-input
18646
+ type="number"
18647
+ size="lg"
18648
+ [(ngModel)]="customPrice"
18649
+ placeholder="75"
18650
+ [suffix]="'kr. per booking'"
18651
+ class="price-input">
18652
+ </ds-input>
18653
+ </div>
18654
+ }
18329
18655
  </div>
18330
18656
  </div>
18331
18657
  </ds-mobile-bottom-sheet-wrapper>
18332
- `, isInline: true, styles: [".form-content{padding:16px}.toggle-row{display:flex;justify-content:space-between;align-items:center;padding:16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:12px;margin-bottom:24px;cursor:pointer;transition:background .2s ease}.toggle-row:hover{background:var(--color-background-neutral-secondary-hover, #ebebeb)}::ng-deep ds-label.form-section-label{display:block;margin-bottom:8px}::ng-deep .form-section-label{font-weight:500}.toggle-switch{width:51px;height:31px;background:var(--color-border-default);border-radius:16px;position:relative;transition:background .3s ease;cursor:pointer}.toggle-switch.active{background:var(--color-accent)}.toggle-knob{width:27px;height:27px;background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:transform .3s ease;box-shadow:0 2px 4px #0000001a}.toggle-switch.active .toggle-knob{transform:translate(20px)}.section{margin-bottom:24px}::ng-deep .price-input .ds-input__field{height:48px!important;min-height:48px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsInputComponent, selector: "ds-input", inputs: ["variant", "type", "placeholder", "disabled", "readonly", "required", "clearable", "ghost", "leadingIcon", "trailingIcon", "prefix", "suffix", "format", "align", "ariaLabel", "ariaDescribedBy", "ariaLabelledBy"], outputs: ["valueChange", "focused", "blurred"] }, { kind: "component", type: DsLabelComponent, selector: "ds-label", inputs: ["className", "for", "id", "size"] }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper" }, { kind: "component", type: DsMobileBottomSheetHeaderComponent, selector: "ds-mobile-bottom-sheet-header", inputs: ["title", "leftButtonLabel", "rightButtonLabel", "rightButtonDisabled"], outputs: ["leftButtonClick", "rightButtonClick"] }] });
18658
+ `, isInline: true, styles: [".form-content{padding:16px}.price-toggle-container{display:flex;flex-direction:column;background:var(--color-background-neutral-primary, #f5f5f5);border:1px solid var(--border-color-default);border-radius:12px;margin-bottom:24px;overflow:hidden}.toggle-row{display:flex;justify-content:space-between;align-items:center;padding:16px;cursor:pointer;transition:background .2s ease}.toggle-row:hover{background:var(--color-background-neutral-primary-hover, #ebebeb)}::ng-deep ds-label.form-section-label{display:block;margin-bottom:8px}::ng-deep .form-section-label{font-weight:500}.toggle-switch{width:51px;height:32px;background:var(--color-background-neutral-tertiary);border-radius:16px;position:relative;transition:background .3s ease;cursor:pointer}.toggle-switch.active{background:var(--color-accent)}.toggle-knob{width:27px;height:27px;background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:transform .3s ease;box-shadow:0 2px 4px #0000001a}.toggle-switch.active .toggle-knob{transform:translate(20px)}.price-input-row{display:flex;flex-direction:column;gap:8px;padding:16px;border-top:1px solid var(--border-color-default)}::ng-deep .price-input .ds-input__field{height:48px!important;min-height:48px!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DsInputComponent, selector: "ds-input", inputs: ["variant", "type", "placeholder", "disabled", "readonly", "required", "clearable", "ghost", "size", "leadingIcon", "trailingIcon", "prefix", "suffix", "format", "align", "ariaLabel", "ariaDescribedBy", "ariaLabelledBy"], outputs: ["valueChange", "focused", "blurred"] }, { kind: "component", type: DsLabelComponent, selector: "ds-label", inputs: ["className", "for", "id", "size"] }, { kind: "component", type: DsMobileBottomSheetWrapperComponent, selector: "ds-mobile-bottom-sheet-wrapper" }, { kind: "component", type: DsMobileBottomSheetHeaderComponent, selector: "ds-mobile-bottom-sheet-header", inputs: ["title", "leftButtonLabel", "rightButtonLabel", "rightButtonDisabled"], outputs: ["leftButtonClick", "rightButtonClick"] }] });
18333
18659
  }
18334
18660
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobilePriceSheetComponent, decorators: [{
18335
18661
  type: Component,
@@ -18353,29 +18679,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
18353
18679
  </ds-mobile-bottom-sheet-header>
18354
18680
 
18355
18681
  <div class="form-content">
18356
- <!-- Toggle for Gratis -->
18357
- <div class="toggle-row" (click)="toggleFree()">
18358
- <ds-label size="md" className="form-section-label">Gratis</ds-label>
18359
- <div class="toggle-switch" [class.active]="isFree()">
18360
- <div class="toggle-knob"></div>
18682
+ <div class="price-toggle-container">
18683
+ <!-- Toggle for Gratis -->
18684
+ <div class="toggle-row" (click)="toggleFree()">
18685
+ <ds-label size="md" className="form-section-label">Gratis</ds-label>
18686
+ <div class="toggle-switch" [class.active]="isFree()">
18687
+ <div class="toggle-knob"></div>
18688
+ </div>
18361
18689
  </div>
18362
- </div>
18363
18690
 
18364
- <!-- Custom price input -->
18365
- <div class="section">
18366
- <ds-label size="md" className="form-section-label">Hvad skal det koste?</ds-label>
18367
- <ds-input
18368
- type="number"
18369
- [(ngModel)]="customPrice"
18370
- [disabled]="isFree()"
18371
- placeholder="75"
18372
- [suffix]="'kr. per booking'"
18373
- class="price-input">
18374
- </ds-input>
18691
+ <!-- Custom price input inside same container -->
18692
+ @if (!isFree()) {
18693
+ <div class="price-input-row" (click)="$event.stopPropagation()">
18694
+ <ds-label size="md" className="form-section-label">Hvad skal det koste?</ds-label>
18695
+ <ds-input
18696
+ type="number"
18697
+ size="lg"
18698
+ [(ngModel)]="customPrice"
18699
+ placeholder="75"
18700
+ [suffix]="'kr. per booking'"
18701
+ class="price-input">
18702
+ </ds-input>
18703
+ </div>
18704
+ }
18375
18705
  </div>
18376
18706
  </div>
18377
18707
  </ds-mobile-bottom-sheet-wrapper>
18378
- `, styles: [".form-content{padding:16px}.toggle-row{display:flex;justify-content:space-between;align-items:center;padding:16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:12px;margin-bottom:24px;cursor:pointer;transition:background .2s ease}.toggle-row:hover{background:var(--color-background-neutral-secondary-hover, #ebebeb)}::ng-deep ds-label.form-section-label{display:block;margin-bottom:8px}::ng-deep .form-section-label{font-weight:500}.toggle-switch{width:51px;height:31px;background:var(--color-border-default);border-radius:16px;position:relative;transition:background .3s ease;cursor:pointer}.toggle-switch.active{background:var(--color-accent)}.toggle-knob{width:27px;height:27px;background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:transform .3s ease;box-shadow:0 2px 4px #0000001a}.toggle-switch.active .toggle-knob{transform:translate(20px)}.section{margin-bottom:24px}::ng-deep .price-input .ds-input__field{height:48px!important;min-height:48px!important}\n"] }]
18708
+ `, styles: [".form-content{padding:16px}.price-toggle-container{display:flex;flex-direction:column;background:var(--color-background-neutral-primary, #f5f5f5);border:1px solid var(--border-color-default);border-radius:12px;margin-bottom:24px;overflow:hidden}.toggle-row{display:flex;justify-content:space-between;align-items:center;padding:16px;cursor:pointer;transition:background .2s ease}.toggle-row:hover{background:var(--color-background-neutral-primary-hover, #ebebeb)}::ng-deep ds-label.form-section-label{display:block;margin-bottom:8px}::ng-deep .form-section-label{font-weight:500}.toggle-switch{width:51px;height:32px;background:var(--color-background-neutral-tertiary);border-radius:16px;position:relative;transition:background .3s ease;cursor:pointer}.toggle-switch.active{background:var(--color-accent)}.toggle-knob{width:27px;height:27px;background:#fff;border-radius:50%;position:absolute;top:2px;left:2px;transition:transform .3s ease;box-shadow:0 2px 4px #0000001a}.toggle-switch.active .toggle-knob{transform:translate(20px)}.price-input-row{display:flex;flex-direction:column;gap:8px;padding:16px;border-top:1px solid var(--border-color-default)}::ng-deep .price-input .ds-input__field{height:48px!important;min-height:48px!important}\n"] }]
18379
18709
  }], ctorParameters: () => [{ type: i1.ModalController }] });
18380
18710
 
18381
18711
  var dsMobilePriceSheet = /*#__PURE__*/Object.freeze({
@@ -18576,25 +18906,6 @@ class DsMobileFacilityDetailModalComponent {
18576
18906
  </div>
18577
18907
  </ds-mobile-swiper>
18578
18908
 
18579
- @if (facilityData.requirements || facilityData.bookingType) {
18580
- <div class="info-items-container">
18581
- <h2 class="section-headline">Ting, du skal vide</h2>
18582
- @if (facilityData.requirements) {
18583
- @for (requirement of facilityData.requirements; track requirement) {
18584
- <div class="info-item">
18585
- <ds-icon name="remixKeyLine" size="16px" color="--text-color-default-secondary" />
18586
- <span>{{ requirement }}</span>
18587
- </div>
18588
- }
18589
- }
18590
- @if (facilityData.bookingType) {
18591
- <div class="info-item">
18592
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="--text-color-default-secondary" />
18593
- <span>{{ facilityData.bookingType }}</span>
18594
- </div>
18595
- }
18596
- </div>
18597
- }
18598
18909
  </ds-mobile-section>
18599
18910
  }
18600
18911
 
@@ -18658,25 +18969,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
18658
18969
  </div>
18659
18970
  </ds-mobile-swiper>
18660
18971
 
18661
- @if (facilityData.requirements || facilityData.bookingType) {
18662
- <div class="info-items-container">
18663
- <h2 class="section-headline">Ting, du skal vide</h2>
18664
- @if (facilityData.requirements) {
18665
- @for (requirement of facilityData.requirements; track requirement) {
18666
- <div class="info-item">
18667
- <ds-icon name="remixKeyLine" size="16px" color="--text-color-default-secondary" />
18668
- <span>{{ requirement }}</span>
18669
- </div>
18670
- }
18671
- }
18672
- @if (facilityData.bookingType) {
18673
- <div class="info-item">
18674
- <ds-icon name="remixCheckboxCircleLine" size="16px" color="--text-color-default-secondary" />
18675
- <span>{{ facilityData.bookingType }}</span>
18676
- </div>
18677
- }
18678
- </div>
18679
- }
18680
18972
  </ds-mobile-section>
18681
18973
  }
18682
18974
 
@@ -21840,11 +22132,91 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
21840
22132
  args: ['pageComponent']
21841
22133
  }] } });
21842
22134
 
22135
+ class TrackingPermissionService {
22136
+ trackingPromptRequestedKey = 'tracking_prompt_requested_v1';
22137
+ platform = inject(Platform);
22138
+ trackingSettingsReminder = signal(false, ...(ngDevMode ? [{ debugName: "trackingSettingsReminder" }] : []));
22139
+ showTrackingSettingsReminder = this.trackingSettingsReminder.asReadonly();
22140
+ async requestOnFirstHomeEntry() {
22141
+ if (!this.isNativeIos()) {
22142
+ this.trackingSettingsReminder.set(false);
22143
+ return;
22144
+ }
22145
+ if (this.hasRequestedTrackingPrompt()) {
22146
+ await this.refreshTrackingStatus();
22147
+ return;
22148
+ }
22149
+ try {
22150
+ const { status } = await AppTrackingTransparency.getStatus();
22151
+ if (status === 'notDetermined') {
22152
+ await AppTrackingTransparency.requestPermission();
22153
+ }
22154
+ }
22155
+ catch (error) {
22156
+ console.log('Unable to request app tracking permission:', error);
22157
+ }
22158
+ finally {
22159
+ this.setTrackingPromptRequested();
22160
+ await this.refreshTrackingStatus();
22161
+ }
22162
+ }
22163
+ async getTrackingStatus() {
22164
+ if (!this.isNativeIos()) {
22165
+ return null;
22166
+ }
22167
+ try {
22168
+ const { status } = await AppTrackingTransparency.getStatus();
22169
+ return status;
22170
+ }
22171
+ catch (error) {
22172
+ console.log('Unable to read app tracking status:', error);
22173
+ return null;
22174
+ }
22175
+ }
22176
+ async openAppSettings() {
22177
+ if (!this.isNativeIos()) {
22178
+ return;
22179
+ }
22180
+ // iOS deep link for opening this app's system settings page.
22181
+ window.location.href = 'app-settings:';
22182
+ }
22183
+ shouldShowSettingsReminder() {
22184
+ return this.trackingSettingsReminder();
22185
+ }
22186
+ async refreshTrackingStatus() {
22187
+ if (!this.isNativeIos()) {
22188
+ this.trackingSettingsReminder.set(false);
22189
+ return;
22190
+ }
22191
+ const status = await this.getTrackingStatus();
22192
+ const hasRequested = this.hasRequestedTrackingPrompt();
22193
+ this.trackingSettingsReminder.set(Boolean(hasRequested && status && status !== 'authorized'));
22194
+ }
22195
+ isNativeIos() {
22196
+ return this.platform.is('ios') && this.platform.is('capacitor');
22197
+ }
22198
+ hasRequestedTrackingPrompt() {
22199
+ return localStorage.getItem(this.trackingPromptRequestedKey) === '1';
22200
+ }
22201
+ setTrackingPromptRequested() {
22202
+ localStorage.setItem(this.trackingPromptRequestedKey, '1');
22203
+ }
22204
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TrackingPermissionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
22205
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TrackingPermissionService, providedIn: 'root' });
22206
+ }
22207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: TrackingPermissionService, decorators: [{
22208
+ type: Injectable,
22209
+ args: [{
22210
+ providedIn: 'root',
22211
+ }]
22212
+ }] });
22213
+
21843
22214
  class MobileHomePageComponent {
21844
22215
  router;
21845
22216
  userService;
21846
22217
  postsService;
21847
22218
  postModal;
22219
+ trackingPermissionService;
21848
22220
  pageComponent;
21849
22221
  // Get recent posts from PostsService - exclude pinned post (post-4) and limit to 3
21850
22222
  recentPosts = computed(() => this.postsService.posts()
@@ -21878,13 +22250,17 @@ class MobileHomePageComponent {
21878
22250
  openInquiries = computed(() => this.allInquiries()
21879
22251
  .filter(inquiry => inquiry.status === 'open')
21880
22252
  .slice(0, 3), ...(ngDevMode ? [{ debugName: "openInquiries" }] : []));
21881
- constructor(router, userService, postsService, postModal) {
22253
+ constructor(router, userService, postsService, postModal, trackingPermissionService) {
21882
22254
  this.router = router;
21883
22255
  this.userService = userService;
21884
22256
  this.postsService = postsService;
21885
22257
  this.postModal = postModal;
22258
+ this.trackingPermissionService = trackingPermissionService;
21886
22259
  console.log('MobileHomePageComponent constructor');
21887
22260
  }
22261
+ ngOnInit() {
22262
+ void this.trackingPermissionService.requestOnFirstHomeEntry();
22263
+ }
21888
22264
  handleRefresh(event) {
21889
22265
  console.log('Pull-to-refresh triggered');
21890
22266
  setTimeout(() => {
@@ -21921,7 +22297,7 @@ class MobileHomePageComponent {
21921
22297
  console.log('Navigating to inquiries page');
21922
22298
  this.router.navigate(['/inquiries']);
21923
22299
  }
21924
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileHomePageComponent, deps: [{ token: i1$4.Router }, { token: UserService }, { token: PostsService }, { token: DsMobilePostDetailModalService }], target: i0.ɵɵFactoryTarget.Component });
22300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MobileHomePageComponent, deps: [{ token: i1$4.Router }, { token: UserService }, { token: PostsService }, { token: DsMobilePostDetailModalService }, { token: TrackingPermissionService }], target: i0.ɵɵFactoryTarget.Component });
21925
22301
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MobileHomePageComponent, isStandalone: true, selector: "app-home-page", viewQueries: [{ propertyName: "pageComponent", first: true, predicate: ["pageComponent"], descendants: true }], ngImport: i0, template: `
21926
22302
  <ds-mobile-page-main
21927
22303
  #pageComponent
@@ -22173,7 +22549,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
22173
22549
  </ds-mobile-section>
22174
22550
  </ds-mobile-page-main>
22175
22551
  `, styles: [".posts-list,.inquiries-list{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;text-align:center}.empty-state ds-button{display:block;margin-top:16px}.empty-state ds-button::ng-deep .btn{width:100%;border-radius:9999px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin-top:-16px;z-index:4}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"] }]
22176
- }], ctorParameters: () => [{ type: i1$4.Router }, { type: UserService }, { type: PostsService }, { type: DsMobilePostDetailModalService }], propDecorators: { pageComponent: [{
22552
+ }], ctorParameters: () => [{ type: i1$4.Router }, { type: UserService }, { type: PostsService }, { type: DsMobilePostDetailModalService }, { type: TrackingPermissionService }], propDecorators: { pageComponent: [{
22177
22553
  type: ViewChild,
22178
22554
  args: ['pageComponent']
22179
22555
  }] } });
@@ -24066,18 +24442,80 @@ class MobileTabsExampleComponent {
24066
24442
  userService;
24067
24443
  router;
24068
24444
  whitelabelDemoModal = inject(WhitelabelDemoModalService);
24445
+ trackingPermissionService = inject(TrackingPermissionService);
24446
+ trackedProfileMenuItems = computed(() => {
24447
+ const accountActions = [
24448
+ {
24449
+ action: 'profile',
24450
+ title: 'Min profil',
24451
+ icon: 'remixUser3Line',
24452
+ destructive: false,
24453
+ },
24454
+ {
24455
+ action: 'settings',
24456
+ title: 'Indstillinger',
24457
+ icon: 'remixSettings3Line',
24458
+ destructive: false,
24459
+ },
24460
+ {
24461
+ action: 'appearance',
24462
+ title: 'Udseende',
24463
+ icon: 'remixPaletteLine',
24464
+ destructive: false,
24465
+ },
24466
+ ];
24467
+ if (this.trackingPermissionService.shouldShowSettingsReminder()) {
24468
+ accountActions.push({
24469
+ action: 'tracking-settings',
24470
+ title: 'Sporingsindstillinger',
24471
+ icon: 'remixSettings3Line',
24472
+ destructive: false,
24473
+ });
24474
+ }
24475
+ return [
24476
+ {
24477
+ actions: accountActions,
24478
+ },
24479
+ {
24480
+ actions: [
24481
+ {
24482
+ action: 'language',
24483
+ title: 'Sprog',
24484
+ subtitle: 'Dansk',
24485
+ flagIcon: '/Assets/country-flags/denmark.svg',
24486
+ icon: 'remixGlobalLine',
24487
+ destructive: false,
24488
+ showChevron: true,
24489
+ },
24490
+ ],
24491
+ },
24492
+ {
24493
+ actions: [
24494
+ {
24495
+ action: 'logout',
24496
+ title: 'Log ud',
24497
+ icon: 'remixLogoutBoxLine',
24498
+ destructive: true,
24499
+ },
24500
+ ],
24501
+ },
24502
+ ];
24503
+ }, ...(ngDevMode ? [{ debugName: "trackedProfileMenuItems" }] : []));
24069
24504
  constructor(userService, router) {
24070
24505
  this.userService = userService;
24071
24506
  this.router = router;
24072
24507
  console.log('MobileTabsExampleComponent constructor');
24508
+ effect(() => {
24509
+ this.userService.setProfileMenuItems(this.trackedProfileMenuItems());
24510
+ });
24073
24511
  }
24074
24512
  ngOnInit() {
24075
24513
  console.log('MobileTabsExampleComponent ngOnInit');
24076
24514
  // Configure user avatar globally - this is now the single source of truth
24077
24515
  this.userService.setAvatarInitials('LM');
24078
24516
  this.userService.setAvatarType('initials');
24079
- // Set profile menu items globally - this will be used by both tab bar and page-main components
24080
- this.userService.setProfileMenuItems(this.profileMenuItems);
24517
+ // Initial status refresh ensures menu reflects past ATT choice.
24518
+ void this.trackingPermissionService.refreshTrackingStatus();
24081
24519
  }
24082
24520
  tabs = [
24083
24521
  {
@@ -24123,53 +24561,7 @@ class MobileTabsExampleComponent {
24123
24561
  * throughout the entire application.
24124
24562
  */
24125
24563
  get profileMenuItems() {
24126
- return [
24127
- {
24128
- actions: [
24129
- {
24130
- action: 'profile',
24131
- title: 'Min profil',
24132
- icon: 'remixUser3Line',
24133
- destructive: false,
24134
- },
24135
- {
24136
- action: 'settings',
24137
- title: 'Indstillinger',
24138
- icon: 'remixSettings3Line',
24139
- destructive: false,
24140
- },
24141
- {
24142
- action: 'appearance',
24143
- title: 'Udseende',
24144
- icon: 'remixPaletteLine',
24145
- destructive: false,
24146
- },
24147
- ],
24148
- },
24149
- {
24150
- actions: [
24151
- {
24152
- action: 'language',
24153
- title: 'Sprog',
24154
- subtitle: 'Dansk',
24155
- flagIcon: '/Assets/country-flags/denmark.svg',
24156
- icon: 'remixGlobalLine',
24157
- destructive: false,
24158
- showChevron: true,
24159
- },
24160
- ],
24161
- },
24162
- {
24163
- actions: [
24164
- {
24165
- action: 'logout',
24166
- title: 'Log ud',
24167
- icon: 'remixLogoutBoxLine',
24168
- destructive: true,
24169
- },
24170
- ],
24171
- },
24172
- ];
24564
+ return this.trackedProfileMenuItems();
24173
24565
  }
24174
24566
  /**
24175
24567
  * Handle profile menu action selection.
@@ -24481,6 +24873,7 @@ class MobileBookingPageComponent {
24481
24873
  [bookingTime]="booking.bookingTime || ''"
24482
24874
  [align]="'center'"
24483
24875
  [clickable]="true"
24876
+ [enableLongPress]="false"
24484
24877
  [showChevron]="false"
24485
24878
  (bookingClick)="openFacilityDetail(booking.id)"
24486
24879
  />
@@ -24588,6 +24981,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
24588
24981
  [bookingTime]="booking.bookingTime || ''"
24589
24982
  [align]="'center'"
24590
24983
  [clickable]="true"
24984
+ [enableLongPress]="false"
24591
24985
  [showChevron]="false"
24592
24986
  (bookingClick)="openFacilityDetail(booking.id)"
24593
24987
  />