@propbinder/mobile-design 0.1.16 → 0.1.18

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.
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, effect, Injectable, inject, Input, Component, input, Directive, EventEmitter, HostListener, Output, PLATFORM_ID, output, ViewChild, ViewEncapsulation, CUSTOM_ELEMENTS_SCHEMA, model, ElementRef, createComponent, forwardRef } from '@angular/core';
2
+ import { signal, computed, effect, Injectable, inject, Input, Component, input, Directive, EventEmitter, HostListener, Output, PLATFORM_ID, output, ViewChild, model, ElementRef, CUSTOM_ELEMENTS_SCHEMA, createComponent, forwardRef, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1$2 from '@angular/common';
4
4
  import { CommonModule, isPlatformBrowser } from '@angular/common';
5
5
  import * as i1$3 from '@angular/router';
6
6
  import { Router, NavigationEnd } from '@angular/router';
7
7
  import * as i1 from '@ionic/angular/standalone';
8
- import { IonHeader, IonToolbar, IonTitle, IonContent, IonButtons, ModalController, Platform, IonRefresher, IonRefresherContent, IonTabBar, IonTabButton, IonLabel, IonTabs, IonTab, IonSpinner, IonButton } from '@ionic/angular/standalone';
8
+ import { IonHeader, IonToolbar, IonTitle, IonContent, IonButtons, Platform, ModalController, IonRefresher, IonRefresherContent, IonTabBar, IonTabButton, IonLabel, IonTabs, IonTab, IonSpinner, IonButton } from '@ionic/angular/standalone';
9
9
  import { ImpactStyle, Haptics } from '@capacitor/haptics';
10
10
  import { DsIconButtonComponent, DsIconComponent, DsButtonComponent, DsAvatarComponent, DsShapeIndicatorComponent } from '@propbinder/design-system';
11
11
  import { StatusBar } from '@capacitor/status-bar';
@@ -1815,1108 +1815,576 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
1815
1815
  }] } });
1816
1816
 
1817
1817
  /**
1818
- * DsAvatarWithBadgeComponent
1819
- *
1820
- * Displays an avatar with a logomark badge overlay.
1821
- * Useful for showing user avatars with organization branding.
1822
- *
1823
- * @example
1824
- * ```html
1825
- * <ds-avatar-with-badge
1826
- * [type]="'initials'"
1827
- * [initials]="'JD'"
1828
- * [size]="'lg'"
1829
- * [badgePosition]="'bottom-right'"
1830
- * />
1831
- * ```
1818
+ * User service for managing current user data globally
1832
1819
  */
1833
- class DsAvatarWithBadgeComponent {
1834
- whitelabelService = inject(WhitelabelService);
1835
- // Avatar props
1836
- type = 'initials';
1837
- size = 'md';
1838
- initials = '';
1839
- src = '';
1840
- iconName = 'remixUser3Fill';
1841
- // Badge props
1842
- showBadge = true;
1843
- badgePosition = 'bottom-right';
1844
- badgeClasses = computed(() => {
1845
- return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
1846
- }, ...(ngDevMode ? [{ debugName: "badgeClasses" }] : []));
1847
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1848
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsAvatarWithBadgeComponent, isStandalone: true, selector: "ds-avatar-with-badge", inputs: { type: "type", size: "size", initials: "initials", src: "src", iconName: "iconName", showBadge: "showBadge", badgePosition: "badgePosition" }, ngImport: i0, template: `
1849
- <div class="avatar-badge-container">
1850
- <ds-avatar
1851
- [type]="type"
1852
- [size]="size"
1853
- [initials]="initials"
1854
- [src]="src"
1855
- [iconName]="iconName"
1856
- />
1857
-
1858
- @if (showBadge) {
1859
- <div [class]="badgeClasses()">
1860
- <img [src]="whitelabelService.logoMarkUrl()" [alt]="whitelabelService.logoAlt()" />
1861
- </div>
1862
- }
1863
- </div>
1864
- `, isInline: true, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:var(--color-brand-secondary, #5d5fef);border-radius:8px;display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge img{width:10px;height:10px;object-fit:contain}.avatar-badge--bottom-right{bottom:-6px;right:-6px}.avatar-badge--bottom-left{bottom:-6px;left:-6px}.avatar-badge--top-right{top:-6px;right:-6px}.avatar-badge--top-left{top:-6px;left:-6px}.avatar-badge--xs{width:16px;height:16px}.avatar-badge--sm{width:18px;height:18px}.avatar-badge--md{width:20px;height:20px}.avatar-badge--lg{width:24px;height:24px}.avatar-badge--xl{width:28px;height:28px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }] });
1820
+ class UserService {
1821
+ // User avatar configuration
1822
+ _avatarInitials = signal('LM', ...(ngDevMode ? [{ debugName: "_avatarInitials" }] : []));
1823
+ _avatarType = signal('initials', ...(ngDevMode ? [{ debugName: "_avatarType" }] : []));
1824
+ _avatarSrc = signal('', ...(ngDevMode ? [{ debugName: "_avatarSrc" }] : []));
1825
+ // Profile menu items configuration
1826
+ _profileMenuItems = signal(undefined, ...(ngDevMode ? [{ debugName: "_profileMenuItems" }] : []));
1827
+ // Readonly computed values
1828
+ avatarInitials = this._avatarInitials.asReadonly();
1829
+ avatarType = this._avatarType.asReadonly();
1830
+ avatarSrc = this._avatarSrc.asReadonly();
1831
+ profileMenuItems = this._profileMenuItems.asReadonly();
1832
+ /**
1833
+ * Update avatar configuration
1834
+ */
1835
+ setAvatarInitials(initials) {
1836
+ this._avatarInitials.set(initials);
1837
+ }
1838
+ setAvatarType(type) {
1839
+ this._avatarType.set(type);
1840
+ }
1841
+ setAvatarSrc(src) {
1842
+ this._avatarSrc.set(src);
1843
+ }
1844
+ /**
1845
+ * Set profile menu items globally.
1846
+ * This will be used by both ds-mobile-tab-bar and ds-mobile-page-main
1847
+ * if they don't receive profileMenuItems as an input.
1848
+ */
1849
+ setProfileMenuItems(items) {
1850
+ this._profileMenuItems.set(items);
1851
+ }
1852
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1853
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, providedIn: 'root' });
1865
1854
  }
1866
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, decorators: [{
1867
- type: Component,
1868
- args: [{ selector: 'ds-avatar-with-badge', standalone: true, imports: [CommonModule, DsAvatarComponent], encapsulation: ViewEncapsulation.Emulated, template: `
1869
- <div class="avatar-badge-container">
1870
- <ds-avatar
1871
- [type]="type"
1872
- [size]="size"
1873
- [initials]="initials"
1874
- [src]="src"
1875
- [iconName]="iconName"
1876
- />
1877
-
1878
- @if (showBadge) {
1879
- <div [class]="badgeClasses()">
1880
- <img [src]="whitelabelService.logoMarkUrl()" [alt]="whitelabelService.logoAlt()" />
1881
- </div>
1882
- }
1883
- </div>
1884
- `, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:var(--color-brand-secondary, #5d5fef);border-radius:8px;display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge img{width:10px;height:10px;object-fit:contain}.avatar-badge--bottom-right{bottom:-6px;right:-6px}.avatar-badge--bottom-left{bottom:-6px;left:-6px}.avatar-badge--top-right{top:-6px;right:-6px}.avatar-badge--top-left{top:-6px;left:-6px}.avatar-badge--xs{width:16px;height:16px}.avatar-badge--sm{width:18px;height:18px}.avatar-badge--md{width:20px;height:20px}.avatar-badge--lg{width:24px;height:24px}.avatar-badge--xl{width:28px;height:28px}\n"] }]
1885
- }], propDecorators: { type: [{
1886
- type: Input
1887
- }], size: [{
1888
- type: Input
1889
- }], initials: [{
1890
- type: Input
1891
- }], src: [{
1892
- type: Input
1893
- }], iconName: [{
1894
- type: Input
1895
- }], showBadge: [{
1896
- type: Input
1897
- }], badgePosition: [{
1898
- type: Input
1899
- }] } });
1855
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, decorators: [{
1856
+ type: Injectable,
1857
+ args: [{
1858
+ providedIn: 'root'
1859
+ }]
1860
+ }] });
1900
1861
 
1901
1862
  /**
1902
- * Whitelabel Demo Modal Component
1863
+ * DsMobilePageMainComponent
1903
1864
  *
1904
- * Demonstrates the whitelabeling system with theme selection, brand colors, and logo previews.
1905
- * Opens as a full-screen modal similar to post details.
1865
+ * A complete mobile page layout for main/tab pages with:
1866
+ * - Fixed header with logomark + title + avatar
1867
+ * - Purple expandable header section (scrolls with content)
1868
+ * - White rounded content wrapper
1869
+ * - Pull-to-refresh support
1870
+ * - Auto scroll title fade-in
1871
+ *
1872
+ * @example
1873
+ * ```html
1874
+ * <!-- Simple page -->
1875
+ * <ds-mobile-page-main
1876
+ * title="Inquiries"
1877
+ * [avatarInitials]="'JD'">
1878
+ * <div class="page-content">
1879
+ * <!-- Your content -->
1880
+ * </div>
1881
+ * </ds-mobile-page-main>
1882
+ *
1883
+ * <!-- Page with custom header content -->
1884
+ * <ds-mobile-page-main
1885
+ * title="Home"
1886
+ * headerTitle="Welcome, Lars"
1887
+ * headerSubtitle="Your rental property at a glance."
1888
+ * [avatarInitials]="'L'">
1889
+ *
1890
+ * <div header-content class="property-tiles">
1891
+ * <!-- Custom header content like tiles -->
1892
+ * </div>
1893
+ *
1894
+ * <div class="page-content">
1895
+ * <!-- Main page content -->
1896
+ * </div>
1897
+ * </ds-mobile-page-main>
1898
+ * ```
1906
1899
  */
1907
- class WhitelabelDemoModalComponent {
1908
- whitelabelService = inject(WhitelabelService);
1900
+ class DsMobilePageMainComponent extends MobilePageBase {
1901
+ elementRef;
1902
+ ionContent;
1903
+ // Platform detection
1904
+ platform = inject(Platform);
1909
1905
  modalController = inject(ModalController);
1910
- // Current active theme
1911
- currentTheme = 'default';
1912
- // Custom color inputs
1913
- customPrimarySurface = '#6B5FF5';
1914
- customPrimaryContent = '#FFFFFF';
1915
- customSecondarySurface = '#221a4c';
1916
- customSecondaryContent = '#FFFFFF';
1917
- ngOnInit() {
1918
- this.updateCustomColorInputs();
1919
- this.detectCurrentTheme();
1920
- }
1906
+ router = inject(Router);
1907
+ userService = inject(UserService);
1908
+ // Computed property to check if running on native platform
1909
+ isNativePlatform = computed(() => this.platform.is('ios') ||
1910
+ this.platform.is('android') ||
1911
+ this.platform.is('capacitor'), ...(ngDevMode ? [{ debugName: "isNativePlatform" }] : []));
1912
+ // Inputs - Title
1913
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : [])); // For fixed header title
1914
+ headerTitle = input('', ...(ngDevMode ? [{ debugName: "headerTitle" }] : [])); // Optional different title for expandable header
1915
+ headerSubtitle = input('', ...(ngDevMode ? [{ debugName: "headerSubtitle" }] : []));
1916
+ // Inputs - Avatar
1917
+ avatarType = input('initials', ...(ngDevMode ? [{ debugName: "avatarType" }] : []));
1918
+ avatarInitials = input('U', ...(ngDevMode ? [{ debugName: "avatarInitials" }] : []));
1919
+ avatarSrc = input('', ...(ngDevMode ? [{ debugName: "avatarSrc" }] : []));
1920
+ avatarIconName = input('remixUser3Line', ...(ngDevMode ? [{ debugName: "avatarIconName" }] : []));
1921
+ // Inputs - Features
1922
+ showRefresh = input(true, ...(ngDevMode ? [{ debugName: "showRefresh" }] : []));
1923
+ showCondensedHeader = input(true, ...(ngDevMode ? [{ debugName: "showCondensedHeader" }] : []));
1924
+ scrollThreshold = input(160, ...(ngDevMode ? [{ debugName: "scrollThreshold" }] : [])); // Pixels to scroll before title appears
1925
+ headerFadeDistance = input(150, ...(ngDevMode ? [{ debugName: "headerFadeDistance" }] : [])); // Distance over which header fades out
1921
1926
  /**
1922
- * Detect the current active theme based on colors
1927
+ * Profile menu action groups to display when avatar is clicked.
1928
+ * If not provided, a default menu will be used (without Whitelabel Demo).
1929
+ *
1930
+ * @example
1931
+ * ```typescript
1932
+ * profileMenuItems: ActionGroup[] = [
1933
+ * {
1934
+ * actions: [
1935
+ * { action: 'profile', title: 'Min profil', icon: 'remixUser3Line' },
1936
+ * { action: 'settings', title: 'Indstillinger', icon: 'remixSettings3Line' }
1937
+ * ]
1938
+ * },
1939
+ * {
1940
+ * actions: [
1941
+ * { action: 'logout', title: 'Log ud', icon: 'remixLogoutBoxLine', destructive: true }
1942
+ * ]
1943
+ * }
1944
+ * ];
1945
+ * ```
1923
1946
  */
1924
- detectCurrentTheme() {
1925
- const secondarySurface = this.whitelabelService.secondarySurface().toUpperCase();
1926
- if (secondarySurface === '#A70923') {
1927
- this.currentTheme = 'cej';
1928
- }
1929
- else if (secondarySurface === '#660036') {
1930
- this.currentTheme = 'pka';
1931
- }
1932
- else if (secondarySurface === '#262424') {
1933
- this.currentTheme = 'clave';
1934
- }
1935
- else {
1936
- this.currentTheme = 'default';
1937
- }
1938
- }
1947
+ profileMenuItems = input(undefined, ...(ngDevMode ? [{ debugName: "profileMenuItems" }] : []));
1948
+ // Outputs
1949
+ avatarClick = output();
1939
1950
  /**
1940
- * Close the modal
1951
+ * Emitted when a profile menu action is selected.
1952
+ * Parent component should handle the action logic (navigation, logout, etc.).
1941
1953
  */
1942
- close() {
1943
- this.modalController.dismiss();
1954
+ profileActionSelected = output();
1955
+ refresh = output();
1956
+ scroll = output();
1957
+ constructor(elementRef) {
1958
+ super();
1959
+ this.elementRef = elementRef;
1944
1960
  }
1945
- applyDefaultTheme() {
1946
- this.currentTheme = 'default';
1947
- this.whitelabelService.updateConfig({
1948
- logoUrl: '/Assets/logos/propbinder-logomark.svg',
1949
- logoMarkUrl: '/Assets/logos/propbinder-logomark.svg',
1950
- logoAlt: 'Propbinder',
1951
- logoHeight: 28,
1952
- primarySurface: '#6B5FF5',
1953
- primaryContent: '#FFFFFF',
1954
- secondarySurface: '#221a4c',
1955
- secondaryContent: '#FFFFFF',
1956
- organizationName: 'Propbinder',
1957
- organizationId: 'default'
1958
- });
1959
- this.updateCustomColorInputs();
1961
+ ngAfterViewInit() {
1962
+ // Initial setup if needed
1960
1963
  }
1961
- applyCejTheme() {
1962
- this.currentTheme = 'cej';
1963
- this.whitelabelService.updateConfig({
1964
- logoUrl: '/Assets/logos/cej-logo.png',
1965
- logoMarkUrl: '/Assets/logos/cej-logo.png',
1966
- logoAlt: 'CEJ',
1967
- logoHeight: 36,
1968
- primarySurface: '#FFFFFF',
1969
- primaryContent: '#A70923',
1970
- secondarySurface: '#A70923',
1971
- secondaryContent: '#FFFFFF',
1972
- organizationName: 'CEJ',
1973
- organizationId: 'cej'
1974
- });
1975
- this.updateCustomColorInputs();
1976
- }
1977
- applyPkaTheme() {
1978
- this.currentTheme = 'pka';
1979
- this.whitelabelService.updateConfig({
1980
- logoUrl: '/Assets/logos/pka-logo.svg',
1981
- logoMarkUrl: '/Assets/logos/pka-logo.svg',
1982
- logoAlt: 'PKA',
1983
- logoHeight: 24,
1984
- primarySurface: '#CC006C',
1985
- primaryContent: '#FFFFFF',
1986
- secondarySurface: '#660036',
1987
- secondaryContent: '#FFFFFF',
1988
- organizationName: 'PKA',
1989
- organizationId: 'pka'
1990
- });
1991
- this.updateCustomColorInputs();
1992
- }
1993
- applyClaveTheme() {
1994
- this.currentTheme = 'clave';
1995
- this.whitelabelService.updateConfig({
1996
- logoUrl: '/Assets/logos/clave-logo.svg',
1997
- logoMarkUrl: '/Assets/logos/clave-logo.svg',
1998
- logoAlt: 'Clave',
1999
- logoHeight: 24,
2000
- primarySurface: '#868764',
2001
- primaryContent: '#262424',
2002
- secondarySurface: '#262424',
2003
- secondaryContent: '#FFFFFF',
2004
- organizationName: 'Clave',
2005
- organizationId: 'clave'
1964
+ /**
1965
+ * Handle avatar click - opens profile actions bottom sheet
1966
+ */
1967
+ async handleAvatarClick() {
1968
+ console.log('Avatar clicked - opening profile bottom sheet');
1969
+ // Emit the event for any parent listeners
1970
+ this.avatarClick.emit();
1971
+ // Use input if provided, otherwise fall back to service, otherwise use default menu
1972
+ const menuItems = this.profileMenuItems() || this.userService.profileMenuItems() || [
1973
+ {
1974
+ actions: [
1975
+ {
1976
+ action: 'profile',
1977
+ title: 'Min profil',
1978
+ icon: 'remixUser3Line',
1979
+ destructive: false
1980
+ },
1981
+ {
1982
+ action: 'settings',
1983
+ title: 'Indstillinger',
1984
+ icon: 'remixSettings3Line',
1985
+ destructive: false
1986
+ }
1987
+ ]
1988
+ },
1989
+ {
1990
+ actions: [
1991
+ {
1992
+ action: 'logout',
1993
+ title: 'Log ud',
1994
+ icon: 'remixLogoutBoxLine',
1995
+ destructive: true
1996
+ }
1997
+ ]
1998
+ }
1999
+ ];
2000
+ // If no menu items configured, just emit and return
2001
+ if (!menuItems || menuItems.length === 0) {
2002
+ return;
2003
+ }
2004
+ const sheet = await this.modalController.create({
2005
+ component: DsMobileActionsBottomSheetComponent,
2006
+ componentProps: {
2007
+ customActionGroups: menuItems
2008
+ },
2009
+ // Auto-height: no breakpoints, no handle
2010
+ cssClass: 'ds-bottom-sheet auto-height'
2006
2011
  });
2007
- this.updateCustomColorInputs();
2012
+ await sheet.present();
2013
+ const result = await sheet.onWillDismiss();
2014
+ if (result.data?.action) {
2015
+ // Emit the selected action to parent
2016
+ this.profileActionSelected.emit(result.data);
2017
+ }
2008
2018
  }
2009
- applyCustomColors() {
2010
- this.whitelabelService.updateColors({
2011
- primarySurface: this.customPrimarySurface,
2012
- primaryContent: this.customPrimaryContent,
2013
- secondarySurface: this.customSecondarySurface,
2014
- secondaryContent: this.customSecondaryContent
2015
- });
2019
+ /**
2020
+ * Handle scroll events
2021
+ * - Shows title in fixed header when scrolled past threshold
2022
+ * - Fades out expandable header content based on scroll position
2023
+ * - Emits scroll event for custom handling
2024
+ */
2025
+ handleScroll(event) {
2026
+ const scrollTop = event.detail.scrollTop;
2027
+ const header = this.elementRef.nativeElement.querySelector('ion-header:not([collapse])');
2028
+ const headerExpandable = this.elementRef.nativeElement.querySelector('.header-expandable');
2029
+ // Show title in fixed header when scrolled past threshold
2030
+ if (scrollTop > this.scrollThreshold()) {
2031
+ header?.classList.add('header-scrolled');
2032
+ }
2033
+ else {
2034
+ header?.classList.remove('header-scrolled');
2035
+ }
2036
+ // Fade out header-expandable content based on scroll
2037
+ if (headerExpandable) {
2038
+ const fadeDistance = this.headerFadeDistance();
2039
+ const fadeProgress = Math.min(scrollTop / fadeDistance, 1);
2040
+ // Calculate opacity (1 to 0)
2041
+ const opacity = 1 - fadeProgress;
2042
+ // Calculate transform (0px to -20px upward)
2043
+ const translateY = fadeProgress * -20;
2044
+ // Apply styles
2045
+ headerExpandable.style.opacity = opacity.toString();
2046
+ headerExpandable.style.transform = `translateY(${translateY}px)`;
2047
+ }
2048
+ this.scroll.emit(event);
2016
2049
  }
2017
- updateCustomColorInputs() {
2018
- this.customPrimarySurface = this.whitelabelService.primarySurface();
2019
- this.customPrimaryContent = this.whitelabelService.primaryContent();
2020
- this.customSecondarySurface = this.whitelabelService.secondarySurface();
2021
- this.customSecondaryContent = this.whitelabelService.secondaryContent();
2050
+ /**
2051
+ * Handle pull-to-refresh
2052
+ * Emits refresh event - parent should call event.target.complete()
2053
+ */
2054
+ async handleRefresh(event) {
2055
+ // Haptic feedback for pull-to-refresh
2056
+ try {
2057
+ await Haptics.impact({ style: ImpactStyle.Medium });
2058
+ }
2059
+ catch {
2060
+ // Fallback to Web Vibration API if Capacitor Haptics is not available
2061
+ if ('vibrate' in navigator) {
2062
+ navigator.vibrate(50);
2063
+ }
2064
+ }
2065
+ this.refresh.emit(event);
2022
2066
  }
2023
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2024
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: WhitelabelDemoModalComponent, isStandalone: true, selector: "ds-whitelabel-demo-modal", ngImport: i0, template: `
2025
- <!-- Fixed Header -->
2026
- <div class="modal-header">
2027
- <div class="header-content">
2028
- <span class="header-title">Whitelabel</span>
2029
- <ds-icon-button
2030
- icon="remixCloseLine"
2031
- variant="secondary"
2032
- size="lg"
2033
- (click)="close()"
2034
- class="close-button"
2035
- aria-label="Close">
2036
- </ds-icon-button>
2037
- </div>
2038
- </div>
2039
-
2040
- <!-- Scrollable Content -->
2041
- <ion-content [scrollY]="true" class="modal-content">
2042
- <div class="demo-container">
2043
- <!-- Theme Selection -->
2044
- <div class="demo-section">
2045
- <h2>Theme</h2>
2046
- <div class="theme-buttons">
2047
- <button class="theme-btn" (click)="applyDefaultTheme()" [class.active]="currentTheme === 'default'">
2048
- Propbinder
2049
- </button>
2050
- <button class="theme-btn" (click)="applyCejTheme()" [class.active]="currentTheme === 'cej'">
2051
- CEJ
2052
- </button>
2053
- <button class="theme-btn" (click)="applyPkaTheme()" [class.active]="currentTheme === 'pka'">
2054
- PKA
2055
- </button>
2056
- <button class="theme-btn" (click)="applyClaveTheme()" [class.active]="currentTheme === 'clave'">
2057
- Clave
2058
- </button>
2059
- </div>
2060
- </div>
2067
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageMainComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2068
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsMobilePageMainComponent, isStandalone: true, selector: "ds-mobile-page-main", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, avatarType: { classPropertyName: "avatarType", publicName: "avatarType", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarIconName: { classPropertyName: "avatarIconName", publicName: "avatarIconName", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showCondensedHeader: { classPropertyName: "showCondensedHeader", publicName: "showCondensedHeader", isSignal: true, isRequired: false, transformFunction: null }, scrollThreshold: { classPropertyName: "scrollThreshold", publicName: "scrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, headerFadeDistance: { classPropertyName: "headerFadeDistance", publicName: "headerFadeDistance", isSignal: true, isRequired: false, transformFunction: null }, profileMenuItems: { classPropertyName: "profileMenuItems", publicName: "profileMenuItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { avatarClick: "avatarClick", profileActionSelected: "profileActionSelected", refresh: "refresh", scroll: "scroll" }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
2069
+ <!-- Fixed header at top -->
2070
+ <ion-header>
2071
+ <ion-toolbar>
2072
+ <div class="header-home">
2073
+ <!-- Whitelabel Logomark -->
2074
+ <ds-logo variant="mark" size="lg" />
2061
2075
 
2062
- <!-- Logo & Logomark Preview -->
2063
- <div class="demo-section">
2064
- <h2>Logo Preview</h2>
2065
- <div class="preview-grid">
2066
- <div class="preview-tile">
2067
- <h3>Logo</h3>
2068
- <div class="logo-preview">
2069
- <ds-logo variant="full" />
2070
- </div>
2071
- </div>
2072
- <div class="preview-tile">
2073
- <h3>Logomark</h3>
2074
- <div class="logomark-preview">
2075
- <ds-avatar-with-badge
2076
- [type]="'initials'"
2077
- [initials]="'KN'"
2078
- [size]="'md'"
2079
- [badgePosition]="'bottom-right'"
2080
- />
2081
- </div>
2082
- </div>
2083
- </div>
2076
+ <!-- Title - fades in on scroll -->
2077
+ <ion-title class="header-home__title">{{ title() }}</ion-title>
2078
+
2079
+ <!-- Avatar -->
2080
+ <div class="header-home__actions">
2081
+ <ds-avatar
2082
+ [size]="'md'"
2083
+ [type]="avatarType()"
2084
+ [initials]="avatarInitials()"
2085
+ [src]="avatarSrc()"
2086
+ [iconName]="avatarIconName()"
2087
+ (click)="handleAvatarClick()"
2088
+ style="cursor: pointer;"
2089
+ />
2090
+ </div>
2091
+ </div>
2092
+ </ion-toolbar>
2093
+ </ion-header>
2094
+
2095
+ <!-- Content with expandable header -->
2096
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
2097
+ <!-- Condensed header for Ionic scroll effects -->
2098
+ @if (showCondensedHeader()) {
2099
+ <ion-header collapse="condense">
2100
+ <ion-toolbar>
2101
+ <ion-title size="large">{{ title() }}</ion-title>
2102
+ </ion-toolbar>
2103
+ </ion-header>
2104
+ }
2105
+
2106
+ <!-- Pull to refresh (only on native iOS/Android) -->
2107
+ @if (showRefresh() && isNativePlatform()) {
2108
+ <ion-refresher
2109
+ slot="fixed"
2110
+ (ionRefresh)="handleRefresh($event)"
2111
+ [pullFactor]="0.4"
2112
+ [pullMin]="80"
2113
+ [pullMax]="240"
2114
+ closeDuration="600ms">
2115
+ <ion-refresher-content
2116
+ pullingIcon="remixArrowDownS"
2117
+ refreshingSpinner="lines">
2118
+ </ion-refresher-content>
2119
+ </ion-refresher>
2120
+ }
2121
+
2122
+ <!-- Expandable header section (purple background) -->
2123
+ <div class="header-expandable">
2124
+ <div class="header-expandable-inner">
2125
+ <div class="header-expandable__text">
2126
+ <h1 class="header-expandable__title">{{ headerTitle() || title() }}</h1>
2127
+ @if (headerSubtitle()) {
2128
+ <p class="header-expandable__subtitle">{{ headerSubtitle() }}</p>
2129
+ }
2084
2130
  </div>
2085
2131
 
2086
- <!-- Brand Colors -->
2087
- <div class="demo-section">
2088
- <h2>Brand Colors</h2>
2089
- <div class="color-section">
2090
- <div class="color-swatches">
2091
- <div class="swatch swatch--primary-surface">
2092
- <div class="swatch-label">Primary Surface</div>
2093
- <div class="swatch-value">{{ whitelabelService.primarySurface() }}</div>
2094
- </div>
2095
- <div class="swatch swatch--primary-content">
2096
- <div class="swatch-label">Primary Content</div>
2097
- <div class="swatch-value">{{ whitelabelService.primaryContent() }}</div>
2098
- </div>
2099
- <div class="swatch swatch--secondary-surface">
2100
- <div class="swatch-label">Secondary Surface</div>
2101
- <div class="swatch-value">{{ whitelabelService.secondarySurface() }}</div>
2102
- </div>
2103
- <div class="swatch swatch--secondary-content">
2104
- <div class="swatch-label">Secondary Content</div>
2105
- <div class="swatch-value">{{ whitelabelService.secondaryContent() }}</div>
2106
- </div>
2107
- </div>
2108
-
2109
- <div class="color-inputs">
2110
- <div class="color-group-label">Primary</div>
2111
- <div class="color-row">
2112
- <label>Surface</label>
2113
- <input
2114
- type="color"
2115
- [(ngModel)]="customPrimarySurface"
2116
- (change)="applyCustomColors()"
2117
- />
2118
- <input
2119
- type="text"
2120
- [(ngModel)]="customPrimarySurface"
2121
- (change)="applyCustomColors()"
2122
- />
2123
- </div>
2124
- <div class="color-row">
2125
- <label>Content</label>
2126
- <input
2127
- type="color"
2128
- [(ngModel)]="customPrimaryContent"
2129
- (change)="applyCustomColors()"
2130
- />
2131
- <input
2132
- type="text"
2133
- [(ngModel)]="customPrimaryContent"
2134
- (change)="applyCustomColors()"
2135
- />
2136
- </div>
2137
-
2138
- <div class="color-group-label">Secondary</div>
2139
- <div class="color-row">
2140
- <label>Surface</label>
2141
- <input
2142
- type="color"
2143
- [(ngModel)]="customSecondarySurface"
2144
- (change)="applyCustomColors()"
2145
- />
2146
- <input
2147
- type="text"
2148
- [(ngModel)]="customSecondarySurface"
2149
- (change)="applyCustomColors()"
2150
- />
2151
- </div>
2152
- <div class="color-row">
2153
- <label>Content</label>
2154
- <input
2155
- type="color"
2156
- [(ngModel)]="customSecondaryContent"
2157
- (change)="applyCustomColors()"
2158
- />
2159
- <input
2160
- type="text"
2161
- [(ngModel)]="customSecondaryContent"
2162
- (change)="applyCustomColors()"
2163
- />
2164
- </div>
2165
- </div>
2166
- </div>
2167
- </div>
2132
+ <!-- Slot for custom header content (e.g., property tiles) -->
2133
+ <ng-content select="[header-content]"></ng-content>
2134
+ </div>
2135
+ </div>
2136
+
2137
+ <!-- Content wrapper -->
2138
+ <div class="content-wrapper">
2139
+ <div class="content-inner">
2140
+ <!-- Main page content -->
2141
+ <ng-content></ng-content>
2168
2142
  </div>
2143
+ </div>
2169
2144
  </ion-content>
2170
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%;background:var(--color-background-neutral-primary, #ffffff)}.modal-header{flex-shrink:0;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default, #e0e0e0);padding:0 16px}ion-content,.modal-content{--background: #ffffff;flex:1;width:100%}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:56px}.header-title{font-family:Brockmann,sans-serif;font-size:17px;font-weight:600;color:var(--color-text-primary, #1a1a1a);flex:1}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;background:var(--color-background-neutral-secondary, #f5f5f5)!important;color:var(--color-text-primary, #1a1a1a)!important}.demo-container{padding:20px;max-width:600px;margin:0 auto;width:100%}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:18px;font-weight:600;color:#333}.theme-buttons{display:flex;gap:12px;flex-wrap:wrap}.theme-btn{padding:8px 16px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;background:#e0e0e0;color:#333}.theme-btn:active{transform:scale(.98)}.theme-btn.active{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.logo-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:var(--color-brand-secondary);border-radius:12px;min-height:80px}.logomark-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:#fff;border:1px solid #e0e0e0;border-radius:12px;min-height:80px}.preview-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.preview-tile{background:#fff;border-radius:12px}.preview-tile h3{font-size:13px;font-weight:600;color:#666;margin-top:0;margin-bottom:12px}.color-section{background:#fff;border-radius:12px}.color-swatches{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px}.swatch{padding:16px;border-radius:8px;text-align:center}.swatch-label{font-size:12px;font-weight:600;margin-bottom:4px}.swatch-value{font-size:11px;opacity:.8;font-family:monospace}.swatch--primary-surface{background:var(--color-primary-surface);color:var(--color-primary-content)}.swatch--primary-content{background:var(--color-primary-content);color:var(--color-primary-surface);border:1px solid #e0e0e0}.swatch--secondary-surface{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.swatch--secondary-content{background:var(--color-secondary-content);color:var(--color-secondary-surface);border:1px solid #e0e0e0}.color-inputs{display:flex;flex-direction:column;gap:12px}.color-group-label{font-size:13px;font-weight:600;color:#333;margin-top:8px}.color-row{display:flex;align-items:center;gap:12px}.color-row label{min-width:70px;font-size:13px;color:#666}.color-row input[type=color]{width:40px;height:32px;border:none;border-radius:6px;cursor:pointer;padding:0}.color-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-family:monospace;font-size:13px}@supports (padding: env(safe-area-inset-bottom)){.demo-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsAvatarWithBadgeComponent, selector: "ds-avatar-with-badge", inputs: ["type", "size", "initials", "src", "iconName", "showBadge", "badgePosition"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
2145
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%}:host ion-header{background:var(--color-brand-secondary);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){ion-header{display:none;height:auto}}.header-home{display:flex;align-items:center;justify-content:space-between;padding:0 20px;background:var(--color-brand-secondary);height:72px}.header-home__title{position:absolute;left:50%;transform:translate(-50%) translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0;transition:transform .6s ease,opacity .6s ease;margin:0;padding:0;--color: white}.header-scrolled .header-home__title{opacity:1;transform:translate(-50%) translateY(0)}.header-home__actions{display:flex;align-items:center;gap:8px}.header-home__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}.logomark{height:28px;width:auto;flex-shrink:0}@media (min-width: 768px){.header-home{padding:16px 24px}.logomark{height:32px}.header-home__title{display:none}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px;overflow:hidden}ion-content::part(scroll){display:flex;flex-direction:column}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-header[collapse=condense]{display:none}@media (min-width: 768px){ion-header[collapse=condense]{display:none}}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}.header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){.header-expandable{padding:48px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}.header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{position:relative;z-index:20;padding:0;flex:1;display:flex;flex-direction:column}@media (min-width: 768px){.content-wrapper{width:100%}}.content-inner{background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary);flex:1;padding:24px 20px 32px;padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + env(safe-area-inset-bottom,0px))}@media (min-width: 768px){.content-inner{border-radius:16px 16px 0 0;padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.content-inner{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.content-inner{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.content-inner{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.content-inner{padding:32px var(--content-padding-3xl)!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { 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"] }] });
2171
2146
  }
2172
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalComponent, decorators: [{
2147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageMainComponent, decorators: [{
2173
2148
  type: Component,
2174
- args: [{ selector: 'ds-whitelabel-demo-modal', standalone: true, imports: [
2149
+ args: [{ selector: 'ds-mobile-page-main', standalone: true, imports: [
2175
2150
  CommonModule,
2176
- FormsModule,
2151
+ IonHeader,
2152
+ IonToolbar,
2153
+ IonTitle,
2177
2154
  IonContent,
2178
- DsLogoComponent,
2179
- DsAvatarWithBadgeComponent,
2180
- DsIconButtonComponent
2181
- ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
2182
- <!-- Fixed Header -->
2183
- <div class="modal-header">
2184
- <div class="header-content">
2185
- <span class="header-title">Whitelabel</span>
2186
- <ds-icon-button
2187
- icon="remixCloseLine"
2188
- variant="secondary"
2189
- size="lg"
2190
- (click)="close()"
2191
- class="close-button"
2192
- aria-label="Close">
2193
- </ds-icon-button>
2194
- </div>
2195
- </div>
2196
-
2197
- <!-- Scrollable Content -->
2198
- <ion-content [scrollY]="true" class="modal-content">
2199
- <div class="demo-container">
2200
- <!-- Theme Selection -->
2201
- <div class="demo-section">
2202
- <h2>Theme</h2>
2203
- <div class="theme-buttons">
2204
- <button class="theme-btn" (click)="applyDefaultTheme()" [class.active]="currentTheme === 'default'">
2205
- Propbinder
2206
- </button>
2207
- <button class="theme-btn" (click)="applyCejTheme()" [class.active]="currentTheme === 'cej'">
2208
- CEJ
2209
- </button>
2210
- <button class="theme-btn" (click)="applyPkaTheme()" [class.active]="currentTheme === 'pka'">
2211
- PKA
2212
- </button>
2213
- <button class="theme-btn" (click)="applyClaveTheme()" [class.active]="currentTheme === 'clave'">
2214
- Clave
2215
- </button>
2216
- </div>
2217
- </div>
2155
+ IonRefresher,
2156
+ IonRefresherContent,
2157
+ DsAvatarComponent,
2158
+ DsLogoComponent
2159
+ ], template: `
2160
+ <!-- Fixed header at top -->
2161
+ <ion-header>
2162
+ <ion-toolbar>
2163
+ <div class="header-home">
2164
+ <!-- Whitelabel Logomark -->
2165
+ <ds-logo variant="mark" size="lg" />
2218
2166
 
2219
- <!-- Logo & Logomark Preview -->
2220
- <div class="demo-section">
2221
- <h2>Logo Preview</h2>
2222
- <div class="preview-grid">
2223
- <div class="preview-tile">
2224
- <h3>Logo</h3>
2225
- <div class="logo-preview">
2226
- <ds-logo variant="full" />
2227
- </div>
2228
- </div>
2229
- <div class="preview-tile">
2230
- <h3>Logomark</h3>
2231
- <div class="logomark-preview">
2232
- <ds-avatar-with-badge
2233
- [type]="'initials'"
2234
- [initials]="'KN'"
2235
- [size]="'md'"
2236
- [badgePosition]="'bottom-right'"
2237
- />
2238
- </div>
2239
- </div>
2240
- </div>
2167
+ <!-- Title - fades in on scroll -->
2168
+ <ion-title class="header-home__title">{{ title() }}</ion-title>
2169
+
2170
+ <!-- Avatar -->
2171
+ <div class="header-home__actions">
2172
+ <ds-avatar
2173
+ [size]="'md'"
2174
+ [type]="avatarType()"
2175
+ [initials]="avatarInitials()"
2176
+ [src]="avatarSrc()"
2177
+ [iconName]="avatarIconName()"
2178
+ (click)="handleAvatarClick()"
2179
+ style="cursor: pointer;"
2180
+ />
2181
+ </div>
2182
+ </div>
2183
+ </ion-toolbar>
2184
+ </ion-header>
2185
+
2186
+ <!-- Content with expandable header -->
2187
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
2188
+ <!-- Condensed header for Ionic scroll effects -->
2189
+ @if (showCondensedHeader()) {
2190
+ <ion-header collapse="condense">
2191
+ <ion-toolbar>
2192
+ <ion-title size="large">{{ title() }}</ion-title>
2193
+ </ion-toolbar>
2194
+ </ion-header>
2195
+ }
2196
+
2197
+ <!-- Pull to refresh (only on native iOS/Android) -->
2198
+ @if (showRefresh() && isNativePlatform()) {
2199
+ <ion-refresher
2200
+ slot="fixed"
2201
+ (ionRefresh)="handleRefresh($event)"
2202
+ [pullFactor]="0.4"
2203
+ [pullMin]="80"
2204
+ [pullMax]="240"
2205
+ closeDuration="600ms">
2206
+ <ion-refresher-content
2207
+ pullingIcon="remixArrowDownS"
2208
+ refreshingSpinner="lines">
2209
+ </ion-refresher-content>
2210
+ </ion-refresher>
2211
+ }
2212
+
2213
+ <!-- Expandable header section (purple background) -->
2214
+ <div class="header-expandable">
2215
+ <div class="header-expandable-inner">
2216
+ <div class="header-expandable__text">
2217
+ <h1 class="header-expandable__title">{{ headerTitle() || title() }}</h1>
2218
+ @if (headerSubtitle()) {
2219
+ <p class="header-expandable__subtitle">{{ headerSubtitle() }}</p>
2220
+ }
2241
2221
  </div>
2242
2222
 
2243
- <!-- Brand Colors -->
2244
- <div class="demo-section">
2245
- <h2>Brand Colors</h2>
2246
- <div class="color-section">
2247
- <div class="color-swatches">
2248
- <div class="swatch swatch--primary-surface">
2249
- <div class="swatch-label">Primary Surface</div>
2250
- <div class="swatch-value">{{ whitelabelService.primarySurface() }}</div>
2251
- </div>
2252
- <div class="swatch swatch--primary-content">
2253
- <div class="swatch-label">Primary Content</div>
2254
- <div class="swatch-value">{{ whitelabelService.primaryContent() }}</div>
2255
- </div>
2256
- <div class="swatch swatch--secondary-surface">
2257
- <div class="swatch-label">Secondary Surface</div>
2258
- <div class="swatch-value">{{ whitelabelService.secondarySurface() }}</div>
2259
- </div>
2260
- <div class="swatch swatch--secondary-content">
2261
- <div class="swatch-label">Secondary Content</div>
2262
- <div class="swatch-value">{{ whitelabelService.secondaryContent() }}</div>
2263
- </div>
2264
- </div>
2265
-
2266
- <div class="color-inputs">
2267
- <div class="color-group-label">Primary</div>
2268
- <div class="color-row">
2269
- <label>Surface</label>
2270
- <input
2271
- type="color"
2272
- [(ngModel)]="customPrimarySurface"
2273
- (change)="applyCustomColors()"
2274
- />
2275
- <input
2276
- type="text"
2277
- [(ngModel)]="customPrimarySurface"
2278
- (change)="applyCustomColors()"
2279
- />
2280
- </div>
2281
- <div class="color-row">
2282
- <label>Content</label>
2283
- <input
2284
- type="color"
2285
- [(ngModel)]="customPrimaryContent"
2286
- (change)="applyCustomColors()"
2287
- />
2288
- <input
2289
- type="text"
2290
- [(ngModel)]="customPrimaryContent"
2291
- (change)="applyCustomColors()"
2292
- />
2293
- </div>
2294
-
2295
- <div class="color-group-label">Secondary</div>
2296
- <div class="color-row">
2297
- <label>Surface</label>
2298
- <input
2299
- type="color"
2300
- [(ngModel)]="customSecondarySurface"
2301
- (change)="applyCustomColors()"
2302
- />
2303
- <input
2304
- type="text"
2305
- [(ngModel)]="customSecondarySurface"
2306
- (change)="applyCustomColors()"
2307
- />
2308
- </div>
2309
- <div class="color-row">
2310
- <label>Content</label>
2311
- <input
2312
- type="color"
2313
- [(ngModel)]="customSecondaryContent"
2314
- (change)="applyCustomColors()"
2315
- />
2316
- <input
2317
- type="text"
2318
- [(ngModel)]="customSecondaryContent"
2319
- (change)="applyCustomColors()"
2320
- />
2321
- </div>
2322
- </div>
2323
- </div>
2324
- </div>
2223
+ <!-- Slot for custom header content (e.g., property tiles) -->
2224
+ <ng-content select="[header-content]"></ng-content>
2225
+ </div>
2226
+ </div>
2227
+
2228
+ <!-- Content wrapper -->
2229
+ <div class="content-wrapper">
2230
+ <div class="content-inner">
2231
+ <!-- Main page content -->
2232
+ <ng-content></ng-content>
2325
2233
  </div>
2234
+ </div>
2326
2235
  </ion-content>
2327
- `, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%;background:var(--color-background-neutral-primary, #ffffff)}.modal-header{flex-shrink:0;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default, #e0e0e0);padding:0 16px}ion-content,.modal-content{--background: #ffffff;flex:1;width:100%}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:56px}.header-title{font-family:Brockmann,sans-serif;font-size:17px;font-weight:600;color:var(--color-text-primary, #1a1a1a);flex:1}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;background:var(--color-background-neutral-secondary, #f5f5f5)!important;color:var(--color-text-primary, #1a1a1a)!important}.demo-container{padding:20px;max-width:600px;margin:0 auto;width:100%}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:18px;font-weight:600;color:#333}.theme-buttons{display:flex;gap:12px;flex-wrap:wrap}.theme-btn{padding:8px 16px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;background:#e0e0e0;color:#333}.theme-btn:active{transform:scale(.98)}.theme-btn.active{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.logo-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:var(--color-brand-secondary);border-radius:12px;min-height:80px}.logomark-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:#fff;border:1px solid #e0e0e0;border-radius:12px;min-height:80px}.preview-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.preview-tile{background:#fff;border-radius:12px}.preview-tile h3{font-size:13px;font-weight:600;color:#666;margin-top:0;margin-bottom:12px}.color-section{background:#fff;border-radius:12px}.color-swatches{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px}.swatch{padding:16px;border-radius:8px;text-align:center}.swatch-label{font-size:12px;font-weight:600;margin-bottom:4px}.swatch-value{font-size:11px;opacity:.8;font-family:monospace}.swatch--primary-surface{background:var(--color-primary-surface);color:var(--color-primary-content)}.swatch--primary-content{background:var(--color-primary-content);color:var(--color-primary-surface);border:1px solid #e0e0e0}.swatch--secondary-surface{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.swatch--secondary-content{background:var(--color-secondary-content);color:var(--color-secondary-surface);border:1px solid #e0e0e0}.color-inputs{display:flex;flex-direction:column;gap:12px}.color-group-label{font-size:13px;font-weight:600;color:#333;margin-top:8px}.color-row{display:flex;align-items:center;gap:12px}.color-row label{min-width:70px;font-size:13px;color:#666}.color-row input[type=color]{width:40px;height:32px;border:none;border-radius:6px;cursor:pointer;padding:0}.color-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-family:monospace;font-size:13px}@supports (padding: env(safe-area-inset-bottom)){.demo-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"] }]
2328
- }] });
2329
-
2330
- /**
2331
- * WhitelabelDemoModalService
2332
- *
2333
- * Service for displaying the whitelabel demo in a full-screen modal.
2334
- * Built on Ionic's modal system with native gestures and animations.
2335
- *
2336
- * @example
2337
- * ```typescript
2338
- * constructor(private whitelabelModal: WhitelabelDemoModalService) {}
2339
- *
2340
- * async openDemo() {
2341
- * await this.whitelabelModal.open();
2342
- * }
2343
- * ```
2344
- */
2345
- class WhitelabelDemoModalService {
2346
- modalController;
2347
- constructor(modalController) {
2348
- this.modalController = modalController;
2349
- }
2350
- /**
2351
- * Open the whitelabel demo modal
2352
- *
2353
- * @returns Promise that resolves when the modal is presented
2354
- */
2355
- async open() {
2356
- try {
2357
- console.log('[WhitelabelDemoModal] Opening...');
2358
- const modal = await this.modalController.create({
2359
- component: WhitelabelDemoModalComponent,
2360
- cssClass: 'ds-whitelabel-demo-modal',
2361
- mode: 'ios',
2362
- presentingElement: document.querySelector('ion-router-outlet') || undefined,
2363
- backdropDismiss: true,
2364
- showBackdrop: true,
2365
- animated: true,
2366
- keyboardClose: true
2367
- });
2368
- console.log('[WhitelabelDemoModal] Modal created, presenting...');
2369
- await modal.present();
2370
- console.log('[WhitelabelDemoModal] Modal presented');
2371
- }
2372
- catch (error) {
2373
- console.error('[WhitelabelDemoModal] Error opening modal:', error);
2374
- throw error;
2375
- }
2376
- }
2377
- /**
2378
- * Close the currently open whitelabel demo modal
2379
- *
2380
- * @param data Optional data to pass back when dismissing
2381
- * @returns Promise that resolves when the modal is dismissed
2382
- */
2383
- async close(data) {
2384
- return this.modalController.dismiss(data);
2385
- }
2386
- /**
2387
- * Get the top-most modal if one exists
2388
- *
2389
- * @returns Promise that resolves to the modal element or undefined
2390
- */
2391
- async getTop() {
2392
- return this.modalController.getTop();
2393
- }
2394
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Injectable });
2395
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, providedIn: 'root' });
2396
- }
2397
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, decorators: [{
2398
- type: Injectable,
2399
- args: [{
2400
- providedIn: 'root'
2401
- }]
2402
- }], ctorParameters: () => [{ type: i1.ModalController }] });
2236
+ `, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%}:host ion-header{background:var(--color-brand-secondary);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){ion-header{display:none;height:auto}}.header-home{display:flex;align-items:center;justify-content:space-between;padding:0 20px;background:var(--color-brand-secondary);height:72px}.header-home__title{position:absolute;left:50%;transform:translate(-50%) translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0;transition:transform .6s ease,opacity .6s ease;margin:0;padding:0;--color: white}.header-scrolled .header-home__title{opacity:1;transform:translate(-50%) translateY(0)}.header-home__actions{display:flex;align-items:center;gap:8px}.header-home__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}.logomark{height:28px;width:auto;flex-shrink:0}@media (min-width: 768px){.header-home{padding:16px 24px}.logomark{height:32px}.header-home__title{display:none}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px;overflow:hidden}ion-content::part(scroll){display:flex;flex-direction:column}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-header[collapse=condense]{display:none}@media (min-width: 768px){ion-header[collapse=condense]{display:none}}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}.header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){.header-expandable{padding:48px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}.header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{position:relative;z-index:20;padding:0;flex:1;display:flex;flex-direction:column}@media (min-width: 768px){.content-wrapper{width:100%}}.content-inner{background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary);flex:1;padding:24px 20px 32px;padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + env(safe-area-inset-bottom,0px))}@media (min-width: 768px){.content-inner{border-radius:16px 16px 0 0;padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.content-inner{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.content-inner{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.content-inner{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.content-inner{padding:32px var(--content-padding-3xl)!important}}\n"] }]
2237
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { ionContent: [{
2238
+ type: ViewChild,
2239
+ args: [IonContent]
2240
+ }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], avatarType: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarType", required: false }] }], avatarInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarInitials", required: false }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarIconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarIconName", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showCondensedHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCondensedHeader", required: false }] }], scrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollThreshold", required: false }] }], headerFadeDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerFadeDistance", required: false }] }], profileMenuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "profileMenuItems", required: false }] }], avatarClick: [{ type: i0.Output, args: ["avatarClick"] }], profileActionSelected: [{ type: i0.Output, args: ["profileActionSelected"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], scroll: [{ type: i0.Output, args: ["scroll"] }] } });
2403
2241
 
2404
2242
  /**
2405
- * DsMobilePageMainComponent
2243
+ * DsMobilePageDetailsComponent
2406
2244
  *
2407
- * A complete mobile page layout for main/tab pages with:
2408
- * - Fixed header with logomark + title + avatar
2409
- * - Purple expandable header section (scrolls with content)
2410
- * - White rounded content wrapper
2411
- * - Pull-to-refresh support
2412
- * - Auto scroll title fade-in
2245
+ * A mobile page layout for detail/drill-down pages with:
2246
+ * - Back button header (mobile + desktop variants)
2247
+ * - White background content area
2248
+ * - Responsive padding
2413
2249
  *
2414
2250
  * @example
2415
2251
  * ```html
2416
- * <!-- Simple page -->
2417
- * <ds-mobile-page-main
2418
- * title="Inquiries"
2419
- * [avatarInitials]="'JD'">
2252
+ * <!-- Simple detail page -->
2253
+ * <ds-mobile-page-details
2254
+ * title="Property Details"
2255
+ * (back)="goBack()">
2420
2256
  * <div class="page-content">
2421
2257
  * <!-- Your content -->
2422
2258
  * </div>
2423
- * </ds-mobile-page-main>
2424
- *
2425
- * <!-- Page with custom header content -->
2426
- * <ds-mobile-page-main
2427
- * title="Home"
2428
- * headerTitle="Welcome, Lars"
2429
- * headerSubtitle="Your rental property at a glance."
2430
- * [avatarInitials]="'L'">
2431
- *
2432
- * <div header-content class="property-tiles">
2433
- * <!-- Custom header content like tiles -->
2434
- * </div>
2259
+ * </ds-mobile-page-details>
2435
2260
  *
2261
+ * <!-- With default back route -->
2262
+ * <ds-mobile-page-details
2263
+ * title="Invoice Details"
2264
+ * backRoute="/invoices">
2436
2265
  * <div class="page-content">
2437
- * <!-- Main page content -->
2266
+ * <!-- Your content -->
2438
2267
  * </div>
2439
- * </ds-mobile-page-main>
2268
+ * </ds-mobile-page-details>
2440
2269
  * ```
2441
2270
  */
2442
- class DsMobilePageMainComponent extends MobilePageBase {
2271
+ class DsMobilePageDetailsComponent extends MobilePageBase {
2272
+ navCtrl;
2443
2273
  elementRef;
2444
- ionContent;
2445
- // Platform detection
2446
- platform = inject(Platform);
2447
- modalController = inject(ModalController);
2448
- router = inject(Router);
2449
- whitelabelDemoModal = inject(WhitelabelDemoModalService);
2450
- // Computed property to check if running on native platform
2451
- isNativePlatform = computed(() => this.platform.is('ios') ||
2452
- this.platform.is('android') ||
2453
- this.platform.is('capacitor'), ...(ngDevMode ? [{ debugName: "isNativePlatform" }] : []));
2454
- // Inputs - Title
2455
- title = input.required(...(ngDevMode ? [{ debugName: "title" }] : [])); // For fixed header title
2456
- headerTitle = input('', ...(ngDevMode ? [{ debugName: "headerTitle" }] : [])); // Optional different title for expandable header
2457
- headerSubtitle = input('', ...(ngDevMode ? [{ debugName: "headerSubtitle" }] : []));
2458
- // Inputs - Avatar
2459
- avatarType = input('initials', ...(ngDevMode ? [{ debugName: "avatarType" }] : []));
2460
- avatarInitials = input('U', ...(ngDevMode ? [{ debugName: "avatarInitials" }] : []));
2461
- avatarSrc = input('', ...(ngDevMode ? [{ debugName: "avatarSrc" }] : []));
2462
- avatarIconName = input('remixUser3Line', ...(ngDevMode ? [{ debugName: "avatarIconName" }] : []));
2463
- // Inputs - Features
2464
- showRefresh = input(true, ...(ngDevMode ? [{ debugName: "showRefresh" }] : []));
2465
- showCondensedHeader = input(true, ...(ngDevMode ? [{ debugName: "showCondensedHeader" }] : []));
2466
- scrollThreshold = input(160, ...(ngDevMode ? [{ debugName: "scrollThreshold" }] : [])); // Pixels to scroll before title appears
2467
- headerFadeDistance = input(150, ...(ngDevMode ? [{ debugName: "headerFadeDistance" }] : [])); // Distance over which header fades out
2274
+ // Inputs
2275
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
2276
+ backRoute = input('', ...(ngDevMode ? [{ debugName: "backRoute" }] : [])); // Optional default back route
2468
2277
  // Outputs
2469
- avatarClick = output();
2470
- refresh = output();
2471
- scroll = output();
2472
- constructor(elementRef) {
2278
+ back = output();
2279
+ constructor(navCtrl, elementRef) {
2473
2280
  super();
2281
+ this.navCtrl = navCtrl;
2474
2282
  this.elementRef = elementRef;
2475
2283
  }
2476
- ngAfterViewInit() {
2477
- // Initial setup if needed
2478
- }
2479
2284
  /**
2480
- * Handle avatar click - opens profile actions bottom sheet
2285
+ * Handle back navigation
2286
+ *
2287
+ * By default, navigates using the provided backRoute or browser back.
2288
+ * Parent components can listen to the (back) event to override this behavior.
2289
+ *
2290
+ * @example
2291
+ * ```html
2292
+ * <!-- Default behavior: uses backRoute or browser back -->
2293
+ * <ds-mobile-page-details
2294
+ * title="Details"
2295
+ * backRoute="/home">
2296
+ * </ds-mobile-page-details>
2297
+ *
2298
+ * <!-- Custom behavior: parent handles navigation -->
2299
+ * <ds-mobile-page-details
2300
+ * title="Details"
2301
+ * (back)="customBackHandler()">
2302
+ * </ds-mobile-page-details>
2303
+ * ```
2481
2304
  */
2482
- async handleAvatarClick() {
2483
- console.log('Avatar clicked - opening profile bottom sheet');
2484
- // Emit the event for any parent listeners
2485
- this.avatarClick.emit();
2486
- const sheet = await this.modalController.create({
2487
- component: DsMobileActionsBottomSheetComponent,
2488
- componentProps: {
2489
- customActionGroups: [
2490
- {
2491
- actions: [
2492
- {
2493
- action: 'profile',
2494
- title: 'Min profil',
2495
- icon: 'remixUser3Line',
2496
- destructive: false
2497
- },
2498
- {
2499
- action: 'settings',
2500
- title: 'Indstillinger',
2501
- icon: 'remixSettings3Line',
2502
- destructive: false
2503
- },
2504
- {
2505
- action: 'whitelabel-demo',
2506
- title: 'Whitelabel Demo',
2507
- icon: 'remixPaletteLine',
2508
- destructive: false
2509
- }
2510
- ]
2511
- },
2512
- {
2513
- actions: [
2514
- {
2515
- action: 'logout',
2516
- title: 'Log ud',
2517
- icon: 'remixLogoutBoxLine',
2518
- destructive: true
2519
- }
2520
- ]
2521
- }
2522
- ]
2523
- },
2524
- // Auto-height: no breakpoints, no handle
2525
- cssClass: 'ds-bottom-sheet auto-height'
2526
- });
2527
- await sheet.present();
2528
- const result = await sheet.onWillDismiss();
2529
- if (result.data?.action) {
2530
- console.log('Profile action selected:', result.data.action);
2531
- switch (result.data.action) {
2532
- case 'logout':
2533
- console.log('Logging out...');
2534
- // TODO: Implement logout logic
2535
- break;
2536
- case 'profile':
2537
- console.log('Opening profile...');
2538
- // TODO: Navigate to profile page
2539
- break;
2540
- case 'settings':
2541
- console.log('Opening settings...');
2542
- // TODO: Navigate to settings page
2543
- break;
2544
- case 'whitelabel-demo':
2545
- console.log('Whitelabel demo selected');
2546
- await this.whitelabelDemoModal.open();
2547
- break;
2548
- }
2549
- }
2550
- }
2551
- /**
2552
- * Handle scroll events
2553
- * - Shows title in fixed header when scrolled past threshold
2554
- * - Fades out expandable header content based on scroll position
2555
- * - Emits scroll event for custom handling
2556
- */
2557
- handleScroll(event) {
2558
- const scrollTop = event.detail.scrollTop;
2559
- const header = this.elementRef.nativeElement.querySelector('ion-header:not([collapse])');
2560
- const headerExpandable = this.elementRef.nativeElement.querySelector('.header-expandable');
2561
- // Show title in fixed header when scrolled past threshold
2562
- if (scrollTop > this.scrollThreshold()) {
2563
- header?.classList.add('header-scrolled');
2305
+ handleBack() {
2306
+ // Add class to trigger reverse animation
2307
+ this.elementRef.nativeElement.classList.add('navigating-back');
2308
+ // Emit event for parent to optionally handle
2309
+ this.back.emit();
2310
+ // Default behavior: navigate using backRoute or browser back
2311
+ if (this.backRoute()) {
2312
+ this.navCtrl.navigateBack(this.backRoute());
2564
2313
  }
2565
2314
  else {
2566
- header?.classList.remove('header-scrolled');
2567
- }
2568
- // Fade out header-expandable content based on scroll
2569
- if (headerExpandable) {
2570
- const fadeDistance = this.headerFadeDistance();
2571
- const fadeProgress = Math.min(scrollTop / fadeDistance, 1);
2572
- // Calculate opacity (1 to 0)
2573
- const opacity = 1 - fadeProgress;
2574
- // Calculate transform (0px to -20px upward)
2575
- const translateY = fadeProgress * -20;
2576
- // Apply styles
2577
- headerExpandable.style.opacity = opacity.toString();
2578
- headerExpandable.style.transform = `translateY(${translateY}px)`;
2579
- }
2580
- this.scroll.emit(event);
2581
- }
2582
- /**
2583
- * Handle pull-to-refresh
2584
- * Emits refresh event - parent should call event.target.complete()
2585
- */
2586
- async handleRefresh(event) {
2587
- // Haptic feedback for pull-to-refresh
2588
- try {
2589
- await Haptics.impact({ style: ImpactStyle.Medium });
2590
- }
2591
- catch {
2592
- // Fallback to Web Vibration API if Capacitor Haptics is not available
2593
- if ('vibrate' in navigator) {
2594
- navigator.vibrate(50);
2595
- }
2315
+ this.navCtrl.back();
2596
2316
  }
2597
- this.refresh.emit(event);
2598
2317
  }
2599
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageMainComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2600
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsMobilePageMainComponent, isStandalone: true, selector: "ds-mobile-page-main", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, headerTitle: { classPropertyName: "headerTitle", publicName: "headerTitle", isSignal: true, isRequired: false, transformFunction: null }, headerSubtitle: { classPropertyName: "headerSubtitle", publicName: "headerSubtitle", isSignal: true, isRequired: false, transformFunction: null }, avatarType: { classPropertyName: "avatarType", publicName: "avatarType", isSignal: true, isRequired: false, transformFunction: null }, avatarInitials: { classPropertyName: "avatarInitials", publicName: "avatarInitials", isSignal: true, isRequired: false, transformFunction: null }, avatarSrc: { classPropertyName: "avatarSrc", publicName: "avatarSrc", isSignal: true, isRequired: false, transformFunction: null }, avatarIconName: { classPropertyName: "avatarIconName", publicName: "avatarIconName", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showCondensedHeader: { classPropertyName: "showCondensedHeader", publicName: "showCondensedHeader", isSignal: true, isRequired: false, transformFunction: null }, scrollThreshold: { classPropertyName: "scrollThreshold", publicName: "scrollThreshold", isSignal: true, isRequired: false, transformFunction: null }, headerFadeDistance: { classPropertyName: "headerFadeDistance", publicName: "headerFadeDistance", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { avatarClick: "avatarClick", refresh: "refresh", scroll: "scroll" }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
2601
- <!-- Fixed header at top -->
2318
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageDetailsComponent, deps: [{ token: i1.NavController }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2319
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.14", type: DsMobilePageDetailsComponent, isStandalone: true, selector: "ds-mobile-page-details", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, backRoute: { classPropertyName: "backRoute", publicName: "backRoute", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, usesInheritance: true, ngImport: i0, template: `
2320
+ <!-- Mobile header - hidden on desktop -->
2602
2321
  <ion-header>
2603
2322
  <ion-toolbar>
2604
- <div class="header-home">
2605
- <!-- Whitelabel Logomark -->
2606
- <ds-logo variant="mark" size="lg" />
2607
-
2608
- <!-- Title - fades in on scroll -->
2609
- <ion-title class="header-home__title">{{ title() }}</ion-title>
2610
-
2611
- <!-- Avatar -->
2612
- <div class="header-home__actions">
2613
- <ds-avatar
2614
- [size]="'md'"
2615
- [type]="avatarType()"
2616
- [initials]="avatarInitials()"
2617
- [src]="avatarSrc()"
2618
- [iconName]="avatarIconName()"
2619
- (click)="handleAvatarClick()"
2620
- style="cursor: pointer;"
2621
- />
2622
- </div>
2323
+ <div class="header-back">
2324
+ <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2325
+ <ds-icon name="remixArrowLeftLine" size="24px" />
2326
+ </button>
2327
+ <h1 class="header-title">{{ title() }}</h1>
2623
2328
  </div>
2624
2329
  </ion-toolbar>
2625
2330
  </ion-header>
2626
2331
 
2627
- <!-- Content with expandable header -->
2628
- <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
2629
- <!-- Condensed header for Ionic scroll effects -->
2630
- @if (showCondensedHeader()) {
2631
- <ion-header collapse="condense">
2632
- <ion-toolbar>
2633
- <ion-title size="large">{{ title() }}</ion-title>
2634
- </ion-toolbar>
2635
- </ion-header>
2636
- }
2637
-
2638
- <!-- Pull to refresh (only on native iOS/Android) -->
2639
- @if (showRefresh() && isNativePlatform()) {
2640
- <ion-refresher
2641
- slot="fixed"
2642
- (ionRefresh)="handleRefresh($event)"
2643
- [pullFactor]="0.4"
2644
- [pullMin]="80"
2645
- [pullMax]="240"
2646
- closeDuration="600ms">
2647
- <ion-refresher-content
2648
- pullingIcon="remixArrowDownS"
2649
- refreshingSpinner="lines">
2650
- </ion-refresher-content>
2651
- </ion-refresher>
2652
- }
2653
-
2654
- <!-- Expandable header section (purple background) -->
2655
- <div class="header-expandable">
2656
- <div class="header-expandable-inner">
2657
- <div class="header-expandable__text">
2658
- <h1 class="header-expandable__title">{{ headerTitle() || title() }}</h1>
2659
- @if (headerSubtitle()) {
2660
- <p class="header-expandable__subtitle">{{ headerSubtitle() }}</p>
2661
- }
2662
- </div>
2663
-
2664
- <!-- Slot for custom header content (e.g., property tiles) -->
2665
- <ng-content select="[header-content]"></ng-content>
2666
- </div>
2332
+ <ion-content>
2333
+ <!-- Desktop header above content -->
2334
+ <div class="desktop-header">
2335
+ <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2336
+ <ds-icon name="remixArrowLeftLine" size="24px" />
2337
+ </button>
2338
+ <h1>{{ title() }}</h1>
2667
2339
  </div>
2668
2340
 
2669
- <!-- Content wrapper -->
2670
- <div class="content-wrapper">
2671
- <div class="content-inner">
2672
- <!-- Main page content -->
2673
- <ng-content></ng-content>
2674
- </div>
2341
+ <!-- Content area -->
2342
+ <div class="detail-content">
2343
+ <ng-content></ng-content>
2675
2344
  </div>
2676
2345
  </ion-content>
2677
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%}:host ion-header{background:var(--color-brand-secondary);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){ion-header{display:none;height:auto}}.header-home{display:flex;align-items:center;justify-content:space-between;padding:0 20px;background:var(--color-brand-secondary);height:72px}.header-home__title{position:absolute;left:50%;transform:translate(-50%) translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0;transition:transform .6s ease,opacity .6s ease;margin:0;padding:0;--color: white}.header-scrolled .header-home__title{opacity:1;transform:translate(-50%) translateY(0)}.header-home__actions{display:flex;align-items:center;gap:8px}.header-home__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}.logomark{height:28px;width:auto;flex-shrink:0}@media (min-width: 768px){.header-home{padding:16px 24px}.logomark{height:32px}.header-home__title{display:none}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px;overflow:hidden}ion-content::part(scroll){display:flex;flex-direction:column}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-header[collapse=condense]{display:none}@media (min-width: 768px){ion-header[collapse=condense]{display:none}}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}.header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){.header-expandable{padding:48px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}.header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{position:relative;z-index:20;padding:0;flex:1;display:flex;flex-direction:column}@media (min-width: 768px){.content-wrapper{width:100%}}.content-inner{background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary);flex:1;padding:24px 20px 32px;padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + env(safe-area-inset-bottom,0px))}@media (min-width: 768px){.content-inner{border-radius:16px 16px 0 0;padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.content-inner{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.content-inner{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.content-inner{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.content-inner{padding:32px var(--content-padding-3xl)!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { 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"] }] });
2346
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}ion-header{background:var(--color-brand-secondary);box-shadow:none;height:64px}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 64px;height:100%;min-height:64px;padding:0}ion-header ion-toolbar::part(native){height:100%;min-height:64px;padding:0}@media (min-width: 768px){ion-header{display:none}}.header-back{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--color-brand-secondary);position:relative;height:100%;min-height:64px}.header-back .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;transition:opacity var(--transition-duration-fast) var(--ease-smooth);z-index:10}.header-back .back-button:hover{opacity:.8}.header-back .back-button:active{opacity:.6}.header-back .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:#fff;margin:0}.desktop-header{display:none}@media (min-width: 768px){.desktop-header{display:flex;align-items:center;gap:16px;padding:32px var(--content-padding-md) 24px var(--content-padding-md);max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}.desktop-header .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-color-default-primary);transition:opacity var(--transition-duration-fast) var(--ease-smooth)}.desktop-header .back-button:hover{opacity:.8}.desktop-header .back-button:active{opacity:.6}.desktop-header h1{font-size:var(--font-size-2xl);font-weight:600;margin:0;color:var(--text-color-default-primary)}}@media (min-width: 992px){.desktop-header{max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.desktop-header{max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2));padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.desktop-header{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.desktop-header{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-content{--background: var(--color-background-neutral-primary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;overflow:hidden}@media (max-width: 767px){ion-content{border-radius:24px 24px 36px 36px;animation:bottomRadiusOut .6s ease .3s forwards}:host(.navigating-back) ion-content{border-radius:24px 24px 0 0;animation:bottomRadiusIn .8s cubic-bezier(.36,.66,.04,1) forwards!important}}@keyframes bottomRadiusOut{0%{border-radius:24px 24px 36px 36px}to{border-radius:24px 24px 0 0}}@keyframes bottomRadiusIn{0%{border-radius:24px 24px 0 0}to{border-radius:24px 24px 36px 36px}}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0;animation:none}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;overscroll-behavior-y:none}.detail-content{padding:24px 20px 32px}@media (min-width: 768px){.detail-content{padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.detail-content{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.detail-content{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.detail-content{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.detail-content{padding:32px var(--content-padding-3xl)!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
2678
2347
  }
2679
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageMainComponent, decorators: [{
2348
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageDetailsComponent, decorators: [{
2680
2349
  type: Component,
2681
- args: [{ selector: 'ds-mobile-page-main', standalone: true, imports: [
2350
+ args: [{ selector: 'ds-mobile-page-details', standalone: true, imports: [
2682
2351
  CommonModule,
2683
2352
  IonHeader,
2684
2353
  IonToolbar,
2685
- IonTitle,
2686
2354
  IonContent,
2687
- IonRefresher,
2688
- IonRefresherContent,
2689
- DsAvatarComponent,
2690
- DsLogoComponent
2355
+ DsIconComponent
2691
2356
  ], template: `
2692
- <!-- Fixed header at top -->
2357
+ <!-- Mobile header - hidden on desktop -->
2693
2358
  <ion-header>
2694
2359
  <ion-toolbar>
2695
- <div class="header-home">
2696
- <!-- Whitelabel Logomark -->
2697
- <ds-logo variant="mark" size="lg" />
2698
-
2699
- <!-- Title - fades in on scroll -->
2700
- <ion-title class="header-home__title">{{ title() }}</ion-title>
2701
-
2702
- <!-- Avatar -->
2703
- <div class="header-home__actions">
2704
- <ds-avatar
2705
- [size]="'md'"
2706
- [type]="avatarType()"
2707
- [initials]="avatarInitials()"
2708
- [src]="avatarSrc()"
2709
- [iconName]="avatarIconName()"
2710
- (click)="handleAvatarClick()"
2711
- style="cursor: pointer;"
2712
- />
2713
- </div>
2360
+ <div class="header-back">
2361
+ <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2362
+ <ds-icon name="remixArrowLeftLine" size="24px" />
2363
+ </button>
2364
+ <h1 class="header-title">{{ title() }}</h1>
2714
2365
  </div>
2715
2366
  </ion-toolbar>
2716
2367
  </ion-header>
2717
2368
 
2718
- <!-- Content with expandable header -->
2719
- <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
2720
- <!-- Condensed header for Ionic scroll effects -->
2721
- @if (showCondensedHeader()) {
2722
- <ion-header collapse="condense">
2723
- <ion-toolbar>
2724
- <ion-title size="large">{{ title() }}</ion-title>
2725
- </ion-toolbar>
2726
- </ion-header>
2727
- }
2728
-
2729
- <!-- Pull to refresh (only on native iOS/Android) -->
2730
- @if (showRefresh() && isNativePlatform()) {
2731
- <ion-refresher
2732
- slot="fixed"
2733
- (ionRefresh)="handleRefresh($event)"
2734
- [pullFactor]="0.4"
2735
- [pullMin]="80"
2736
- [pullMax]="240"
2737
- closeDuration="600ms">
2738
- <ion-refresher-content
2739
- pullingIcon="remixArrowDownS"
2740
- refreshingSpinner="lines">
2741
- </ion-refresher-content>
2742
- </ion-refresher>
2743
- }
2744
-
2745
- <!-- Expandable header section (purple background) -->
2746
- <div class="header-expandable">
2747
- <div class="header-expandable-inner">
2748
- <div class="header-expandable__text">
2749
- <h1 class="header-expandable__title">{{ headerTitle() || title() }}</h1>
2750
- @if (headerSubtitle()) {
2751
- <p class="header-expandable__subtitle">{{ headerSubtitle() }}</p>
2752
- }
2753
- </div>
2754
-
2755
- <!-- Slot for custom header content (e.g., property tiles) -->
2756
- <ng-content select="[header-content]"></ng-content>
2757
- </div>
2369
+ <ion-content>
2370
+ <!-- Desktop header above content -->
2371
+ <div class="desktop-header">
2372
+ <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2373
+ <ds-icon name="remixArrowLeftLine" size="24px" />
2374
+ </button>
2375
+ <h1>{{ title() }}</h1>
2758
2376
  </div>
2759
2377
 
2760
- <!-- Content wrapper -->
2761
- <div class="content-wrapper">
2762
- <div class="content-inner">
2763
- <!-- Main page content -->
2764
- <ng-content></ng-content>
2765
- </div>
2378
+ <!-- Content area -->
2379
+ <div class="detail-content">
2380
+ <ng-content></ng-content>
2766
2381
  </div>
2767
2382
  </ion-content>
2768
- `, styles: [":host{display:flex;flex-direction:column;align-items:center;height:100%}:host ion-header{background:var(--color-brand-secondary);box-shadow:none;height:72px;min-height:72px;margin-top:var(--app-header-top-offset)}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:72px;min-height:72px;padding:0}@media (min-width: 768px){ion-header{display:none;height:auto}}.header-home{display:flex;align-items:center;justify-content:space-between;padding:0 20px;background:var(--color-brand-secondary);height:72px}.header-home__title{position:absolute;left:50%;transform:translate(-50%) translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0;transition:transform .6s ease,opacity .6s ease;margin:0;padding:0;--color: white}.header-scrolled .header-home__title{opacity:1;transform:translate(-50%) translateY(0)}.header-home__actions{display:flex;align-items:center;gap:8px}.header-home__actions ds-avatar{cursor:pointer;-webkit-tap-highlight-color:transparent}.logomark{height:28px;width:auto;flex-shrink:0}@media (min-width: 768px){.header-home{padding:16px 24px}.logomark{height:32px}.header-home__title{display:none}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px;overflow:hidden}ion-content::part(scroll){display:flex;flex-direction:column}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-header[collapse=condense]{display:none}@media (min-width: 768px){ion-header[collapse=condense]{display:none}}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px;margin:0 auto}.header-expandable__text{margin-bottom:0}.header-expandable__title{font-size:var(--font-size-2xl);font-weight:600;color:var(--header-content-color, white);margin:0}.header-expandable__subtitle{font-size:var(--font-size-sm);font-weight:400;color:var(--header-content-color, white);opacity:.85;margin:0}@media (min-width: 768px){.header-expandable{padding:48px var(--content-padding-md)}.header-expandable__title{font-size:var(--font-size-3xl)}.header-expandable__subtitle{font-size:var(--font-size-base)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{position:relative;z-index:20;padding:0;flex:1;display:flex;flex-direction:column}@media (min-width: 768px){.content-wrapper{width:100%}}.content-inner{background:var(--color-background-neutral-primary);border-radius:24px 24px 0 0;transform:translateZ(0);will-change:transform;isolation:isolate;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary);flex:1;padding:24px 20px 32px;padding-bottom:calc(var(--mobile-content-spacing) + var(--mobile-tab-bar-height) + env(safe-area-inset-bottom,0px))}@media (min-width: 768px){.content-inner{border-radius:16px 16px 0 0;padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.content-inner{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.content-inner{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.content-inner{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.content-inner{padding:32px var(--content-padding-3xl)!important}}\n"] }]
2769
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { ionContent: [{
2770
- type: ViewChild,
2771
- args: [IonContent]
2772
- }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], avatarType: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarType", required: false }] }], avatarInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarInitials", required: false }] }], avatarSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarSrc", required: false }] }], avatarIconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarIconName", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showCondensedHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCondensedHeader", required: false }] }], scrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollThreshold", required: false }] }], headerFadeDistance: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerFadeDistance", required: false }] }], avatarClick: [{ type: i0.Output, args: ["avatarClick"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], scroll: [{ type: i0.Output, args: ["scroll"] }] } });
2383
+ `, styles: [":host{display:flex;flex-direction:column;height:100%}ion-header{background:var(--color-brand-secondary);box-shadow:none;height:64px}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 64px;height:100%;min-height:64px;padding:0}ion-header ion-toolbar::part(native){height:100%;min-height:64px;padding:0}@media (min-width: 768px){ion-header{display:none}}.header-back{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--color-brand-secondary);position:relative;height:100%;min-height:64px}.header-back .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;transition:opacity var(--transition-duration-fast) var(--ease-smooth);z-index:10}.header-back .back-button:hover{opacity:.8}.header-back .back-button:active{opacity:.6}.header-back .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:#fff;margin:0}.desktop-header{display:none}@media (min-width: 768px){.desktop-header{display:flex;align-items:center;gap:16px;padding:32px var(--content-padding-md) 24px var(--content-padding-md);max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}.desktop-header .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-color-default-primary);transition:opacity var(--transition-duration-fast) var(--ease-smooth)}.desktop-header .back-button:hover{opacity:.8}.desktop-header .back-button:active{opacity:.6}.desktop-header h1{font-size:var(--font-size-2xl);font-weight:600;margin:0;color:var(--text-color-default-primary)}}@media (min-width: 992px){.desktop-header{max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.desktop-header{max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2));padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.desktop-header{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.desktop-header{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-content{--background: var(--color-background-neutral-primary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;overflow:hidden}@media (max-width: 767px){ion-content{border-radius:24px 24px 36px 36px;animation:bottomRadiusOut .6s ease .3s forwards}:host(.navigating-back) ion-content{border-radius:24px 24px 0 0;animation:bottomRadiusIn .8s cubic-bezier(.36,.66,.04,1) forwards!important}}@keyframes bottomRadiusOut{0%{border-radius:24px 24px 36px 36px}to{border-radius:24px 24px 0 0}}@keyframes bottomRadiusIn{0%{border-radius:24px 24px 0 0}to{border-radius:24px 24px 36px 36px}}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0;animation:none}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;overscroll-behavior-y:none}.detail-content{padding:24px 20px 32px}@media (min-width: 768px){.detail-content{padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.detail-content{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.detail-content{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.detail-content{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.detail-content{padding:32px var(--content-padding-3xl)!important}}\n"] }]
2384
+ }], ctorParameters: () => [{ type: i1.NavController }, { type: i0.ElementRef }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], backRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "backRoute", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
2773
2385
 
2774
2386
  /**
2775
- * DsMobilePageDetailsComponent
2776
- *
2777
- * A mobile page layout for detail/drill-down pages with:
2778
- * - Back button header (mobile + desktop variants)
2779
- * - White background content area
2780
- * - Responsive padding
2781
- *
2782
- * @example
2783
- * ```html
2784
- * <!-- Simple detail page -->
2785
- * <ds-mobile-page-details
2786
- * title="Property Details"
2787
- * (back)="goBack()">
2788
- * <div class="page-content">
2789
- * <!-- Your content -->
2790
- * </div>
2791
- * </ds-mobile-page-details>
2792
- *
2793
- * <!-- With default back route -->
2794
- * <ds-mobile-page-details
2795
- * title="Invoice Details"
2796
- * backRoute="/invoices">
2797
- * <div class="page-content">
2798
- * <!-- Your content -->
2799
- * </div>
2800
- * </ds-mobile-page-details>
2801
- * ```
2802
- */
2803
- class DsMobilePageDetailsComponent extends MobilePageBase {
2804
- navCtrl;
2805
- elementRef;
2806
- // Inputs
2807
- title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
2808
- backRoute = input('', ...(ngDevMode ? [{ debugName: "backRoute" }] : [])); // Optional default back route
2809
- // Outputs
2810
- back = output();
2811
- constructor(navCtrl, elementRef) {
2812
- super();
2813
- this.navCtrl = navCtrl;
2814
- this.elementRef = elementRef;
2815
- }
2816
- /**
2817
- * Handle back navigation
2818
- *
2819
- * By default, navigates using the provided backRoute or browser back.
2820
- * Parent components can listen to the (back) event to override this behavior.
2821
- *
2822
- * @example
2823
- * ```html
2824
- * <!-- Default behavior: uses backRoute or browser back -->
2825
- * <ds-mobile-page-details
2826
- * title="Details"
2827
- * backRoute="/home">
2828
- * </ds-mobile-page-details>
2829
- *
2830
- * <!-- Custom behavior: parent handles navigation -->
2831
- * <ds-mobile-page-details
2832
- * title="Details"
2833
- * (back)="customBackHandler()">
2834
- * </ds-mobile-page-details>
2835
- * ```
2836
- */
2837
- handleBack() {
2838
- // Add class to trigger reverse animation
2839
- this.elementRef.nativeElement.classList.add('navigating-back');
2840
- // Emit event for parent to optionally handle
2841
- this.back.emit();
2842
- // Default behavior: navigate using backRoute or browser back
2843
- if (this.backRoute()) {
2844
- this.navCtrl.navigateBack(this.backRoute());
2845
- }
2846
- else {
2847
- this.navCtrl.back();
2848
- }
2849
- }
2850
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageDetailsComponent, deps: [{ token: i1.NavController }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
2851
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.14", type: DsMobilePageDetailsComponent, isStandalone: true, selector: "ds-mobile-page-details", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, backRoute: { classPropertyName: "backRoute", publicName: "backRoute", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back" }, usesInheritance: true, ngImport: i0, template: `
2852
- <!-- Mobile header - hidden on desktop -->
2853
- <ion-header>
2854
- <ion-toolbar>
2855
- <div class="header-back">
2856
- <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2857
- <ds-icon name="remixArrowLeftLine" size="24px" />
2858
- </button>
2859
- <h1 class="header-title">{{ title() }}</h1>
2860
- </div>
2861
- </ion-toolbar>
2862
- </ion-header>
2863
-
2864
- <ion-content>
2865
- <!-- Desktop header above content -->
2866
- <div class="desktop-header">
2867
- <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2868
- <ds-icon name="remixArrowLeftLine" size="24px" />
2869
- </button>
2870
- <h1>{{ title() }}</h1>
2871
- </div>
2872
-
2873
- <!-- Content area -->
2874
- <div class="detail-content">
2875
- <ng-content></ng-content>
2876
- </div>
2877
- </ion-content>
2878
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%}ion-header{background:var(--color-brand-secondary);box-shadow:none;height:64px}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 64px;height:100%;min-height:64px;padding:0}ion-header ion-toolbar::part(native){height:100%;min-height:64px;padding:0}@media (min-width: 768px){ion-header{display:none}}.header-back{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--color-brand-secondary);position:relative;height:100%;min-height:64px}.header-back .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;transition:opacity var(--transition-duration-fast) var(--ease-smooth);z-index:10}.header-back .back-button:hover{opacity:.8}.header-back .back-button:active{opacity:.6}.header-back .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:#fff;margin:0}.desktop-header{display:none}@media (min-width: 768px){.desktop-header{display:flex;align-items:center;gap:16px;padding:32px var(--content-padding-md) 24px var(--content-padding-md);max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}.desktop-header .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-color-default-primary);transition:opacity var(--transition-duration-fast) var(--ease-smooth)}.desktop-header .back-button:hover{opacity:.8}.desktop-header .back-button:active{opacity:.6}.desktop-header h1{font-size:var(--font-size-2xl);font-weight:600;margin:0;color:var(--text-color-default-primary)}}@media (min-width: 992px){.desktop-header{max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.desktop-header{max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2));padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.desktop-header{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.desktop-header{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-content{--background: var(--color-background-neutral-primary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;overflow:hidden}@media (max-width: 767px){ion-content{border-radius:24px 24px 36px 36px;animation:bottomRadiusOut .6s ease .3s forwards}:host(.navigating-back) ion-content{border-radius:24px 24px 0 0;animation:bottomRadiusIn .8s cubic-bezier(.36,.66,.04,1) forwards!important}}@keyframes bottomRadiusOut{0%{border-radius:24px 24px 36px 36px}to{border-radius:24px 24px 0 0}}@keyframes bottomRadiusIn{0%{border-radius:24px 24px 0 0}to{border-radius:24px 24px 36px 36px}}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0;animation:none}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;overscroll-behavior-y:none}.detail-content{padding:24px 20px 32px}@media (min-width: 768px){.detail-content{padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.detail-content{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.detail-content{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.detail-content{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.detail-content{padding:32px var(--content-padding-3xl)!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
2879
- }
2880
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePageDetailsComponent, decorators: [{
2881
- type: Component,
2882
- args: [{ selector: 'ds-mobile-page-details', standalone: true, imports: [
2883
- CommonModule,
2884
- IonHeader,
2885
- IonToolbar,
2886
- IonContent,
2887
- DsIconComponent
2888
- ], template: `
2889
- <!-- Mobile header - hidden on desktop -->
2890
- <ion-header>
2891
- <ion-toolbar>
2892
- <div class="header-back">
2893
- <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2894
- <ds-icon name="remixArrowLeftLine" size="24px" />
2895
- </button>
2896
- <h1 class="header-title">{{ title() }}</h1>
2897
- </div>
2898
- </ion-toolbar>
2899
- </ion-header>
2900
-
2901
- <ion-content>
2902
- <!-- Desktop header above content -->
2903
- <div class="desktop-header">
2904
- <button class="back-button" (click)="handleBack()" [attr.aria-label]="'Go back'">
2905
- <ds-icon name="remixArrowLeftLine" size="24px" />
2906
- </button>
2907
- <h1>{{ title() }}</h1>
2908
- </div>
2909
-
2910
- <!-- Content area -->
2911
- <div class="detail-content">
2912
- <ng-content></ng-content>
2913
- </div>
2914
- </ion-content>
2915
- `, styles: [":host{display:flex;flex-direction:column;height:100%}ion-header{background:var(--color-brand-secondary);box-shadow:none;height:64px}ion-header ion-toolbar{--background: var(--color-brand-secondary);--border-width: 0;--box-shadow: none;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 64px;height:100%;min-height:64px;padding:0}ion-header ion-toolbar::part(native){height:100%;min-height:64px;padding:0}@media (min-width: 768px){ion-header{display:none}}.header-back{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:var(--color-brand-secondary);position:relative;height:100%;min-height:64px}.header-back .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;transition:opacity var(--transition-duration-fast) var(--ease-smooth);z-index:10}.header-back .back-button:hover{opacity:.8}.header-back .back-button:active{opacity:.6}.header-back .header-title{position:absolute;left:50%;transform:translate(-50%);font-size:var(--font-size-base);font-weight:600;color:#fff;margin:0}.desktop-header{display:none}@media (min-width: 768px){.desktop-header{display:flex;align-items:center;gap:16px;padding:32px var(--content-padding-md) 24px var(--content-padding-md);max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}.desktop-header .back-button{background:none;border:none;padding:0;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-color-default-primary);transition:opacity var(--transition-duration-fast) var(--ease-smooth)}.desktop-header .back-button:hover{opacity:.8}.desktop-header .back-button:active{opacity:.6}.desktop-header h1{font-size:var(--font-size-2xl);font-weight:600;margin:0;color:var(--text-color-default-primary)}}@media (min-width: 992px){.desktop-header{max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.desktop-header{max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2));padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.desktop-header{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.desktop-header{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}ion-content{--background: var(--color-background-neutral-primary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;overflow:hidden}@media (max-width: 767px){ion-content{border-radius:24px 24px 36px 36px;animation:bottomRadiusOut .6s ease .3s forwards}:host(.navigating-back) ion-content{border-radius:24px 24px 0 0;animation:bottomRadiusIn .8s cubic-bezier(.36,.66,.04,1) forwards!important}}@keyframes bottomRadiusOut{0%{border-radius:24px 24px 36px 36px}to{border-radius:24px 24px 0 0}}@keyframes bottomRadiusIn{0%{border-radius:24px 24px 0 0}to{border-radius:24px 24px 36px 36px}}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0;animation:none}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;overscroll-behavior-y:none}.detail-content{padding:24px 20px 32px}@media (min-width: 768px){.detail-content{padding:32px var(--content-padding-md)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2));margin:0 auto;width:100%}}@media (min-width: 992px){.detail-content{padding:32px var(--content-padding-lg)!important;max-width:calc(var(--content-max-width-md) + (var(--content-padding-md) * 2))}}@media (min-width: 1440px){.detail-content{padding:32px var(--content-padding-xl)!important;max-width:calc(var(--content-max-width-lg) + (var(--content-padding-lg) * 2))}}@media (min-width: 1768px){.detail-content{padding:32px var(--content-padding-2xl)!important}}@media (min-width: 1920px){.detail-content{padding:32px var(--content-padding-3xl)!important}}\n"] }]
2916
- }], ctorParameters: () => [{ type: i1.NavController }, { type: i0.ElementRef }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], backRoute: [{ type: i0.Input, args: [{ isSignal: true, alias: "backRoute", required: false }] }], back: [{ type: i0.Output, args: ["back"] }] } });
2917
-
2918
- /**
2919
- * DsMobileContentComponent
2387
+ * DsMobileContentComponent
2920
2388
  *
2921
2389
  * Main content container for mobile pages with flexible layout options.
2922
2390
  * Provides consistent spacing and layout patterns.
@@ -4720,6 +4188,7 @@ class DsMobileTabBarComponent {
4720
4188
  routerSubscription;
4721
4189
  router;
4722
4190
  modalController = inject(ModalController);
4191
+ userService = inject(UserService);
4723
4192
  constructor(elementRef) {
4724
4193
  this.elementRef = elementRef;
4725
4194
  // Inject Router optionally
@@ -5168,15 +4637,17 @@ class DsMobileTabBarComponent {
5168
4637
  async handleAvatarClick() {
5169
4638
  // Emit the basic click event (for backwards compatibility)
5170
4639
  this.avatarClick.emit();
4640
+ // Use input if provided, otherwise fall back to service
4641
+ const menuItems = this.profileMenuItems || this.userService.profileMenuItems();
5171
4642
  // If no menu items configured, just emit and return
5172
- if (!this.profileMenuItems || this.profileMenuItems.length === 0) {
4643
+ if (!menuItems || menuItems.length === 0) {
5173
4644
  return;
5174
4645
  }
5175
4646
  // Open the bottom sheet with configured menu items
5176
4647
  const sheet = await this.modalController.create({
5177
4648
  component: DsMobileActionsBottomSheetComponent,
5178
4649
  componentProps: {
5179
- customActionGroups: this.profileMenuItems
4650
+ customActionGroups: menuItems
5180
4651
  },
5181
4652
  // Auto-height: no breakpoints, no handle
5182
4653
  cssClass: 'ds-bottom-sheet auto-height'
@@ -7691,7 +7162,9 @@ class DsMobilePostDetailModalComponent {
7691
7162
  <!-- Post Section -->
7692
7163
  <div class="post-section">
7693
7164
  <div class="post-content-only">
7694
- <post-text>{{ post().content }}</post-text>
7165
+ <post-text>
7166
+ <div [innerHTML]="post().content"></div>
7167
+ </post-text>
7695
7168
  @if (post().imageSrc) {
7696
7169
  <post-media>
7697
7170
  <img
@@ -7840,7 +7313,7 @@ class DsMobilePostDetailModalComponent {
7840
7313
  </div>
7841
7314
  </div>
7842
7315
  }
7843
- `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}\n", ":host{display:block;position:relative;height:100%;width:100%}.post-modal-content{--background: var(--color-background-neutral-primary, #ffffff)}.post-modal-wrapper{display:flex;flex-direction:column;min-height:100%;min-height:100dvh;background:var(--color-background-neutral-primary, #ffffff)}.post-modal-header{position:sticky;top:0;z-index:10;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default);padding:0 16px}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:72px}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.post-detail-container{display:flex;flex-direction:column;gap:16px;width:100%;max-width:640px;margin:0 auto;padding:16px 0 20px;flex:1}.post-section{width:100%;border-bottom:1px solid var(--border-color-default);padding:0 0 16px}.post-content-only{font-size:var(--font-size-sm);line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding:0 20px}.post-content-only post-media{margin-top:16px}.post-actions{display:flex;align-items:center;gap:16px;padding:0 20px}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.comments-section{display:flex;flex-direction:column;margin-left:0;margin-right:0;padding:0 20px}.comments-header{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0 0 16px;padding-left:0;padding-right:0}.comments-list{display:flex;flex-direction:column}.comments-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}.composer-spacer{height:calc(81px + var(--app-sheet-top-offset) + env(safe-area-inset-bottom,0px))}.bottom-spacer{height:0px}.comment-composer-fixed{position:fixed;bottom:var(--app-sheet-top-offset);left:0;right:0;z-index:1000;pointer-events:none;transform:translateY(calc(-1 * var(--keyboard-height, 0px)));transition:transform .3s ease-out;max-width:100vw}.comment-composer{pointer-events:auto;background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);padding:12px 16px;padding-bottom:max(12px,env(safe-area-inset-bottom,0px));width:100%;display:flex;flex-direction:column;gap:8px;box-shadow:100px 150px 0 150px var(--color-background-neutral-primary, #ffffff)}.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-brand-base, #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-brand-base, #6B5FF5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-brand-base, #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-brand-base, #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)}}.composer-content{display:flex;align-items:flex-start;gap:12px;width:100%;position:relative}.composer-content ds-avatar{position:relative;top:6px}.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 48px 12px 16px;min-height:44px;position:relative}.mention-menu{position:absolute;bottom:100%;left:0;right:0;background:var(--color-background-neutral-primary, #ffffff);border-radius:12px;box-shadow:0 4px 12px #00000026;margin-bottom:8px;max-height:200px;overflow-y:auto;z-index:10;animation:slideUp .2s ease-out}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.mention-menu-item{display:flex;align-items:center;gap:12px;padding:12px;border:none;background:none;width:100%;text-align:left;cursor:pointer;transition:background .2s ease;border-bottom:1px solid var(--border-color-default)}.mention-menu-item:last-child{border-bottom:none}.mention-menu-item:active{background:var(--color-background-neutral-secondary, #f5f5f5)}.mention-user-info{display:flex;align-items:center;gap:8px;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-fixed{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;animation:slideInFromRight .2s ease-out}.send-button-fixed::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}.composer-input-wrapper ds-icon-button{flex-shrink:0;animation:slideInFromRight .2s ease-out}.composer-input-wrapper ds-icon-button::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)}}@supports (padding: env(safe-area-inset-bottom)){.post-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}.post-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.loading-spinner{width:48px;height:48px;border:3px solid var(--color-background-neutral-secondary, #f0f0f0);border-top-color:var(--color-primary-base, #2563eb);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin-top:16px}.post-error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:16px}.error-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.error-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCommentComponent, selector: "ds-mobile-comment", inputs: ["authorName", "authorRole", "timestamp", "content", "avatarInitials", "avatarType", "clickable", "isOwnComment", "isLiked", "likeCount"], outputs: ["isLikedChange", "likeCountChange", "commentClick", "replyClick", "editClick", "longPress"] }] });
7316
+ `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}\n", ":host{display:block;position:relative;height:100%;width:100%}.post-modal-content{--background: var(--color-background-neutral-primary, #ffffff)}.post-modal-wrapper{display:flex;flex-direction:column;min-height:100%;min-height:100dvh;background:var(--color-background-neutral-primary, #ffffff)}.post-modal-header{position:sticky;top:0;z-index:10;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default);padding:0 16px}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:72px}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.post-detail-container{display:flex;flex-direction:column;gap:16px;width:100%;max-width:640px;margin:0 auto;padding:16px 0 20px;flex:1}.post-section{width:100%;border-bottom:1px solid var(--border-color-default);padding:0 0 16px}.post-content-only{font-size:var(--font-size-sm);line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding:0 20px}.post-content-only post-media{margin-top:16px}.post-actions{display:flex;align-items:center;gap:16px;padding:0 20px}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.comments-section{display:flex;flex-direction:column;margin-left:0;margin-right:0;padding:0 20px}.comments-header{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0 0 16px;padding-left:0;padding-right:0}.comments-list{display:flex;flex-direction:column}.comments-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}.composer-spacer{height:calc(81px + env(safe-area-inset-bottom,0px))}.bottom-spacer{height:0px}.comment-composer-fixed{position:fixed;bottom:0;left:0;right:0;z-index:1000;pointer-events:none;transform:translateY(calc(-1 * var(--keyboard-height, 0px)));transition:transform .3s ease-out;max-width:100vw}.comment-composer{pointer-events:auto;background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);padding:12px 16px;padding-bottom:max(12px,env(safe-area-inset-bottom,0px));width:100%;display:flex;flex-direction:column;gap:8px;box-shadow:100px 150px 0 150px var(--color-background-neutral-primary, #ffffff)}.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-brand-base, #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-brand-base, #6B5FF5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-brand-base, #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-brand-base, #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)}}.composer-content{display:flex;align-items:flex-start;gap:12px;width:100%;position:relative}.composer-content ds-avatar{position:relative;top:6px}.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 48px 12px 16px;min-height:44px;position:relative}.mention-menu{position:absolute;bottom:100%;left:0;right:0;background:var(--color-background-neutral-primary, #ffffff);border-radius:12px;box-shadow:0 4px 12px #00000026;margin-bottom:8px;max-height:200px;overflow-y:auto;z-index:10;animation:slideUp .2s ease-out}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.mention-menu-item{display:flex;align-items:center;gap:12px;padding:12px;border:none;background:none;width:100%;text-align:left;cursor:pointer;transition:background .2s ease;border-bottom:1px solid var(--border-color-default)}.mention-menu-item:last-child{border-bottom:none}.mention-menu-item:active{background:var(--color-background-neutral-secondary, #f5f5f5)}.mention-user-info{display:flex;align-items:center;gap:8px;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-fixed{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;animation:slideInFromRight .2s ease-out}.send-button-fixed::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}.composer-input-wrapper ds-icon-button{flex-shrink:0;animation:slideInFromRight .2s ease-out}.composer-input-wrapper ds-icon-button::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)}}@supports (padding: env(safe-area-inset-bottom)){.post-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}.post-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.loading-spinner{width:48px;height:48px;border:3px solid var(--color-background-neutral-secondary, #f0f0f0);border-top-color:var(--color-primary-base, #2563eb);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin-top:16px}.post-error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:16px}.error-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.error-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { 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: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCommentComponent, selector: "ds-mobile-comment", inputs: ["authorName", "authorRole", "timestamp", "content", "avatarInitials", "avatarType", "clickable", "isOwnComment", "isLiked", "likeCount"], outputs: ["isLikedChange", "likeCountChange", "commentClick", "replyClick", "editClick", "longPress"] }] });
7844
7317
  }
7845
7318
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsMobilePostDetailModalComponent, decorators: [{
7846
7319
  type: Component,
@@ -7911,7 +7384,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
7911
7384
  <!-- Post Section -->
7912
7385
  <div class="post-section">
7913
7386
  <div class="post-content-only">
7914
- <post-text>{{ post().content }}</post-text>
7387
+ <post-text>
7388
+ <div [innerHTML]="post().content"></div>
7389
+ </post-text>
7915
7390
  @if (post().imageSrc) {
7916
7391
  <post-media>
7917
7392
  <img
@@ -8060,7 +7535,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
8060
7535
  </div>
8061
7536
  </div>
8062
7537
  }
8063
- `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}\n", ":host{display:block;position:relative;height:100%;width:100%}.post-modal-content{--background: var(--color-background-neutral-primary, #ffffff)}.post-modal-wrapper{display:flex;flex-direction:column;min-height:100%;min-height:100dvh;background:var(--color-background-neutral-primary, #ffffff)}.post-modal-header{position:sticky;top:0;z-index:10;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default);padding:0 16px}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:72px}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.post-detail-container{display:flex;flex-direction:column;gap:16px;width:100%;max-width:640px;margin:0 auto;padding:16px 0 20px;flex:1}.post-section{width:100%;border-bottom:1px solid var(--border-color-default);padding:0 0 16px}.post-content-only{font-size:var(--font-size-sm);line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding:0 20px}.post-content-only post-media{margin-top:16px}.post-actions{display:flex;align-items:center;gap:16px;padding:0 20px}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.comments-section{display:flex;flex-direction:column;margin-left:0;margin-right:0;padding:0 20px}.comments-header{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0 0 16px;padding-left:0;padding-right:0}.comments-list{display:flex;flex-direction:column}.comments-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}.composer-spacer{height:calc(81px + var(--app-sheet-top-offset) + env(safe-area-inset-bottom,0px))}.bottom-spacer{height:0px}.comment-composer-fixed{position:fixed;bottom:var(--app-sheet-top-offset);left:0;right:0;z-index:1000;pointer-events:none;transform:translateY(calc(-1 * var(--keyboard-height, 0px)));transition:transform .3s ease-out;max-width:100vw}.comment-composer{pointer-events:auto;background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);padding:12px 16px;padding-bottom:max(12px,env(safe-area-inset-bottom,0px));width:100%;display:flex;flex-direction:column;gap:8px;box-shadow:100px 150px 0 150px var(--color-background-neutral-primary, #ffffff)}.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-brand-base, #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-brand-base, #6B5FF5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-brand-base, #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-brand-base, #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)}}.composer-content{display:flex;align-items:flex-start;gap:12px;width:100%;position:relative}.composer-content ds-avatar{position:relative;top:6px}.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 48px 12px 16px;min-height:44px;position:relative}.mention-menu{position:absolute;bottom:100%;left:0;right:0;background:var(--color-background-neutral-primary, #ffffff);border-radius:12px;box-shadow:0 4px 12px #00000026;margin-bottom:8px;max-height:200px;overflow-y:auto;z-index:10;animation:slideUp .2s ease-out}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.mention-menu-item{display:flex;align-items:center;gap:12px;padding:12px;border:none;background:none;width:100%;text-align:left;cursor:pointer;transition:background .2s ease;border-bottom:1px solid var(--border-color-default)}.mention-menu-item:last-child{border-bottom:none}.mention-menu-item:active{background:var(--color-background-neutral-secondary, #f5f5f5)}.mention-user-info{display:flex;align-items:center;gap:8px;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-fixed{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;animation:slideInFromRight .2s ease-out}.send-button-fixed::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}.composer-input-wrapper ds-icon-button{flex-shrink:0;animation:slideInFromRight .2s ease-out}.composer-input-wrapper ds-icon-button::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)}}@supports (padding: env(safe-area-inset-bottom)){.post-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}.post-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.loading-spinner{width:48px;height:48px;border:3px solid var(--color-background-neutral-secondary, #f0f0f0);border-top-color:var(--color-primary-base, #2563eb);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin-top:16px}.post-error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:16px}.error-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.error-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"] }]
7538
+ `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-sm);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}\n", ":host{display:block;position:relative;height:100%;width:100%}.post-modal-content{--background: var(--color-background-neutral-primary, #ffffff)}.post-modal-wrapper{display:flex;flex-direction:column;min-height:100%;min-height:100dvh;background:var(--color-background-neutral-primary, #ffffff)}.post-modal-header{position:sticky;top:0;z-index:10;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default);padding:0 16px}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:72px}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.post-detail-container{display:flex;flex-direction:column;gap:16px;width:100%;max-width:640px;margin:0 auto;padding:16px 0 20px;flex:1}.post-section{width:100%;border-bottom:1px solid var(--border-color-default);padding:0 0 16px}.post-content-only{font-size:var(--font-size-sm);line-height:24px;color:var(--color-text-primary, #1a1a1a);margin-bottom:16px;padding:0 20px}.post-content-only post-media{margin-top:16px}.post-actions{display:flex;align-items:center;gap:16px;padding:0 20px}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.comments-section{display:flex;flex-direction:column;margin-left:0;margin-right:0;padding:0 20px}.comments-header{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:24px;color:var(--color-text-primary, #1a1a1a);margin:0 0 16px;padding-left:0;padding-right:0}.comments-list{display:flex;flex-direction:column}.comments-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}.composer-spacer{height:calc(81px + env(safe-area-inset-bottom,0px))}.bottom-spacer{height:0px}.comment-composer-fixed{position:fixed;bottom:0;left:0;right:0;z-index:1000;pointer-events:none;transform:translateY(calc(-1 * var(--keyboard-height, 0px)));transition:transform .3s ease-out;max-width:100vw}.comment-composer{pointer-events:auto;background:var(--color-background-neutral-primary, #ffffff);border-top:1px solid var(--border-color-default);padding:12px 16px;padding-bottom:max(12px,env(safe-area-inset-bottom,0px));width:100%;display:flex;flex-direction:column;gap:8px;box-shadow:100px 150px 0 150px var(--color-background-neutral-primary, #ffffff)}.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-brand-base, #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-brand-base, #6B5FF5)}.cancel-edit{background:none;border:none;padding:4px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--color-brand-base, #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-brand-base, #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)}}.composer-content{display:flex;align-items:flex-start;gap:12px;width:100%;position:relative}.composer-content ds-avatar{position:relative;top:6px}.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 48px 12px 16px;min-height:44px;position:relative}.mention-menu{position:absolute;bottom:100%;left:0;right:0;background:var(--color-background-neutral-primary, #ffffff);border-radius:12px;box-shadow:0 4px 12px #00000026;margin-bottom:8px;max-height:200px;overflow-y:auto;z-index:10;animation:slideUp .2s ease-out}@keyframes slideUp{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.mention-menu-item{display:flex;align-items:center;gap:12px;padding:12px;border:none;background:none;width:100%;text-align:left;cursor:pointer;transition:background .2s ease;border-bottom:1px solid var(--border-color-default)}.mention-menu-item:last-child{border-bottom:none}.mention-menu-item:active{background:var(--color-background-neutral-secondary, #f5f5f5)}.mention-user-info{display:flex;align-items:center;gap:8px;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-fixed{position:absolute;top:6px;right:6px;z-index:10;flex-shrink:0;animation:slideInFromRight .2s ease-out}.send-button-fixed::ng-deep button{width:32px!important;height:32px!important;min-width:32px!important;min-height:32px!important;padding:0!important;border-radius:50%!important}.composer-input-wrapper ds-icon-button{flex-shrink:0;animation:slideInFromRight .2s ease-out}.composer-input-wrapper ds-icon-button::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)}}@supports (padding: env(safe-area-inset-bottom)){.post-detail-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}.post-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.loading-spinner{width:48px;height:48px;border:3px solid var(--color-background-neutral-secondary, #f0f0f0);border-top-color:var(--color-primary-base, #2563eb);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin-top:16px}.post-error-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center;gap:16px}.error-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--color-text-primary, #1a1a1a);margin:0}.error-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-text-secondary, #737373);margin:0}\n"] }]
8064
7539
  }], ctorParameters: () => [{ type: i1.ModalController }, { type: DsMobileLightboxService }, { type: DsMobileBottomSheetService }], propDecorators: { postData: [{
8065
7540
  type: Input
8066
7541
  }], loading: [{
@@ -9774,40 +9249,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
9774
9249
 
9775
9250
  // Mobile Page Components
9776
9251
 
9777
- /**
9778
- * User service for managing current user data globally
9779
- */
9780
- class UserService {
9781
- // User avatar configuration
9782
- _avatarInitials = signal('LM', ...(ngDevMode ? [{ debugName: "_avatarInitials" }] : []));
9783
- _avatarType = signal('initials', ...(ngDevMode ? [{ debugName: "_avatarType" }] : []));
9784
- _avatarSrc = signal('', ...(ngDevMode ? [{ debugName: "_avatarSrc" }] : []));
9785
- // Readonly computed values
9786
- avatarInitials = this._avatarInitials.asReadonly();
9787
- avatarType = this._avatarType.asReadonly();
9788
- avatarSrc = this._avatarSrc.asReadonly();
9789
- /**
9790
- * Update avatar configuration
9791
- */
9792
- setAvatarInitials(initials) {
9793
- this._avatarInitials.set(initials);
9794
- }
9795
- setAvatarType(type) {
9796
- this._avatarType.set(type);
9797
- }
9798
- setAvatarSrc(src) {
9799
- this._avatarSrc.set(src);
9800
- }
9801
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
9802
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, providedIn: 'root' });
9803
- }
9804
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: UserService, decorators: [{
9805
- type: Injectable,
9806
- args: [{
9807
- providedIn: 'root'
9808
- }]
9809
- }] });
9810
-
9811
9252
  class MobileCommunityPageComponent {
9812
9253
  router;
9813
9254
  route;
@@ -10453,7 +9894,7 @@ class MobileCommunityPageComponent {
10453
9894
  </div>
10454
9895
  </ds-mobile-content>
10455
9896
  </ds-mobile-page-main>
10456
- `, isInline: true, styles: [".post-feed{display:flex;flex-direction:column;max-width:640px}.post-list-wrapper{display:flex;flex-direction:column}.pinned-posts-section{margin:-12px -12px 12px;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.community-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "clickable"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobilePostComposerComponent, selector: "ds-mobile-post-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "placeholder", "buttonText"], outputs: ["composerClick"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: PostAttachmentsComponent, selector: "post-attachments" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileInlinePhotoComponent, selector: "ds-mobile-inline-photo", inputs: ["images", "author", "maxVisible"], outputs: ["photoClick"] }] });
9897
+ `, isInline: true, styles: [".post-feed{display:flex;flex-direction:column;max-width:640px}.post-list-wrapper{display:flex;flex-direction:column}.pinned-posts-section{margin:-12px -12px 12px;padding:0 12px 12px;box-shadow:var(--box-shadow-sm);border-radius:16px;border:1px solid var(--border-color-default)}.clickable-image{cursor:pointer;transition:transform .2s ease,opacity .2s ease;border-radius:8px;display:block;width:100%;aspect-ratio:16/9;object-fit:cover}.clickable-image:active{transform:scale(.98);opacity:.9}.community-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-image{width:96px;height:96px;margin-bottom:24px}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemPostComponent, selector: "ds-mobile-interactive-list-item-post", inputs: ["authorName", "authorRole", "timestamp", "avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "showBadge", "variant", "clickable"], outputs: ["postClick", "commentClick", "longPress"] }, { kind: "component", type: DsMobilePostComposerComponent, selector: "ds-mobile-post-composer", inputs: ["avatarInitials", "avatarType", "avatarSrc", "avatarIconName", "placeholder", "buttonText"], outputs: ["composerClick"] }, { kind: "component", type: PostContentComponent, selector: "post-content" }, { kind: "component", type: PostTextComponent, selector: "post-text" }, { kind: "component", type: PostMediaComponent, selector: "post-media" }, { kind: "component", type: PostAttachmentsComponent, selector: "post-attachments" }, { kind: "component", type: PostActionsComponent, selector: "post-actions" }, { kind: "component", type: ActionLikeComponent, selector: "action-like", inputs: ["active", "count"], outputs: ["activeChange", "countChange", "likeClick"] }, { kind: "component", type: ActionCommentComponent, selector: "action-comment", inputs: ["count"], outputs: ["commentClick"] }, { kind: "component", type: DsMobileCardInlineFileComponent, selector: "ds-mobile-card-inline-file", inputs: ["fileName", "fileSize", "variant", "layout"], outputs: ["fileClick"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileInlinePhotoComponent, selector: "ds-mobile-inline-photo", inputs: ["images", "author", "maxVisible"], outputs: ["photoClick"] }] });
10457
9898
  }
10458
9899
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileCommunityPageComponent, decorators: [{
10459
9900
  type: Component,
@@ -10982,7 +10423,7 @@ class MobileHandbookPageComponent {
10982
10423
  </ds-mobile-content-section>
10983
10424
  </ds-mobile-content>
10984
10425
  </ds-mobile-page-main>
10985
- `, isInline: true, styles: [".folders-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;justify-items:center}@media (min-width: 768px){.folders-grid{grid-template-columns:repeat(3,1fr)}}ds-mobile-handbook-folder{width:100%}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: DsMobileHandbookFolderComponent, selector: "ds-mobile-handbook-folder", inputs: ["variant", "iconName", "itemCount", "label", "items", "loading", "error"] }] });
10426
+ `, isInline: true, styles: [".folders-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;justify-items:center}@media (min-width: 768px){.folders-grid{grid-template-columns:repeat(3,1fr)}}ds-mobile-handbook-folder{width:100%}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: DsMobileHandbookFolderComponent, selector: "ds-mobile-handbook-folder", inputs: ["variant", "iconName", "itemCount", "label", "items", "loading", "error"] }] });
10986
10427
  }
10987
10428
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHandbookPageComponent, decorators: [{
10988
10429
  type: Component,
@@ -11173,7 +10614,7 @@ class MobileHomePageComponent {
11173
10614
  </ds-mobile-content-section>
11174
10615
  </ds-mobile-content>
11175
10616
  </ds-mobile-page-main>
11176
- `, isInline: true, styles: [".grey-box{height:120px;border-radius:12px;background:var(--color-background-neutral-tertiary);flex:1}.grey-box.clickable{background:var(--color-background-brand);cursor:pointer;transition:transform var(--transition-duration-fast) var(--ease-smooth)}.grey-box.clickable:active{transform:scale(.98)}\n"], dependencies: [{ kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileHeaderContentComponent, selector: "ds-mobile-header-content" }, { kind: "component", type: DsMobileHeaderContentTileComponent, selector: "ds-mobile-header-content-tile" }, { kind: "component", type: TileIconComponent, selector: "tile-icon" }, { kind: "component", type: TileContentComponent, selector: "tile-content" }, { kind: "component", type: TileLabelComponent, selector: "tile-label" }, { kind: "component", type: TileValueComponent, selector: "tile-value" }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: SectionHeaderComponent, selector: "section-header", inputs: ["width"] }, { kind: "component", type: ContentRowComponent, selector: "content-row" }] });
10617
+ `, isInline: true, styles: [".grey-box{height:120px;border-radius:12px;background:var(--color-background-neutral-tertiary);flex:1}.grey-box.clickable{background:var(--color-background-brand);cursor:pointer;transition:transform var(--transition-duration-fast) var(--ease-smooth)}.grey-box.clickable:active{transform:scale(.98)}\n"], dependencies: [{ kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileHeaderContentComponent, selector: "ds-mobile-header-content" }, { kind: "component", type: DsMobileHeaderContentTileComponent, selector: "ds-mobile-header-content-tile" }, { kind: "component", type: TileIconComponent, selector: "tile-icon" }, { kind: "component", type: TileContentComponent, selector: "tile-content" }, { kind: "component", type: TileLabelComponent, selector: "tile-label" }, { kind: "component", type: TileValueComponent, selector: "tile-value" }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileContentSectionComponent, selector: "ds-mobile-content-section" }, { kind: "component", type: SectionHeaderComponent, selector: "section-header", inputs: ["width"] }, { kind: "component", type: ContentRowComponent, selector: "content-row" }] });
11177
10618
  }
11178
10619
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileHomePageComponent, decorators: [{
11179
10620
  type: Component,
@@ -11501,7 +10942,7 @@ class MobileInquiriesPageComponent {
11501
10942
  </div>
11502
10943
  </ds-mobile-content>
11503
10944
  </ds-mobile-page-main>
11504
- `, isInline: true, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance"], outputs: ["avatarClick", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "clickable", "showChevron"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }] });
10945
+ `, isInline: true, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"], dependencies: [{ kind: "component", type: DsMobilePageMainComponent, selector: "ds-mobile-page-main", inputs: ["title", "headerTitle", "headerSubtitle", "avatarType", "avatarInitials", "avatarSrc", "avatarIconName", "showRefresh", "showCondensedHeader", "scrollThreshold", "headerFadeDistance", "profileMenuItems"], outputs: ["avatarClick", "profileActionSelected", "refresh", "scroll"] }, { kind: "component", type: DsMobileContentComponent, selector: "ds-mobile-content", inputs: ["layout"] }, { kind: "component", type: DsMobileInteractiveListItemInquiryComponent, selector: "ds-mobile-interactive-list-item-inquiry", inputs: ["title", "description", "status", "statusLabel", "timestamp", "iconName", "iconColor", "variant", "clickable", "showChevron"], outputs: ["inquiryClick", "longPress"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }, { kind: "component", type: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }] });
11505
10946
  }
11506
10947
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiriesPageComponent, decorators: [{
11507
10948
  type: Component,
@@ -11528,600 +10969,1187 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
11528
10969
  </ds-mobile-inline-tabs>
11529
10970
  </div>
11530
10971
 
11531
- <ds-mobile-content>
11532
- <div class="inquiries-container">
11533
- @if (filteredInquiries().length > 0) {
11534
- <div class="inquiry-list-wrapper">
11535
- @for (inquiry of filteredInquiries(); track inquiry.id; let idx = $index) {
11536
- <ds-mobile-interactive-list-item-inquiry
11537
- [title]="inquiry.title"
11538
- [description]="inquiry.description"
11539
- [status]="inquiry.status"
11540
- [timestamp]="inquiry.timestamp"
11541
- [iconName]="getInquiryIcon(inquiry.category)"
11542
- [clickable]="true"
11543
- [showChevron]="false"
11544
- (inquiryClick)="openInquiryDetail(inquiry.id)"
11545
- (longPress)="showInquiryActions(inquiry.id)">
11546
- </ds-mobile-interactive-list-item-inquiry>
11547
-
11548
- }
11549
- </div>
11550
- } @else {
11551
- <!-- Empty state -->
11552
- <div class="empty-state">
11553
- <ds-icon name="remixInboxLine" size="48px" color="tertiary" />
11554
- <h3 class="empty-state-title">Ingen henvendelser endnu</h3>
11555
- <p class="empty-state-description">
11556
- @if (filterStatus() === 'open') {
11557
- Du har ingen åbne henvendelser
11558
- } @else if (filterStatus() === 'closed') {
11559
- Du har ingen lukkede henvendelser
11560
- } @else {
11561
- Du har ikke oprettet nogen henvendelser endnu
11562
- }
11563
- </p>
11564
- </div>
11565
- }
10972
+ <ds-mobile-content>
10973
+ <div class="inquiries-container">
10974
+ @if (filteredInquiries().length > 0) {
10975
+ <div class="inquiry-list-wrapper">
10976
+ @for (inquiry of filteredInquiries(); track inquiry.id; let idx = $index) {
10977
+ <ds-mobile-interactive-list-item-inquiry
10978
+ [title]="inquiry.title"
10979
+ [description]="inquiry.description"
10980
+ [status]="inquiry.status"
10981
+ [timestamp]="inquiry.timestamp"
10982
+ [iconName]="getInquiryIcon(inquiry.category)"
10983
+ [clickable]="true"
10984
+ [showChevron]="false"
10985
+ (inquiryClick)="openInquiryDetail(inquiry.id)"
10986
+ (longPress)="showInquiryActions(inquiry.id)">
10987
+ </ds-mobile-interactive-list-item-inquiry>
10988
+
10989
+ }
10990
+ </div>
10991
+ } @else {
10992
+ <!-- Empty state -->
10993
+ <div class="empty-state">
10994
+ <ds-icon name="remixInboxLine" size="48px" color="tertiary" />
10995
+ <h3 class="empty-state-title">Ingen henvendelser endnu</h3>
10996
+ <p class="empty-state-description">
10997
+ @if (filterStatus() === 'open') {
10998
+ Du har ingen åbne henvendelser
10999
+ } @else if (filterStatus() === 'closed') {
11000
+ Du har ingen lukkede henvendelser
11001
+ } @else {
11002
+ Du har ikke oprettet nogen henvendelser endnu
11003
+ }
11004
+ </p>
11005
+ </div>
11006
+ }
11007
+ </div>
11008
+ </ds-mobile-content>
11009
+ </ds-mobile-page-main>
11010
+ `, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"] }]
11011
+ }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }] });
11012
+
11013
+ class MobileInquiryDetailPageComponent extends MobilePageBase {
11014
+ userService;
11015
+ navCtrl;
11016
+ elementRef;
11017
+ ionContent;
11018
+ // Platform detection
11019
+ platform = inject(Platform);
11020
+ // Computed property to check if running on native platform
11021
+ isNativePlatform = computed(() => this.platform.is('ios') ||
11022
+ this.platform.is('android') ||
11023
+ this.platform.is('capacitor'), ...(ngDevMode ? [{ debugName: "isNativePlatform" }] : []));
11024
+ inquiryTitle = 'Tørretumbler virker ikke';
11025
+ activeTab = signal('activity', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
11026
+ tabItems = [
11027
+ { id: 'activity', label: 'Aktivitet' },
11028
+ { id: 'messages', label: 'Beskeder', badge: 0 },
11029
+ { id: 'details', label: 'Detaljer' }
11030
+ ];
11031
+ activities = [
11032
+ {
11033
+ id: '1',
11034
+ type: 'assignment',
11035
+ actor: 'Martin Smith',
11036
+ actorInitials: 'MS',
11037
+ title: 'er blevet tildelt som din dedikerede tekniker.',
11038
+ timestamp: '2 dage siden',
11039
+ date: '28. feb 2025',
11040
+ iconName: 'remixUserAddLine'
11041
+ },
11042
+ {
11043
+ id: '2',
11044
+ type: 'assignment',
11045
+ actor: 'Ricki Meihlen',
11046
+ actorInitials: 'RM',
11047
+ title: 'er blevet tildelt til at håndtere din henvendelse.',
11048
+ timestamp: '8 dage siden',
11049
+ date: '22. feb 2025',
11050
+ iconName: 'remixUserLine'
11051
+ },
11052
+ {
11053
+ id: '3',
11054
+ type: 'creation',
11055
+ title: 'Henvendelse oprettet',
11056
+ timestamp: '8 dage siden',
11057
+ date: '22. feb 2025',
11058
+ iconName: 'remixAddCircleLine'
11059
+ }
11060
+ ];
11061
+ messageThreads = [
11062
+ {
11063
+ id: '1',
11064
+ senderName: 'Ove Hindborg',
11065
+ senderAvatar: '',
11066
+ senderInitials: 'OH',
11067
+ message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
11068
+ role: 'Sagsbehandler',
11069
+ timestamp: '2t siden',
11070
+ unread: true
11071
+ },
11072
+ {
11073
+ id: '2',
11074
+ senderName: 'Martin Smith',
11075
+ senderAvatar: '',
11076
+ senderInitials: 'MS',
11077
+ message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
11078
+ role: 'Tekniker',
11079
+ timestamp: '4t siden',
11080
+ unread: true
11081
+ }
11082
+ ];
11083
+ unreadMessagesCount = computed(() => {
11084
+ const count = this.messageThreads.filter(m => m.unread).length;
11085
+ // Update badge in tab items
11086
+ const messagesTab = this.tabItems.find(t => t.id === 'messages');
11087
+ if (messagesTab) {
11088
+ messagesTab.badge = count;
11089
+ }
11090
+ return count;
11091
+ }, ...(ngDevMode ? [{ debugName: "unreadMessagesCount" }] : []));
11092
+ constructor(userService, navCtrl, elementRef) {
11093
+ super();
11094
+ this.userService = userService;
11095
+ this.navCtrl = navCtrl;
11096
+ this.elementRef = elementRef;
11097
+ // Trigger initial badge update
11098
+ this.unreadMessagesCount();
11099
+ }
11100
+ ngAfterViewInit() {
11101
+ // Initial setup if needed
11102
+ }
11103
+ setActiveTab(tabId) {
11104
+ this.activeTab.set(tabId);
11105
+ }
11106
+ goBack() {
11107
+ this.navCtrl.back({ animation: customBackTransition });
11108
+ }
11109
+ handleScroll(event) {
11110
+ const scrollTop = event.detail.scrollTop;
11111
+ const threshold = 160;
11112
+ const fadeDistance = 200;
11113
+ const header = this.elementRef.nativeElement.querySelector('ion-header:not([collapse])');
11114
+ const headerExpandable = this.elementRef.nativeElement.querySelector('.header-expandable');
11115
+ // Show title in fixed header when scrolled past threshold
11116
+ if (scrollTop > threshold) {
11117
+ header?.classList.add('header-scrolled');
11118
+ }
11119
+ else {
11120
+ header?.classList.remove('header-scrolled');
11121
+ }
11122
+ // Fade out header-expandable content based on scroll
11123
+ if (headerExpandable) {
11124
+ const fadeProgress = Math.min(scrollTop / fadeDistance, 1);
11125
+ // Calculate opacity (1 to 0)
11126
+ const opacity = 1 - fadeProgress;
11127
+ // Calculate transform (0px to -20px upward)
11128
+ const translateY = fadeProgress * -20;
11129
+ // Apply styles
11130
+ headerExpandable.style.opacity = opacity.toString();
11131
+ headerExpandable.style.transform = `translateY(${translateY}px)`;
11132
+ }
11133
+ }
11134
+ handleRefresh(event) {
11135
+ console.log('Pull-to-refresh triggered');
11136
+ setTimeout(() => {
11137
+ console.log('Refresh complete');
11138
+ event.target.complete();
11139
+ }, 1000);
11140
+ }
11141
+ openMessage(messageId) {
11142
+ console.log('Opening message:', messageId);
11143
+ // Navigate to message thread detail
11144
+ }
11145
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, deps: [{ token: UserService }, { token: i1.NavController }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
11146
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MobileInquiryDetailPageComponent, isStandalone: true, selector: "app-mobile-inquiry-detail-page", host: { classAttribute: "ion-page" }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
11147
+ <!-- Fixed header at top -->
11148
+ <ion-header>
11149
+ <ion-toolbar>
11150
+ <div class="header-detail">
11151
+ <!-- Back Button -->
11152
+ <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
11153
+ <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
11154
+ </button>
11155
+
11156
+ <!-- Title - fades in on scroll -->
11157
+ <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
11158
+ </div>
11159
+ </ion-toolbar>
11160
+ </ion-header>
11161
+
11162
+ <!-- Content with expandable header -->
11163
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
11164
+ <!-- Pull to refresh (only on native iOS/Android) -->
11165
+ @if (isNativePlatform()) {
11166
+ <ion-refresher
11167
+ slot="fixed"
11168
+ (ionRefresh)="handleRefresh($event)"
11169
+ [pullFactor]="0.4"
11170
+ [pullMin]="80"
11171
+ [pullMax]="240"
11172
+ closeDuration="600ms">
11173
+ <ion-refresher-content
11174
+ pullingIcon="remixArrowDownS"
11175
+ refreshingSpinner="lines">
11176
+ </ion-refresher-content>
11177
+ </ion-refresher>
11178
+ }
11179
+
11180
+ <!-- Expandable header section (purple background) -->
11181
+ <div class="header-expandable">
11182
+ <div class="header-expandable-inner">
11183
+ <div class="header-expandable__text">
11184
+ <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
11185
+ </div>
11186
+
11187
+ <!-- Tabs in header -->
11188
+ <ds-mobile-inline-tabs
11189
+ [tabs]="tabItems"
11190
+ [activeTab]="activeTab()"
11191
+ (tabChange)="setActiveTab($event)">
11192
+ </ds-mobile-inline-tabs>
11193
+ </div>
11194
+ </div>
11195
+
11196
+ <!-- Content wrapper -->
11197
+ <div class="content-wrapper">
11198
+ <div class="content-inner">
11199
+ <!-- Activity Tab Content -->
11200
+ @if (activeTab() === 'activity') {
11201
+ <div class="activity-list">
11202
+ @for (activity of activities; track activity.id) {
11203
+ <div class="activity-item">
11204
+ @if (activity.actor) {
11205
+ <!-- Avatar with badge for actor activities -->
11206
+ <div class="avatar-wrapper">
11207
+ <ds-avatar
11208
+ [type]="'initials'"
11209
+ [initials]="activity.actorInitials || ''"
11210
+ size="md" />
11211
+
11212
+ <div class="avatar-badge">
11213
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
11214
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
11215
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
11216
+ </svg>
11217
+ </div>
11218
+ </div>
11219
+ } @else {
11220
+ <!-- Icon wrapper for non-actor activities -->
11221
+ <div class="activity-icon-wrapper">
11222
+ <ds-icon
11223
+ [name]="activity.iconName"
11224
+ size="18px"
11225
+ color="secondary" />
11226
+ </div>
11227
+ }
11228
+
11229
+ <div class="activity-content">
11230
+ <p class="activity-title">
11231
+ @if (activity.actor) {
11232
+ <span class="actor-name">{{ activity.actor }}</span>
11233
+ <span class="activity-text"> {{ activity.title }}</span>
11234
+ } @else {
11235
+ <span class="actor-name">{{ activity.title }}</span>
11236
+ }
11237
+ </p>
11238
+ @if (activity.description) {
11239
+ <p class="activity-description">{{ activity.description }}</p>
11240
+ }
11241
+ <div class="activity-timestamp">
11242
+ <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
11243
+ <span>{{ activity.date }}</span>
11244
+ </div>
11245
+ </div>
11246
+ </div>
11247
+ }
11248
+ </div>
11249
+ }
11250
+
11251
+ <!-- Messages Tab Content -->
11252
+ @if (activeTab() === 'messages') {
11253
+ <div class="messages-list">
11254
+ @for (message of messageThreads; track message.id) {
11255
+ <ds-mobile-interactive-list-item-message
11256
+ [senderName]="message.senderName"
11257
+ [senderRole]="message.role"
11258
+ [message]="message.message"
11259
+ [avatarInitials]="message.senderInitials"
11260
+ [unread]="message.unread"
11261
+ (messageClick)="openMessage(message.id)">
11262
+ </ds-mobile-interactive-list-item-message>
11263
+ }
11264
+ </div>
11265
+ }
11266
+
11267
+ <!-- Details Tab Content -->
11268
+ @if (activeTab() === 'details') {
11269
+ <div class="details-list">
11270
+ <!-- Assignee -->
11271
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11272
+ <div content-leading>
11273
+ <ds-avatar
11274
+ [size]="'sm'"
11275
+ [type]="'initials'"
11276
+ [initials]="'R'" />
11277
+ </div>
11278
+ <div content-main>
11279
+ <div class="detail-label">Sagsbehandler</div>
11280
+ <div class="detail-value">Ricki Meihlen</div>
11281
+ </div>
11282
+ </ds-mobile-list-item-static>
11283
+
11284
+ <!-- Technician -->
11285
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11286
+ <div content-leading>
11287
+ <ds-avatar
11288
+ [size]="'sm'"
11289
+ [type]="'initials'"
11290
+ [initials]="'M'" />
11291
+ </div>
11292
+ <div content-main>
11293
+ <div class="detail-label">Tekniker</div>
11294
+ <div class="detail-value">Martin Smith</div>
11295
+ </div>
11296
+ </ds-mobile-list-item-static>
11297
+
11298
+ <!-- Title -->
11299
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11300
+ <div content-leading>
11301
+ <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
11302
+ </div>
11303
+ <div content-main>
11304
+ <div class="detail-value">{{ inquiryTitle }}</div>
11305
+ </div>
11306
+ </ds-mobile-list-item-static>
11307
+
11308
+ <!-- Description -->
11309
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11310
+ <div content-leading>
11311
+ <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
11312
+ </div>
11313
+ <div content-main>
11314
+ <div class="detail-value description-text">
11315
+ I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
11316
+ </div>
11317
+ <div class="detail-tag">Husholdningsapparater</div>
11318
+ </div>
11319
+ </ds-mobile-list-item-static>
11320
+
11321
+ <!-- Photos -->
11322
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11323
+ <div content-leading>
11324
+ <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
11325
+ </div>
11326
+ <div content-main>
11327
+ <div class="photo-grid">
11328
+ <button class="photo-add">
11329
+ <ds-icon name="remixAddLine" size="20px" color="tertiary" />
11330
+ </button>
11331
+ <!-- Placeholder photos -->
11332
+ <div class="photo-item"></div>
11333
+ <div class="photo-item"></div>
11334
+ <div class="photo-item"></div>
11335
+ <div class="photo-item"></div>
11336
+ </div>
11337
+ </div>
11338
+ </ds-mobile-list-item-static>
11339
+ </div>
11340
+ }
11341
+ </div>
11342
+ </div>
11343
+ </ion-content>
11344
+ `, isInline: true, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.avatar-badge{position:absolute;bottom:-6px;right:-6px;width:20px;height:20px;border-radius:8px;background:var(--color-brand-secondary, #5d5fef);display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge svg{width:10px;position:relative;top:1px;fill:#fff}.activity-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.activity-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);margin:0}.activity-title .actor-name{font-weight:600;color:var(--text-color-default-primary, #202227)}.activity-title .activity-text{color:var(--text-color-default-secondary, #545B66);font-weight:400}.activity-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0}.activity-timestamp{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);display:flex;align-items:center;gap:4px;margin-top:2px}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-tertiary, #737373)}.detail-value{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:24px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227)}.detail-value.description-text{padding:8px 0}.detail-tag{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;background:var(--color-background-neutral-secondary, #f5f5f5);font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--text-color-default-secondary, #525866);margin-top:4px;margin-bottom:10px;width:-moz-fit-content;width:fit-content}.photo-grid{display:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;border-radius:12px;border:1px dashed var(--border-color-default, #e5e5e5);background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { 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: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }, { kind: "component", type: DsMobileListItemStaticComponent, selector: "ds-mobile-list-item-static", inputs: ["leadingSize"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "clickable"], outputs: ["messageClick", "longPress"] }] });
11345
+ }
11346
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, decorators: [{
11347
+ type: Component,
11348
+ args: [{ selector: 'app-mobile-inquiry-detail-page', standalone: true, imports: [
11349
+ CommonModule,
11350
+ IonHeader,
11351
+ IonToolbar,
11352
+ IonTitle,
11353
+ IonContent,
11354
+ IonRefresher,
11355
+ IonRefresherContent,
11356
+ DsIconComponent,
11357
+ DsAvatarComponent,
11358
+ DsMobileInlineTabsComponent,
11359
+ DsMobileListItemStaticComponent,
11360
+ DsMobileInteractiveListItemMessageComponent
11361
+ ], host: {
11362
+ class: 'ion-page'
11363
+ }, template: `
11364
+ <!-- Fixed header at top -->
11365
+ <ion-header>
11366
+ <ion-toolbar>
11367
+ <div class="header-detail">
11368
+ <!-- Back Button -->
11369
+ <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
11370
+ <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
11371
+ </button>
11372
+
11373
+ <!-- Title - fades in on scroll -->
11374
+ <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
11375
+ </div>
11376
+ </ion-toolbar>
11377
+ </ion-header>
11378
+
11379
+ <!-- Content with expandable header -->
11380
+ <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
11381
+ <!-- Pull to refresh (only on native iOS/Android) -->
11382
+ @if (isNativePlatform()) {
11383
+ <ion-refresher
11384
+ slot="fixed"
11385
+ (ionRefresh)="handleRefresh($event)"
11386
+ [pullFactor]="0.4"
11387
+ [pullMin]="80"
11388
+ [pullMax]="240"
11389
+ closeDuration="600ms">
11390
+ <ion-refresher-content
11391
+ pullingIcon="remixArrowDownS"
11392
+ refreshingSpinner="lines">
11393
+ </ion-refresher-content>
11394
+ </ion-refresher>
11395
+ }
11396
+
11397
+ <!-- Expandable header section (purple background) -->
11398
+ <div class="header-expandable">
11399
+ <div class="header-expandable-inner">
11400
+ <div class="header-expandable__text">
11401
+ <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
11402
+ </div>
11403
+
11404
+ <!-- Tabs in header -->
11405
+ <ds-mobile-inline-tabs
11406
+ [tabs]="tabItems"
11407
+ [activeTab]="activeTab()"
11408
+ (tabChange)="setActiveTab($event)">
11409
+ </ds-mobile-inline-tabs>
11410
+ </div>
11411
+ </div>
11412
+
11413
+ <!-- Content wrapper -->
11414
+ <div class="content-wrapper">
11415
+ <div class="content-inner">
11416
+ <!-- Activity Tab Content -->
11417
+ @if (activeTab() === 'activity') {
11418
+ <div class="activity-list">
11419
+ @for (activity of activities; track activity.id) {
11420
+ <div class="activity-item">
11421
+ @if (activity.actor) {
11422
+ <!-- Avatar with badge for actor activities -->
11423
+ <div class="avatar-wrapper">
11424
+ <ds-avatar
11425
+ [type]="'initials'"
11426
+ [initials]="activity.actorInitials || ''"
11427
+ size="md" />
11428
+
11429
+ <div class="avatar-badge">
11430
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
11431
+ <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
11432
+ <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
11433
+ </svg>
11434
+ </div>
11435
+ </div>
11436
+ } @else {
11437
+ <!-- Icon wrapper for non-actor activities -->
11438
+ <div class="activity-icon-wrapper">
11439
+ <ds-icon
11440
+ [name]="activity.iconName"
11441
+ size="18px"
11442
+ color="secondary" />
11443
+ </div>
11444
+ }
11445
+
11446
+ <div class="activity-content">
11447
+ <p class="activity-title">
11448
+ @if (activity.actor) {
11449
+ <span class="actor-name">{{ activity.actor }}</span>
11450
+ <span class="activity-text"> {{ activity.title }}</span>
11451
+ } @else {
11452
+ <span class="actor-name">{{ activity.title }}</span>
11453
+ }
11454
+ </p>
11455
+ @if (activity.description) {
11456
+ <p class="activity-description">{{ activity.description }}</p>
11457
+ }
11458
+ <div class="activity-timestamp">
11459
+ <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
11460
+ <span>{{ activity.date }}</span>
11461
+ </div>
11462
+ </div>
11463
+ </div>
11464
+ }
11465
+ </div>
11466
+ }
11467
+
11468
+ <!-- Messages Tab Content -->
11469
+ @if (activeTab() === 'messages') {
11470
+ <div class="messages-list">
11471
+ @for (message of messageThreads; track message.id) {
11472
+ <ds-mobile-interactive-list-item-message
11473
+ [senderName]="message.senderName"
11474
+ [senderRole]="message.role"
11475
+ [message]="message.message"
11476
+ [avatarInitials]="message.senderInitials"
11477
+ [unread]="message.unread"
11478
+ (messageClick)="openMessage(message.id)">
11479
+ </ds-mobile-interactive-list-item-message>
11480
+ }
11481
+ </div>
11482
+ }
11483
+
11484
+ <!-- Details Tab Content -->
11485
+ @if (activeTab() === 'details') {
11486
+ <div class="details-list">
11487
+ <!-- Assignee -->
11488
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11489
+ <div content-leading>
11490
+ <ds-avatar
11491
+ [size]="'sm'"
11492
+ [type]="'initials'"
11493
+ [initials]="'R'" />
11494
+ </div>
11495
+ <div content-main>
11496
+ <div class="detail-label">Sagsbehandler</div>
11497
+ <div class="detail-value">Ricki Meihlen</div>
11498
+ </div>
11499
+ </ds-mobile-list-item-static>
11500
+
11501
+ <!-- Technician -->
11502
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11503
+ <div content-leading>
11504
+ <ds-avatar
11505
+ [size]="'sm'"
11506
+ [type]="'initials'"
11507
+ [initials]="'M'" />
11508
+ </div>
11509
+ <div content-main>
11510
+ <div class="detail-label">Tekniker</div>
11511
+ <div class="detail-value">Martin Smith</div>
11512
+ </div>
11513
+ </ds-mobile-list-item-static>
11514
+
11515
+ <!-- Title -->
11516
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11517
+ <div content-leading>
11518
+ <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
11519
+ </div>
11520
+ <div content-main>
11521
+ <div class="detail-value">{{ inquiryTitle }}</div>
11522
+ </div>
11523
+ </ds-mobile-list-item-static>
11524
+
11525
+ <!-- Description -->
11526
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11527
+ <div content-leading>
11528
+ <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
11529
+ </div>
11530
+ <div content-main>
11531
+ <div class="detail-value description-text">
11532
+ I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
11533
+ </div>
11534
+ <div class="detail-tag">Husholdningsapparater</div>
11535
+ </div>
11536
+ </ds-mobile-list-item-static>
11537
+
11538
+ <!-- Photos -->
11539
+ <ds-mobile-list-item-static [leadingSize]="'32px'">
11540
+ <div content-leading>
11541
+ <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
11542
+ </div>
11543
+ <div content-main>
11544
+ <div class="photo-grid">
11545
+ <button class="photo-add">
11546
+ <ds-icon name="remixAddLine" size="20px" color="tertiary" />
11547
+ </button>
11548
+ <!-- Placeholder photos -->
11549
+ <div class="photo-item"></div>
11550
+ <div class="photo-item"></div>
11551
+ <div class="photo-item"></div>
11552
+ <div class="photo-item"></div>
11553
+ </div>
11554
+ </div>
11555
+ </ds-mobile-list-item-static>
11556
+ </div>
11557
+ }
11558
+ </div>
11559
+ </div>
11560
+ </ion-content>
11561
+ `, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.avatar-badge{position:absolute;bottom:-6px;right:-6px;width:20px;height:20px;border-radius:8px;background:var(--color-brand-secondary, #5d5fef);display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge svg{width:10px;position:relative;top:1px;fill:#fff}.activity-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.activity-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);margin:0}.activity-title .actor-name{font-weight:600;color:var(--text-color-default-primary, #202227)}.activity-title .activity-text{color:var(--text-color-default-secondary, #545B66);font-weight:400}.activity-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0}.activity-timestamp{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);display:flex;align-items:center;gap:4px;margin-top:2px}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-tertiary, #737373)}.detail-value{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:24px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227)}.detail-value.description-text{padding:8px 0}.detail-tag{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;background:var(--color-background-neutral-secondary, #f5f5f5);font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--text-color-default-secondary, #525866);margin-top:4px;margin-bottom:10px;width:-moz-fit-content;width:fit-content}.photo-grid{display:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;border-radius:12px;border:1px dashed var(--border-color-default, #e5e5e5);background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\n"] }]
11562
+ }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }, { type: i0.ElementRef }], propDecorators: { ionContent: [{
11563
+ type: ViewChild,
11564
+ args: [IonContent]
11565
+ }] } });
11566
+
11567
+ /**
11568
+ * DsAvatarWithBadgeComponent
11569
+ *
11570
+ * Displays an avatar with a logomark badge overlay.
11571
+ * Useful for showing user avatars with organization branding.
11572
+ *
11573
+ * @example
11574
+ * ```html
11575
+ * <ds-avatar-with-badge
11576
+ * [type]="'initials'"
11577
+ * [initials]="'JD'"
11578
+ * [size]="'lg'"
11579
+ * [badgePosition]="'bottom-right'"
11580
+ * />
11581
+ * ```
11582
+ */
11583
+ class DsAvatarWithBadgeComponent {
11584
+ whitelabelService = inject(WhitelabelService);
11585
+ // Avatar props
11586
+ type = 'initials';
11587
+ size = 'md';
11588
+ initials = '';
11589
+ src = '';
11590
+ iconName = 'remixUser3Fill';
11591
+ // Badge props
11592
+ showBadge = true;
11593
+ badgePosition = 'bottom-right';
11594
+ badgeClasses = computed(() => {
11595
+ return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
11596
+ }, ...(ngDevMode ? [{ debugName: "badgeClasses" }] : []));
11597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: DsAvatarWithBadgeComponent, isStandalone: true, selector: "ds-avatar-with-badge", inputs: { type: "type", size: "size", initials: "initials", src: "src", iconName: "iconName", showBadge: "showBadge", badgePosition: "badgePosition" }, ngImport: i0, template: `
11599
+ <div class="avatar-badge-container">
11600
+ <ds-avatar
11601
+ [type]="type"
11602
+ [size]="size"
11603
+ [initials]="initials"
11604
+ [src]="src"
11605
+ [iconName]="iconName"
11606
+ />
11607
+
11608
+ @if (showBadge) {
11609
+ <div [class]="badgeClasses()">
11610
+ <img [src]="whitelabelService.logoMarkUrl()" [alt]="whitelabelService.logoAlt()" />
11611
+ </div>
11612
+ }
11613
+ </div>
11614
+ `, isInline: true, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:var(--color-brand-secondary, #5d5fef);border-radius:8px;display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge img{width:10px;height:10px;object-fit:contain}.avatar-badge--bottom-right{bottom:-6px;right:-6px}.avatar-badge--bottom-left{bottom:-6px;left:-6px}.avatar-badge--top-right{top:-6px;right:-6px}.avatar-badge--top-left{top:-6px;left:-6px}.avatar-badge--xs{width:16px;height:16px}.avatar-badge--sm{width:18px;height:18px}.avatar-badge--md{width:20px;height:20px}.avatar-badge--lg{width:24px;height:24px}.avatar-badge--xl{width:28px;height:28px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }] });
11615
+ }
11616
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: DsAvatarWithBadgeComponent, decorators: [{
11617
+ type: Component,
11618
+ args: [{ selector: 'ds-avatar-with-badge', standalone: true, imports: [CommonModule, DsAvatarComponent], encapsulation: ViewEncapsulation.Emulated, template: `
11619
+ <div class="avatar-badge-container">
11620
+ <ds-avatar
11621
+ [type]="type"
11622
+ [size]="size"
11623
+ [initials]="initials"
11624
+ [src]="src"
11625
+ [iconName]="iconName"
11626
+ />
11627
+
11628
+ @if (showBadge) {
11629
+ <div [class]="badgeClasses()">
11630
+ <img [src]="whitelabelService.logoMarkUrl()" [alt]="whitelabelService.logoAlt()" />
11566
11631
  </div>
11567
- </ds-mobile-content>
11568
- </ds-mobile-page-main>
11569
- `, styles: [".inquiries-container{display:flex;flex-direction:column;max-width:640px}.inquiry-list-wrapper{display:flex;flex-direction:column}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 20px;text-align:center}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;color:var(--color-text-primary);margin:16px 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);color:var(--color-text-secondary);margin:0}\n"] }]
11570
- }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }] });
11632
+ }
11633
+ </div>
11634
+ `, styles: [":host{display:inline-block;position:relative}.avatar-badge-container{position:relative;display:inline-block}.avatar-badge{position:absolute;background:var(--color-brand-secondary, #5d5fef);border-radius:8px;display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge img{width:10px;height:10px;object-fit:contain}.avatar-badge--bottom-right{bottom:-6px;right:-6px}.avatar-badge--bottom-left{bottom:-6px;left:-6px}.avatar-badge--top-right{top:-6px;right:-6px}.avatar-badge--top-left{top:-6px;left:-6px}.avatar-badge--xs{width:16px;height:16px}.avatar-badge--sm{width:18px;height:18px}.avatar-badge--md{width:20px;height:20px}.avatar-badge--lg{width:24px;height:24px}.avatar-badge--xl{width:28px;height:28px}\n"] }]
11635
+ }], propDecorators: { type: [{
11636
+ type: Input
11637
+ }], size: [{
11638
+ type: Input
11639
+ }], initials: [{
11640
+ type: Input
11641
+ }], src: [{
11642
+ type: Input
11643
+ }], iconName: [{
11644
+ type: Input
11645
+ }], showBadge: [{
11646
+ type: Input
11647
+ }], badgePosition: [{
11648
+ type: Input
11649
+ }] } });
11571
11650
 
11572
- class MobileInquiryDetailPageComponent extends MobilePageBase {
11573
- userService;
11574
- navCtrl;
11575
- elementRef;
11576
- ionContent;
11577
- // Platform detection
11578
- platform = inject(Platform);
11579
- // Computed property to check if running on native platform
11580
- isNativePlatform = computed(() => this.platform.is('ios') ||
11581
- this.platform.is('android') ||
11582
- this.platform.is('capacitor'), ...(ngDevMode ? [{ debugName: "isNativePlatform" }] : []));
11583
- inquiryTitle = 'Tørretumbler virker ikke';
11584
- activeTab = signal('activity', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
11585
- tabItems = [
11586
- { id: 'activity', label: 'Aktivitet' },
11587
- { id: 'messages', label: 'Beskeder', badge: 0 },
11588
- { id: 'details', label: 'Detaljer' }
11589
- ];
11590
- activities = [
11591
- {
11592
- id: '1',
11593
- type: 'assignment',
11594
- actor: 'Martin Smith',
11595
- actorInitials: 'MS',
11596
- title: 'er blevet tildelt som din dedikerede tekniker.',
11597
- timestamp: '2 dage siden',
11598
- date: '28. feb 2025',
11599
- iconName: 'remixUserAddLine'
11600
- },
11601
- {
11602
- id: '2',
11603
- type: 'assignment',
11604
- actor: 'Ricki Meihlen',
11605
- actorInitials: 'RM',
11606
- title: 'er blevet tildelt til at håndtere din henvendelse.',
11607
- timestamp: '8 dage siden',
11608
- date: '22. feb 2025',
11609
- iconName: 'remixUserLine'
11610
- },
11611
- {
11612
- id: '3',
11613
- type: 'creation',
11614
- title: 'Henvendelse oprettet',
11615
- timestamp: '8 dage siden',
11616
- date: '22. feb 2025',
11617
- iconName: 'remixAddCircleLine'
11651
+ /**
11652
+ * Whitelabel Demo Modal Component
11653
+ *
11654
+ * Demonstrates the whitelabeling system with theme selection, brand colors, and logo previews.
11655
+ * Opens as a full-screen modal similar to post details.
11656
+ */
11657
+ class WhitelabelDemoModalComponent {
11658
+ whitelabelService = inject(WhitelabelService);
11659
+ modalController = inject(ModalController);
11660
+ // Current active theme
11661
+ currentTheme = 'default';
11662
+ // Custom color inputs
11663
+ customPrimarySurface = '#6B5FF5';
11664
+ customPrimaryContent = '#FFFFFF';
11665
+ customSecondarySurface = '#221a4c';
11666
+ customSecondaryContent = '#FFFFFF';
11667
+ ngOnInit() {
11668
+ this.updateCustomColorInputs();
11669
+ this.detectCurrentTheme();
11670
+ }
11671
+ /**
11672
+ * Detect the current active theme based on colors
11673
+ */
11674
+ detectCurrentTheme() {
11675
+ const secondarySurface = this.whitelabelService.secondarySurface().toUpperCase();
11676
+ if (secondarySurface === '#A70923') {
11677
+ this.currentTheme = 'cej';
11618
11678
  }
11619
- ];
11620
- messageThreads = [
11621
- {
11622
- id: '1',
11623
- senderName: 'Ove Hindborg',
11624
- senderAvatar: '',
11625
- senderInitials: 'OH',
11626
- message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
11627
- role: 'Sagsbehandler',
11628
- timestamp: '2t siden',
11629
- unread: true
11630
- },
11631
- {
11632
- id: '2',
11633
- senderName: 'Martin Smith',
11634
- senderAvatar: '',
11635
- senderInitials: 'MS',
11636
- message: 'Dejligt at høre! Jeg venter på din teknikerbesøg tidsplan.',
11637
- role: 'Tekniker',
11638
- timestamp: '4t siden',
11639
- unread: true
11679
+ else if (secondarySurface === '#660036') {
11680
+ this.currentTheme = 'pka';
11640
11681
  }
11641
- ];
11642
- unreadMessagesCount = computed(() => {
11643
- const count = this.messageThreads.filter(m => m.unread).length;
11644
- // Update badge in tab items
11645
- const messagesTab = this.tabItems.find(t => t.id === 'messages');
11646
- if (messagesTab) {
11647
- messagesTab.badge = count;
11682
+ else if (secondarySurface === '#262424') {
11683
+ this.currentTheme = 'clave';
11684
+ }
11685
+ else {
11686
+ this.currentTheme = 'default';
11648
11687
  }
11649
- return count;
11650
- }, ...(ngDevMode ? [{ debugName: "unreadMessagesCount" }] : []));
11651
- constructor(userService, navCtrl, elementRef) {
11652
- super();
11653
- this.userService = userService;
11654
- this.navCtrl = navCtrl;
11655
- this.elementRef = elementRef;
11656
- // Trigger initial badge update
11657
- this.unreadMessagesCount();
11658
11688
  }
11659
- ngAfterViewInit() {
11660
- // Initial setup if needed
11689
+ /**
11690
+ * Close the modal
11691
+ */
11692
+ close() {
11693
+ this.modalController.dismiss();
11661
11694
  }
11662
- setActiveTab(tabId) {
11663
- this.activeTab.set(tabId);
11695
+ applyDefaultTheme() {
11696
+ this.currentTheme = 'default';
11697
+ this.whitelabelService.updateConfig({
11698
+ logoUrl: '/Assets/logos/propbinder-logomark.svg',
11699
+ logoMarkUrl: '/Assets/logos/propbinder-logomark.svg',
11700
+ logoAlt: 'Propbinder',
11701
+ logoHeight: 28,
11702
+ primarySurface: '#6B5FF5',
11703
+ primaryContent: '#FFFFFF',
11704
+ secondarySurface: '#221a4c',
11705
+ secondaryContent: '#FFFFFF',
11706
+ organizationName: 'Propbinder',
11707
+ organizationId: 'default'
11708
+ });
11709
+ this.updateCustomColorInputs();
11664
11710
  }
11665
- goBack() {
11666
- this.navCtrl.back({ animation: customBackTransition });
11711
+ applyCejTheme() {
11712
+ this.currentTheme = 'cej';
11713
+ this.whitelabelService.updateConfig({
11714
+ logoUrl: '/Assets/logos/cej-logo.png',
11715
+ logoMarkUrl: '/Assets/logos/cej-logo.png',
11716
+ logoAlt: 'CEJ',
11717
+ logoHeight: 36,
11718
+ primarySurface: '#FFFFFF',
11719
+ primaryContent: '#A70923',
11720
+ secondarySurface: '#A70923',
11721
+ secondaryContent: '#FFFFFF',
11722
+ organizationName: 'CEJ',
11723
+ organizationId: 'cej'
11724
+ });
11725
+ this.updateCustomColorInputs();
11667
11726
  }
11668
- handleScroll(event) {
11669
- const scrollTop = event.detail.scrollTop;
11670
- const threshold = 160;
11671
- const fadeDistance = 200;
11672
- const header = this.elementRef.nativeElement.querySelector('ion-header:not([collapse])');
11673
- const headerExpandable = this.elementRef.nativeElement.querySelector('.header-expandable');
11674
- // Show title in fixed header when scrolled past threshold
11675
- if (scrollTop > threshold) {
11676
- header?.classList.add('header-scrolled');
11677
- }
11678
- else {
11679
- header?.classList.remove('header-scrolled');
11680
- }
11681
- // Fade out header-expandable content based on scroll
11682
- if (headerExpandable) {
11683
- const fadeProgress = Math.min(scrollTop / fadeDistance, 1);
11684
- // Calculate opacity (1 to 0)
11685
- const opacity = 1 - fadeProgress;
11686
- // Calculate transform (0px to -20px upward)
11687
- const translateY = fadeProgress * -20;
11688
- // Apply styles
11689
- headerExpandable.style.opacity = opacity.toString();
11690
- headerExpandable.style.transform = `translateY(${translateY}px)`;
11691
- }
11727
+ applyPkaTheme() {
11728
+ this.currentTheme = 'pka';
11729
+ this.whitelabelService.updateConfig({
11730
+ logoUrl: '/Assets/logos/pka-logo.svg',
11731
+ logoMarkUrl: '/Assets/logos/pka-logo.svg',
11732
+ logoAlt: 'PKA',
11733
+ logoHeight: 24,
11734
+ primarySurface: '#CC006C',
11735
+ primaryContent: '#FFFFFF',
11736
+ secondarySurface: '#660036',
11737
+ secondaryContent: '#FFFFFF',
11738
+ organizationName: 'PKA',
11739
+ organizationId: 'pka'
11740
+ });
11741
+ this.updateCustomColorInputs();
11692
11742
  }
11693
- handleRefresh(event) {
11694
- console.log('Pull-to-refresh triggered');
11695
- setTimeout(() => {
11696
- console.log('Refresh complete');
11697
- event.target.complete();
11698
- }, 1000);
11743
+ applyClaveTheme() {
11744
+ this.currentTheme = 'clave';
11745
+ this.whitelabelService.updateConfig({
11746
+ logoUrl: '/Assets/logos/clave-logo.svg',
11747
+ logoMarkUrl: '/Assets/logos/clave-logo.svg',
11748
+ logoAlt: 'Clave',
11749
+ logoHeight: 24,
11750
+ primarySurface: '#868764',
11751
+ primaryContent: '#262424',
11752
+ secondarySurface: '#262424',
11753
+ secondaryContent: '#FFFFFF',
11754
+ organizationName: 'Clave',
11755
+ organizationId: 'clave'
11756
+ });
11757
+ this.updateCustomColorInputs();
11699
11758
  }
11700
- openMessage(messageId) {
11701
- console.log('Opening message:', messageId);
11702
- // Navigate to message thread detail
11759
+ applyCustomColors() {
11760
+ this.whitelabelService.updateColors({
11761
+ primarySurface: this.customPrimarySurface,
11762
+ primaryContent: this.customPrimaryContent,
11763
+ secondarySurface: this.customSecondarySurface,
11764
+ secondaryContent: this.customSecondaryContent
11765
+ });
11703
11766
  }
11704
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, deps: [{ token: UserService }, { token: i1.NavController }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
11705
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.14", type: MobileInquiryDetailPageComponent, isStandalone: true, selector: "app-mobile-inquiry-detail-page", host: { classAttribute: "ion-page" }, viewQueries: [{ propertyName: "ionContent", first: true, predicate: IonContent, descendants: true }], usesInheritance: true, ngImport: i0, template: `
11706
- <!-- Fixed header at top -->
11707
- <ion-header>
11708
- <ion-toolbar>
11709
- <div class="header-detail">
11710
- <!-- Back Button -->
11711
- <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
11712
- <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
11713
- </button>
11714
-
11715
- <!-- Title - fades in on scroll -->
11716
- <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
11717
- </div>
11718
- </ion-toolbar>
11719
- </ion-header>
11720
-
11721
- <!-- Content with expandable header -->
11722
- <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
11723
- <!-- Pull to refresh (only on native iOS/Android) -->
11724
- @if (isNativePlatform()) {
11725
- <ion-refresher
11726
- slot="fixed"
11727
- (ionRefresh)="handleRefresh($event)"
11728
- [pullFactor]="0.4"
11729
- [pullMin]="80"
11730
- [pullMax]="240"
11731
- closeDuration="600ms">
11732
- <ion-refresher-content
11733
- pullingIcon="remixArrowDownS"
11734
- refreshingSpinner="lines">
11735
- </ion-refresher-content>
11736
- </ion-refresher>
11737
- }
11738
-
11739
- <!-- Expandable header section (purple background) -->
11740
- <div class="header-expandable">
11741
- <div class="header-expandable-inner">
11742
- <div class="header-expandable__text">
11743
- <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
11744
- </div>
11745
-
11746
- <!-- Tabs in header -->
11747
- <ds-mobile-inline-tabs
11748
- [tabs]="tabItems"
11749
- [activeTab]="activeTab()"
11750
- (tabChange)="setActiveTab($event)">
11751
- </ds-mobile-inline-tabs>
11752
- </div>
11767
+ updateCustomColorInputs() {
11768
+ this.customPrimarySurface = this.whitelabelService.primarySurface();
11769
+ this.customPrimaryContent = this.whitelabelService.primaryContent();
11770
+ this.customSecondarySurface = this.whitelabelService.secondarySurface();
11771
+ this.customSecondaryContent = this.whitelabelService.secondaryContent();
11772
+ }
11773
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11774
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: WhitelabelDemoModalComponent, isStandalone: true, selector: "ds-whitelabel-demo-modal", ngImport: i0, template: `
11775
+ <!-- Fixed Header -->
11776
+ <div class="modal-header">
11777
+ <div class="header-content">
11778
+ <span class="header-title">Whitelabel</span>
11779
+ <ds-icon-button
11780
+ icon="remixCloseLine"
11781
+ variant="secondary"
11782
+ size="lg"
11783
+ (click)="close()"
11784
+ class="close-button"
11785
+ aria-label="Close">
11786
+ </ds-icon-button>
11753
11787
  </div>
11754
-
11755
- <!-- Content wrapper -->
11756
- <div class="content-wrapper">
11757
- <div class="content-inner">
11758
- <!-- Activity Tab Content -->
11759
- @if (activeTab() === 'activity') {
11760
- <div class="activity-list">
11761
- @for (activity of activities; track activity.id) {
11762
- <div class="activity-item">
11763
- @if (activity.actor) {
11764
- <!-- Avatar with badge for actor activities -->
11765
- <div class="avatar-wrapper">
11766
- <ds-avatar
11767
- [type]="'initials'"
11768
- [initials]="activity.actorInitials || ''"
11769
- size="md" />
11770
-
11771
- <div class="avatar-badge">
11772
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
11773
- <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
11774
- <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
11775
- </svg>
11776
- </div>
11777
- </div>
11778
- } @else {
11779
- <!-- Icon wrapper for non-actor activities -->
11780
- <div class="activity-icon-wrapper">
11781
- <ds-icon
11782
- [name]="activity.iconName"
11783
- size="18px"
11784
- color="secondary" />
11785
- </div>
11786
- }
11787
-
11788
- <div class="activity-content">
11789
- <p class="activity-title">
11790
- @if (activity.actor) {
11791
- <span class="actor-name">{{ activity.actor }}</span>
11792
- <span class="activity-text"> {{ activity.title }}</span>
11793
- } @else {
11794
- <span class="actor-name">{{ activity.title }}</span>
11795
- }
11796
- </p>
11797
- @if (activity.description) {
11798
- <p class="activity-description">{{ activity.description }}</p>
11799
- }
11800
- <div class="activity-timestamp">
11801
- <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
11802
- <span>{{ activity.date }}</span>
11803
- </div>
11804
- </div>
11805
- </div>
11806
- }
11807
- </div>
11808
- }
11809
-
11810
- <!-- Messages Tab Content -->
11811
- @if (activeTab() === 'messages') {
11812
- <div class="messages-list">
11813
- @for (message of messageThreads; track message.id) {
11814
- <ds-mobile-interactive-list-item-message
11815
- [senderName]="message.senderName"
11816
- [senderRole]="message.role"
11817
- [message]="message.message"
11818
- [avatarInitials]="message.senderInitials"
11819
- [unread]="message.unread"
11820
- (messageClick)="openMessage(message.id)">
11821
- </ds-mobile-interactive-list-item-message>
11822
- }
11788
+ </div>
11789
+
11790
+ <!-- Scrollable Content -->
11791
+ <ion-content [scrollY]="true" class="modal-content">
11792
+ <div class="demo-container">
11793
+ <!-- Theme Selection -->
11794
+ <div class="demo-section">
11795
+ <h2>Theme</h2>
11796
+ <div class="theme-buttons">
11797
+ <button class="theme-btn" (click)="applyDefaultTheme()" [class.active]="currentTheme === 'default'">
11798
+ Propbinder
11799
+ </button>
11800
+ <button class="theme-btn" (click)="applyCejTheme()" [class.active]="currentTheme === 'cej'">
11801
+ CEJ
11802
+ </button>
11803
+ <button class="theme-btn" (click)="applyPkaTheme()" [class.active]="currentTheme === 'pka'">
11804
+ PKA
11805
+ </button>
11806
+ <button class="theme-btn" (click)="applyClaveTheme()" [class.active]="currentTheme === 'clave'">
11807
+ Clave
11808
+ </button>
11823
11809
  </div>
11824
- }
11810
+ </div>
11825
11811
 
11826
- <!-- Details Tab Content -->
11827
- @if (activeTab() === 'details') {
11828
- <div class="details-list">
11829
- <!-- Assignee -->
11830
- <ds-mobile-list-item-static [leadingSize]="'32px'">
11831
- <div content-leading>
11832
- <ds-avatar
11833
- [size]="'sm'"
11834
- [type]="'initials'"
11835
- [initials]="'R'" />
11836
- </div>
11837
- <div content-main>
11838
- <div class="detail-label">Sagsbehandler</div>
11839
- <div class="detail-value">Ricki Meihlen</div>
11812
+ <!-- Logo & Logomark Preview -->
11813
+ <div class="demo-section">
11814
+ <h2>Logo Preview</h2>
11815
+ <div class="preview-grid">
11816
+ <div class="preview-tile">
11817
+ <h3>Logo</h3>
11818
+ <div class="logo-preview">
11819
+ <ds-logo variant="full" />
11840
11820
  </div>
11841
- </ds-mobile-list-item-static>
11842
-
11843
- <!-- Technician -->
11844
- <ds-mobile-list-item-static [leadingSize]="'32px'">
11845
- <div content-leading>
11846
- <ds-avatar
11847
- [size]="'sm'"
11821
+ </div>
11822
+ <div class="preview-tile">
11823
+ <h3>Logomark</h3>
11824
+ <div class="logomark-preview">
11825
+ <ds-avatar-with-badge
11848
11826
  [type]="'initials'"
11849
- [initials]="'M'" />
11827
+ [initials]="'KN'"
11828
+ [size]="'md'"
11829
+ [badgePosition]="'bottom-right'"
11830
+ />
11850
11831
  </div>
11851
- <div content-main>
11852
- <div class="detail-label">Tekniker</div>
11853
- <div class="detail-value">Martin Smith</div>
11832
+ </div>
11833
+ </div>
11834
+ </div>
11835
+
11836
+ <!-- Brand Colors -->
11837
+ <div class="demo-section">
11838
+ <h2>Brand Colors</h2>
11839
+ <div class="color-section">
11840
+ <div class="color-swatches">
11841
+ <div class="swatch swatch--primary-surface">
11842
+ <div class="swatch-label">Primary Surface</div>
11843
+ <div class="swatch-value">{{ whitelabelService.primarySurface() }}</div>
11844
+ </div>
11845
+ <div class="swatch swatch--primary-content">
11846
+ <div class="swatch-label">Primary Content</div>
11847
+ <div class="swatch-value">{{ whitelabelService.primaryContent() }}</div>
11854
11848
  </div>
11855
- </ds-mobile-list-item-static>
11856
-
11857
- <!-- Title -->
11858
- <ds-mobile-list-item-static [leadingSize]="'32px'">
11859
- <div content-leading>
11860
- <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
11849
+ <div class="swatch swatch--secondary-surface">
11850
+ <div class="swatch-label">Secondary Surface</div>
11851
+ <div class="swatch-value">{{ whitelabelService.secondarySurface() }}</div>
11861
11852
  </div>
11862
- <div content-main>
11863
- <div class="detail-value">{{ inquiryTitle }}</div>
11853
+ <div class="swatch swatch--secondary-content">
11854
+ <div class="swatch-label">Secondary Content</div>
11855
+ <div class="swatch-value">{{ whitelabelService.secondaryContent() }}</div>
11864
11856
  </div>
11865
- </ds-mobile-list-item-static>
11857
+ </div>
11866
11858
 
11867
- <!-- Description -->
11868
- <ds-mobile-list-item-static [leadingSize]="'32px'">
11869
- <div content-leading>
11870
- <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
11859
+ <div class="color-inputs">
11860
+ <div class="color-group-label">Primary</div>
11861
+ <div class="color-row">
11862
+ <label>Surface</label>
11863
+ <input
11864
+ type="color"
11865
+ [(ngModel)]="customPrimarySurface"
11866
+ (change)="applyCustomColors()"
11867
+ />
11868
+ <input
11869
+ type="text"
11870
+ [(ngModel)]="customPrimarySurface"
11871
+ (change)="applyCustomColors()"
11872
+ />
11871
11873
  </div>
11872
- <div content-main>
11873
- <div class="detail-value description-text">
11874
- I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
11875
- </div>
11876
- <div class="detail-tag">Husholdningsapparater</div>
11874
+ <div class="color-row">
11875
+ <label>Content</label>
11876
+ <input
11877
+ type="color"
11878
+ [(ngModel)]="customPrimaryContent"
11879
+ (change)="applyCustomColors()"
11880
+ />
11881
+ <input
11882
+ type="text"
11883
+ [(ngModel)]="customPrimaryContent"
11884
+ (change)="applyCustomColors()"
11885
+ />
11877
11886
  </div>
11878
- </ds-mobile-list-item-static>
11879
-
11880
- <!-- Photos -->
11881
- <ds-mobile-list-item-static [leadingSize]="'32px'">
11882
- <div content-leading>
11883
- <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
11887
+
11888
+ <div class="color-group-label">Secondary</div>
11889
+ <div class="color-row">
11890
+ <label>Surface</label>
11891
+ <input
11892
+ type="color"
11893
+ [(ngModel)]="customSecondarySurface"
11894
+ (change)="applyCustomColors()"
11895
+ />
11896
+ <input
11897
+ type="text"
11898
+ [(ngModel)]="customSecondarySurface"
11899
+ (change)="applyCustomColors()"
11900
+ />
11884
11901
  </div>
11885
- <div content-main>
11886
- <div class="photo-grid">
11887
- <button class="photo-add">
11888
- <ds-icon name="remixAddLine" size="20px" color="tertiary" />
11889
- </button>
11890
- <!-- Placeholder photos -->
11891
- <div class="photo-item"></div>
11892
- <div class="photo-item"></div>
11893
- <div class="photo-item"></div>
11894
- <div class="photo-item"></div>
11895
- </div>
11902
+ <div class="color-row">
11903
+ <label>Content</label>
11904
+ <input
11905
+ type="color"
11906
+ [(ngModel)]="customSecondaryContent"
11907
+ (change)="applyCustomColors()"
11908
+ />
11909
+ <input
11910
+ type="text"
11911
+ [(ngModel)]="customSecondaryContent"
11912
+ (change)="applyCustomColors()"
11913
+ />
11896
11914
  </div>
11897
- </ds-mobile-list-item-static>
11915
+ </div>
11898
11916
  </div>
11899
- }
11917
+ </div>
11900
11918
  </div>
11901
- </div>
11902
11919
  </ion-content>
11903
- `, isInline: true, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.avatar-badge{position:absolute;bottom:-6px;right:-6px;width:20px;height:20px;border-radius:8px;background:var(--color-brand-secondary, #5d5fef);display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge svg{width:10px;position:relative;top:1px;fill:#fff}.activity-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.activity-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);margin:0}.activity-title .actor-name{font-weight:600;color:var(--text-color-default-primary, #202227)}.activity-title .activity-text{color:var(--text-color-default-secondary, #545B66);font-weight:400}.activity-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0}.activity-timestamp{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);display:flex;align-items:center;gap:4px;margin-top:2px}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-tertiary, #737373)}.detail-value{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:24px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227)}.detail-value.description-text{padding:8px 0}.detail-tag{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;background:var(--color-background-neutral-secondary, #f5f5f5);font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--text-color-default-secondary, #525866);margin-top:4px;margin-bottom:10px;width:-moz-fit-content;width:fit-content}.photo-grid{display:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;border-radius:12px;border:1px dashed var(--border-color-default, #e5e5e5);background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonRefresher, selector: "ion-refresher", inputs: ["closeDuration", "disabled", "mode", "pullFactor", "pullMax", "pullMin", "snapbackDuration"] }, { kind: "component", type: IonRefresherContent, selector: "ion-refresher-content", inputs: ["pullingIcon", "pullingText", "refreshingSpinner", "refreshingText"] }, { 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: DsMobileInlineTabsComponent, selector: "ds-mobile-inline-tabs", inputs: ["tabs", "activeTab"], outputs: ["tabChange"] }, { kind: "component", type: DsMobileListItemStaticComponent, selector: "ds-mobile-list-item-static", inputs: ["leadingSize"] }, { kind: "component", type: DsMobileInteractiveListItemMessageComponent, selector: "ds-mobile-interactive-list-item-message", inputs: ["senderName", "senderRole", "message", "avatarInitials", "avatarType", "avatarSrc", "unread", "clickable"], outputs: ["messageClick", "longPress"] }] });
11920
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%;background:var(--color-background-neutral-primary, #ffffff)}.modal-header{flex-shrink:0;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default, #e0e0e0);padding:0 16px}ion-content,.modal-content{--background: #ffffff;flex:1;width:100%}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:56px}.header-title{font-family:Brockmann,sans-serif;font-size:17px;font-weight:600;color:var(--color-text-primary, #1a1a1a);flex:1}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;background:var(--color-background-neutral-secondary, #f5f5f5)!important;color:var(--color-text-primary, #1a1a1a)!important}.demo-container{padding:20px;max-width:600px;margin:0 auto;width:100%}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:18px;font-weight:600;color:#333}.theme-buttons{display:flex;gap:12px;flex-wrap:wrap}.theme-btn{padding:8px 16px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;background:#e0e0e0;color:#333}.theme-btn:active{transform:scale(.98)}.theme-btn.active{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.logo-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:var(--color-brand-secondary);border-radius:12px;min-height:80px}.logomark-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:#fff;border:1px solid #e0e0e0;border-radius:12px;min-height:80px}.preview-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.preview-tile{background:#fff;border-radius:12px}.preview-tile h3{font-size:13px;font-weight:600;color:#666;margin-top:0;margin-bottom:12px}.color-section{background:#fff;border-radius:12px}.color-swatches{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px}.swatch{padding:16px;border-radius:8px;text-align:center}.swatch-label{font-size:12px;font-weight:600;margin-bottom:4px}.swatch-value{font-size:11px;opacity:.8;font-family:monospace}.swatch--primary-surface{background:var(--color-primary-surface);color:var(--color-primary-content)}.swatch--primary-content{background:var(--color-primary-content);color:var(--color-primary-surface);border:1px solid #e0e0e0}.swatch--secondary-surface{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.swatch--secondary-content{background:var(--color-secondary-content);color:var(--color-secondary-surface);border:1px solid #e0e0e0}.color-inputs{display:flex;flex-direction:column;gap:12px}.color-group-label{font-size:13px;font-weight:600;color:#333;margin-top:8px}.color-row{display:flex;align-items:center;gap:12px}.color-row label{min-width:70px;font-size:13px;color:#666}.color-row input[type=color]{width:40px;height:32px;border:none;border-radius:6px;cursor:pointer;padding:0}.color-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-family:monospace;font-size:13px}@supports (padding: env(safe-area-inset-bottom)){.demo-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: DsLogoComponent, selector: "ds-logo", inputs: ["variant", "size", "customHeight", "customWidth"] }, { kind: "component", type: DsAvatarWithBadgeComponent, selector: "ds-avatar-with-badge", inputs: ["type", "size", "initials", "src", "iconName", "showBadge", "badgePosition"] }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
11904
11921
  }
11905
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: MobileInquiryDetailPageComponent, decorators: [{
11922
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalComponent, decorators: [{
11906
11923
  type: Component,
11907
- args: [{ selector: 'app-mobile-inquiry-detail-page', standalone: true, imports: [
11924
+ args: [{ selector: 'ds-whitelabel-demo-modal', standalone: true, imports: [
11908
11925
  CommonModule,
11909
- IonHeader,
11910
- IonToolbar,
11911
- IonTitle,
11926
+ FormsModule,
11912
11927
  IonContent,
11913
- IonRefresher,
11914
- IonRefresherContent,
11915
- DsIconComponent,
11916
- DsAvatarComponent,
11917
- DsMobileInlineTabsComponent,
11918
- DsMobileListItemStaticComponent,
11919
- DsMobileInteractiveListItemMessageComponent
11920
- ], host: {
11921
- class: 'ion-page'
11922
- }, template: `
11923
- <!-- Fixed header at top -->
11924
- <ion-header>
11925
- <ion-toolbar>
11926
- <div class="header-detail">
11927
- <!-- Back Button -->
11928
- <button class="back-button" (click)="goBack()" [attr.aria-label]="'Go back'">
11929
- <ds-icon name="remixArrowLeftSLine" size="24px" color="white" />
11930
- </button>
11931
-
11932
- <!-- Title - fades in on scroll -->
11933
- <ion-title class="header-detail__title">{{ inquiryTitle }}</ion-title>
11934
- </div>
11935
- </ion-toolbar>
11936
- </ion-header>
11937
-
11938
- <!-- Content with expandable header -->
11939
- <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
11940
- <!-- Pull to refresh (only on native iOS/Android) -->
11941
- @if (isNativePlatform()) {
11942
- <ion-refresher
11943
- slot="fixed"
11944
- (ionRefresh)="handleRefresh($event)"
11945
- [pullFactor]="0.4"
11946
- [pullMin]="80"
11947
- [pullMax]="240"
11948
- closeDuration="600ms">
11949
- <ion-refresher-content
11950
- pullingIcon="remixArrowDownS"
11951
- refreshingSpinner="lines">
11952
- </ion-refresher-content>
11953
- </ion-refresher>
11954
- }
11955
-
11956
- <!-- Expandable header section (purple background) -->
11957
- <div class="header-expandable">
11958
- <div class="header-expandable-inner">
11959
- <div class="header-expandable__text">
11960
- <h1 class="header-expandable__title">{{ inquiryTitle }}</h1>
11961
- </div>
11962
-
11963
- <!-- Tabs in header -->
11964
- <ds-mobile-inline-tabs
11965
- [tabs]="tabItems"
11966
- [activeTab]="activeTab()"
11967
- (tabChange)="setActiveTab($event)">
11968
- </ds-mobile-inline-tabs>
11969
- </div>
11928
+ DsLogoComponent,
11929
+ DsAvatarWithBadgeComponent,
11930
+ DsIconButtonComponent
11931
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
11932
+ <!-- Fixed Header -->
11933
+ <div class="modal-header">
11934
+ <div class="header-content">
11935
+ <span class="header-title">Whitelabel</span>
11936
+ <ds-icon-button
11937
+ icon="remixCloseLine"
11938
+ variant="secondary"
11939
+ size="lg"
11940
+ (click)="close()"
11941
+ class="close-button"
11942
+ aria-label="Close">
11943
+ </ds-icon-button>
11970
11944
  </div>
11971
-
11972
- <!-- Content wrapper -->
11973
- <div class="content-wrapper">
11974
- <div class="content-inner">
11975
- <!-- Activity Tab Content -->
11976
- @if (activeTab() === 'activity') {
11977
- <div class="activity-list">
11978
- @for (activity of activities; track activity.id) {
11979
- <div class="activity-item">
11980
- @if (activity.actor) {
11981
- <!-- Avatar with badge for actor activities -->
11982
- <div class="avatar-wrapper">
11983
- <ds-avatar
11984
- [type]="'initials'"
11985
- [initials]="activity.actorInitials || ''"
11986
- size="md" />
11987
-
11988
- <div class="avatar-badge">
11989
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 32" fill="none">
11990
- <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
11991
- <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
11992
- </svg>
11993
- </div>
11994
- </div>
11995
- } @else {
11996
- <!-- Icon wrapper for non-actor activities -->
11997
- <div class="activity-icon-wrapper">
11998
- <ds-icon
11999
- [name]="activity.iconName"
12000
- size="18px"
12001
- color="secondary" />
12002
- </div>
12003
- }
12004
-
12005
- <div class="activity-content">
12006
- <p class="activity-title">
12007
- @if (activity.actor) {
12008
- <span class="actor-name">{{ activity.actor }}</span>
12009
- <span class="activity-text"> {{ activity.title }}</span>
12010
- } @else {
12011
- <span class="actor-name">{{ activity.title }}</span>
12012
- }
12013
- </p>
12014
- @if (activity.description) {
12015
- <p class="activity-description">{{ activity.description }}</p>
12016
- }
12017
- <div class="activity-timestamp">
12018
- <ds-icon name="remixCalendarLine" size="12px" color="--color-text-tertiary" />
12019
- <span>{{ activity.date }}</span>
12020
- </div>
12021
- </div>
12022
- </div>
12023
- }
12024
- </div>
12025
- }
12026
-
12027
- <!-- Messages Tab Content -->
12028
- @if (activeTab() === 'messages') {
12029
- <div class="messages-list">
12030
- @for (message of messageThreads; track message.id) {
12031
- <ds-mobile-interactive-list-item-message
12032
- [senderName]="message.senderName"
12033
- [senderRole]="message.role"
12034
- [message]="message.message"
12035
- [avatarInitials]="message.senderInitials"
12036
- [unread]="message.unread"
12037
- (messageClick)="openMessage(message.id)">
12038
- </ds-mobile-interactive-list-item-message>
12039
- }
11945
+ </div>
11946
+
11947
+ <!-- Scrollable Content -->
11948
+ <ion-content [scrollY]="true" class="modal-content">
11949
+ <div class="demo-container">
11950
+ <!-- Theme Selection -->
11951
+ <div class="demo-section">
11952
+ <h2>Theme</h2>
11953
+ <div class="theme-buttons">
11954
+ <button class="theme-btn" (click)="applyDefaultTheme()" [class.active]="currentTheme === 'default'">
11955
+ Propbinder
11956
+ </button>
11957
+ <button class="theme-btn" (click)="applyCejTheme()" [class.active]="currentTheme === 'cej'">
11958
+ CEJ
11959
+ </button>
11960
+ <button class="theme-btn" (click)="applyPkaTheme()" [class.active]="currentTheme === 'pka'">
11961
+ PKA
11962
+ </button>
11963
+ <button class="theme-btn" (click)="applyClaveTheme()" [class.active]="currentTheme === 'clave'">
11964
+ Clave
11965
+ </button>
12040
11966
  </div>
12041
- }
11967
+ </div>
12042
11968
 
12043
- <!-- Details Tab Content -->
12044
- @if (activeTab() === 'details') {
12045
- <div class="details-list">
12046
- <!-- Assignee -->
12047
- <ds-mobile-list-item-static [leadingSize]="'32px'">
12048
- <div content-leading>
12049
- <ds-avatar
12050
- [size]="'sm'"
12051
- [type]="'initials'"
12052
- [initials]="'R'" />
12053
- </div>
12054
- <div content-main>
12055
- <div class="detail-label">Sagsbehandler</div>
12056
- <div class="detail-value">Ricki Meihlen</div>
11969
+ <!-- Logo & Logomark Preview -->
11970
+ <div class="demo-section">
11971
+ <h2>Logo Preview</h2>
11972
+ <div class="preview-grid">
11973
+ <div class="preview-tile">
11974
+ <h3>Logo</h3>
11975
+ <div class="logo-preview">
11976
+ <ds-logo variant="full" />
12057
11977
  </div>
12058
- </ds-mobile-list-item-static>
12059
-
12060
- <!-- Technician -->
12061
- <ds-mobile-list-item-static [leadingSize]="'32px'">
12062
- <div content-leading>
12063
- <ds-avatar
12064
- [size]="'sm'"
11978
+ </div>
11979
+ <div class="preview-tile">
11980
+ <h3>Logomark</h3>
11981
+ <div class="logomark-preview">
11982
+ <ds-avatar-with-badge
12065
11983
  [type]="'initials'"
12066
- [initials]="'M'" />
11984
+ [initials]="'KN'"
11985
+ [size]="'md'"
11986
+ [badgePosition]="'bottom-right'"
11987
+ />
12067
11988
  </div>
12068
- <div content-main>
12069
- <div class="detail-label">Tekniker</div>
12070
- <div class="detail-value">Martin Smith</div>
11989
+ </div>
11990
+ </div>
11991
+ </div>
11992
+
11993
+ <!-- Brand Colors -->
11994
+ <div class="demo-section">
11995
+ <h2>Brand Colors</h2>
11996
+ <div class="color-section">
11997
+ <div class="color-swatches">
11998
+ <div class="swatch swatch--primary-surface">
11999
+ <div class="swatch-label">Primary Surface</div>
12000
+ <div class="swatch-value">{{ whitelabelService.primarySurface() }}</div>
12071
12001
  </div>
12072
- </ds-mobile-list-item-static>
12073
-
12074
- <!-- Title -->
12075
- <ds-mobile-list-item-static [leadingSize]="'32px'">
12076
- <div content-leading>
12077
- <ds-icon name="remixTextBlock" size="20px" color="tertiary" />
12002
+ <div class="swatch swatch--primary-content">
12003
+ <div class="swatch-label">Primary Content</div>
12004
+ <div class="swatch-value">{{ whitelabelService.primaryContent() }}</div>
12078
12005
  </div>
12079
- <div content-main>
12080
- <div class="detail-value">{{ inquiryTitle }}</div>
12006
+ <div class="swatch swatch--secondary-surface">
12007
+ <div class="swatch-label">Secondary Surface</div>
12008
+ <div class="swatch-value">{{ whitelabelService.secondarySurface() }}</div>
12081
12009
  </div>
12082
- </ds-mobile-list-item-static>
12010
+ <div class="swatch swatch--secondary-content">
12011
+ <div class="swatch-label">Secondary Content</div>
12012
+ <div class="swatch-value">{{ whitelabelService.secondaryContent() }}</div>
12013
+ </div>
12014
+ </div>
12083
12015
 
12084
- <!-- Description -->
12085
- <ds-mobile-list-item-static [leadingSize]="'32px'">
12086
- <div content-leading>
12087
- <ds-icon name="remixAlignLeft" size="20px" color="tertiary" />
12016
+ <div class="color-inputs">
12017
+ <div class="color-group-label">Primary</div>
12018
+ <div class="color-row">
12019
+ <label>Surface</label>
12020
+ <input
12021
+ type="color"
12022
+ [(ngModel)]="customPrimarySurface"
12023
+ (change)="applyCustomColors()"
12024
+ />
12025
+ <input
12026
+ type="text"
12027
+ [(ngModel)]="customPrimarySurface"
12028
+ (change)="applyCustomColors()"
12029
+ />
12088
12030
  </div>
12089
- <div content-main>
12090
- <div class="detail-value description-text">
12091
- I de sidste tre dage har vi oplevet vedvarende problemer med tørretumbleren i vores lejlighed. På trods af at vi følger betjeningsvejledningen, fejler maskinen konsekvent i at fuldføre sine tørrecyklusser.
12092
- </div>
12093
- <div class="detail-tag">Husholdningsapparater</div>
12031
+ <div class="color-row">
12032
+ <label>Content</label>
12033
+ <input
12034
+ type="color"
12035
+ [(ngModel)]="customPrimaryContent"
12036
+ (change)="applyCustomColors()"
12037
+ />
12038
+ <input
12039
+ type="text"
12040
+ [(ngModel)]="customPrimaryContent"
12041
+ (change)="applyCustomColors()"
12042
+ />
12094
12043
  </div>
12095
- </ds-mobile-list-item-static>
12096
-
12097
- <!-- Photos -->
12098
- <ds-mobile-list-item-static [leadingSize]="'32px'">
12099
- <div content-leading>
12100
- <ds-icon name="remixCameraLine" size="20px" color="tertiary" />
12044
+
12045
+ <div class="color-group-label">Secondary</div>
12046
+ <div class="color-row">
12047
+ <label>Surface</label>
12048
+ <input
12049
+ type="color"
12050
+ [(ngModel)]="customSecondarySurface"
12051
+ (change)="applyCustomColors()"
12052
+ />
12053
+ <input
12054
+ type="text"
12055
+ [(ngModel)]="customSecondarySurface"
12056
+ (change)="applyCustomColors()"
12057
+ />
12101
12058
  </div>
12102
- <div content-main>
12103
- <div class="photo-grid">
12104
- <button class="photo-add">
12105
- <ds-icon name="remixAddLine" size="20px" color="tertiary" />
12106
- </button>
12107
- <!-- Placeholder photos -->
12108
- <div class="photo-item"></div>
12109
- <div class="photo-item"></div>
12110
- <div class="photo-item"></div>
12111
- <div class="photo-item"></div>
12112
- </div>
12059
+ <div class="color-row">
12060
+ <label>Content</label>
12061
+ <input
12062
+ type="color"
12063
+ [(ngModel)]="customSecondaryContent"
12064
+ (change)="applyCustomColors()"
12065
+ />
12066
+ <input
12067
+ type="text"
12068
+ [(ngModel)]="customSecondaryContent"
12069
+ (change)="applyCustomColors()"
12070
+ />
12113
12071
  </div>
12114
- </ds-mobile-list-item-static>
12072
+ </div>
12115
12073
  </div>
12116
- }
12074
+ </div>
12117
12075
  </div>
12118
- </div>
12119
12076
  </ion-content>
12120
- `, styles: ["ion-header{--background: var(--color-brand-secondary, #5d5fef);height:72px;min-height:72px}ion-toolbar{--background: var(--color-brand-secondary, #5d5fef);--color: white;--padding-top: 0;--padding-bottom: 0;--padding-start: 0;--padding-end: 0;--min-height: 72px;height:100%;min-height:72px}ion-toolbar ion-title{transition:transform .2s ease,opacity .2s ease!important}.header-detail{display:flex;align-items:center;gap:12px;padding:0 20px;height:100%}.back-button{width:36px;height:36px;border-radius:50%;background:#ffffff1a;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:background .2s ease;color:#fff;padding:0;flex-shrink:0}.back-button:hover,.back-button:active{background:#ffffff26}.header-detail__title{position:absolute;left:64px;transform:translateY(-100%);font-size:var(--font-size-base);font-weight:600;color:#fff;opacity:0!important;pointer-events:none;transition:transform .2s ease,opacity .2s ease!important;margin:0;padding:0;--color: white;text-align:left!important}.header-scrolled .header-detail__title{opacity:1!important;pointer-events:auto;transform:translateY(0)}@media (min-width: 768px){.header-detail{padding:16px 24px}}ion-content{--background: var(--color-brand-secondary);--padding-top: 0;--padding-start: 0;--padding-end: 0;--padding-bottom: 0;border-radius:24px 24px 0 0;overflow:hidden}.plt-ios ion-content{--background: var(--color-background-neutral-primary)}@media (min-width: 768px){ion-content{border-radius:24px 24px 0 0}}ion-content::part(scroll){-webkit-overflow-scrolling:touch;display:flex;flex-direction:column}ion-refresher{z-index:0}ion-refresher-content{--color: white}.header-expandable{background:var(--color-brand-secondary);padding:32px 20px 24px;color:var(--header-content-color, white);position:sticky;top:0;z-index:5;transition:opacity .1s ease-out,transform .1s ease-out}.header-expandable-inner{display:flex;flex-direction:column;gap:20px;max-width:640px}.header-expandable__text{margin-bottom:0}@media (min-width: 768px){.header-expandable{padding:40px var(--content-padding-md) 32px var(--content-padding-md)}}@media (min-width: 992px){.header-expandable{padding-left:var(--content-padding-lg);padding-right:var(--content-padding-lg)}}@media (min-width: 1440px){.header-expandable{padding-left:var(--content-padding-xl);padding-right:var(--content-padding-xl)}}@media (min-width: 1768px){.header-expandable{padding-left:var(--content-padding-2xl);padding-right:var(--content-padding-2xl)}}@media (min-width: 1920px){.header-expandable{padding-left:var(--content-padding-3xl);padding-right:var(--content-padding-3xl)}}.content-wrapper{background:var(--color-background-neutral-primary, white);border-radius:24px 24px 0 0;flex:1 1 auto;min-height:100%;position:relative;z-index:10;box-shadow:0 200vh 0 0 var(--color-background-neutral-primary)}.content-inner{padding:24px 20px 40px}@media (min-width: 768px){.content-inner{padding:32px}}.activity-list{display:flex;flex-direction:column;gap:32px}.activity-item{display:flex;gap:12px;position:relative}.activity-item:after{content:\"\";position:absolute;bottom:-16px;left:44px;right:8px;height:1px;background:var(--border-color-default, #e5e5e5)}.activity-item:last-child:after{display:none}.activity-icon-wrapper{width:32px;height:32px;border-radius:8px;background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;flex-shrink:0}.avatar-wrapper{position:relative;display:flex;align-items:start;justify-content:center;flex-shrink:0;width:32px;height:32px}.avatar-badge{position:absolute;bottom:-6px;right:-6px;width:20px;height:20px;border-radius:8px;background:var(--color-brand-secondary, #5d5fef);display:flex;align-items:center;justify-content:center;border:2px solid var(--color-background-primary, #ffffff)}.avatar-badge svg{width:10px;position:relative;top:1px;fill:#fff}.activity-content{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}.activity-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227);margin:0}.activity-title .actor-name{font-weight:600;color:var(--text-color-default-primary, #202227)}.activity-title .activity-text{color:var(--text-color-default-secondary, #545B66);font-weight:400}.activity-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-secondary, #545B66);margin:0}.activity-timestamp{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--text-color-default-tertiary, #737373);display:flex;align-items:center;gap:4px;margin-top:2px}.messages-list{display:flex;flex-direction:column;gap:8px;margin:-8px}.details-list{display:flex;flex-direction:column;gap:20px}.detail-label{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:20px;letter-spacing:-.3px;color:var(--text-color-default-tertiary, #737373)}.detail-value{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:24px;letter-spacing:-.3px;color:var(--text-color-default-primary, #202227)}.detail-value.description-text{padding:8px 0}.detail-tag{display:inline-flex;align-items:center;padding:4px 12px;border-radius:12px;background:var(--color-background-neutral-secondary, #f5f5f5);font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;color:var(--text-color-default-secondary, #525866);margin-top:4px;margin-bottom:10px;width:-moz-fit-content;width:fit-content}.photo-grid{display:flex;gap:8px;margin-top:8px;overflow-x:auto}.photo-add{width:80px;height:80px;border-radius:12px;border:1px dashed var(--border-color-default, #e5e5e5);background:var(--color-background-neutral-secondary, #f5f5f5);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0}.photo-item{width:80px;height:80px;border-radius:12px;background:var(--color-background-neutral-secondary, #e5e5e5);flex-shrink:0}\n"] }]
12121
- }], ctorParameters: () => [{ type: UserService }, { type: i1.NavController }, { type: i0.ElementRef }], propDecorators: { ionContent: [{
12122
- type: ViewChild,
12123
- args: [IonContent]
12124
- }] } });
12077
+ `, styles: [":host{display:flex;flex-direction:column;height:100%;width:100%;background:var(--color-background-neutral-primary, #ffffff)}.modal-header{flex-shrink:0;background:var(--color-background-neutral-primary, #ffffff);border-bottom:1px solid var(--border-color-default, #e0e0e0);padding:0 16px}ion-content,.modal-content{--background: #ffffff;flex:1;width:100%}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:56px}.header-title{font-family:Brockmann,sans-serif;font-size:17px;font-weight:600;color:var(--color-text-primary, #1a1a1a);flex:1}.close-button{flex-shrink:0;border-radius:50%}.close-button::ng-deep button{border-radius:50%!important;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important;background:var(--color-background-neutral-secondary, #f5f5f5)!important;color:var(--color-text-primary, #1a1a1a)!important}.demo-container{padding:20px;max-width:600px;margin:0 auto;width:100%}.demo-section{margin-bottom:32px}.demo-section h2{margin-bottom:16px;font-size:18px;font-weight:600;color:#333}.theme-buttons{display:flex;gap:12px;flex-wrap:wrap}.theme-btn{padding:8px 16px;border-radius:8px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .2s ease;background:#e0e0e0;color:#333}.theme-btn:active{transform:scale(.98)}.theme-btn.active{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.logo-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:var(--color-brand-secondary);border-radius:12px;min-height:80px}.logomark-preview{display:flex;align-items:center;justify-content:center;padding:24px;background:#fff;border:1px solid #e0e0e0;border-radius:12px;min-height:80px}.preview-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}.preview-tile{background:#fff;border-radius:12px}.preview-tile h3{font-size:13px;font-weight:600;color:#666;margin-top:0;margin-bottom:12px}.color-section{background:#fff;border-radius:12px}.color-swatches{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px}.swatch{padding:16px;border-radius:8px;text-align:center}.swatch-label{font-size:12px;font-weight:600;margin-bottom:4px}.swatch-value{font-size:11px;opacity:.8;font-family:monospace}.swatch--primary-surface{background:var(--color-primary-surface);color:var(--color-primary-content)}.swatch--primary-content{background:var(--color-primary-content);color:var(--color-primary-surface);border:1px solid #e0e0e0}.swatch--secondary-surface{background:var(--color-secondary-surface);color:var(--color-secondary-content)}.swatch--secondary-content{background:var(--color-secondary-content);color:var(--color-secondary-surface);border:1px solid #e0e0e0}.color-inputs{display:flex;flex-direction:column;gap:12px}.color-group-label{font-size:13px;font-weight:600;color:#333;margin-top:8px}.color-row{display:flex;align-items:center;gap:12px}.color-row label{min-width:70px;font-size:13px;color:#666}.color-row input[type=color]{width:40px;height:32px;border:none;border-radius:6px;cursor:pointer;padding:0}.color-row input[type=text]{flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-family:monospace;font-size:13px}@supports (padding: env(safe-area-inset-bottom)){.demo-container{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"] }]
12078
+ }] });
12079
+
12080
+ /**
12081
+ * WhitelabelDemoModalService
12082
+ *
12083
+ * Service for displaying the whitelabel demo in a full-screen modal.
12084
+ * Built on Ionic's modal system with native gestures and animations.
12085
+ *
12086
+ * @example
12087
+ * ```typescript
12088
+ * constructor(private whitelabelModal: WhitelabelDemoModalService) {}
12089
+ *
12090
+ * async openDemo() {
12091
+ * await this.whitelabelModal.open();
12092
+ * }
12093
+ * ```
12094
+ */
12095
+ class WhitelabelDemoModalService {
12096
+ modalController;
12097
+ constructor(modalController) {
12098
+ this.modalController = modalController;
12099
+ }
12100
+ /**
12101
+ * Open the whitelabel demo modal
12102
+ *
12103
+ * @returns Promise that resolves when the modal is presented
12104
+ */
12105
+ async open() {
12106
+ try {
12107
+ console.log('[WhitelabelDemoModal] Opening...');
12108
+ const modal = await this.modalController.create({
12109
+ component: WhitelabelDemoModalComponent,
12110
+ cssClass: 'ds-whitelabel-demo-modal',
12111
+ mode: 'ios',
12112
+ presentingElement: document.querySelector('ion-router-outlet') || undefined,
12113
+ backdropDismiss: true,
12114
+ showBackdrop: true,
12115
+ animated: true,
12116
+ keyboardClose: true
12117
+ });
12118
+ console.log('[WhitelabelDemoModal] Modal created, presenting...');
12119
+ await modal.present();
12120
+ console.log('[WhitelabelDemoModal] Modal presented');
12121
+ }
12122
+ catch (error) {
12123
+ console.error('[WhitelabelDemoModal] Error opening modal:', error);
12124
+ throw error;
12125
+ }
12126
+ }
12127
+ /**
12128
+ * Close the currently open whitelabel demo modal
12129
+ *
12130
+ * @param data Optional data to pass back when dismissing
12131
+ * @returns Promise that resolves when the modal is dismissed
12132
+ */
12133
+ async close(data) {
12134
+ return this.modalController.dismiss(data);
12135
+ }
12136
+ /**
12137
+ * Get the top-most modal if one exists
12138
+ *
12139
+ * @returns Promise that resolves to the modal element or undefined
12140
+ */
12141
+ async getTop() {
12142
+ return this.modalController.getTop();
12143
+ }
12144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Injectable });
12145
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, providedIn: 'root' });
12146
+ }
12147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: WhitelabelDemoModalService, decorators: [{
12148
+ type: Injectable,
12149
+ args: [{
12150
+ providedIn: 'root'
12151
+ }]
12152
+ }], ctorParameters: () => [{ type: i1.ModalController }] });
12125
12153
 
12126
12154
  /**
12127
12155
  * MobileTabsExampleComponent
@@ -12146,6 +12174,8 @@ class MobileTabsExampleComponent {
12146
12174
  // Configure user avatar globally - this is now the single source of truth
12147
12175
  this.userService.setAvatarInitials('LM');
12148
12176
  this.userService.setAvatarType('initials');
12177
+ // Set profile menu items globally - this will be used by both tab bar and page-main components
12178
+ this.userService.setProfileMenuItems(this.profileMenuItems);
12149
12179
  }
12150
12180
  tabs = [
12151
12181
  {
@@ -12179,7 +12209,9 @@ class MobileTabsExampleComponent {
12179
12209
  ];
12180
12210
  /**
12181
12211
  * Profile menu items configuration.
12182
- * Define once here, and the tab bar component handles opening/closing the menu.
12212
+ * Define once here - this is set globally in UserService in ngOnInit(),
12213
+ * so it will be used by both ds-mobile-tab-bar and ds-mobile-page-main components
12214
+ * throughout the entire application.
12183
12215
  */
12184
12216
  profileMenuItems = [
12185
12217
  {