@propbinder/mobile-design 0.2.50 → 0.2.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/fesm2022/propbinder-mobile-design.mjs +26206 -0
  2. package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
  3. package/index.d.ts +8193 -0
  4. package/package.json +39 -3
  5. package/ng-package.json +0 -24
  6. package/src/animations/page-transitions.ts +0 -165
  7. package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -102
  8. package/src/components/action-list-item/index.ts +0 -2
  9. package/src/components/app-icon/ds-app-icon.ts +0 -133
  10. package/src/components/app-icon/index.ts +0 -2
  11. package/src/components/attachment-preview/ds-mobile-attachment-preview.css +0 -139
  12. package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +0 -164
  13. package/src/components/attachment-preview/index.ts +0 -1
  14. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -142
  15. package/src/components/avatar-with-badge/index.ts +0 -2
  16. package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +0 -71
  17. package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +0 -121
  18. package/src/components/booking-modal/ds-mobile-booking-modal.ts +0 -598
  19. package/src/components/booking-modal/ds-mobile-booking-summary.ts +0 -161
  20. package/src/components/booking-modal/index.ts +0 -4
  21. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -266
  22. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +0 -146
  23. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +0 -156
  24. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -101
  25. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -169
  26. package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +0 -211
  27. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -578
  28. package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +0 -614
  29. package/src/components/bottom-sheet/index.ts +0 -8
  30. package/src/components/bottom-sheet/modal-shadow-fix.ts +0 -42
  31. package/src/components/card-inline/ds-mobile-card-inline.ts +0 -301
  32. package/src/components/card-inline/index.ts +0 -2
  33. package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +0 -118
  34. package/src/components/card-inline-banner/index.ts +0 -1
  35. package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +0 -120
  36. package/src/components/card-inline-contact/index.ts +0 -1
  37. package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +0 -141
  38. package/src/components/card-inline-file/index.ts +0 -1
  39. package/src/components/chat-modal/ds-mobile-chat-modal.css +0 -159
  40. package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +0 -105
  41. package/src/components/chat-modal/ds-mobile-chat-modal.ts +0 -918
  42. package/src/components/chat-modal/index.ts +0 -8
  43. package/src/components/comment/ds-mobile-comment.ts +0 -568
  44. package/src/components/comment/index.ts +0 -2
  45. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
  46. package/src/components/contact-list-item/index.ts +0 -2
  47. package/src/components/content/ds-mobile-content.ts +0 -139
  48. package/src/components/content/index.ts +0 -2
  49. package/src/components/dropdown/ds-mobile-dropdown.css +0 -199
  50. package/src/components/dropdown/ds-mobile-dropdown.ts +0 -340
  51. package/src/components/dropdown/index.ts +0 -2
  52. package/src/components/ds-mobile-tabs.css +0 -407
  53. package/src/components/ds-mobile-tabs.ts +0 -216
  54. package/src/components/empty-state/ds-mobile-empty-state.ts +0 -120
  55. package/src/components/empty-state/index.ts +0 -2
  56. package/src/components/fab/ds-mobile-fab.ts +0 -315
  57. package/src/components/fab/index.ts +0 -1
  58. package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +0 -121
  59. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +0 -189
  60. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +0 -135
  61. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +0 -656
  62. package/src/components/facility-creation-modal/index.ts +0 -9
  63. package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +0 -105
  64. package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +0 -188
  65. package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +0 -460
  66. package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +0 -134
  67. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +0 -69
  68. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +0 -379
  69. package/src/components/facility-detail-modal/index.ts +0 -2
  70. package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
  71. package/src/components/file-attachment/index.ts +0 -2
  72. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +0 -214
  73. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -84
  74. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -424
  75. package/src/components/handbook-detail-modal/index.ts +0 -3
  76. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -175
  77. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -533
  78. package/src/components/handbook-folder/index.ts +0 -4
  79. package/src/components/header-content/ds-mobile-header-content.ts +0 -222
  80. package/src/components/header-content/index.ts +0 -2
  81. package/src/components/illustration/ds-mobile-illustration.ts +0 -124
  82. package/src/components/illustration/index.ts +0 -2
  83. package/src/components/index.ts +0 -124
  84. package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -361
  85. package/src/components/inline-photo/index.ts +0 -1
  86. package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +0 -132
  87. package/src/components/inline-tabs/index.ts +0 -2
  88. package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +0 -350
  89. package/src/components/interactive-list-item-booking/index.ts +0 -1
  90. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -321
  91. package/src/components/interactive-list-item-inquiry/index.ts +0 -2
  92. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -237
  93. package/src/components/interactive-list-item-message/index.ts +0 -2
  94. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -549
  95. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
  96. package/src/components/interactive-list-item-post/index.ts +0 -13
  97. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -315
  98. package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -202
  99. package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -484
  100. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -377
  101. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
  102. package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
  103. package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -296
  104. package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
  105. package/src/components/lightbox/index.ts +0 -22
  106. package/src/components/list-item/ds-mobile-list-item.ts +0 -603
  107. package/src/components/list-item/index.ts +0 -2
  108. package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
  109. package/src/components/list-item-static/index.ts +0 -2
  110. package/src/components/loader-overlay/ds-mobile-loader-overlay.css +0 -49
  111. package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +0 -77
  112. package/src/components/loader-overlay/index.ts +0 -1
  113. package/src/components/logo/ds-logo.ts +0 -95
  114. package/src/components/logo/index.ts +0 -2
  115. package/src/components/message-bubble/ds-mobile-message-bubble.ts +0 -633
  116. package/src/components/message-bubble/index.ts +0 -7
  117. package/src/components/message-composer/ds-mobile-message-composer.ts +0 -1146
  118. package/src/components/message-composer/index.ts +0 -7
  119. package/src/components/modal/ds-mobile-modal.css +0 -163
  120. package/src/components/modal/ds-mobile-modal.service.ts +0 -329
  121. package/src/components/modal/index.ts +0 -8
  122. package/src/components/modal-base/ds-mobile-modal-base.css +0 -378
  123. package/src/components/modal-base/ds-mobile-modal-base.ts +0 -261
  124. package/src/components/modal-base/index.ts +0 -2
  125. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +0 -112
  126. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +0 -93
  127. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +0 -442
  128. package/src/components/new-inquiry-modal/index.ts +0 -4
  129. package/src/components/offline-banner/ds-mobile-offline-banner.ts +0 -135
  130. package/src/components/offline-banner/index.ts +0 -1
  131. package/src/components/page-details/ds-mobile-page-details.css +0 -83
  132. package/src/components/page-details/ds-mobile-page-details.ts +0 -282
  133. package/src/components/page-details/index.ts +0 -2
  134. package/src/components/page-main/ds-mobile-page-main.css +0 -68
  135. package/src/components/page-main/ds-mobile-page-main.ts +0 -421
  136. package/src/components/page-main/index.ts +0 -2
  137. package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
  138. package/src/components/post-composer/index.ts +0 -2
  139. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +0 -390
  140. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -108
  141. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -722
  142. package/src/components/post-detail-modal/index.ts +0 -9
  143. package/src/components/property-banner/ds-mobile-property-banner.ts +0 -95
  144. package/src/components/property-banner/index.ts +0 -2
  145. package/src/components/section/ds-mobile-section.ts +0 -263
  146. package/src/components/section/index.ts +0 -2
  147. package/src/components/shared/directives/index.ts +0 -2
  148. package/src/components/shared/directives/long-press.directive.ts +0 -212
  149. package/src/components/shared/index.ts +0 -3
  150. package/src/components/shared/mobile-modal-base.ts +0 -457
  151. package/src/components/shared/mobile-page-base.ts +0 -204
  152. package/src/components/swiper/ds-mobile-swiper-with-nav.ts +0 -160
  153. package/src/components/swiper/ds-mobile-swiper.ts +0 -327
  154. package/src/components/swiper/index.ts +0 -3
  155. package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +0 -129
  156. package/src/components/system-message-banner/index.ts +0 -2
  157. package/src/components/tab-bar/ds-mobile-tab-bar.css +0 -533
  158. package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -735
  159. package/src/components/tab-bar/index.ts +0 -2
  160. package/src/components/tabs/ds-mobile-tabs.css +0 -25
  161. package/src/components/tabs/ds-mobile-tabs.ts +0 -89
  162. package/src/components/tabs/index.ts +0 -2
  163. package/src/components/text-input/ds-text-input.ts +0 -287
  164. package/src/components/text-input/index.ts +0 -2
  165. package/src/examples/booking.page.ts +0 -434
  166. package/src/examples/community.page.ts +0 -776
  167. package/src/examples/handbook.page.ts +0 -324
  168. package/src/examples/home.page.ts +0 -347
  169. package/src/examples/index.ts +0 -12
  170. package/src/examples/inquiries.example.ts +0 -273
  171. package/src/examples/inquiry-detail.example.css +0 -189
  172. package/src/examples/inquiry-detail.example.ts +0 -415
  173. package/src/examples/mobile-tabs-example.component.ts +0 -208
  174. package/src/examples/post-create.page.ts +0 -311
  175. package/src/examples/post-detail.page.ts +0 -296
  176. package/src/examples/sign-in.page.ts +0 -291
  177. package/src/examples/whitelabel-demo-modal.component.ts +0 -1094
  178. package/src/examples/whitelabel-demo-modal.service.ts +0 -77
  179. package/src/models/index.ts +0 -7
  180. package/src/models/post.model.ts +0 -41
  181. package/src/pages/community.page.ts +0 -769
  182. package/src/pages/handbook.page.ts +0 -388
  183. package/src/pages/home.page.ts +0 -303
  184. package/src/pages/index.ts +0 -11
  185. package/src/pages/inquiries.example.ts +0 -273
  186. package/src/pages/inquiry-detail.example.css +0 -189
  187. package/src/pages/inquiry-detail.example.ts +0 -415
  188. package/src/pages/mobile-tabs-example.component.ts +0 -179
  189. package/src/pages/post-create.page.ts +0 -311
  190. package/src/pages/post-detail.page.ts +0 -296
  191. package/src/pages/sign-in.page.ts +0 -291
  192. package/src/pages/whitelabel-demo-modal.component.ts +0 -1094
  193. package/src/pages/whitelabel-demo-modal.service.ts +0 -77
  194. package/src/public-api.ts +0 -6
  195. package/src/services/base-modal.service.ts +0 -101
  196. package/src/services/index.ts +0 -11
  197. package/src/services/posts.service.ts +0 -542
  198. package/src/services/tracking-permission.service.ts +0 -88
  199. package/src/services/user.service.ts +0 -60
  200. package/src/services/whitelabel.service.ts +0 -675
  201. package/tsconfig.lib.json +0 -17
  202. package/tsconfig.lib.prod.json +0 -9
  203. package/tsconfig.spec.json +0 -13
  204. /package/{src/assets → assets}/fonts/Brockmann-Bold.otf +0 -0
  205. /package/{src/assets → assets}/fonts/Brockmann-BoldItalic.otf +0 -0
  206. /package/{src/assets → assets}/fonts/Brockmann-Medium.otf +0 -0
  207. /package/{src/assets → assets}/fonts/Brockmann-MediumItalic.otf +0 -0
  208. /package/{src/assets → assets}/fonts/Brockmann-Regular.otf +0 -0
  209. /package/{src/assets → assets}/fonts/Brockmann-RegularItalic.otf +0 -0
  210. /package/{src/assets → assets}/fonts/Brockmann-SemiBold.otf +0 -0
  211. /package/{src/assets → assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  212. /package/{src/assets → assets}/fonts/Brockmann_desktop_license.pdf +0 -0
  213. /package/{src/assets → assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
  214. /package/{src/assets → assets}/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
  215. /package/{src/assets → assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
  216. /package/{src/assets → assets}/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
  217. /package/{src/assets → assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
  218. /package/{src/assets → assets}/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
  219. /package/{src/styles → styles}/ionic.css +0 -0
  220. /package/{src/components/shared → styles}/mobile-common.css +0 -0
  221. /package/{src/components/shared → styles}/mobile-page-base.css +0 -0
@@ -1,675 +0,0 @@
1
- import { Injectable, signal, effect, computed } from '@angular/core';
2
- import { StatusBar, Style } from '@capacitor/status-bar';
3
-
4
- export interface WhitelabelConfig {
5
- // Logo assets
6
- logoUrl: string; // Full logo for header (typically horizontal)
7
- logoMarkUrl: string; // Compact logo mark for avatars/badges
8
- logoAlt: string; // Alt text for accessibility
9
- logoSize: 'sm' | 'md' | 'lg' | 'xl'; // Logo size in header (sm: 24px, md: 28px, lg: 32px, xl: 36px)
10
-
11
- // Logo dimensions (optional, for optimization)
12
- logoWidth?: number;
13
- logoHeight?: number;
14
- logoMarkWidth?: number;
15
- logoMarkHeight?: number;
16
-
17
- // ============================================
18
- // APP ICON (app icons, logo badges)
19
- // ============================================
20
- appIconSurface: string; // App icon background fill
21
- appIconContent: string; // Logomark color on app icon
22
-
23
- // ============================================
24
- // ACTIONS & SELECTIONS (buttons, FABs, active tabs, selected items)
25
- // ============================================
26
- accent: string; // Main accent color (button bg, active tab icon)
27
- onAccent: string; // Content on accent-colored surfaces (button text/icon)
28
-
29
- // ============================================
30
- // HEADER/NAVIGATION (includes ion-header and header-expandable)
31
- // ============================================
32
- headerSurface: string; // Header background
33
- headerContent: string; // Header text/icons
34
- headerAccent: string; // Accent elements in header (e.g., subtle overlay)
35
- onHeaderAccent: string; // Content on header accent elements
36
-
37
- // ============================================
38
- // SIGN-IN PAGE CUSTOMIZATION
39
- // ============================================
40
- showCityIllustration: boolean; // Show/hide city illustration on sign-in page
41
-
42
- // Sign-in background
43
- signInBgType: 'solid' | 'gradient'; // Background type
44
- signInBgSolid: string; // Solid background color
45
- signInBgGradientStart: string; // Gradient start color (top)
46
- signInBgGradientEnd: string; // Gradient end color (bottom)
47
-
48
- // Sign-in content color
49
- signInContentColor: string; // Text color on sign-in page (headings, body text, links)
50
-
51
- // Organization info
52
- organizationName: string;
53
- organizationId: string;
54
- }
55
-
56
- const DEFAULT_CONFIG: WhitelabelConfig = {
57
- logoUrl: '/Assets/logos/propbinder-logomark.svg',
58
- logoMarkUrl: '/Assets/logos/propbinder-logomark.svg',
59
- logoAlt: 'Propbinder',
60
- logoSize: 'md', // Propbinder default: md (28px mobile, 32px desktop)
61
-
62
- // App icon (brand identity)
63
- appIconSurface: '#6B5FF5', // Purple
64
- appIconContent: '#FFFFFF', // White
65
-
66
- // Accent (buttons, FABs, active tabs)
67
- accent: '#6B5FF5', // Purple
68
- onAccent: '#FFFFFF', // White
69
-
70
- // Header/navigation
71
- headerSurface: '#221a4c', // Dark purple
72
- headerContent: '#FFFFFF', // White
73
- headerAccent: '#6B5FF5', // Purple accent (matches Propbinder theme)
74
- onHeaderAccent: '#FFFFFF', // White
75
-
76
- // Sign-in page
77
- showCityIllustration: true, // Show city illustration by default
78
- signInBgType: 'gradient', // Use gradient by default
79
- signInBgSolid: '#D6C7FF', // Fallback solid color
80
- signInBgGradientStart: '#D6C7FF', // Gradient top color
81
- signInBgGradientEnd: '#8A9BFF', // Gradient bottom color
82
- signInContentColor: '#1a1a1a', // Default dark text color
83
-
84
- organizationName: 'Propbinder',
85
- organizationId: 'default',
86
- };
87
-
88
- /**
89
- * WhitelabelService
90
- *
91
- * Manages whitelabel configuration including logos and brand colors.
92
- * Automatically updates CSS custom properties when colors change.
93
- *
94
- * @example
95
- * Initialize with custom config:
96
- * ```typescript
97
- * whitelabelService.initialize({
98
- * logoUrl: '/Assets/logos/acme-logo.svg',
99
- * logoMarkUrl: '/Assets/logos/acme-mark.svg',
100
- * accent: '#2563eb',
101
- * onAccent: '#ffffff',
102
- * headerSurface: '#1e40af',
103
- * headerContent: '#ffffff',
104
- * organizationName: 'Acme Corp'
105
- * });
106
- * ```
107
- *
108
- * Load from API:
109
- * ```typescript
110
- * await whitelabelService.loadFromApi('acme-corp');
111
- * ```
112
- */
113
- @Injectable({
114
- providedIn: 'root',
115
- })
116
- export class WhitelabelService {
117
- private _config = signal<WhitelabelConfig>(DEFAULT_CONFIG);
118
-
119
- // Readonly computed signals for accessing config values
120
- readonly logoUrl = computed(() => this._config().logoUrl);
121
- readonly logoMarkUrl = computed(() => this._config().logoMarkUrl);
122
- readonly logoAlt = computed(() => this._config().logoAlt);
123
- readonly logoSize = computed(() => this._config().logoSize);
124
- readonly logoHeight = computed(() => {
125
- const customHeight = this._config().logoHeight;
126
- const size = this._config().logoSize;
127
-
128
- // If custom height is explicitly specified, always use it
129
- if (customHeight !== undefined) {
130
- return customHeight;
131
- }
132
-
133
- // Otherwise calculate height based on logoSize
134
- switch (size) {
135
- case 'sm':
136
- return 24;
137
- case 'md':
138
- return 28;
139
- case 'lg':
140
- return 32;
141
- case 'xl':
142
- return 36;
143
- default:
144
- return 28;
145
- }
146
- });
147
- readonly appIconSurface = computed(() => this._config().appIconSurface);
148
- readonly appIconContent = computed(() => this._config().appIconContent);
149
- readonly accent = computed(() => this._config().accent);
150
- readonly onAccent = computed(() => this._config().onAccent);
151
- readonly headerSurface = computed(() => this._config().headerSurface);
152
- readonly headerContent = computed(() => this._config().headerContent);
153
- readonly headerAccent = computed(() => this._config().headerAccent);
154
- readonly onHeaderAccent = computed(() => this._config().onHeaderAccent);
155
- readonly showCityIllustration = computed(() => this._config().showCityIllustration);
156
- readonly signInBgType = computed(() => this._config().signInBgType);
157
- readonly signInBgSolid = computed(() => this._config().signInBgSolid);
158
- readonly signInBgGradientStart = computed(() => this._config().signInBgGradientStart);
159
- readonly signInBgGradientEnd = computed(() => this._config().signInBgGradientEnd);
160
- readonly signInContentColor = computed(() => this._config().signInContentColor);
161
- readonly organizationName = computed(() => this._config().organizationName);
162
- readonly organizationId = computed(() => this._config().organizationId);
163
-
164
- // Computed background style for sign-in page
165
- readonly signInBgStyle = computed(() => {
166
- const config = this._config();
167
- if (config.signInBgType === 'solid') {
168
- return config.signInBgSolid;
169
- } else {
170
- return `linear-gradient(180deg, ${config.signInBgGradientStart} 0%, ${config.signInBgGradientEnd} 100%)`;
171
- }
172
- });
173
-
174
- // Full config accessor
175
- readonly config = this._config.asReadonly();
176
-
177
- constructor() {
178
- // Apply default colors on initialization
179
- this.applyColors(DEFAULT_CONFIG);
180
-
181
- // Watch for config changes and update CSS custom properties
182
- effect(() => {
183
- const config = this._config();
184
- this.applyColors(config);
185
- });
186
-
187
- // Listen for modal dismissals and re-apply status bar
188
- // This fixes the issue where Ionic restores cached status bar state
189
- this.setupModalDismissListener();
190
- }
191
-
192
- /**
193
- * Setup global listener for modal dismiss events
194
- * Re-applies status bar after modals close to override Ionic's cached state restoration
195
- */
196
- private setupModalDismissListener(): void {
197
- if (typeof document === 'undefined') return;
198
-
199
- document.addEventListener('ionModalDidDismiss', () => {
200
- // Small delay to let Ionic's restoration happen first
201
- setTimeout(() => {
202
- // Check if we're on the sign-in page
203
- const isOnSignInPage = document.querySelector('app-sign-in') !== null;
204
-
205
- if (isOnSignInPage) {
206
- // On sign-in page - use sign-in background color
207
- const config = this._config();
208
- const backgroundColor = config.signInBgType === 'gradient' ? config.signInBgGradientStart : config.signInBgSolid;
209
- const style = this.getStatusBarStyleForColor(backgroundColor);
210
-
211
- StatusBar.setBackgroundColor({ color: backgroundColor }).catch(() => {});
212
- StatusBar.setStyle({ style }).catch(() => {});
213
- } else {
214
- // On regular page - use header color
215
- const config = this._config();
216
- const headerColor = config.headerSurface;
217
- const style = this.getStatusBarStyleForColor(headerColor);
218
-
219
- StatusBar.setStyle({ style }).catch(() => {});
220
- }
221
- }, 50);
222
- });
223
- }
224
-
225
- /**
226
- * Initialize whitelabel configuration
227
- * Call this early in app initialization (app.config.ts or app.component.ts)
228
- */
229
- initialize(config: Partial<WhitelabelConfig>) {
230
- this._config.update((current) => ({
231
- ...current,
232
- ...config,
233
- }));
234
- }
235
-
236
- /**
237
- * Load whitelabel config from API
238
- * Typically called on app startup based on subdomain, user tenant, etc.
239
- *
240
- * @param organizationId - The organization identifier (subdomain, tenant ID, etc.)
241
- */
242
- async loadFromApi(organizationId?: string): Promise<void> {
243
- try {
244
- // Example API call structure
245
- // const response = await fetch(`/api/whitelabel/${organizationId || 'default'}`);
246
- // const config = await response.json();
247
- // this.initialize(config);
248
-
249
- //console.log('Loading whitelabel config from API for:', organizationId);
250
-
251
- // Example: Different configs for different organizations
252
- if (organizationId === 'demo-client') {
253
- this.initialize({
254
- logoUrl: '/Assets/logos/demo-logo.svg',
255
- logoMarkUrl: '/Assets/logos/demo-mark.svg',
256
- logoAlt: 'Demo Client',
257
- appIconSurface: '#2563eb',
258
- appIconContent: '#FFFFFF',
259
- accent: '#2563eb',
260
- onAccent: '#FFFFFF',
261
- headerSurface: '#1e40af',
262
- headerContent: '#FFFFFF',
263
- headerAccent: 'rgba(255, 255, 255, 0.15)',
264
- onHeaderAccent: '#FFFFFF',
265
- organizationName: 'Demo Client',
266
- organizationId: 'demo-client',
267
- });
268
- } else if (organizationId === 'cobblestone') {
269
- this.initialize({
270
- logoUrl: '/Assets/logos/cobblestone-logo.svg',
271
- logoMarkUrl: '/Assets/logos/cobblestone-logomark.svg',
272
- logoAlt: 'Cobblestone',
273
- logoSize: 'sm',
274
- appIconSurface: '#2C3E50',
275
- appIconContent: '#FFFFFF',
276
- accent: '#3498DB',
277
- onAccent: '#FFFFFF',
278
- headerSurface: '#2C3E50',
279
- headerContent: '#FFFFFF',
280
- headerAccent: '#3498DB',
281
- onHeaderAccent: '#FFFFFF',
282
- showCityIllustration: false,
283
- signInBgType: 'gradient',
284
- signInBgSolid: '#E8EEF2',
285
- signInBgGradientStart: '#E8EEF2',
286
- signInBgGradientEnd: '#BDC3C7',
287
- signInContentColor: '#1a1a1a',
288
- organizationName: 'Cobblestone',
289
- organizationId: 'cobblestone',
290
- });
291
- }
292
- // Add more organization-specific configs as needed
293
- } catch (error) {
294
- console.error('Failed to load whitelabel config:', error);
295
- // Fallback to defaults already set
296
- }
297
- }
298
-
299
- /**
300
- * Update config dynamically (e.g., when user switches organizations)
301
- */
302
- updateConfig(updates: Partial<WhitelabelConfig>) {
303
- this.initialize(updates);
304
- }
305
-
306
- /**
307
- * Update only the brand colors
308
- */
309
- updateColors(colors: {
310
- appIconSurface?: string;
311
- appIconContent?: string;
312
- accent?: string;
313
- onAccent?: string;
314
- headerSurface?: string;
315
- headerContent?: string;
316
- headerAccent?: string;
317
- onHeaderAccent?: string;
318
- }) {
319
- this._config.update((current) => ({
320
- ...current,
321
- ...colors,
322
- }));
323
- }
324
-
325
- /**
326
- * Reset to default configuration
327
- */
328
- resetToDefault() {
329
- this._config.set(DEFAULT_CONFIG);
330
- }
331
-
332
- /**
333
- * Convert hex color to RGB values
334
- */
335
- private hexToRgb(hex: string): { r: number; g: number; b: number } | null {
336
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
337
- return result
338
- ? {
339
- r: parseInt(result[1], 16),
340
- g: parseInt(result[2], 16),
341
- b: parseInt(result[3], 16),
342
- }
343
- : null;
344
- }
345
-
346
- /**
347
- * Calculate relative luminance of a color (WCAG standard)
348
- * Returns a value between 0 (darkest) and 1 (lightest)
349
- */
350
- private getRelativeLuminance(color: string): number {
351
- const rgb = this.hexToRgb(color);
352
- if (!rgb) return 0;
353
-
354
- const rsRGB = rgb.r / 255;
355
- const gsRGB = rgb.g / 255;
356
- const bsRGB = rgb.b / 255;
357
-
358
- const r = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
359
- const g = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
360
- const b = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);
361
-
362
- return 0.2126 * r + 0.7152 * g + 0.0722 * b;
363
- }
364
-
365
- /**
366
- * Determine if a color is light (needs dark status bar content)
367
- */
368
- private isColorLight(color: string): boolean {
369
- const luminance = this.getRelativeLuminance(color);
370
- return luminance > 0.5;
371
- }
372
-
373
- /**
374
- * Get the appropriate status bar style for a background color
375
- * Public method for use by sign-in page
376
- */
377
- getStatusBarStyleForColor(color: string): Style {
378
- // Style.Dark = black icons (for light backgrounds)
379
- // Style.Light = white icons (for dark backgrounds)
380
- return this.isColorLight(color) ? Style.Light : Style.Dark;
381
- }
382
-
383
- /**
384
- * Generate a hover state color by applying a black overlay
385
- * This simulates the effect of overlaying #000000 with 10% opacity
386
- *
387
- * @param baseColor - Hex color to overlay on (e.g., '#6B5FF5')
388
- * @param overlayColor - Overlay color (default: '#000000' for darkening)
389
- * @param overlayAlpha - Opacity of overlay (0-1, default: 0.1 = 10%)
390
- * @returns Hex color with overlay applied
391
- *
392
- * @example
393
- * generateHoverColor('#6B5FF5') // Returns darker purple for hover state
394
- * generateHoverColor('#FF0000', '#FFFFFF', 0.2) // Lighten red by 20%
395
- */
396
- private generateHoverColor(baseColor: string, overlayColor: string = '#000000', overlayAlpha: number = 0.1): string {
397
- const base = this.hexToRgb(baseColor);
398
- const overlay = this.hexToRgb(overlayColor);
399
-
400
- if (!base || !overlay) return baseColor;
401
-
402
- // Alpha blending formula: result = overlay * alpha + base * (1 - alpha)
403
- const r = Math.round(overlay.r * overlayAlpha + base.r * (1 - overlayAlpha));
404
- const g = Math.round(overlay.g * overlayAlpha + base.g * (1 - overlayAlpha));
405
- const b = Math.round(overlay.b * overlayAlpha + base.b * (1 - overlayAlpha));
406
-
407
- // Convert to hex and ensure 2-digit format
408
- const toHex = (n: number) => Math.max(0, Math.min(255, n)).toString(16).padStart(2, '0');
409
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
410
- }
411
-
412
- /**
413
- * Generate an active/pressed state color (darker than hover)
414
- * Uses 20% black overlay for a more pronounced pressed effect
415
- *
416
- * @param baseColor - Hex color to overlay on
417
- * @returns Hex color for active/pressed state
418
- */
419
- private generateActiveColor(baseColor: string): string {
420
- return this.generateHoverColor(baseColor, '#000000', 0.2); // 20% darker
421
- }
422
-
423
- /**
424
- * Apply colors to CSS custom properties and native StatusBar
425
- * This updates the actual CSS variables used throughout the app
426
- * and the native status bar color on mobile devices
427
- */
428
- private applyColors(config: WhitelabelConfig) {
429
- if (typeof document !== 'undefined') {
430
- const { appIconSurface, appIconContent, accent, onAccent, headerSurface, headerContent, headerAccent, onHeaderAccent, signInContentColor } = config;
431
- const root = document.documentElement;
432
- const body = document.body;
433
- const ionApp = document.querySelector('ion-app');
434
-
435
- // Generate hover and active state colors for accent
436
- const accentHover = this.generateHoverColor(accent);
437
- const accentActive = this.generateActiveColor(accent);
438
- const headerAccentHover = this.generateHoverColor(headerAccent);
439
- const headerAccentActive = this.generateActiveColor(headerAccent);
440
-
441
- // Generate RGB values for Ionic color system
442
- const accentRgb = this.hexToRgb(accent);
443
- const accentRgbString = accentRgb ? `${accentRgb.r}, ${accentRgb.g}, ${accentRgb.b}` : '107, 95, 245';
444
- const onAccentRgb = this.hexToRgb(onAccent);
445
- const onAccentRgbString = onAccentRgb ? `${onAccentRgb.r}, ${onAccentRgb.g}, ${onAccentRgb.b}` : '255, 255, 255';
446
-
447
- // ============================================
448
- // APP ICON COLORS
449
- // ============================================
450
- root.style.setProperty('--color-app-icon-surface', appIconSurface);
451
- root.style.setProperty('--color-app-icon-content', appIconContent);
452
- body.style.setProperty('--color-app-icon-surface', appIconSurface);
453
- body.style.setProperty('--color-app-icon-content', appIconContent);
454
-
455
- // ============================================
456
- // ACCENT COLORS (buttons, FABs, active tabs, selections)
457
- // ============================================
458
-
459
- // Base color
460
- root.style.setProperty('--color-accent', accent);
461
- root.style.setProperty('--color-on-accent', onAccent);
462
- body.style.setProperty('--color-accent', accent);
463
- body.style.setProperty('--color-on-accent', onAccent);
464
-
465
- // Hover state (10% darker)
466
- root.style.setProperty('--color-accent-hover', accentHover);
467
- body.style.setProperty('--color-accent-hover', accentHover);
468
-
469
- // Active/pressed state (20% darker)
470
- root.style.setProperty('--color-accent-active', accentActive);
471
- body.style.setProperty('--color-accent-active', accentActive);
472
-
473
- // Legacy aliases for backward compatibility
474
- root.style.setProperty('--color-background-brand', accent);
475
- root.style.setProperty('--color-brand-base', accent);
476
- root.style.setProperty('--color-primary-surface', accent);
477
- root.style.setProperty('--color-primary-content', onAccent);
478
- root.style.setProperty('--color-brand-base-hover', accentHover);
479
- root.style.setProperty('--color-primary-surface-hover', accentHover);
480
- root.style.setProperty('--color-brand-base-active', accentActive);
481
- root.style.setProperty('--color-primary-surface-active', accentActive);
482
-
483
- body.style.setProperty('--color-background-brand', accent);
484
- body.style.setProperty('--color-brand-base', accent);
485
- body.style.setProperty('--color-primary-surface', accent);
486
- body.style.setProperty('--color-primary-content', onAccent);
487
- body.style.setProperty('--color-brand-base-hover', accentHover);
488
- body.style.setProperty('--color-primary-surface-hover', accentHover);
489
- body.style.setProperty('--color-brand-base-active', accentActive);
490
- body.style.setProperty('--color-primary-surface-active', accentActive);
491
-
492
- if (ionApp) {
493
- (ionApp as HTMLElement).style.setProperty('--color-accent', accent);
494
- (ionApp as HTMLElement).style.setProperty('--color-on-accent', onAccent);
495
- (ionApp as HTMLElement).style.setProperty('--color-accent-hover', accentHover);
496
- (ionApp as HTMLElement).style.setProperty('--color-accent-active', accentActive);
497
- (ionApp as HTMLElement).style.setProperty('--color-background-brand', accent);
498
- (ionApp as HTMLElement).style.setProperty('--color-brand-base', accent);
499
- (ionApp as HTMLElement).style.setProperty('--color-primary-surface', accent);
500
- (ionApp as HTMLElement).style.setProperty('--color-primary-content', onAccent);
501
- (ionApp as HTMLElement).style.setProperty('--color-brand-base-hover', accentHover);
502
- (ionApp as HTMLElement).style.setProperty('--color-primary-surface-hover', accentHover);
503
- (ionApp as HTMLElement).style.setProperty('--color-brand-base-active', accentActive);
504
- (ionApp as HTMLElement).style.setProperty('--color-primary-surface-active', accentActive);
505
- (ionApp as HTMLElement).style.setProperty('--color-selected', accent);
506
- }
507
-
508
- // Update tab button selected color directly
509
- // CSS variable inheritance doesn't always work dynamically with Ionic components
510
- document.querySelectorAll('ion-tab-button').forEach((tabButton) => {
511
- (tabButton as HTMLElement).style.setProperty('--color-selected', accent);
512
- });
513
-
514
- // ============================================
515
- // IONIC COLOR SYSTEM
516
- // ============================================
517
- // Update Ionic's primary color system with RGB values
518
- root.style.setProperty('--ion-color-primary', accent);
519
- root.style.setProperty('--ion-color-primary-rgb', accentRgbString);
520
- root.style.setProperty('--ion-color-primary-contrast', onAccent);
521
- root.style.setProperty('--ion-color-primary-contrast-rgb', onAccentRgbString);
522
- root.style.setProperty('--ion-color-primary-shade', accentHover);
523
- root.style.setProperty('--ion-color-primary-tint', accent);
524
-
525
- body.style.setProperty('--ion-color-primary', accent);
526
- body.style.setProperty('--ion-color-primary-rgb', accentRgbString);
527
- body.style.setProperty('--ion-color-primary-contrast', onAccent);
528
- body.style.setProperty('--ion-color-primary-contrast-rgb', onAccentRgbString);
529
- body.style.setProperty('--ion-color-primary-shade', accentHover);
530
- body.style.setProperty('--ion-color-primary-tint', accent);
531
-
532
- if (ionApp) {
533
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary', accent);
534
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary-rgb', accentRgbString);
535
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary-contrast', onAccent);
536
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary-contrast-rgb', onAccentRgbString);
537
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary-shade', accentHover);
538
- (ionApp as HTMLElement).style.setProperty('--ion-color-primary-tint', accent);
539
- }
540
-
541
- // ============================================
542
- // HEADER COLORS (navigation bar, header-expandable)
543
- // ============================================
544
-
545
- // Base colors
546
- root.style.setProperty('--color-header-surface', headerSurface);
547
- root.style.setProperty('--color-header-content', headerContent);
548
- root.style.setProperty('--color-header-accent', headerAccent);
549
- root.style.setProperty('--color-on-header-accent', onHeaderAccent);
550
- body.style.setProperty('--color-header-surface', headerSurface);
551
- body.style.setProperty('--color-header-content', headerContent);
552
- body.style.setProperty('--color-header-accent', headerAccent);
553
- body.style.setProperty('--color-on-header-accent', onHeaderAccent);
554
-
555
- // Hover/active states for header accent
556
- root.style.setProperty('--color-header-accent-hover', headerAccentHover);
557
- root.style.setProperty('--color-header-accent-active', headerAccentActive);
558
- body.style.setProperty('--color-header-accent-hover', headerAccentHover);
559
- body.style.setProperty('--color-header-accent-active', headerAccentActive);
560
-
561
- // Legacy aliases for backward compatibility
562
- root.style.setProperty('--color-brand-secondary', headerSurface);
563
- root.style.setProperty('--color-secondary-surface', headerSurface);
564
- root.style.setProperty('--color-secondary-content', headerContent);
565
- root.style.setProperty('--header-content-color', headerContent);
566
- root.style.setProperty('--color-brand-secondary-hover', this.generateHoverColor(headerSurface));
567
- root.style.setProperty('--color-secondary-surface-hover', this.generateHoverColor(headerSurface));
568
- root.style.setProperty('--color-brand-secondary-active', this.generateActiveColor(headerSurface));
569
- root.style.setProperty('--color-secondary-surface-active', this.generateActiveColor(headerSurface));
570
-
571
- body.style.setProperty('--color-brand-secondary', headerSurface);
572
- body.style.setProperty('--color-secondary-surface', headerSurface);
573
- body.style.setProperty('--color-secondary-content', headerContent);
574
- body.style.setProperty('--header-content-color', headerContent);
575
- body.style.setProperty('--color-brand-secondary-hover', this.generateHoverColor(headerSurface));
576
- body.style.setProperty('--color-secondary-surface-hover', this.generateHoverColor(headerSurface));
577
- body.style.setProperty('--color-brand-secondary-active', this.generateActiveColor(headerSurface));
578
- body.style.setProperty('--color-secondary-surface-active', this.generateActiveColor(headerSurface));
579
-
580
- if (ionApp) {
581
- (ionApp as HTMLElement).style.setProperty('--color-header-surface', headerSurface);
582
- (ionApp as HTMLElement).style.setProperty('--color-header-content', headerContent);
583
- (ionApp as HTMLElement).style.setProperty('--color-header-accent', headerAccent);
584
- (ionApp as HTMLElement).style.setProperty('--color-on-header-accent', onHeaderAccent);
585
- (ionApp as HTMLElement).style.setProperty('--color-header-accent-hover', headerAccentHover);
586
- (ionApp as HTMLElement).style.setProperty('--color-header-accent-active', headerAccentActive);
587
- (ionApp as HTMLElement).style.setProperty('--color-brand-secondary', headerSurface);
588
- (ionApp as HTMLElement).style.setProperty('--color-secondary-surface', headerSurface);
589
- (ionApp as HTMLElement).style.setProperty('--color-secondary-content', headerContent);
590
- (ionApp as HTMLElement).style.setProperty('--header-content-color', headerContent);
591
- (ionApp as HTMLElement).style.setProperty('--color-brand-secondary-hover', this.generateHoverColor(headerSurface));
592
- (ionApp as HTMLElement).style.setProperty('--color-secondary-surface-hover', this.generateHoverColor(headerSurface));
593
- (ionApp as HTMLElement).style.setProperty('--color-brand-secondary-active', this.generateActiveColor(headerSurface));
594
- (ionApp as HTMLElement).style.setProperty('--color-secondary-surface-active', this.generateActiveColor(headerSurface));
595
- }
596
-
597
- // Also set RGB values for use with rgba() opacity variations
598
- const rgbContent = this.hexToRgb(headerContent);
599
- if (rgbContent) {
600
- const rgbValue = `${rgbContent.r}, ${rgbContent.g}, ${rgbContent.b}`;
601
- root.style.setProperty('--header-content-color-rgb', rgbValue);
602
- root.style.setProperty('--color-secondary-content-rgb', rgbValue);
603
- root.style.setProperty('--color-header-content-rgb', rgbValue);
604
- body.style.setProperty('--header-content-color-rgb', rgbValue);
605
- body.style.setProperty('--color-secondary-content-rgb', rgbValue);
606
- body.style.setProperty('--color-header-content-rgb', rgbValue);
607
- if (ionApp) {
608
- (ionApp as HTMLElement).style.setProperty('--header-content-color-rgb', rgbValue);
609
- (ionApp as HTMLElement).style.setProperty('--color-secondary-content-rgb', rgbValue);
610
- (ionApp as HTMLElement).style.setProperty('--color-header-content-rgb', rgbValue);
611
- }
612
- }
613
-
614
- const rgbOnHeaderAccent = this.hexToRgb(onHeaderAccent);
615
- if (rgbOnHeaderAccent) {
616
- const rgbValue = `${rgbOnHeaderAccent.r}, ${rgbOnHeaderAccent.g}, ${rgbOnHeaderAccent.b}`;
617
- root.style.setProperty('--color-on-header-accent-rgb', rgbValue);
618
- body.style.setProperty('--color-on-header-accent-rgb', rgbValue);
619
- if (ionApp) {
620
- (ionApp as HTMLElement).style.setProperty('--color-on-header-accent-rgb', rgbValue);
621
- }
622
- }
623
-
624
- // Update theme-color meta tag for browser chrome/status bar (PWA/iOS)
625
- const metaThemeColor = document.querySelector('meta[name="theme-color"]');
626
- if (metaThemeColor) {
627
- metaThemeColor.setAttribute('content', headerSurface);
628
- }
629
-
630
- // ============================================
631
- // SIGN-IN PAGE COLORS
632
- // ============================================
633
- root.style.setProperty('--color-signin-content', signInContentColor);
634
- body.style.setProperty('--color-signin-content', signInContentColor);
635
- if (ionApp) {
636
- (ionApp as HTMLElement).style.setProperty('--color-signin-content', signInContentColor);
637
- }
638
-
639
- // Update native StatusBar color (Capacitor - Android only, iOS ignores this)
640
- this.updateNativeStatusBar(headerSurface);
641
-
642
- // console.log('Applied whitelabel colors:', {
643
- // appIconSurface,
644
- // appIconContent,
645
- // accent,
646
- // accentHover,
647
- // accentActive,
648
- // onAccent,
649
- // headerSurface,
650
- // headerContent,
651
- // headerAccent,
652
- // headerAccentHover,
653
- // headerAccentActive,
654
- // onHeaderAccent,
655
- // signInContentColor
656
- // });
657
- }
658
- }
659
-
660
- /**
661
- * Update the native status bar color AND style
662
- * Sets background color (Android) and content style (iOS/Android)
663
- */
664
- private async updateNativeStatusBar(color: string): Promise<void> {
665
- try {
666
- await StatusBar.setBackgroundColor({ color });
667
-
668
- // Calculate and set appropriate style for status bar content
669
- const style = this.getStatusBarStyleForColor(color);
670
- await StatusBar.setStyle({ style });
671
- } catch (e) {
672
- // StatusBar API not available (web browser) or failed
673
- }
674
- }
675
- }