@propbinder/mobile-design 0.2.48 → 0.2.50

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/ng-package.json +24 -0
  2. package/package.json +3 -39
  3. package/src/animations/page-transitions.ts +165 -0
  4. package/src/assets/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
  5. package/src/assets/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
  6. package/src/assets/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
  7. package/src/components/action-list-item/ds-mobile-action-list-item.ts +102 -0
  8. package/src/components/action-list-item/index.ts +2 -0
  9. package/src/components/app-icon/ds-app-icon.ts +133 -0
  10. package/src/components/app-icon/index.ts +2 -0
  11. package/src/components/attachment-preview/ds-mobile-attachment-preview.css +139 -0
  12. package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +164 -0
  13. package/src/components/attachment-preview/index.ts +1 -0
  14. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +142 -0
  15. package/src/components/avatar-with-badge/index.ts +2 -0
  16. package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +71 -0
  17. package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +121 -0
  18. package/src/components/booking-modal/ds-mobile-booking-modal.ts +598 -0
  19. package/src/components/booking-modal/ds-mobile-booking-summary.ts +161 -0
  20. package/src/components/booking-modal/index.ts +4 -0
  21. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +266 -0
  22. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +146 -0
  23. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +156 -0
  24. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +101 -0
  25. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +169 -0
  26. package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +211 -0
  27. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +578 -0
  28. package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +614 -0
  29. package/src/components/bottom-sheet/index.ts +8 -0
  30. package/src/components/bottom-sheet/modal-shadow-fix.ts +42 -0
  31. package/src/components/card-inline/ds-mobile-card-inline.ts +301 -0
  32. package/src/components/card-inline/index.ts +2 -0
  33. package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +118 -0
  34. package/src/components/card-inline-banner/index.ts +1 -0
  35. package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +120 -0
  36. package/src/components/card-inline-contact/index.ts +1 -0
  37. package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +141 -0
  38. package/src/components/card-inline-file/index.ts +1 -0
  39. package/src/components/chat-modal/ds-mobile-chat-modal.css +159 -0
  40. package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +105 -0
  41. package/src/components/chat-modal/ds-mobile-chat-modal.ts +918 -0
  42. package/src/components/chat-modal/index.ts +8 -0
  43. package/src/components/comment/ds-mobile-comment.ts +568 -0
  44. package/src/components/comment/index.ts +2 -0
  45. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +182 -0
  46. package/src/components/contact-list-item/index.ts +2 -0
  47. package/src/components/content/ds-mobile-content.ts +139 -0
  48. package/src/components/content/index.ts +2 -0
  49. package/src/components/dropdown/ds-mobile-dropdown.css +199 -0
  50. package/src/components/dropdown/ds-mobile-dropdown.ts +340 -0
  51. package/src/components/dropdown/index.ts +2 -0
  52. package/src/components/ds-mobile-tabs.css +407 -0
  53. package/src/components/ds-mobile-tabs.ts +216 -0
  54. package/src/components/empty-state/ds-mobile-empty-state.ts +120 -0
  55. package/src/components/empty-state/index.ts +2 -0
  56. package/src/components/fab/ds-mobile-fab.ts +315 -0
  57. package/src/components/fab/index.ts +1 -0
  58. package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +121 -0
  59. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +189 -0
  60. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +135 -0
  61. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +656 -0
  62. package/src/components/facility-creation-modal/index.ts +9 -0
  63. package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +105 -0
  64. package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +188 -0
  65. package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +460 -0
  66. package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +134 -0
  67. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +69 -0
  68. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +379 -0
  69. package/src/components/facility-detail-modal/index.ts +2 -0
  70. package/src/components/file-attachment/ds-mobile-file-attachment.ts +164 -0
  71. package/src/components/file-attachment/index.ts +2 -0
  72. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +214 -0
  73. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +84 -0
  74. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +424 -0
  75. package/src/components/handbook-detail-modal/index.ts +3 -0
  76. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +175 -0
  77. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +533 -0
  78. package/src/components/handbook-folder/index.ts +4 -0
  79. package/src/components/header-content/ds-mobile-header-content.ts +222 -0
  80. package/src/components/header-content/index.ts +2 -0
  81. package/src/components/illustration/ds-mobile-illustration.ts +124 -0
  82. package/src/components/illustration/index.ts +2 -0
  83. package/src/components/index.ts +124 -0
  84. package/src/components/inline-photo/ds-mobile-inline-photo.ts +361 -0
  85. package/src/components/inline-photo/index.ts +1 -0
  86. package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +132 -0
  87. package/src/components/inline-tabs/index.ts +2 -0
  88. package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +350 -0
  89. package/src/components/interactive-list-item-booking/index.ts +1 -0
  90. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +321 -0
  91. package/src/components/interactive-list-item-inquiry/index.ts +2 -0
  92. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +237 -0
  93. package/src/components/interactive-list-item-message/index.ts +2 -0
  94. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +549 -0
  95. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +124 -0
  96. package/src/components/interactive-list-item-post/index.ts +13 -0
  97. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +315 -0
  98. package/src/components/lightbox/ds-mobile-lightbox-header.ts +202 -0
  99. package/src/components/lightbox/ds-mobile-lightbox-image.ts +484 -0
  100. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +377 -0
  101. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +374 -0
  102. package/src/components/lightbox/ds-mobile-lightbox.css +587 -0
  103. package/src/components/lightbox/ds-mobile-lightbox.service.ts +296 -0
  104. package/src/components/lightbox/ds-mobile-lightbox.ts +529 -0
  105. package/src/components/lightbox/index.ts +22 -0
  106. package/src/components/list-item/ds-mobile-list-item.ts +603 -0
  107. package/src/components/list-item/index.ts +2 -0
  108. package/src/components/list-item-static/ds-mobile-list-item-static.ts +133 -0
  109. package/src/components/list-item-static/index.ts +2 -0
  110. package/src/components/loader-overlay/ds-mobile-loader-overlay.css +49 -0
  111. package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +77 -0
  112. package/src/components/loader-overlay/index.ts +1 -0
  113. package/src/components/logo/ds-logo.ts +95 -0
  114. package/src/components/logo/index.ts +2 -0
  115. package/src/components/message-bubble/ds-mobile-message-bubble.ts +633 -0
  116. package/src/components/message-bubble/index.ts +7 -0
  117. package/src/components/message-composer/ds-mobile-message-composer.ts +1146 -0
  118. package/src/components/message-composer/index.ts +7 -0
  119. package/src/components/modal/ds-mobile-modal.css +163 -0
  120. package/src/components/modal/ds-mobile-modal.service.ts +329 -0
  121. package/src/components/modal/index.ts +8 -0
  122. package/src/components/modal-base/ds-mobile-modal-base.css +378 -0
  123. package/src/components/modal-base/ds-mobile-modal-base.ts +261 -0
  124. package/src/components/modal-base/index.ts +2 -0
  125. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +112 -0
  126. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +93 -0
  127. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +442 -0
  128. package/src/components/new-inquiry-modal/index.ts +4 -0
  129. package/src/components/offline-banner/ds-mobile-offline-banner.ts +135 -0
  130. package/src/components/offline-banner/index.ts +1 -0
  131. package/src/components/page-details/ds-mobile-page-details.css +83 -0
  132. package/src/components/page-details/ds-mobile-page-details.ts +282 -0
  133. package/src/components/page-details/index.ts +2 -0
  134. package/src/components/page-main/ds-mobile-page-main.css +68 -0
  135. package/src/components/page-main/ds-mobile-page-main.ts +421 -0
  136. package/src/components/page-main/index.ts +2 -0
  137. package/src/components/post-composer/ds-mobile-post-composer.ts +140 -0
  138. package/src/components/post-composer/index.ts +2 -0
  139. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +390 -0
  140. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +108 -0
  141. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +722 -0
  142. package/src/components/post-detail-modal/index.ts +9 -0
  143. package/src/components/property-banner/ds-mobile-property-banner.ts +95 -0
  144. package/src/components/property-banner/index.ts +2 -0
  145. package/src/components/section/ds-mobile-section.ts +263 -0
  146. package/src/components/section/index.ts +2 -0
  147. package/src/components/shared/directives/index.ts +2 -0
  148. package/src/components/shared/directives/long-press.directive.ts +212 -0
  149. package/src/components/shared/index.ts +3 -0
  150. package/src/components/shared/mobile-modal-base.ts +457 -0
  151. package/src/components/shared/mobile-page-base.ts +204 -0
  152. package/src/components/swiper/ds-mobile-swiper-with-nav.ts +160 -0
  153. package/src/components/swiper/ds-mobile-swiper.ts +327 -0
  154. package/src/components/swiper/index.ts +3 -0
  155. package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +129 -0
  156. package/src/components/system-message-banner/index.ts +2 -0
  157. package/src/components/tab-bar/ds-mobile-tab-bar.css +533 -0
  158. package/src/components/tab-bar/ds-mobile-tab-bar.ts +735 -0
  159. package/src/components/tab-bar/index.ts +2 -0
  160. package/src/components/tabs/ds-mobile-tabs.css +25 -0
  161. package/src/components/tabs/ds-mobile-tabs.ts +89 -0
  162. package/src/components/tabs/index.ts +2 -0
  163. package/src/components/text-input/ds-text-input.ts +287 -0
  164. package/src/components/text-input/index.ts +2 -0
  165. package/src/examples/booking.page.ts +434 -0
  166. package/src/examples/community.page.ts +776 -0
  167. package/src/examples/handbook.page.ts +324 -0
  168. package/src/examples/home.page.ts +347 -0
  169. package/src/examples/index.ts +12 -0
  170. package/src/examples/inquiries.example.ts +273 -0
  171. package/src/examples/inquiry-detail.example.css +189 -0
  172. package/src/examples/inquiry-detail.example.ts +415 -0
  173. package/src/examples/mobile-tabs-example.component.ts +208 -0
  174. package/src/examples/post-create.page.ts +311 -0
  175. package/src/examples/post-detail.page.ts +296 -0
  176. package/src/examples/sign-in.page.ts +291 -0
  177. package/src/examples/whitelabel-demo-modal.component.ts +1094 -0
  178. package/src/examples/whitelabel-demo-modal.service.ts +77 -0
  179. package/src/models/index.ts +7 -0
  180. package/src/models/post.model.ts +41 -0
  181. package/src/pages/community.page.ts +769 -0
  182. package/src/pages/handbook.page.ts +388 -0
  183. package/src/pages/home.page.ts +303 -0
  184. package/src/pages/index.ts +11 -0
  185. package/src/pages/inquiries.example.ts +273 -0
  186. package/src/pages/inquiry-detail.example.css +189 -0
  187. package/src/pages/inquiry-detail.example.ts +415 -0
  188. package/src/pages/mobile-tabs-example.component.ts +179 -0
  189. package/src/pages/post-create.page.ts +311 -0
  190. package/src/pages/post-detail.page.ts +296 -0
  191. package/src/pages/sign-in.page.ts +291 -0
  192. package/src/pages/whitelabel-demo-modal.component.ts +1094 -0
  193. package/src/pages/whitelabel-demo-modal.service.ts +77 -0
  194. package/src/public-api.ts +6 -0
  195. package/src/services/base-modal.service.ts +101 -0
  196. package/src/services/index.ts +11 -0
  197. package/src/services/posts.service.ts +542 -0
  198. package/src/services/tracking-permission.service.ts +88 -0
  199. package/src/services/user.service.ts +60 -0
  200. package/src/services/whitelabel.service.ts +675 -0
  201. package/{styles → src/styles}/ionic.css +25 -0
  202. package/tsconfig.lib.json +17 -0
  203. package/tsconfig.lib.prod.json +9 -0
  204. package/tsconfig.spec.json +13 -0
  205. package/fesm2022/propbinder-mobile-design.mjs +0 -26168
  206. package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
  207. package/index.d.ts +0 -8169
  208. /package/{assets → src/assets}/fonts/Brockmann-Bold.otf +0 -0
  209. /package/{assets → src/assets}/fonts/Brockmann-BoldItalic.otf +0 -0
  210. /package/{assets → src/assets}/fonts/Brockmann-Medium.otf +0 -0
  211. /package/{assets → src/assets}/fonts/Brockmann-MediumItalic.otf +0 -0
  212. /package/{assets → src/assets}/fonts/Brockmann-Regular.otf +0 -0
  213. /package/{assets → src/assets}/fonts/Brockmann-RegularItalic.otf +0 -0
  214. /package/{assets → src/assets}/fonts/Brockmann-SemiBold.otf +0 -0
  215. /package/{assets → src/assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  216. /package/{assets → src/assets}/fonts/Brockmann_desktop_license.pdf +0 -0
  217. /package/{assets → src/assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
  218. /package/{assets → src/assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
  219. /package/{assets → src/assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
  220. /package/{styles → src/components/shared}/mobile-common.css +0 -0
  221. /package/{styles → src/components/shared}/mobile-page-base.css +0 -0
@@ -0,0 +1,164 @@
1
+ import { Component, input, output } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsAvatarComponent } from '@propbinder/design-system';
4
+ import { DsMobileLoaderOverlayComponent } from '../loader-overlay';
5
+
6
+ /**
7
+ * File type for attachment preview
8
+ */
9
+ export type AttachmentFileType = 'image' | 'pdf' | 'doc' | 'docx' | 'xls' | 'xlsx' | 'other';
10
+
11
+ /**
12
+ * Attachment data interface
13
+ */
14
+ export interface AttachmentData {
15
+ id: string;
16
+ src: string;
17
+ type: AttachmentFileType;
18
+ name?: string;
19
+ size?: string;
20
+ isLoading?: boolean;
21
+ }
22
+
23
+ /**
24
+ * DsMobileAttachmentPreviewComponent
25
+ *
26
+ * Reusable component for displaying attachment previews.
27
+ * Supports both image previews and file type indicators (PDF, DOCX, etc.).
28
+ *
29
+ * Features:
30
+ * - Image preview for photos
31
+ * - File type indicator box for documents
32
+ * - Remove button overlay
33
+ * - Consistent 96x96 size
34
+ *
35
+ * @example
36
+ * ```html
37
+ * <!-- Image attachment -->
38
+ * <ds-mobile-attachment-preview
39
+ * [attachment]="{ id: '1', src: 'photo.jpg', type: 'image' }"
40
+ * (remove)="handleRemove($event)">
41
+ * </ds-mobile-attachment-preview>
42
+ *
43
+ * <!-- PDF attachment -->
44
+ * <ds-mobile-attachment-preview
45
+ * [attachment]="{ id: '2', src: 'doc.pdf', type: 'pdf', name: 'Document.pdf' }"
46
+ * (remove)="handleRemove($event)">
47
+ * </ds-mobile-attachment-preview>
48
+ * ```
49
+ */
50
+ @Component({
51
+ selector: 'ds-mobile-attachment-preview',
52
+ standalone: true,
53
+ imports: [CommonModule, DsAvatarComponent, DsMobileLoaderOverlayComponent],
54
+ styleUrls: ['./ds-mobile-attachment-preview.css'],
55
+ template: `
56
+ <div class="attachment-preview" [class.loading]="attachment().isLoading">
57
+ @if (attachment().type === 'image') {
58
+ <!-- Image Preview -->
59
+ <img
60
+ [src]="attachment().src"
61
+ [alt]="attachment().name || 'Attachment'"
62
+ class="preview-image"
63
+ />
64
+ } @else {
65
+ <!-- File Type Preview -->
66
+ <div class="preview-file">
67
+ <div class="file-avatar">
68
+ <ds-avatar
69
+ type="icon"
70
+ [iconName]="getIconName()"
71
+ size="md"
72
+ [class]="'file-' + attachment().type"
73
+ />
74
+ </div>
75
+ <div class="file-info">
76
+ <div class="file-name" [title]="attachment().name || 'Unknown file'">
77
+ {{ attachment().name || 'Unknown file' }}
78
+ </div>
79
+ <div class="file-type-label">{{ getFileTypeLabel() }}</div>
80
+ </div>
81
+ </div>
82
+ }
83
+
84
+ <!-- Loading Overlay -->
85
+ @if (attachment().isLoading) {
86
+ <ds-mobile-loader-overlay [borderRadius]="12" />
87
+ }
88
+
89
+ <!-- Remove Button (hidden when loading) -->
90
+ @if (!attachment().isLoading) {
91
+ <button
92
+ class="remove-btn"
93
+ (mousedown)="handleRemove($event)"
94
+ type="button"
95
+ [attr.aria-label]="'Remove ' + (attachment().name || 'attachment')">
96
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
97
+ <path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
98
+ </svg>
99
+ </button>
100
+ }
101
+ </div>
102
+ `
103
+ })
104
+ export class DsMobileAttachmentPreviewComponent {
105
+ /**
106
+ * Attachment data to display
107
+ */
108
+ attachment = input.required<AttachmentData>();
109
+
110
+ /**
111
+ * Emits when the remove button is clicked
112
+ */
113
+ remove = output<string>();
114
+
115
+ /**
116
+ * Get the file type label (PDF, DOCX, etc.)
117
+ */
118
+ getFileTypeLabel(): string {
119
+ const type = this.attachment().type.toUpperCase();
120
+
121
+ // Map common types to their display labels
122
+ const labelMap: Record<string, string> = {
123
+ 'PDF': 'PDF',
124
+ 'DOC': 'DOC',
125
+ 'DOCX': 'DOCX',
126
+ 'XLS': 'XLS',
127
+ 'XLSX': 'XLSX',
128
+ 'OTHER': 'FILE'
129
+ };
130
+
131
+ return labelMap[type] || type;
132
+ }
133
+
134
+ /**
135
+ * Get the appropriate icon name based on file type
136
+ */
137
+ getIconName(): string {
138
+ const type = this.attachment().type;
139
+
140
+ const iconMap: Record<AttachmentFileType, string> = {
141
+ 'image': 'remixImageLine',
142
+ 'pdf': 'remixFileTextLine',
143
+ 'doc': 'remixFileTextLine',
144
+ 'docx': 'remixFileTextLine',
145
+ 'xls': 'remixFileList3Line',
146
+ 'xlsx': 'remixFileList3Line',
147
+ 'other': 'remixAttachmentLine'
148
+ };
149
+
150
+ return iconMap[type] || 'remixAttachmentLine';
151
+ }
152
+
153
+ /**
154
+ * Handle remove button click
155
+ * Uses mousedown to prevent keyboard from closing
156
+ */
157
+ handleRemove(event?: MouseEvent): void {
158
+ if (event) {
159
+ event.preventDefault();
160
+ event.stopPropagation();
161
+ }
162
+ this.remove.emit(this.attachment().id);
163
+ }
164
+ }
@@ -0,0 +1 @@
1
+ export * from './ds-mobile-attachment-preview';
@@ -0,0 +1,142 @@
1
+ import { Component, Input, computed, ViewEncapsulation, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsAvatarComponent } from '@propbinder/design-system';
4
+ import { DsAppIconComponent, AppIconSize } from '../app-icon/ds-app-icon';
5
+ import { WhitelabelService } from '../../services/whitelabel.service';
6
+
7
+ export type AvatarType = 'initials' | 'photo' | 'icon';
8
+ export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
+ export type BadgePosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
10
+
11
+ /**
12
+ * DsAvatarWithBadgeComponent
13
+ *
14
+ * Displays an avatar with a logomark badge overlay.
15
+ * Useful for showing user avatars with organization branding.
16
+ *
17
+ * @example
18
+ * ```html
19
+ * <ds-avatar-with-badge
20
+ * [type]="'initials'"
21
+ * [initials]="'JD'"
22
+ * [size]="'lg'"
23
+ * [badgePosition]="'bottom-right'"
24
+ * />
25
+ * ```
26
+ */
27
+ @Component({
28
+ selector: 'ds-avatar-with-badge',
29
+ standalone: true,
30
+ imports: [CommonModule, DsAvatarComponent, DsAppIconComponent],
31
+ encapsulation: ViewEncapsulation.Emulated,
32
+ styles: [`
33
+ :host {
34
+ display: inline-block;
35
+ position: relative;
36
+ }
37
+
38
+ .avatar-badge-container {
39
+ position: relative;
40
+ display: inline-block;
41
+ }
42
+
43
+ .avatar-badge {
44
+ position: absolute;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ border-radius: 22%;
49
+ /* White shadow acting as a border */
50
+ box-shadow: 0 0 0 2px var(--color-background-primary, #ffffff);
51
+ /* Set background color for app icon inside */
52
+ --app-icon-background: var(--color-app-icon-surface, #5d5fef);
53
+ }
54
+
55
+ /* Prevent the app icon from stretching */
56
+ .avatar-badge ds-app-icon {
57
+ flex-shrink: 0;
58
+ }
59
+
60
+
61
+ /* Badge positioning */
62
+ .avatar-badge--bottom-right {
63
+ bottom: -4px;
64
+ right: -4px;
65
+ }
66
+
67
+ .avatar-badge--bottom-left {
68
+ bottom: -6px;
69
+ left: -6px;
70
+ }
71
+
72
+ .avatar-badge--top-right {
73
+ top: -6px;
74
+ right: -6px;
75
+ }
76
+
77
+ .avatar-badge--top-left {
78
+ top: -6px;
79
+ left: -6px;
80
+ }
81
+
82
+ /* Badge container sizes - matches the app-icon size inside
83
+ xs/sm/md/lg use 16px (app-icon sm)
84
+ xl uses 32px (app-icon md) */
85
+ .avatar-badge--xs { width: 16px; height: 16px; }
86
+ .avatar-badge--sm { width: 16px; height: 16px; }
87
+ .avatar-badge--md { width: 16px; height: 16px; }
88
+ .avatar-badge--lg { width: 16px; height: 16px; }
89
+ .avatar-badge--xl { width: 24px; height: 24px; }
90
+ `],
91
+ template: `
92
+ <div class="avatar-badge-container">
93
+ <ds-avatar
94
+ [type]="type"
95
+ [size]="size"
96
+ [initials]="initials"
97
+ [src]="src"
98
+ [iconName]="iconName"
99
+ />
100
+
101
+ @if (showBadge) {
102
+ <div [class]="badgeClasses()">
103
+ <ds-app-icon [size]="badgeIconSize()" />
104
+ </div>
105
+ }
106
+ </div>
107
+ `
108
+ })
109
+ export class DsAvatarWithBadgeComponent {
110
+ whitelabelService = inject(WhitelabelService);
111
+
112
+ // Avatar props
113
+ @Input() type: AvatarType = 'initials';
114
+ @Input() size: AvatarSize = 'md';
115
+ @Input() initials: string = '';
116
+ @Input() src: string = '';
117
+ @Input() iconName: string = 'remixUser3Fill';
118
+
119
+ // Badge props
120
+ @Input() showBadge: boolean = true;
121
+ @Input() badgePosition: BadgePosition = 'bottom-right';
122
+
123
+ badgeClasses = computed(() => {
124
+ return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
125
+ });
126
+
127
+ /**
128
+ * Computed badge icon size that scales with avatar size
129
+ * Maps avatar sizes to appropriate app icon sizes
130
+ */
131
+ badgeIconSize = computed((): AppIconSize => {
132
+ const sizeMap: Record<AvatarSize, AppIconSize> = {
133
+ xs: 'sm',
134
+ sm: 'sm',
135
+ md: 'sm',
136
+ lg: 'sm',
137
+ xl: 'md',
138
+ };
139
+ return sizeMap[this.size];
140
+ });
141
+ }
142
+
@@ -0,0 +1,2 @@
1
+ export * from './ds-avatar-with-badge';
2
+
@@ -0,0 +1,71 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsMobileConfirmationSheetComponent } from '../bottom-sheet/ds-mobile-confirmation-sheet';
4
+ import { DsMobileBookingSummaryComponent } from './ds-mobile-booking-summary';
5
+ import { BookingResult } from './ds-mobile-booking-modal';
6
+
7
+ /**
8
+ * DsMobileBookingConfirmationWrapperComponent
9
+ *
10
+ * Wrapper component that uses the generic confirmation sheet
11
+ * specifically for booking confirmations.
12
+ *
13
+ * This component handles the booking-specific formatting and
14
+ * uses content projection to pass the booking summary to the generic sheet.
15
+ */
16
+ @Component({
17
+ selector: 'ds-mobile-booking-confirmation-wrapper',
18
+ standalone: true,
19
+ imports: [
20
+ CommonModule,
21
+ DsMobileConfirmationSheetComponent,
22
+ DsMobileBookingSummaryComponent
23
+ ],
24
+ template: `
25
+ <ds-mobile-confirmation-sheet
26
+ [title]="'Booking accepteret'"
27
+ [message]="confirmationMessage"
28
+ [buttonText]="'Luk'"
29
+ [illustrationVariant]="'confirmation'">
30
+ <ng-template #summary>
31
+ <ds-mobile-booking-summary
32
+ [facilityTitle]="booking.facilityTitle"
33
+ [facilityThumbnail]="facilityThumbnail"
34
+ [date]="formattedDate"
35
+ [time]="formattedTime"
36
+ />
37
+ </ng-template>
38
+ </ds-mobile-confirmation-sheet>
39
+ `
40
+ })
41
+ export class DsMobileBookingConfirmationWrapperComponent {
42
+ @Input() booking!: BookingResult;
43
+ @Input() facilityThumbnail?: string;
44
+
45
+ get confirmationMessage(): string {
46
+ const date = this.booking.selectedDate.fullDate;
47
+ const dateStr = this.formatFullDate(date);
48
+ return `Din booking til den ${dateStr} er bekræftet. Du kan annullere fra startdatoen.`;
49
+ }
50
+
51
+ get formattedDate(): string {
52
+ const { dayName, date, monthName } = this.booking.selectedDate;
53
+ return `${dayName} ${date} ${monthName}`;
54
+ }
55
+
56
+ get formattedTime(): string {
57
+ const { startTime, endTime } = this.booking.selectedTimeSlot;
58
+ return `${startTime} - ${endTime}`;
59
+ }
60
+
61
+ private formatFullDate(date: Date): string {
62
+ const day = date.getDate();
63
+ const monthNames = [
64
+ 'januar', 'februar', 'marts', 'april', 'maj', 'juni',
65
+ 'juli', 'august', 'september', 'oktober', 'november', 'december'
66
+ ];
67
+ const month = monthNames[date.getMonth()];
68
+ const year = date.getFullYear();
69
+ return `${day}. ${month}, ${year}`;
70
+ }
71
+ }
@@ -0,0 +1,121 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { ModalController } from '@ionic/angular/standalone';
3
+ import { BaseModalService } from '../../services/base-modal.service';
4
+ import { DsMobileBookingModalComponent, BookingResult, DateOption, TimeSlot } from './ds-mobile-booking-modal';
5
+ import { DsMobileBookingConfirmationWrapperComponent } from './ds-mobile-booking-confirmation-wrapper';
6
+ import { disableModalShadowPointerEvents } from '../bottom-sheet/modal-shadow-fix';
7
+
8
+ /**
9
+ * DsMobileBookingModalService
10
+ *
11
+ * Service for managing the booking modal flow:
12
+ * 1. Opens the booking modal for date/time selection
13
+ * 2. On confirmation, opens the confirmation bottom sheet
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * constructor(private bookingModalService: DsMobileBookingModalService) {}
18
+ *
19
+ * async openBooking() {
20
+ * await this.bookingModalService.open('facility-1', 'Boremaskinen');
21
+ * }
22
+ * ```
23
+ */
24
+ export interface BookingModalOptions {
25
+ facilityId: string;
26
+ facilityTitle: string;
27
+ facilityThumbnail?: string;
28
+ daysAhead?: number;
29
+ availableDates?: DateOption[];
30
+ availableTimeSlots?: Record<string, TimeSlot[]>;
31
+ labels?: {
32
+ selectFromCalendar?: string;
33
+ confirmBooking?: string;
34
+ };
35
+ }
36
+
37
+ @Injectable({ providedIn: 'root' })
38
+ export class DsMobileBookingModalService extends BaseModalService {
39
+ constructor(modalController: ModalController) {
40
+ super(modalController);
41
+ }
42
+
43
+ /**
44
+ * Open the booking modal for a facility
45
+ *
46
+ * @param options The booking modal options including dynamic data and texts
47
+ * @returns Promise that resolves when the booking flow is complete
48
+ */
49
+ async open(options: BookingModalOptions): Promise<void> {
50
+ const componentProps: any = {
51
+ facilityId: options.facilityId,
52
+ facilityTitle: options.facilityTitle,
53
+ };
54
+
55
+ if (options.daysAhead !== undefined) componentProps.daysAhead = options.daysAhead;
56
+ if (options.availableDates !== undefined) componentProps.availableDates = options.availableDates;
57
+ if (options.availableTimeSlots !== undefined) componentProps.availableTimeSlots = options.availableTimeSlots;
58
+ if (options.labels?.selectFromCalendar) componentProps.selectFromCalendarText = options.labels.selectFromCalendar;
59
+ if (options.labels?.confirmBooking) componentProps.confirmBookingText = options.labels.confirmBooking;
60
+
61
+ const modal = await this.createModal(
62
+ DsMobileBookingModalComponent,
63
+ componentProps,
64
+ { keyboardClose: true }
65
+ );
66
+
67
+ await modal.present();
68
+
69
+ const result = await modal.onWillDismiss<BookingResult>();
70
+
71
+ // If user confirmed the booking, dismiss all modals and show confirmation sheet
72
+ if (result.role === 'confirm' && result.data) {
73
+ // Dismiss all open modals (booking modal + facility detail modal)
74
+ await this.dismissAllModals();
75
+
76
+ // Show confirmation sheet after all modals are dismissed
77
+ await this.openConfirmationSheet(result.data, options.facilityThumbnail);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Dismiss all open modals
83
+ */
84
+ private async dismissAllModals(): Promise<void> {
85
+ let topModal = await this.modalController.getTop();
86
+ while (topModal) {
87
+ await topModal.dismiss();
88
+ topModal = await this.modalController.getTop();
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Open the confirmation bottom sheet after successful booking
94
+ *
95
+ * @param booking The booking result data
96
+ * @param facilityThumbnail Optional thumbnail image URL
97
+ * @returns Promise that resolves when the sheet is dismissed
98
+ */
99
+ private async openConfirmationSheet(
100
+ booking: BookingResult,
101
+ facilityThumbnail?: string
102
+ ): Promise<void> {
103
+ const sheet = await this.modalController.create({
104
+ component: DsMobileBookingConfirmationWrapperComponent,
105
+ componentProps: {
106
+ booking,
107
+ facilityThumbnail
108
+ },
109
+ breakpoints: [0, 1],
110
+ initialBreakpoint: 1,
111
+ handle: true,
112
+ cssClass: ['ds-bottom-sheet', 'auto-height'],
113
+ backdropDismiss: true,
114
+ showBackdrop: true,
115
+ mode: 'ios'
116
+ });
117
+
118
+ await sheet.present();
119
+ disableModalShadowPointerEvents(sheet);
120
+ }
121
+ }