@propbinder/mobile-design 0.2.14 → 0.2.16

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.
package/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { AfterViewInit, OnInit, ElementRef, Injector, TemplateRef, OnDestroy, EventEmitter, ApplicationRef, EnvironmentInjector, Type, AfterContentInit, ChangeDetectorRef } from '@angular/core';
2
+ import { OnDestroy, AfterViewInit, OnInit, ElementRef, Injector, TemplateRef, EventEmitter, ApplicationRef, EnvironmentInjector, Type, AfterContentInit, ChangeDetectorRef } from '@angular/core';
3
3
  import { ModalController, IonContent, NavController, GestureController, ModalOptions as ModalOptions$1 } from '@ionic/angular/standalone';
4
+ import { Style } from '@capacitor/status-bar';
4
5
  import { ImpactStyle } from '@capacitor/haptics';
5
6
  import { DsTextareaComponent } from '@propbinder/design-system';
6
7
  import { ControlValueAccessor } from '@angular/forms';
8
+ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
7
9
  import { Router, ActivatedRoute } from '@angular/router';
8
10
  import * as rxjs from 'rxjs';
9
11
  import * as _propbinder_mobile_design from '@propbinder/mobile-design';
@@ -17,20 +19,29 @@ import { Animation } from '@ionic/angular';
17
19
  * - 'full' - 100% width (no max)
18
20
  */
19
21
  type ContentWidth = 'narrow' | 'standard' | 'wide' | 'full';
22
+ /**
23
+ * Network status type
24
+ */
25
+ type NetworkStatus = 'online' | 'offline' | 'unknown';
20
26
  /**
21
27
  * MobilePageBase
22
28
  *
23
29
  * Shared base class for mobile page components (ds-mobile-page-main, ds-mobile-page-details).
24
- * Provides consistent content width control across all page types.
30
+ * Provides consistent content width control and network status monitoring across all page types.
25
31
  *
26
32
  * **Padding Strategy:**
27
33
  * - All pages use 20px horizontal padding globally
28
34
  * - For tappable lists, use negative margins (e.g., margin: 0 -8px) to create full-width sections
29
35
  * - This approach simplifies padding management and provides consistency
30
36
  *
37
+ * **Network Monitoring:**
38
+ * - Tracks online/offline status via Capacitor Network plugin (native) or browser API (web)
39
+ * - Exposes `isOffline()` computed signal for easy consumption by child components
40
+ * - Pages can use this to conditionally disable features or show offline indicators
41
+ *
31
42
  * @internal This is a base class and should not be used directly.
32
43
  */
33
- declare abstract class MobilePageBase {
44
+ declare abstract class MobilePageBase implements OnDestroy {
34
45
  /**
35
46
  * Maximum content width (desktop only)
36
47
  *
@@ -61,6 +72,68 @@ declare abstract class MobilePageBase {
61
72
  * @internal
62
73
  */
63
74
  protected maxWidthValue: _angular_core.Signal<string>;
75
+ /**
76
+ * Network status signal
77
+ * Tracks current online/offline state
78
+ *
79
+ * @internal
80
+ */
81
+ protected networkStatus: _angular_core.WritableSignal<NetworkStatus>;
82
+ /**
83
+ * Is the device currently offline?
84
+ * Public computed signal for consumption by child components and pages
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // In a page component
89
+ * @ViewChild(DsMobilePageMainComponent) pageComponent!: DsMobilePageMainComponent;
90
+ *
91
+ * get isOffline() {
92
+ * return this.pageComponent?.isOffline();
93
+ * }
94
+ * ```
95
+ */
96
+ isOffline: _angular_core.Signal<boolean>;
97
+ /**
98
+ * Is the device currently online?
99
+ * Public computed signal for consumption by child components and pages
100
+ */
101
+ isOnline: _angular_core.Signal<boolean>;
102
+ /**
103
+ * Network listener ID for Capacitor Network plugin
104
+ * Used to clean up listener on destroy
105
+ *
106
+ * @internal
107
+ */
108
+ private networkListenerId?;
109
+ /**
110
+ * Browser API event handlers
111
+ * Stored as class properties for proper cleanup
112
+ *
113
+ * @internal
114
+ */
115
+ private handleOnline;
116
+ private handleOffline;
117
+ /**
118
+ * Initialize network status monitoring
119
+ * Uses Capacitor Network plugin for native apps, browser API for web
120
+ *
121
+ * @param isNative - Whether running on native platform (iOS/Android)
122
+ * @internal Called by child components in ngAfterViewInit
123
+ */
124
+ protected initNetworkMonitoring(isNative: boolean): Promise<void>;
125
+ /**
126
+ * Initialize browser-based network monitoring
127
+ * Uses navigator.onLine and window events
128
+ *
129
+ * @internal
130
+ */
131
+ private initBrowserNetworkMonitoring;
132
+ /**
133
+ * Cleanup network monitoring listeners
134
+ * Called automatically on component destroy
135
+ */
136
+ ngOnDestroy(): void;
64
137
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobilePageBase, never>;
65
138
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MobilePageBase, never, never, { "contentWidth": { "alias": "contentWidth"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
66
139
  }
@@ -273,6 +346,7 @@ declare class DsMobilePostCreateBottomSheetComponent implements AfterViewInit, O
273
346
  private elementRef;
274
347
  textareaInput?: ElementRef<HTMLTextAreaElement>;
275
348
  fileInput?: ElementRef<HTMLInputElement>;
349
+ private whitelabelService;
276
350
  autoFocus: boolean;
277
351
  isReadonly: boolean;
278
352
  isEditMode: boolean;
@@ -308,11 +382,6 @@ declare class DsMobilePostCreateBottomSheetComponent implements AfterViewInit, O
308
382
  handleCancel(): Promise<void>;
309
383
  handlePost(): Promise<void>;
310
384
  handleAddImage(): Promise<void>;
311
- /**
312
- * Restore StatusBar configuration (background task)
313
- * Safe area padding is now handled preventively via applySafeAreaToToolbar()
314
- */
315
- private restoreStatusBar;
316
385
  handleRemoveImage(index: number): void;
317
386
  handleAddAttachment(): void;
318
387
  handleFileSelect(event: Event): void;
@@ -383,7 +452,7 @@ declare class DsMobileProfileActionsSheetComponent {
383
452
  /**
384
453
  * Current view state
385
454
  */
386
- currentView: _angular_core.WritableSignal<"language" | "main">;
455
+ currentView: _angular_core.WritableSignal<"main" | "language">;
387
456
  /**
388
457
  * Reference to the view container for height calculations
389
458
  */
@@ -499,6 +568,28 @@ declare class DsMobilePageMainComponent extends MobilePageBase implements AfterV
499
568
  showCondensedHeader: _angular_core.InputSignal<boolean>;
500
569
  scrollThreshold: _angular_core.InputSignal<number>;
501
570
  headerFadeDistance: _angular_core.InputSignal<number>;
571
+ /**
572
+ * Content wrapper padding
573
+ * - '0' (default) - No padding, use ds-mobile-section for content organization
574
+ * - '20px' - Legacy padding for content without sections
575
+ * - Any custom CSS padding value
576
+ *
577
+ * Note: Bottom padding for safe area and tab bar is always preserved
578
+ *
579
+ * @example
580
+ * ```html
581
+ * <!-- Default: sections handle padding -->
582
+ * <ds-mobile-page-main title="Home">
583
+ * <ds-mobile-section>...</ds-mobile-section>
584
+ * </ds-mobile-page-main>
585
+ *
586
+ * <!-- Legacy: content without sections -->
587
+ * <ds-mobile-page-main title="Home" contentPadding="20px">
588
+ * <div>Content without sections...</div>
589
+ * </ds-mobile-page-main>
590
+ * ```
591
+ */
592
+ contentPadding: _angular_core.InputSignal<string>;
502
593
  /**
503
594
  * Profile menu action groups to display when avatar is clicked.
504
595
  * If not provided, a default menu will be used (without Whitelabel Demo).
@@ -548,7 +639,7 @@ declare class DsMobilePageMainComponent extends MobilePageBase implements AfterV
548
639
  */
549
640
  handleRefresh(event: any): Promise<void>;
550
641
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobilePageMainComponent, never>;
551
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobilePageMainComponent, "ds-mobile-page-main", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "headerTitle": { "alias": "headerTitle"; "required": false; "isSignal": true; }; "headerSubtitle": { "alias": "headerSubtitle"; "required": false; "isSignal": true; }; "avatarType": { "alias": "avatarType"; "required": false; "isSignal": true; }; "avatarInitials": { "alias": "avatarInitials"; "required": false; "isSignal": true; }; "avatarSrc": { "alias": "avatarSrc"; "required": false; "isSignal": true; }; "avatarIconName": { "alias": "avatarIconName"; "required": false; "isSignal": true; }; "showRefresh": { "alias": "showRefresh"; "required": false; "isSignal": true; }; "showCondensedHeader": { "alias": "showCondensedHeader"; "required": false; "isSignal": true; }; "scrollThreshold": { "alias": "scrollThreshold"; "required": false; "isSignal": true; }; "headerFadeDistance": { "alias": "headerFadeDistance"; "required": false; "isSignal": true; }; "profileMenuItems": { "alias": "profileMenuItems"; "required": false; "isSignal": true; }; }, { "avatarClick": "avatarClick"; "profileActionSelected": "profileActionSelected"; "refresh": "refresh"; "scroll": "scroll"; }, never, ["[header-content]", "*"], true, never>;
642
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobilePageMainComponent, "ds-mobile-page-main", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "headerTitle": { "alias": "headerTitle"; "required": false; "isSignal": true; }; "headerSubtitle": { "alias": "headerSubtitle"; "required": false; "isSignal": true; }; "avatarType": { "alias": "avatarType"; "required": false; "isSignal": true; }; "avatarInitials": { "alias": "avatarInitials"; "required": false; "isSignal": true; }; "avatarSrc": { "alias": "avatarSrc"; "required": false; "isSignal": true; }; "avatarIconName": { "alias": "avatarIconName"; "required": false; "isSignal": true; }; "showRefresh": { "alias": "showRefresh"; "required": false; "isSignal": true; }; "showCondensedHeader": { "alias": "showCondensedHeader"; "required": false; "isSignal": true; }; "scrollThreshold": { "alias": "scrollThreshold"; "required": false; "isSignal": true; }; "headerFadeDistance": { "alias": "headerFadeDistance"; "required": false; "isSignal": true; }; "contentPadding": { "alias": "contentPadding"; "required": false; "isSignal": true; }; "profileMenuItems": { "alias": "profileMenuItems"; "required": false; "isSignal": true; }; }, { "avatarClick": "avatarClick"; "profileActionSelected": "profileActionSelected"; "refresh": "refresh"; "scroll": "scroll"; }, never, ["[header-content]", "[offline-indicator]", "*"], true, never>;
552
643
  }
553
644
 
554
645
  interface InlineTabItem {
@@ -593,6 +684,176 @@ declare class DsMobileInlineTabsComponent {
593
684
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileInlineTabsComponent, "ds-mobile-inline-tabs", never, { "tabs": { "alias": "tabs"; "required": true; "isSignal": true; }; "activeTab": { "alias": "activeTab"; "required": true; "isSignal": true; }; }, { "tabChange": "tabChange"; }, never, never, true, never>;
594
685
  }
595
686
 
687
+ interface WhitelabelConfig {
688
+ logoUrl: string;
689
+ logoMarkUrl: string;
690
+ logoAlt: string;
691
+ logoSize: 'sm' | 'md' | 'lg' | 'xl';
692
+ logoWidth?: number;
693
+ logoHeight?: number;
694
+ logoMarkWidth?: number;
695
+ logoMarkHeight?: number;
696
+ appIconSurface: string;
697
+ appIconContent: string;
698
+ accent: string;
699
+ onAccent: string;
700
+ headerSurface: string;
701
+ headerContent: string;
702
+ headerAccent: string;
703
+ onHeaderAccent: string;
704
+ showCityIllustration: boolean;
705
+ signInBgType: 'solid' | 'gradient';
706
+ signInBgSolid: string;
707
+ signInBgGradientStart: string;
708
+ signInBgGradientEnd: string;
709
+ signInContentColor: string;
710
+ organizationName: string;
711
+ organizationId: string;
712
+ }
713
+ /**
714
+ * WhitelabelService
715
+ *
716
+ * Manages whitelabel configuration including logos and brand colors.
717
+ * Automatically updates CSS custom properties when colors change.
718
+ *
719
+ * @example
720
+ * Initialize with custom config:
721
+ * ```typescript
722
+ * whitelabelService.initialize({
723
+ * logoUrl: '/Assets/logos/acme-logo.svg',
724
+ * logoMarkUrl: '/Assets/logos/acme-mark.svg',
725
+ * accent: '#2563eb',
726
+ * onAccent: '#ffffff',
727
+ * headerSurface: '#1e40af',
728
+ * headerContent: '#ffffff',
729
+ * organizationName: 'Acme Corp'
730
+ * });
731
+ * ```
732
+ *
733
+ * Load from API:
734
+ * ```typescript
735
+ * await whitelabelService.loadFromApi('acme-corp');
736
+ * ```
737
+ */
738
+ declare class WhitelabelService {
739
+ private _config;
740
+ readonly logoUrl: _angular_core.Signal<string>;
741
+ readonly logoMarkUrl: _angular_core.Signal<string>;
742
+ readonly logoAlt: _angular_core.Signal<string>;
743
+ readonly logoSize: _angular_core.Signal<"sm" | "md" | "lg" | "xl">;
744
+ readonly logoHeight: _angular_core.Signal<number>;
745
+ readonly appIconSurface: _angular_core.Signal<string>;
746
+ readonly appIconContent: _angular_core.Signal<string>;
747
+ readonly accent: _angular_core.Signal<string>;
748
+ readonly onAccent: _angular_core.Signal<string>;
749
+ readonly headerSurface: _angular_core.Signal<string>;
750
+ readonly headerContent: _angular_core.Signal<string>;
751
+ readonly headerAccent: _angular_core.Signal<string>;
752
+ readonly onHeaderAccent: _angular_core.Signal<string>;
753
+ readonly showCityIllustration: _angular_core.Signal<boolean>;
754
+ readonly signInBgType: _angular_core.Signal<"solid" | "gradient">;
755
+ readonly signInBgSolid: _angular_core.Signal<string>;
756
+ readonly signInBgGradientStart: _angular_core.Signal<string>;
757
+ readonly signInBgGradientEnd: _angular_core.Signal<string>;
758
+ readonly signInContentColor: _angular_core.Signal<string>;
759
+ readonly organizationName: _angular_core.Signal<string>;
760
+ readonly organizationId: _angular_core.Signal<string>;
761
+ readonly signInBgStyle: _angular_core.Signal<string>;
762
+ readonly config: _angular_core.Signal<WhitelabelConfig>;
763
+ constructor();
764
+ /**
765
+ * Setup global listener for modal dismiss events
766
+ * Re-applies status bar after modals close to override Ionic's cached state restoration
767
+ */
768
+ private setupModalDismissListener;
769
+ /**
770
+ * Initialize whitelabel configuration
771
+ * Call this early in app initialization (app.config.ts or app.component.ts)
772
+ */
773
+ initialize(config: Partial<WhitelabelConfig>): void;
774
+ /**
775
+ * Load whitelabel config from API
776
+ * Typically called on app startup based on subdomain, user tenant, etc.
777
+ *
778
+ * @param organizationId - The organization identifier (subdomain, tenant ID, etc.)
779
+ */
780
+ loadFromApi(organizationId?: string): Promise<void>;
781
+ /**
782
+ * Update config dynamically (e.g., when user switches organizations)
783
+ */
784
+ updateConfig(updates: Partial<WhitelabelConfig>): void;
785
+ /**
786
+ * Update only the brand colors
787
+ */
788
+ updateColors(colors: {
789
+ appIconSurface?: string;
790
+ appIconContent?: string;
791
+ accent?: string;
792
+ onAccent?: string;
793
+ headerSurface?: string;
794
+ headerContent?: string;
795
+ headerAccent?: string;
796
+ onHeaderAccent?: string;
797
+ }): void;
798
+ /**
799
+ * Reset to default configuration
800
+ */
801
+ resetToDefault(): void;
802
+ /**
803
+ * Convert hex color to RGB values
804
+ */
805
+ private hexToRgb;
806
+ /**
807
+ * Calculate relative luminance of a color (WCAG standard)
808
+ * Returns a value between 0 (darkest) and 1 (lightest)
809
+ */
810
+ private getRelativeLuminance;
811
+ /**
812
+ * Determine if a color is light (needs dark status bar content)
813
+ */
814
+ private isColorLight;
815
+ /**
816
+ * Get the appropriate status bar style for a background color
817
+ * Public method for use by sign-in page
818
+ */
819
+ getStatusBarStyleForColor(color: string): Style;
820
+ /**
821
+ * Generate a hover state color by applying a black overlay
822
+ * This simulates the effect of overlaying #000000 with 10% opacity
823
+ *
824
+ * @param baseColor - Hex color to overlay on (e.g., '#6B5FF5')
825
+ * @param overlayColor - Overlay color (default: '#000000' for darkening)
826
+ * @param overlayAlpha - Opacity of overlay (0-1, default: 0.1 = 10%)
827
+ * @returns Hex color with overlay applied
828
+ *
829
+ * @example
830
+ * generateHoverColor('#6B5FF5') // Returns darker purple for hover state
831
+ * generateHoverColor('#FF0000', '#FFFFFF', 0.2) // Lighten red by 20%
832
+ */
833
+ private generateHoverColor;
834
+ /**
835
+ * Generate an active/pressed state color (darker than hover)
836
+ * Uses 20% black overlay for a more pronounced pressed effect
837
+ *
838
+ * @param baseColor - Hex color to overlay on
839
+ * @returns Hex color for active/pressed state
840
+ */
841
+ private generateActiveColor;
842
+ /**
843
+ * Apply colors to CSS custom properties and native StatusBar
844
+ * This updates the actual CSS variables used throughout the app
845
+ * and the native status bar color on mobile devices
846
+ */
847
+ private applyColors;
848
+ /**
849
+ * Update the native status bar color AND style
850
+ * Sets background color (Android) and content style (iOS/Android)
851
+ */
852
+ private updateNativeStatusBar;
853
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<WhitelabelService, never>;
854
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<WhitelabelService>;
855
+ }
856
+
596
857
  /**
597
858
  * DsMobilePageDetailsComponent
598
859
  *
@@ -633,9 +894,19 @@ declare class DsMobilePageDetailsComponent extends MobilePageBase implements Aft
633
894
  private elementRef;
634
895
  ionContent?: IonContent;
635
896
  private platform;
897
+ whitelabelService: WhitelabelService;
636
898
  isNativePlatform: _angular_core.Signal<boolean>;
637
899
  title: _angular_core.InputSignal<string>;
638
900
  backRoute: _angular_core.InputSignal<string>;
901
+ /**
902
+ * Content wrapper padding
903
+ * - '0' (default) - No padding, use ds-mobile-section for content organization
904
+ * - '20px' - Legacy padding for content without sections
905
+ * - Any custom CSS padding value
906
+ *
907
+ * Note: Bottom padding for safe area and tab bar is always preserved
908
+ */
909
+ contentPadding: _angular_core.InputSignal<string>;
639
910
  tabs: _angular_core.InputSignal<InlineTabItem[] | undefined>;
640
911
  activeTab: _angular_core.InputSignal<string>;
641
912
  showRefresh: _angular_core.InputSignal<boolean>;
@@ -671,7 +942,7 @@ declare class DsMobilePageDetailsComponent extends MobilePageBase implements Aft
671
942
  */
672
943
  handleRefresh(event: any): Promise<void>;
673
944
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobilePageDetailsComponent, never>;
674
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobilePageDetailsComponent, "ds-mobile-page-details", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "backRoute": { "alias": "backRoute"; "required": false; "isSignal": true; }; "tabs": { "alias": "tabs"; "required": false; "isSignal": true; }; "activeTab": { "alias": "activeTab"; "required": false; "isSignal": true; }; "showRefresh": { "alias": "showRefresh"; "required": false; "isSignal": true; }; "scrollThreshold": { "alias": "scrollThreshold"; "required": false; "isSignal": true; }; "headerFadeDistance": { "alias": "headerFadeDistance"; "required": false; "isSignal": true; }; }, { "back": "back"; "tabChange": "tabChange"; "refresh": "refresh"; "scroll": "scroll"; }, never, ["*"], true, never>;
945
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobilePageDetailsComponent, "ds-mobile-page-details", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "backRoute": { "alias": "backRoute"; "required": false; "isSignal": true; }; "contentPadding": { "alias": "contentPadding"; "required": false; "isSignal": true; }; "tabs": { "alias": "tabs"; "required": false; "isSignal": true; }; "activeTab": { "alias": "activeTab"; "required": false; "isSignal": true; }; "showRefresh": { "alias": "showRefresh"; "required": false; "isSignal": true; }; "scrollThreshold": { "alias": "scrollThreshold"; "required": false; "isSignal": true; }; "headerFadeDistance": { "alias": "headerFadeDistance"; "required": false; "isSignal": true; }; }, { "back": "back"; "tabChange": "tabChange"; "refresh": "refresh"; "scroll": "scroll"; }, never, ["[offline-indicator]", "*"], true, never>;
675
946
  }
676
947
 
677
948
  /**
@@ -680,18 +951,21 @@ declare class DsMobilePageDetailsComponent extends MobilePageBase implements Aft
680
951
  * Main content container for mobile pages with flexible layout options.
681
952
  * Provides consistent spacing and layout patterns.
682
953
  *
954
+ * **Note:** For production use, prefer `ds-mobile-section` for individual sections.
955
+ * This component is maintained for grid layouts and prototyping.
956
+ *
683
957
  * @example
684
958
  * ```html
685
959
  * <!-- Default: stacked layout -->
686
960
  * <ds-mobile-content>
687
- * <ds-mobile-content-section>...</ds-mobile-content-section>
688
- * <ds-mobile-content-section>...</ds-mobile-content-section>
961
+ * <ds-mobile-section>...</ds-mobile-section>
962
+ * <ds-mobile-section>...</ds-mobile-section>
689
963
  * </ds-mobile-content>
690
964
  *
691
965
  * <!-- Grid layout -->
692
966
  * <ds-mobile-content layout="grid-2">
693
- * <ds-mobile-content-section>...</ds-mobile-content-section>
694
- * <ds-mobile-content-section>...</ds-mobile-content-section>
967
+ * <div>Grid item 1</div>
968
+ * <div>Grid item 2</div>
695
969
  * </ds-mobile-content>
696
970
  * ```
697
971
  */
@@ -706,25 +980,6 @@ declare class DsMobileContentComponent {
706
980
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileContentComponent, never>;
707
981
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileContentComponent, "ds-mobile-content", never, { "layout": { "alias": "layout"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
708
982
  }
709
- /**
710
- * DsMobileContentSectionComponent
711
- *
712
- * Section within mobile content with optional header.
713
- *
714
- * @example
715
- * ```html
716
- * <ds-mobile-content-section>
717
- * <section-header width="half"></section-header>
718
- * <content-row>
719
- * <div class="grey-box"></div>
720
- * </content-row>
721
- * </ds-mobile-content-section>
722
- * ```
723
- */
724
- declare class DsMobileContentSectionComponent {
725
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileContentSectionComponent, never>;
726
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileContentSectionComponent, "ds-mobile-content-section", never, {}, {}, never, ["section-header", "*"], true, never>;
727
- }
728
983
  /**
729
984
  * SectionHeaderComponent
730
985
  *
@@ -747,6 +1002,128 @@ declare class ContentRowComponent {
747
1002
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContentRowComponent, "content-row", never, {}, {}, never, ["*"], true, never>;
748
1003
  }
749
1004
 
1005
+ /**
1006
+ * DsMobileSectionComponent
1007
+ *
1008
+ * Universal section component for mobile pages, modals, and content containers.
1009
+ * Provides consistent layout with optional headlines, action links, flexible padding, and borders.
1010
+ *
1011
+ * **Features:**
1012
+ * - Optional section headline with icon support
1013
+ * - Optional action link with click handler
1014
+ * - Flexible padding control
1015
+ * - Border management (bottom border by default)
1016
+ * - Works in pages, modals, and any content container
1017
+ *
1018
+ * **Usage Patterns:**
1019
+ *
1020
+ * @example
1021
+ * ```html
1022
+ * <!-- Section with headline and link -->
1023
+ * <ds-mobile-section
1024
+ * headline="Recent Posts"
1025
+ * linkText="See all"
1026
+ * (linkClick)="navigate()">
1027
+ * <div class="posts-list">...</div>
1028
+ * </ds-mobile-section>
1029
+ *
1030
+ * <!-- Section with icon in headline -->
1031
+ * <ds-mobile-section
1032
+ * headline="Messages"
1033
+ * icon="remixChat3Line"
1034
+ * linkText="View all"
1035
+ * (linkClick)="viewMessages()">
1036
+ * <div class="messages">...</div>
1037
+ * </ds-mobile-section>
1038
+ *
1039
+ * <!-- Empty state section (no headline) -->
1040
+ * <ds-mobile-section>
1041
+ * <div class="empty-state">
1042
+ * <ds-avatar type="icon" iconName="remixInboxLine" />
1043
+ * <h3>No messages yet</h3>
1044
+ * </div>
1045
+ * </ds-mobile-section>
1046
+ *
1047
+ * <!-- Last section without border -->
1048
+ * <ds-mobile-section
1049
+ * headline="Contact"
1050
+ * [showBorder]="false">
1051
+ * <div class="contact-form">...</div>
1052
+ * </ds-mobile-section>
1053
+ *
1054
+ * <!-- Section with custom padding -->
1055
+ * <ds-mobile-section
1056
+ * headline="Photos"
1057
+ * padding="20px 0">
1058
+ * <div class="photo-grid">...</div>
1059
+ * </ds-mobile-section>
1060
+ *
1061
+ * <!-- Full-width section in page (default behavior) -->
1062
+ * <ds-mobile-page-main title="Home">
1063
+ * <ds-mobile-section headline="Posts">
1064
+ * <div class="posts">...</div>
1065
+ * </ds-mobile-section>
1066
+ * </ds-mobile-page-main>
1067
+ * ```
1068
+ */
1069
+ declare class DsMobileSectionComponent {
1070
+ /**
1071
+ * Section headline text
1072
+ * @default ''
1073
+ */
1074
+ headline: _angular_core.InputSignal<string>;
1075
+ /**
1076
+ * Optional icon to display before headline
1077
+ * @default ''
1078
+ */
1079
+ icon: _angular_core.InputSignal<string>;
1080
+ /**
1081
+ * Link text (e.g., "See all", "View more")
1082
+ * When provided, displays a clickable link in the section header
1083
+ * @default ''
1084
+ */
1085
+ linkText: _angular_core.InputSignal<string>;
1086
+ /**
1087
+ * Section padding
1088
+ * Accepts any valid CSS padding value
1089
+ * When not set, defaults to 20px on mobile and 32px on desktop
1090
+ * @default ''
1091
+ */
1092
+ padding: _angular_core.InputSignal<string>;
1093
+ /**
1094
+ * Gap between section header and content
1095
+ * Accepts any valid CSS gap value
1096
+ * @default '12px'
1097
+ */
1098
+ gap: _angular_core.InputSignal<string>;
1099
+ /**
1100
+ * Gap between child elements within section-content
1101
+ * Accepts any valid CSS gap value
1102
+ * @default '12px'
1103
+ */
1104
+ contentGap: _angular_core.InputSignal<string>;
1105
+ /**
1106
+ * Whether to show bottom border
1107
+ * @default true
1108
+ */
1109
+ showBorder: _angular_core.InputSignal<boolean>;
1110
+ /**
1111
+ * CSS overflow property for the section
1112
+ * @default 'visible'
1113
+ */
1114
+ overflow: _angular_core.InputSignal<string>;
1115
+ /**
1116
+ * Emitted when section link is clicked
1117
+ */
1118
+ linkClick: _angular_core.OutputEmitterRef<void>;
1119
+ /**
1120
+ * Handle link click event
1121
+ */
1122
+ handleLinkClick(): void;
1123
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileSectionComponent, never>;
1124
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileSectionComponent, "ds-mobile-section", never, { "headline": { "alias": "headline"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "linkText": { "alias": "linkText"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; "contentGap": { "alias": "contentGap"; "required": false; "isSignal": true; }; "showBorder": { "alias": "showBorder"; "required": false; "isSignal": true; }; "overflow": { "alias": "overflow"; "required": false; "isSignal": true; }; }, { "linkClick": "linkClick"; }, never, ["*"], true, never>;
1125
+ }
1126
+
750
1127
  /**
751
1128
  * DsMobileHeaderContentComponent
752
1129
  *
@@ -2532,7 +2909,7 @@ interface TabConfig {
2532
2909
  * ```css
2533
2910
  * ion-tabs {
2534
2911
  * height: 100%;
2535
- * background: var(--color-brand-secondary);
2912
+ * background: var(--color-header-surface);
2536
2913
  * }
2537
2914
  * ```
2538
2915
  */
@@ -2638,18 +3015,144 @@ declare class DsMobileTabsComponent implements OnInit {
2638
3015
  }
2639
3016
 
2640
3017
  /**
2641
- * Media file types supported by the lightbox
3018
+ * DsMobileSwiperComponent
3019
+ *
3020
+ * A reusable swiper/carousel component with configurable child width and spacing.
3021
+ *
3022
+ * Features:
3023
+ * - First slide is left-aligned
3024
+ * - Middle slides are centered when active
3025
+ * - Last slide is right-aligned
3026
+ * - Configurable slide width and gap
3027
+ * - Content projection via ng-content
3028
+ *
3029
+ * Usage:
3030
+ * ```html
3031
+ * <ds-mobile-swiper [slideWidth]="'75vw'" [gap]="16">
3032
+ * <div class="swiper-slide">Slide 1</div>
3033
+ * <div class="swiper-slide">Slide 2</div>
3034
+ * <div class="swiper-slide">Slide 3</div>
3035
+ * </ds-mobile-swiper>
3036
+ * ```
2642
3037
  */
2643
- type LightboxMediaType = 'image' | 'pdf';
3038
+ declare class DsMobileSwiperComponent implements AfterViewInit, OnDestroy {
3039
+ private elementRef;
3040
+ /**
3041
+ * Width of each slide (e.g., '75vw', '300px', '80%')
3042
+ */
3043
+ slideWidth: _angular_core.InputSignal<string>;
3044
+ /**
3045
+ * Gap between slides in pixels
3046
+ */
3047
+ gap: _angular_core.InputSignal<number>;
3048
+ /**
3049
+ * Enable pagination dots
3050
+ */
3051
+ pagination: _angular_core.InputSignal<boolean>;
3052
+ /**
3053
+ * Enable auto height - container adapts to active slide's height
3054
+ */
3055
+ autoHeight: _angular_core.InputSignal<boolean>;
3056
+ /**
3057
+ * Enable progressive opacity based on slide position
3058
+ * Slides fade in/out smoothly as they move toward/away from center
3059
+ */
3060
+ progressiveOpacity: _angular_core.InputSignal<boolean>;
3061
+ /**
3062
+ * Enable progressive scale based on slide position
3063
+ * Slides scale down smoothly as they move away from center
3064
+ */
3065
+ progressiveScale: _angular_core.InputSignal<boolean>;
3066
+ swiperContainer: ElementRef;
3067
+ private swiperInstance;
3068
+ constructor(elementRef: ElementRef);
3069
+ ngAfterViewInit(): void;
3070
+ private initializeSwiper;
3071
+ /**
3072
+ * Navigate to previous slide
3073
+ */
3074
+ slidePrev(): void;
3075
+ /**
3076
+ * Navigate to next slide
3077
+ */
3078
+ slideNext(): void;
3079
+ /**
3080
+ * Check if at the beginning
3081
+ */
3082
+ isBeginning(): boolean;
3083
+ /**
3084
+ * Check if at the end
3085
+ */
3086
+ isEnd(): boolean;
3087
+ ngOnDestroy(): void;
3088
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileSwiperComponent, never>;
3089
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileSwiperComponent, "ds-mobile-swiper", never, { "slideWidth": { "alias": "slideWidth"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; "pagination": { "alias": "pagination"; "required": false; "isSignal": true; }; "autoHeight": { "alias": "autoHeight"; "required": false; "isSignal": true; }; "progressiveOpacity": { "alias": "progressiveOpacity"; "required": false; "isSignal": true; }; "progressiveScale": { "alias": "progressiveScale"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3090
+ }
3091
+
2644
3092
  /**
2645
- * Base media file interface
2646
- */
2647
- interface LightboxMediaFile {
2648
- /** File source URL */
2649
- src: string;
2650
- /** Media type - determines which viewer to use */
2651
- type: LightboxMediaType;
2652
- /** File title */
3093
+ * DsMobileSystemMessageBannerComponent
3094
+ *
3095
+ * Full-width centered banner component for displaying system messages in chat conversations.
3096
+ * Uses the same text styling as message bubbles for consistency.
3097
+ *
3098
+ * Features:
3099
+ * - Full-width centered layout
3100
+ * - Subtle background with theming support
3101
+ * - Same typography as message bubbles
3102
+ * - Optional icon support
3103
+ *
3104
+ * Common use cases:
3105
+ * - Inquiry status updates ("Your inquiry has been assigned to...")
3106
+ * - System notifications ("This inquiry is marked as resolved")
3107
+ * - Auto-replies ("We aim to respond within 24 hours...")
3108
+ * - Time/date separators
3109
+ *
3110
+ * @example
3111
+ * ```html
3112
+ * <!-- Simple system message -->
3113
+ * <ds-mobile-system-message-banner
3114
+ * [message]="'Ricki Meihlen har overtaget din henvendelse og vil kontakte dig snart.'">
3115
+ * </ds-mobile-system-message-banner>
3116
+ *
3117
+ * <!-- With icon -->
3118
+ * <ds-mobile-system-message-banner
3119
+ * [message]="'Vi bestræber os på at svare inden for 24 timer på hverdage.'"
3120
+ * [iconName]="'remixInformationLine'">
3121
+ * </ds-mobile-system-message-banner>
3122
+ * ```
3123
+ */
3124
+ declare class DsMobileSystemMessageBannerComponent {
3125
+ /**
3126
+ * System message text to display
3127
+ */
3128
+ message: _angular_core.InputSignal<string>;
3129
+ /**
3130
+ * Optional icon name (currently using inline SVG for info icon)
3131
+ * Can be extended to support full icon library integration
3132
+ */
3133
+ iconName: _angular_core.InputSignal<string>;
3134
+ /**
3135
+ * Whether this system message appears directly after a timestamp
3136
+ * When true, removes top padding to reduce spacing
3137
+ */
3138
+ afterTimestamp: _angular_core.InputSignal<boolean>;
3139
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileSystemMessageBannerComponent, never>;
3140
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileSystemMessageBannerComponent, "ds-mobile-system-message-banner", never, { "message": { "alias": "message"; "required": true; "isSignal": true; }; "iconName": { "alias": "iconName"; "required": false; "isSignal": true; }; "afterTimestamp": { "alias": "afterTimestamp"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3141
+ }
3142
+
3143
+ /**
3144
+ * Media file types supported by the lightbox
3145
+ */
3146
+ type LightboxMediaType = 'image' | 'pdf';
3147
+ /**
3148
+ * Base media file interface
3149
+ */
3150
+ interface LightboxMediaFile {
3151
+ /** File source URL */
3152
+ src: string;
3153
+ /** Media type - determines which viewer to use */
3154
+ type: LightboxMediaType;
3155
+ /** File title */
2653
3156
  title?: string;
2654
3157
  /** File description */
2655
3158
  description?: string;
@@ -3115,12 +3618,21 @@ declare class DsMobileLightboxFooterComponent {
3115
3618
  * />
3116
3619
  * ```
3117
3620
  */
3118
- declare class DsMobileInlinePhotoComponent {
3621
+ declare class DsMobileInlinePhotoComponent implements OnInit {
3119
3622
  private lightboxService;
3120
3623
  /**
3121
3624
  * Array of image URLs to display
3122
3625
  */
3123
3626
  images: string[];
3627
+ /**
3628
+ * Optional array of loading states for each image (by index)
3629
+ * If provided, shows loader overlay for images that are still loading
3630
+ */
3631
+ loadingStates?: boolean[];
3632
+ /**
3633
+ * Internal signal to track image loading states
3634
+ */
3635
+ private internalLoadingStates;
3124
3636
  /**
3125
3637
  * Author information (passed to lightbox)
3126
3638
  */
@@ -3137,6 +3649,11 @@ declare class DsMobileInlinePhotoComponent {
3137
3649
  * Remaining images shown in lightbox only
3138
3650
  */
3139
3651
  maxVisible: number;
3652
+ /**
3653
+ * Whether to use grid layout (true) or flex-wrap layout (false)
3654
+ * @default true
3655
+ */
3656
+ useGrid: boolean;
3140
3657
  /**
3141
3658
  * Event emitted when lightbox is opened
3142
3659
  */
@@ -3145,10 +3662,26 @@ declare class DsMobileInlinePhotoComponent {
3145
3662
  totalImages: number;
3146
3663
  }>;
3147
3664
  constructor(lightboxService: DsMobileLightboxService);
3665
+ /**
3666
+ * Initialize loading states for all visible images
3667
+ */
3668
+ ngOnInit(): void;
3669
+ /**
3670
+ * Handle image load event
3671
+ */
3672
+ onImageLoad(index: number): void;
3673
+ /**
3674
+ * Handle image error event
3675
+ */
3676
+ onImageError(index: number): void;
3148
3677
  /**
3149
3678
  * Get the first N images to display inline
3150
3679
  */
3151
3680
  get visibleImages(): string[];
3681
+ /**
3682
+ * Check if a specific image is loading
3683
+ */
3684
+ isImageLoading(index: number): boolean;
3152
3685
  /**
3153
3686
  * Calculate how many images are hidden
3154
3687
  */
@@ -3158,7 +3691,63 @@ declare class DsMobileInlinePhotoComponent {
3158
3691
  */
3159
3692
  openLightbox(index: number, event?: Event): void;
3160
3693
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileInlinePhotoComponent, never>;
3161
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileInlinePhotoComponent, "ds-mobile-inline-photo", never, { "images": { "alias": "images"; "required": false; }; "author": { "alias": "author"; "required": false; }; "maxVisible": { "alias": "maxVisible"; "required": false; }; }, { "photoClick": "photoClick"; }, never, never, true, never>;
3694
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileInlinePhotoComponent, "ds-mobile-inline-photo", never, { "images": { "alias": "images"; "required": false; }; "loadingStates": { "alias": "loadingStates"; "required": false; }; "author": { "alias": "author"; "required": false; }; "maxVisible": { "alias": "maxVisible"; "required": false; }; "useGrid": { "alias": "useGrid"; "required": false; }; }, { "photoClick": "photoClick"; }, never, never, true, never>;
3695
+ }
3696
+
3697
+ /**
3698
+ * DsMobileLoaderOverlayComponent
3699
+ *
3700
+ * Reusable loader overlay component that displays a spinner centered over its container.
3701
+ * Designed to overlay images or other content during loading states.
3702
+ *
3703
+ * Features:
3704
+ * - Semi-transparent white background overlay
3705
+ * - Centered animated spinner
3706
+ * - Customizable spinner size
3707
+ * - Absolute positioning to cover parent
3708
+ * - Accessible with proper z-index stacking
3709
+ *
3710
+ * @example
3711
+ * ```html
3712
+ * <!-- Basic usage -->
3713
+ * <div style="position: relative;">
3714
+ * <img src="image.jpg" alt="Content" />
3715
+ * @if (isLoading) {
3716
+ * <ds-mobile-loader-overlay />
3717
+ * }
3718
+ * </div>
3719
+ *
3720
+ * <!-- With custom spinner size -->
3721
+ * <div style="position: relative;">
3722
+ * <div class="content">Loading content...</div>
3723
+ * <ds-mobile-loader-overlay [spinnerSize]="32" />
3724
+ * </div>
3725
+ *
3726
+ * <!-- Over an image with rounded corners -->
3727
+ * <div style="position: relative; border-radius: 12px; overflow: hidden;">
3728
+ * <img src="photo.jpg" />
3729
+ * <ds-mobile-loader-overlay [borderRadius]="12" />
3730
+ * </div>
3731
+ * ```
3732
+ *
3733
+ * @notes
3734
+ * - Parent container must have position: relative for the overlay to work correctly
3735
+ * - The overlay covers the entire parent element using inset: 0
3736
+ * - Spinner animation runs continuously at 0.6s per rotation
3737
+ */
3738
+ declare class DsMobileLoaderOverlayComponent {
3739
+ /**
3740
+ * Size of the spinner in pixels
3741
+ * @default 24
3742
+ */
3743
+ spinnerSize: _angular_core.InputSignal<number>;
3744
+ /**
3745
+ * Border radius of the overlay in pixels (should match parent's border radius)
3746
+ * @default 0
3747
+ */
3748
+ borderRadius: _angular_core.InputSignal<number>;
3749
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileLoaderOverlayComponent, never>;
3750
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileLoaderOverlayComponent, "ds-mobile-loader-overlay", never, { "spinnerSize": { "alias": "spinnerSize"; "required": false; "isSignal": true; }; "borderRadius": { "alias": "borderRadius"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3162
3751
  }
3163
3752
 
3164
3753
  /**
@@ -3420,6 +4009,29 @@ declare abstract class MobileModalBase implements OnInit, OnDestroy {
3420
4009
  * @default false
3421
4010
  */
3422
4011
  hasFixedBottom: _angular_core.InputSignal<boolean>;
4012
+ /**
4013
+ * Content padding for modal content
4014
+ * Controls padding inside .modal-main-content
4015
+ * - '0' (default) - No padding, use ds-mobile-section for content organization
4016
+ * - '20px' - Legacy padding for content without sections
4017
+ * - Any custom CSS padding value
4018
+ *
4019
+ * @default '0'
4020
+ *
4021
+ * @example
4022
+ * ```html
4023
+ * <!-- Default: sections handle padding -->
4024
+ * <ds-mobile-modal-base headerTitle="Details">
4025
+ * <ds-mobile-section headline="Info">...</ds-mobile-section>
4026
+ * </ds-mobile-modal-base>
4027
+ *
4028
+ * <!-- Legacy: content without sections -->
4029
+ * <ds-mobile-modal-base headerTitle="Details" contentPadding="20px">
4030
+ * <div>Padded content</div>
4031
+ * </ds-mobile-modal-base>
4032
+ * ```
4033
+ */
4034
+ contentPadding: _angular_core.InputSignal<string>;
3423
4035
  /**
3424
4036
  * Emitted when modal is closed
3425
4037
  */
@@ -3461,7 +4073,7 @@ declare abstract class MobileModalBase implements OnInit, OnDestroy {
3461
4073
  */
3462
4074
  protected cleanupKeyboardListeners(): void;
3463
4075
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileModalBase, never>;
3464
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MobileModalBase, never, never, { "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "headerTitle": { "alias": "headerTitle"; "required": false; "isSignal": true; }; "headerMeta": { "alias": "headerMeta"; "required": false; "isSignal": true; }; "closeButtonLabel": { "alias": "closeButtonLabel"; "required": false; "isSignal": true; }; "enableKeyboardHandling": { "alias": "enableKeyboardHandling"; "required": false; "isSignal": true; }; "hasFixedBottom": { "alias": "hasFixedBottom"; "required": false; "isSignal": true; }; }, { "closed": "closed"; "keyboardWillShow": "keyboardWillShow"; "keyboardWillHide": "keyboardWillHide"; }, never, never, true, never>;
4076
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MobileModalBase, never, never, { "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "headerTitle": { "alias": "headerTitle"; "required": false; "isSignal": true; }; "headerMeta": { "alias": "headerMeta"; "required": false; "isSignal": true; }; "closeButtonLabel": { "alias": "closeButtonLabel"; "required": false; "isSignal": true; }; "enableKeyboardHandling": { "alias": "enableKeyboardHandling"; "required": false; "isSignal": true; }; "hasFixedBottom": { "alias": "hasFixedBottom"; "required": false; "isSignal": true; }; "contentPadding": { "alias": "contentPadding"; "required": false; "isSignal": true; }; }, { "closed": "closed"; "keyboardWillShow": "keyboardWillShow"; "keyboardWillHide": "keyboardWillHide"; }, never, never, true, never>;
3465
4077
  }
3466
4078
 
3467
4079
  /**
@@ -4881,6 +5493,7 @@ interface ContactItem {
4881
5493
  * This component is typically not used directly - use DsMobileHandbookDetailModalService instead.
4882
5494
  */
4883
5495
  declare class DsMobileHandbookDetailModalComponent implements OnInit {
5496
+ private modalController;
4884
5497
  handbookData: HandbookDetailData;
4885
5498
  /**
4886
5499
  * Loading state - when true, shows loading indicator
@@ -4891,6 +5504,7 @@ declare class DsMobileHandbookDetailModalComponent implements OnInit {
4891
5504
  */
4892
5505
  error?: string;
4893
5506
  handbook: _angular_core.WritableSignal<HandbookDetailData>;
5507
+ constructor(modalController: ModalController);
4894
5508
  ngOnInit(): void;
4895
5509
  /**
4896
5510
  * Split handbook items to enforce content structure rules:
@@ -4906,9 +5520,17 @@ declare class DsMobileHandbookDetailModalComponent implements OnInit {
4906
5520
  */
4907
5521
  getDisplayItems(): HandbookItem[];
4908
5522
  /**
4909
- * Handle contact click
5523
+ * Handle contact click - shows bottom sheet with call and copy actions
4910
5524
  */
4911
- handleContactClick(contact: ContactItem): void;
5525
+ handleContactClick(contact: ContactItem): Promise<void>;
5526
+ /**
5527
+ * Handle the selected contact action
5528
+ */
5529
+ private handleContactAction;
5530
+ /**
5531
+ * Fallback method for copying text to clipboard (for older browsers/webviews)
5532
+ */
5533
+ private fallbackCopyToClipboard;
4912
5534
  /**
4913
5535
  * Handle attachment click
4914
5536
  */
@@ -5189,6 +5811,56 @@ declare class DsTextInputComponent implements ControlValueAccessor {
5189
5811
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsTextInputComponent, "ds-text-input", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "autocomplete": { "alias": "autocomplete"; "required": false; "isSignal": true; }; "inputmode": { "alias": "inputmode"; "required": false; "isSignal": true; }; "autoClearError": { "alias": "autoClearError"; "required": false; "isSignal": true; }; "validator": { "alias": "validator"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; "blur": "blur"; "focus": "focus"; "errorCleared": "errorCleared"; }, never, never, true, never>;
5190
5812
  }
5191
5813
 
5814
+ /**
5815
+ * DsMobileFileAttachmentComponent
5816
+ *
5817
+ * File attachment display for various document types.
5818
+ * Shows file info card with icon, filename, and file size.
5819
+ * Supports PDF and generic document formats.
5820
+ * Emits click event to open file in viewer.
5821
+ *
5822
+ * @example
5823
+ * ```html
5824
+ * <ds-mobile-file-attachment
5825
+ * [fileName]="'Document.pdf'"
5826
+ * [fileSize]="'1.2 MB'"
5827
+ * [variant]="'pdf'"
5828
+ * (fileClick)="openFile()">
5829
+ * </ds-mobile-file-attachment>
5830
+ * ```
5831
+ */
5832
+ declare class DsMobileFileAttachmentComponent {
5833
+ /**
5834
+ * File name
5835
+ */
5836
+ fileName: _angular_core.InputSignal<string>;
5837
+ /**
5838
+ * File size display (e.g., "1.2 MB")
5839
+ */
5840
+ fileSize: _angular_core.InputSignal<string>;
5841
+ /**
5842
+ * File type variant
5843
+ * - 'pdf' - PDF document (red icon)
5844
+ * - 'doc' - Generic document (blue icon)
5845
+ */
5846
+ variant: _angular_core.InputSignal<"pdf" | "doc">;
5847
+ /**
5848
+ * Emits when the file attachment is clicked
5849
+ */
5850
+ fileClick: _angular_core.OutputEmitterRef<void>;
5851
+ /**
5852
+ * Get the appropriate icon name based on variant
5853
+ */
5854
+ getIconName(): string;
5855
+ /**
5856
+ * Get the file type label based on variant
5857
+ */
5858
+ getFileTypeLabel(): string;
5859
+ handleClick(event: Event): void;
5860
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileFileAttachmentComponent, never>;
5861
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileFileAttachmentComponent, "ds-mobile-file-attachment", never, { "fileName": { "alias": "fileName"; "required": false; "isSignal": true; }; "fileSize": { "alias": "fileSize"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, { "fileClick": "fileClick"; }, never, never, true, never>;
5862
+ }
5863
+
5192
5864
  /**
5193
5865
  * DsMobileFabComponent
5194
5866
  *
@@ -5267,127 +5939,43 @@ declare class DsMobileFabComponent implements AfterViewInit, OnDestroy {
5267
5939
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileFabComponent, "ds-mobile-fab", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "fabClick": "fabClick"; }, never, never, true, never>;
5268
5940
  }
5269
5941
 
5270
- interface WhitelabelConfig {
5271
- logoUrl: string;
5272
- logoMarkUrl: string;
5273
- logoAlt: string;
5274
- logoWidth?: number;
5275
- logoHeight?: number;
5276
- logoMarkWidth?: number;
5277
- logoMarkHeight?: number;
5278
- primarySurface: string;
5279
- primaryContent: string;
5280
- secondarySurface: string;
5281
- secondaryContent: string;
5282
- organizationName: string;
5283
- organizationId: string;
5284
- }
5942
+ type AppIconSize = 'sm' | 'md' | 'lg' | 'xl';
5285
5943
  /**
5286
- * WhitelabelService
5944
+ * DsAppIconComponent
5287
5945
  *
5288
- * Manages whitelabel configuration including logos and brand colors.
5289
- * Automatically updates CSS custom properties when colors change.
5946
+ * Displays the organization's logomark styled as an app icon with rounded corners
5947
+ * and shadow. The logomark fill color adapts to the background.
5948
+ * Perfect for sign-in pages, splash screens, etc.
5949
+ *
5950
+ * Uses percentage-based padding and border-radius for perfect proportional scaling.
5290
5951
  *
5291
5952
  * @example
5292
- * Initialize with custom config:
5293
- * ```typescript
5294
- * whitelabelService.initialize({
5295
- * logoUrl: '/Assets/logos/acme-logo.svg',
5296
- * logoMarkUrl: '/Assets/logos/acme-mark.svg',
5297
- * primaryColor: '#2563eb',
5298
- * secondaryColor: '#3b82f6',
5299
- * organizationName: 'Acme Corp'
5300
- * });
5953
+ * Sign-in page (large):
5954
+ * ```html
5955
+ * <ds-app-icon size="xl" />
5301
5956
  * ```
5302
5957
  *
5303
- * Load from API:
5304
- * ```typescript
5305
- * await whitelabelService.loadFromApi('acme-corp');
5958
+ * Settings preview (small):
5959
+ * ```html
5960
+ * <ds-app-icon size="sm" />
5306
5961
  * ```
5307
5962
  */
5308
- declare class WhitelabelService {
5309
- private _config;
5310
- readonly logoUrl: _angular_core.Signal<string>;
5311
- readonly logoMarkUrl: _angular_core.Signal<string>;
5312
- readonly logoAlt: _angular_core.Signal<string>;
5313
- readonly logoHeight: _angular_core.Signal<number>;
5314
- readonly primarySurface: _angular_core.Signal<string>;
5315
- readonly primaryContent: _angular_core.Signal<string>;
5316
- readonly secondarySurface: _angular_core.Signal<string>;
5317
- readonly secondaryContent: _angular_core.Signal<string>;
5318
- readonly organizationName: _angular_core.Signal<string>;
5319
- readonly organizationId: _angular_core.Signal<string>;
5320
- readonly config: _angular_core.Signal<WhitelabelConfig>;
5321
- constructor();
5322
- /**
5323
- * Initialize whitelabel configuration
5324
- * Call this early in app initialization (app.config.ts or app.component.ts)
5325
- */
5326
- initialize(config: Partial<WhitelabelConfig>): void;
5327
- /**
5328
- * Load whitelabel config from API
5329
- * Typically called on app startup based on subdomain, user tenant, etc.
5330
- *
5331
- * @param organizationId - The organization identifier (subdomain, tenant ID, etc.)
5332
- */
5333
- loadFromApi(organizationId?: string): Promise<void>;
5334
- /**
5335
- * Update config dynamically (e.g., when user switches organizations)
5336
- */
5337
- updateConfig(updates: Partial<WhitelabelConfig>): void;
5338
- /**
5339
- * Update only the brand colors
5340
- */
5341
- updateColors(colors: {
5342
- primarySurface?: string;
5343
- primaryContent?: string;
5344
- secondarySurface?: string;
5345
- secondaryContent?: string;
5346
- }): void;
5347
- /**
5348
- * Reset to default configuration
5349
- */
5350
- resetToDefault(): void;
5351
- /**
5352
- * Convert hex color to RGB values
5353
- */
5354
- private hexToRgb;
5355
- /**
5356
- * Generate a hover state color by applying a black overlay
5357
- * This simulates the effect of overlaying #000000 with 10% opacity
5358
- *
5359
- * @param baseColor - Hex color to overlay on (e.g., '#6B5FF5')
5360
- * @param overlayColor - Overlay color (default: '#000000' for darkening)
5361
- * @param overlayAlpha - Opacity of overlay (0-1, default: 0.1 = 10%)
5362
- * @returns Hex color with overlay applied
5363
- *
5364
- * @example
5365
- * generateHoverColor('#6B5FF5') // Returns darker purple for hover state
5366
- * generateHoverColor('#FF0000', '#FFFFFF', 0.2) // Lighten red by 20%
5367
- */
5368
- private generateHoverColor;
5369
- /**
5370
- * Generate an active/pressed state color (darker than hover)
5371
- * Uses 20% black overlay for a more pronounced pressed effect
5372
- *
5373
- * @param baseColor - Hex color to overlay on
5374
- * @returns Hex color for active/pressed state
5375
- */
5376
- private generateActiveColor;
5963
+ declare class DsAppIconComponent {
5964
+ whitelabelService: WhitelabelService;
5377
5965
  /**
5378
- * Apply colors to CSS custom properties and native StatusBar
5379
- * This updates the actual CSS variables used throughout the app
5380
- * and the native status bar color on mobile devices
5966
+ * Size of the app icon
5967
+ * - sm: 16x16px (padding: 2.4px, radius: 3.2px)
5968
+ * - md: 32x32px (padding: 4.8px, radius: 6.4px)
5969
+ * - lg: 48x48px (padding: 7.2px, radius: 9.6px)
5970
+ * - xl: 64x64px (padding: 9.6px, radius: 12.8px)
5381
5971
  */
5382
- private applyColors;
5972
+ size: AppIconSize;
5383
5973
  /**
5384
- * Update the native status bar color
5385
- * Note: This only works on Android. On iOS, the status bar is transparent
5386
- * and shows the web content behind it (controlled by CSS variables)
5974
+ * Computed CSS class for size variant
5387
5975
  */
5388
- private updateNativeStatusBar;
5389
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<WhitelabelService, never>;
5390
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<WhitelabelService>;
5976
+ sizeClass: _angular_core.Signal<string>;
5977
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsAppIconComponent, never>;
5978
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsAppIconComponent, "ds-app-icon", never, { "size": { "alias": "size"; "required": false; }; }, {}, never, never, true, never>;
5391
5979
  }
5392
5980
 
5393
5981
  type AvatarType = 'initials' | 'photo' | 'icon';
@@ -5419,10 +6007,92 @@ declare class DsAvatarWithBadgeComponent {
5419
6007
  showBadge: boolean;
5420
6008
  badgePosition: BadgePosition;
5421
6009
  badgeClasses: _angular_core.Signal<string>;
6010
+ /**
6011
+ * Computed badge icon size that scales with avatar size
6012
+ * Maps avatar sizes to appropriate app icon sizes
6013
+ */
6014
+ badgeIconSize: _angular_core.Signal<AppIconSize>;
5422
6015
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsAvatarWithBadgeComponent, never>;
5423
6016
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsAvatarWithBadgeComponent, "ds-avatar-with-badge", never, { "type": { "alias": "type"; "required": false; }; "size": { "alias": "size"; "required": false; }; "initials": { "alias": "initials"; "required": false; }; "src": { "alias": "src"; "required": false; }; "iconName": { "alias": "iconName"; "required": false; }; "showBadge": { "alias": "showBadge"; "required": false; }; "badgePosition": { "alias": "badgePosition"; "required": false; }; }, {}, never, never, true, never>;
5424
6017
  }
5425
6018
 
6019
+ /**
6020
+ * DsMobileActionListItemComponent
6021
+ *
6022
+ * Specialized list item for action sheets and menus.
6023
+ * Wraps ds-mobile-list-item with action-specific styling:
6024
+ * - Vertically centered content
6025
+ * - Interactive by default
6026
+ * - No dividers (controlled per-item)
6027
+ *
6028
+ * @example
6029
+ * ```html
6030
+ * <ds-mobile-action-list-item
6031
+ * title="Edit"
6032
+ * [showDivider]="true"
6033
+ * (itemClick)="handleEdit()">
6034
+ * <ds-icon content-leading name="remixEditLine" size="20px" />
6035
+ * </ds-mobile-action-list-item>
6036
+ * ```
6037
+ */
6038
+ declare class DsMobileActionListItemComponent {
6039
+ /**
6040
+ * Action title text
6041
+ */
6042
+ title: _angular_core.InputSignal<string>;
6043
+ /**
6044
+ * Whether to show divider below item
6045
+ * @default false
6046
+ */
6047
+ showDivider: _angular_core.InputSignal<boolean>;
6048
+ /**
6049
+ * Whether the action is disabled
6050
+ * @default false
6051
+ */
6052
+ disabled: _angular_core.InputSignal<boolean>;
6053
+ /**
6054
+ * Emits when the action item is clicked
6055
+ */
6056
+ itemClick: _angular_core.OutputEmitterRef<void>;
6057
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileActionListItemComponent, never>;
6058
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileActionListItemComponent, "ds-mobile-action-list-item", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "showDivider": { "alias": "showDivider"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["[action-icon]", "[content-trailing]"], true, never>;
6059
+ }
6060
+
6061
+ type LogoVariant = 'full' | 'mark';
6062
+ type LogoSize = 'sm' | 'md' | 'lg' | 'xl';
6063
+ /**
6064
+ * DsLogoComponent
6065
+ *
6066
+ * Displays the whitelabeled logo or logomark based on current configuration.
6067
+ * Automatically pulls logo assets from WhitelabelService.
6068
+ *
6069
+ * @example
6070
+ * Full logo in header:
6071
+ * ```html
6072
+ * <ds-logo variant="full" size="md" />
6073
+ * ```
6074
+ *
6075
+ * Logomark for compact spaces:
6076
+ * ```html
6077
+ * <ds-logo variant="mark" size="sm" />
6078
+ * ```
6079
+ */
6080
+ declare class DsLogoComponent {
6081
+ whitelabelService: WhitelabelService;
6082
+ variant: LogoVariant;
6083
+ size: LogoSize;
6084
+ customHeight?: number;
6085
+ customWidth?: number;
6086
+ get logoSrc(): string;
6087
+ get logoAlt(): string;
6088
+ /**
6089
+ * Priority: customHeight input > whitelabel config logoHeight > default 32px
6090
+ */
6091
+ get effectiveHeight(): number;
6092
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsLogoComponent, never>;
6093
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsLogoComponent, "ds-logo", never, { "variant": { "alias": "variant"; "required": false; }; "size": { "alias": "size"; "required": false; }; "customHeight": { "alias": "customHeight"; "required": false; }; "customWidth": { "alias": "customWidth"; "required": false; }; }, {}, never, never, true, never>;
6094
+ }
6095
+
5426
6096
  /**
5427
6097
  * DsMobileEmptyStateComponent
5428
6098
  *
@@ -5439,7 +6109,7 @@ declare class DsAvatarWithBadgeComponent {
5439
6109
  * ```html
5440
6110
  * <!-- With image -->
5441
6111
  * <ds-mobile-empty-state
5442
- * [imageSrc]="'/Assets/Empty state-chat.png'"
6112
+ * [imageSrc]="'/Assets/empty-state-inquiry.svg'"
5443
6113
  * [imageAlt]="'No messages'"
5444
6114
  * [title]="'Ingen beskeder endnu'"
5445
6115
  * [description]="'Start samtalen ved at sende en besked'">
@@ -5473,6 +6143,126 @@ declare class DsMobileEmptyStateComponent {
5473
6143
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileEmptyStateComponent, "ds-mobile-empty-state", never, { "imageSrc": { "alias": "imageSrc"; "required": false; "isSignal": true; }; "imageAlt": { "alias": "imageAlt"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
5474
6144
  }
5475
6145
 
6146
+ /**
6147
+ * DsMobileOfflineBannerComponent
6148
+ *
6149
+ * A compact banner component to indicate offline status.
6150
+ * Designed to be used in the [offline-indicator] slot of page components.
6151
+ *
6152
+ * Features:
6153
+ * - Amber/yellow warning styling
6154
+ * - WiFi off icon
6155
+ * - Customizable message
6156
+ * - Compact, non-intrusive design
6157
+ * - Smooth slide-in animation
6158
+ *
6159
+ * @example
6160
+ * ```html
6161
+ * <ds-mobile-page-main title="Community">
6162
+ * <ds-mobile-offline-banner
6163
+ * offline-indicator
6164
+ * *ngIf="pageComponent.isOffline()">
6165
+ * </ds-mobile-offline-banner>
6166
+ *
6167
+ * <!-- Page content -->
6168
+ * </ds-mobile-page-main>
6169
+ * ```
6170
+ */
6171
+ declare class DsMobileOfflineBannerComponent {
6172
+ /**
6173
+ * Icon to display (default: WiFi off icon)
6174
+ */
6175
+ icon: _angular_core.InputSignal<string>;
6176
+ /**
6177
+ * Title text for the banner
6178
+ */
6179
+ title: _angular_core.InputSignal<string>;
6180
+ /**
6181
+ * Optional secondary message
6182
+ */
6183
+ message: _angular_core.InputSignal<string>;
6184
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileOfflineBannerComponent, never>;
6185
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileOfflineBannerComponent, "ds-mobile-offline-banner", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6186
+ }
6187
+
6188
+ /**
6189
+ * DsMobileIllustrationComponent
6190
+ *
6191
+ * A component that displays illustrations with dynamic whitelabel color adaptation.
6192
+ * Inlines SVG and uses CSS variables to adapt colors to your theme.
6193
+ *
6194
+ * **Features:**
6195
+ * - Predefined variants (post, inquiry) for common empty states
6196
+ * - Automatic color adaptation using CSS variables
6197
+ * - Preserves all SVG details (textures, filters, gradients, shadows)
6198
+ * - White radial gradient overlay for depth effect
6199
+ * - Configurable size
6200
+ *
6201
+ * **Color Adaptation:**
6202
+ * The SVGs use the `--color-accent` CSS variable for the main page colors.
6203
+ * All texture details, shadows, and effects are preserved.
6204
+ *
6205
+ * @example
6206
+ * ```html
6207
+ * <!-- Using predefined variant -->
6208
+ * <ds-mobile-illustration variant="post" />
6209
+ *
6210
+ * <!-- Using predefined variant with custom size -->
6211
+ * <ds-mobile-illustration variant="inquiry" size="150px" />
6212
+ * ```
6213
+ */
6214
+ declare class DsMobileIllustrationComponent {
6215
+ private sanitizer;
6216
+ /**
6217
+ * Predefined illustration variant
6218
+ * Available variants: 'post', 'inquiry'
6219
+ */
6220
+ variant: _angular_core.InputSignal<"post" | "inquiry">;
6221
+ /**
6222
+ * Illustration size (width and height)
6223
+ * @default '120px'
6224
+ */
6225
+ size: _angular_core.InputSignal<string>;
6226
+ constructor(sanitizer: DomSanitizer);
6227
+ /**
6228
+ * Inline SVG content for each variant
6229
+ */
6230
+ private svgMap;
6231
+ /**
6232
+ * Computed SVG content - sanitized for security
6233
+ */
6234
+ svgContent: _angular_core.Signal<SafeHtml>;
6235
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobileIllustrationComponent, never>;
6236
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobileIllustrationComponent, "ds-mobile-illustration", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
6237
+ }
6238
+
6239
+ /**
6240
+ * DsMobilePropertyBannerComponent
6241
+ *
6242
+ * Compact banner displaying property photo and address.
6243
+ * Designed for use in page headers to show current property context.
6244
+ *
6245
+ * @example
6246
+ * ```html
6247
+ * <ds-mobile-property-banner
6248
+ * address="Toftegårds Allé 5A, 2. tv."
6249
+ * photoUrl="/Assets/building.jpg">
6250
+ * </ds-mobile-property-banner>
6251
+ * ```
6252
+ */
6253
+ declare class DsMobilePropertyBannerComponent {
6254
+ /**
6255
+ * Property address text
6256
+ */
6257
+ address: _angular_core.InputSignal<string>;
6258
+ /**
6259
+ * URL to property photo
6260
+ */
6261
+ photoUrl: _angular_core.InputSignal<string>;
6262
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DsMobilePropertyBannerComponent, never>;
6263
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DsMobilePropertyBannerComponent, "ds-mobile-property-banner", never, { "address": { "alias": "address"; "required": true; "isSignal": true; }; "photoUrl": { "alias": "photoUrl"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
6264
+ }
6265
+
5476
6266
  /**
5477
6267
  * User service for managing current user data globally
5478
6268
  */
@@ -5615,6 +6405,8 @@ declare class MobileCommunityPageComponent {
5615
6405
  private postModal;
5616
6406
  userService: UserService;
5617
6407
  private postsService;
6408
+ pageComponent: DsMobilePageMainComponent;
6409
+ pinnedSwiper?: DsMobileSwiperComponent;
5618
6410
  allPosts: _angular_core.Signal<Post[]>;
5619
6411
  pinnedPosts: _angular_core.Signal<Post[]>;
5620
6412
  hasAnyPosts: _angular_core.Signal<boolean>;
@@ -5642,12 +6434,29 @@ declare class MobileCommunityPageComponent {
5642
6434
  * Handle long press on a post to show action sheet
5643
6435
  */
5644
6436
  handlePostLongPress(postId: string, isOwnPost: boolean): Promise<void>;
6437
+ /**
6438
+ * Navigate to previous slide in pinned posts
6439
+ */
6440
+ slidePrev(): void;
6441
+ /**
6442
+ * Navigate to next slide in pinned posts
6443
+ */
6444
+ slideNext(): void;
6445
+ /**
6446
+ * Check if currently on first slide
6447
+ */
6448
+ isFirstSlide(): boolean;
6449
+ /**
6450
+ * Check if currently on last slide
6451
+ */
6452
+ isLastSlide(): boolean;
5645
6453
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileCommunityPageComponent, never>;
5646
6454
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileCommunityPageComponent, "app-mobile-community-page", never, {}, {}, never, never, true, never>;
5647
6455
  }
5648
6456
 
5649
6457
  declare class MobileHandbookPageComponent {
5650
6458
  userService: UserService;
6459
+ pageComponent: DsMobilePageMainComponent;
5651
6460
  utilitiesItems: HandbookItem[];
5652
6461
  sikkerhedsudstyrItems: HandbookItem[];
5653
6462
  serviceContractsItems: HandbookItem[];
@@ -5663,6 +6472,7 @@ declare class MobileHomePageComponent {
5663
6472
  userService: UserService;
5664
6473
  private postsService;
5665
6474
  private postModal;
6475
+ pageComponent: DsMobilePageMainComponent;
5666
6476
  recentPosts: _angular_core.Signal<_propbinder_mobile_design.Post[]>;
5667
6477
  private allInquiries;
5668
6478
  openInquiries: _angular_core.Signal<{
@@ -5694,6 +6504,7 @@ declare class MobileInquiriesPageComponent {
5694
6504
  userService: UserService;
5695
6505
  private navCtrl;
5696
6506
  private newInquiryModal;
6507
+ pageComponent: DsMobilePageMainComponent;
5697
6508
  constructor(userService: UserService, navCtrl: NavController, newInquiryModal: DsMobileNewInquiryModalService);
5698
6509
  filterStatus: _angular_core.WritableSignal<"open" | "closed" | "all">;
5699
6510
  tabItems: InlineTabItem[];
@@ -5731,6 +6542,7 @@ declare class MobileInquiryDetailPageComponent {
5731
6542
  messageThreads: MessageThread[];
5732
6543
  unreadMessagesCount: _angular_core.Signal<number>;
5733
6544
  photos: LightboxImage[];
6545
+ get photoUrls(): string[];
5734
6546
  constructor(userService: UserService, lightbox: DsMobileLightboxService, chatModal: DsMobileChatModalService);
5735
6547
  setActiveTab(tabId: string): void;
5736
6548
  goBack(): void;
@@ -5776,6 +6588,9 @@ declare class MobileTabsExampleComponent implements OnInit {
5776
6588
  * Handle profile menu action selection.
5777
6589
  * The tab bar component handles the UI (opening/closing menu),
5778
6590
  * this method handles the business logic.
6591
+ *
6592
+ * NOTE: Desktop only - called directly from tab bar.
6593
+ * Mobile actions are handled globally in AppComponent via UserService.
5779
6594
  */
5780
6595
  handleProfileAction(result: ActionResult): void;
5781
6596
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileTabsExampleComponent, never>;
@@ -5835,40 +6650,127 @@ declare class MobilePostDetailPageComponent {
5835
6650
  *
5836
6651
  * Sign-in page with email authentication.
5837
6652
  * Features logomark, decorative city background, and form validation.
6653
+ * Auto-focuses email input on iOS to trigger keyboard.
5838
6654
  */
5839
- declare class SignInPageComponent {
6655
+ declare class SignInPageComponent implements AfterViewInit, OnDestroy {
6656
+ emailInputRef?: ElementRef;
6657
+ whitelabelService: WhitelabelService;
5840
6658
  email: string;
5841
6659
  emailError: _angular_core.WritableSignal<boolean>;
5842
6660
  isSubmitting: _angular_core.WritableSignal<boolean>;
5843
6661
  emailSent: _angular_core.WritableSignal<boolean>;
6662
+ ngAfterViewInit(): void;
6663
+ /**
6664
+ * Show the keyboard on mobile platforms
6665
+ */
6666
+ private showKeyboard;
5844
6667
  handleSubmit(): void;
5845
6668
  handleBackToLogin(): void;
6669
+ /**
6670
+ * Update status bar for sign-in page background
6671
+ * Uses the sign-in background color to determine appropriate status bar style
6672
+ */
6673
+ private updateSignInStatusBar;
6674
+ /**
6675
+ * Restore status bar to header color when leaving sign-in page
6676
+ */
6677
+ ngOnDestroy(): void;
6678
+ /**
6679
+ * Restore status bar to use header surface color
6680
+ */
6681
+ private restoreHeaderStatusBar;
5846
6682
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SignInPageComponent, never>;
5847
6683
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SignInPageComponent, "app-sign-in", never, {}, {}, never, never, true, never>;
5848
6684
  }
5849
6685
 
5850
6686
  /**
5851
- * Whitelabel Demo Page
6687
+ * Whitelabel Demo Modal Component
5852
6688
  *
5853
6689
  * Demonstrates the whitelabeling system with theme selection, brand colors, and logo previews.
6690
+ * Opens as a full-screen modal similar to post details.
5854
6691
  */
5855
- declare class WhitelabelDemoPage implements OnInit {
6692
+ declare class WhitelabelDemoModalComponent implements OnInit {
5856
6693
  whitelabelService: WhitelabelService;
6694
+ private modalController;
5857
6695
  currentTheme: string;
5858
- customPrimarySurface: string;
5859
- customPrimaryContent: string;
5860
- customSecondarySurface: string;
5861
- customSecondaryContent: string;
6696
+ customAppIconSurface: string;
6697
+ customAppIconContent: string;
6698
+ customAccent: string;
6699
+ customOnAccent: string;
6700
+ customHeaderSurface: string;
6701
+ customHeaderContent: string;
6702
+ customHeaderAccent: string;
6703
+ customOnHeaderAccent: string;
6704
+ customSignInBgSolid: string;
6705
+ customSignInBgGradientStart: string;
6706
+ customSignInBgGradientEnd: string;
6707
+ customSignInContentColor: string;
5862
6708
  ngOnInit(): void;
6709
+ /**
6710
+ * Detect the current active theme based on colors
6711
+ */
6712
+ private detectCurrentTheme;
6713
+ /**
6714
+ * Close the modal
6715
+ */
6716
+ close(): void;
5863
6717
  applyDefaultTheme(): void;
5864
6718
  applyCejTheme(): void;
5865
6719
  applyPkaTheme(): void;
5866
6720
  applyClaveTheme(): void;
5867
6721
  applyFreedomTheme(): void;
5868
6722
  applyCustomColors(): void;
6723
+ toggleCityIllustration(): void;
6724
+ updateSignInBgType(type: 'solid' | 'gradient'): void;
6725
+ updateLogoSize(size: 'sm' | 'md' | 'lg' | 'xl'): void;
6726
+ applySignInBackground(): void;
6727
+ applySignInContentColor(): void;
6728
+ private updateSignInBgInputs;
6729
+ private updateSignInContentColorInput;
5869
6730
  private updateCustomColorInputs;
5870
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<WhitelabelDemoPage, never>;
5871
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WhitelabelDemoPage, "app-whitelabel-demo", never, {}, {}, never, never, true, never>;
6731
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<WhitelabelDemoModalComponent, never>;
6732
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WhitelabelDemoModalComponent, "ds-whitelabel-demo-modal", never, {}, {}, never, never, true, never>;
6733
+ }
6734
+
6735
+ /**
6736
+ * WhitelabelDemoModalService
6737
+ *
6738
+ * Service for displaying the whitelabel demo in a full-screen modal.
6739
+ * Built on Ionic's modal system with native gestures and animations.
6740
+ *
6741
+ * @example
6742
+ * ```typescript
6743
+ * constructor(private whitelabelModal: WhitelabelDemoModalService) {}
6744
+ *
6745
+ * async openDemo() {
6746
+ * await this.whitelabelModal.open();
6747
+ * }
6748
+ * ```
6749
+ */
6750
+ declare class WhitelabelDemoModalService {
6751
+ private modalController;
6752
+ constructor(modalController: ModalController);
6753
+ /**
6754
+ * Open the whitelabel demo modal
6755
+ *
6756
+ * @returns Promise that resolves when the modal is presented
6757
+ */
6758
+ open(): Promise<void>;
6759
+ /**
6760
+ * Close the currently open whitelabel demo modal
6761
+ *
6762
+ * @param data Optional data to pass back when dismissing
6763
+ * @returns Promise that resolves when the modal is dismissed
6764
+ */
6765
+ close(data?: any): Promise<boolean>;
6766
+ /**
6767
+ * Get the top-most modal if one exists
6768
+ *
6769
+ * @returns Promise that resolves to the modal element or undefined
6770
+ */
6771
+ getTop(): Promise<HTMLIonModalElement | undefined>;
6772
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<WhitelabelDemoModalService, never>;
6773
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<WhitelabelDemoModalService>;
5872
6774
  }
5873
6775
 
5874
6776
  /**
@@ -5893,5 +6795,5 @@ declare const customPageTransition: (_: HTMLElement, opts: any) => Animation;
5893
6795
  */
5894
6796
  declare const customBackTransition: (_: HTMLElement, opts: any) => Animation;
5895
6797
 
5896
- export { ActionCommentComponent, ActionLikeComponent, ContentRowComponent, DsAvatarWithBadgeComponent, DsMobileActionsBottomSheetComponent, DsMobileAttachmentPreviewComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileContentSectionComponent, DsMobileDropdownComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileLongPressDirective, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileProfileActionsSheetComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsTextInputComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, SectionHeaderComponent, SignInPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, UserService, WhitelabelDemoPage, WhitelabelService, customBackTransition, customPageTransition };
5897
- export type { ActionGroup, ActionItem, ActionResult, AttachmentData, AttachmentFileType, AttachmentItem, AvatarSize, AvatarType, BadgePosition, BottomSheetOptions, ChatAttachment, ChatMessage, ChatModalData, ChatParticipant, Comment, ActionResult as CommentActionResult, CommentData, ContactItem, ContentWidth, DropdownAlign, DropdownPosition, DsMobileDropdownItem, HandbookDetailData, HandbookItem, InlineTabItem, InquiryPhoto, Language, LightboxAuthor, LightboxImage, LightboxImageOptions, LightboxMediaFile, LightboxMediaType, LightboxOptions, LightboxPdf, LightboxPdfOptions, ModalOptions, NewInquiryData, NewInquiryModalOptions, Post, ActionResult as PostActionResult, PostDetailData, TabConfig, WhitelabelConfig };
6798
+ export { ActionCommentComponent, ActionLikeComponent, ContentRowComponent, DsAppIconComponent, DsAvatarWithBadgeComponent, DsLogoComponent, DsMobileActionListItemComponent, DsMobileActionsBottomSheetComponent, DsMobileAttachmentPreviewComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileDropdownComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileFileAttachmentComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileIllustrationComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileLoaderOverlayComponent, DsMobileLongPressDirective, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobileOfflineBannerComponent, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileProfileActionsSheetComponent, DsMobilePropertyBannerComponent, DsMobileSectionComponent, DsMobileSwiperComponent, DsMobileSystemMessageBannerComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsTextInputComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, SectionHeaderComponent, SignInPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, UserService, WhitelabelDemoModalComponent, WhitelabelDemoModalService, WhitelabelService, customBackTransition, customPageTransition };
6799
+ export type { ActionGroup, ActionItem, ActionResult, AppIconSize, AttachmentData, AttachmentFileType, AttachmentItem, AvatarSize, AvatarType, BadgePosition, BottomSheetOptions, ChatAttachment, ChatMessage, ChatModalData, ChatParticipant, Comment, ActionResult as CommentActionResult, CommentData, ContactItem, ContentWidth, DropdownAlign, DropdownPosition, DsMobileDropdownItem, HandbookDetailData, HandbookItem, InlineTabItem, InquiryPhoto, Language, LightboxAuthor, LightboxImage, LightboxImageOptions, LightboxMediaFile, LightboxMediaType, LightboxOptions, LightboxPdf, LightboxPdfOptions, LogoSize, LogoVariant, ModalOptions, NetworkStatus, NewInquiryData, NewInquiryModalOptions, Post, ActionResult as PostActionResult, PostDetailData, TabConfig, WhitelabelConfig };