@propbinder/mobile-design 0.2.47 → 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 -26136
  206. package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
  207. package/index.d.ts +0 -8154
  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,424 @@
1
+ import { Component, signal, Input, OnInit, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ModalController } from '@ionic/angular/standalone';
4
+ import { DsMobileHandbookFolderMiniComponent } from '../handbook-folder/ds-mobile-handbook-folder-mini';
5
+ import { DsMobileCardInlineFileComponent } from '../card-inline-file';
6
+ import { DsMobileCardInlineContactComponent } from '../card-inline-contact';
7
+ import { DsMobileSwiperComponent } from '../swiper';
8
+ import { DsMobileModalBaseComponent } from '../modal-base/ds-mobile-modal-base';
9
+ import { DsMobileSectionComponent } from '../section';
10
+ import { DsMobileActionsBottomSheetComponent, ActionGroup } from '../bottom-sheet/ds-mobile-actions-bottom-sheet';
11
+ import { disableModalShadowPointerEvents } from '../bottom-sheet/modal-shadow-fix';
12
+
13
+ /**
14
+ * Handbook detail data interface
15
+ *
16
+ * Represents a handbook category/folder with its sections/items.
17
+ * Use this interface to map your API response data to the component.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const handbookData: HandbookDetailData = {
22
+ * title: 'Access Systems',
23
+ * variant: 'blue',
24
+ * iconName: 'remixKey2Line',
25
+ * itemCount: 5,
26
+ * items: [...]
27
+ * };
28
+ * ```
29
+ */
30
+ export interface HandbookDetailData {
31
+ /** Category/folder title */
32
+ title: string;
33
+ /** Color variant: 'success', 'warning', 'destructive', 'blue', 'light-purple', 'pink', 'salmon-orange', 'orange', 'lime-green', 'grey' */
34
+ variant: string;
35
+ /** Optional custom hex color */
36
+ customColor?: string;
37
+ /** Icon name from design system (e.g., 'remixKey2Line', 'remixLightbulbLine') */
38
+ iconName: string;
39
+ /** Total number of items/sections in this category */
40
+ itemCount: number;
41
+ /** Array of handbook sections/items */
42
+ items?: HandbookItem[];
43
+ }
44
+
45
+ /**
46
+ * Handbook section/item interface
47
+ *
48
+ * Represents a single section within a handbook category.
49
+ * Each section can have a title, description, images, contacts, and attachments.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const item: HandbookItem = {
54
+ * title: 'Key Box Entrance',
55
+ * description: 'Key box located at main entrance...',
56
+ * images: ['https://api.example.com/images/keybox.jpg'],
57
+ * contacts: [{ name: 'Security Co', initials: 'S', phoneNumber: '+45 12345678' }],
58
+ * attachments: [{ name: 'Manual.pdf', type: 'pdf', url: 'https://...' }]
59
+ * };
60
+ * ```
61
+ */
62
+ export interface HandbookItem {
63
+ /** Section title */
64
+ title: string;
65
+ /** Optional section description */
66
+ description?: string;
67
+ /** Array of image URLs to display */
68
+ images?: string[];
69
+ /** Array of file attachments */
70
+ attachments?: AttachmentItem[];
71
+ /** Array of contact information */
72
+ contacts?: ContactItem[];
73
+ }
74
+
75
+ /**
76
+ * File attachment interface
77
+ *
78
+ * Represents a file attachment in a handbook section.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const attachment: AttachmentItem = {
83
+ * name: 'Installation_Manual.pdf',
84
+ * type: 'pdf',
85
+ * url: 'https://api.example.com/files/manual.pdf'
86
+ * };
87
+ * ```
88
+ */
89
+ export interface AttachmentItem {
90
+ /** File name to display */
91
+ name: string;
92
+ /** File type: 'pdf', 'doc', etc. (used for icon display) */
93
+ type?: string;
94
+ /** Optional URL for downloading/opening the file */
95
+ url?: string;
96
+ }
97
+
98
+ /**
99
+ * Contact information interface
100
+ *
101
+ * Represents contact information in a handbook section.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const contact: ContactItem = {
106
+ * name: 'Propbinder ApS',
107
+ * initials: 'P',
108
+ * contactPerson: 'John Doe',
109
+ * phoneNumber: '+45 12345678',
110
+ * email: 'support@propbinder.dk'
111
+ * };
112
+ * ```
113
+ */
114
+ export interface ContactItem {
115
+ /** Company or contact name */
116
+ name: string;
117
+ /** Initials for avatar (1-2 letters) */
118
+ initials: string;
119
+ /** Optional contact person name */
120
+ contactPerson?: string;
121
+ /** Optional phone number */
122
+ phoneNumber?: string;
123
+ /** Optional email address */
124
+ email?: string;
125
+ }
126
+
127
+ /**
128
+ * DsMobileHandbookDetailModalComponent
129
+ *
130
+ * Modal wrapper for displaying handbook folder details.
131
+ *
132
+ * Features:
133
+ * - Folder content display
134
+ * - Items list with descriptions
135
+ * - Images and attachments
136
+ * - Contact information
137
+ * - Native modal controls (close, swipe down)
138
+ * - Safe area support
139
+ *
140
+ * This component is typically not used directly - use DsMobileHandbookDetailModalService instead.
141
+ */
142
+ @Component({
143
+ selector: 'ds-mobile-handbook-detail-modal',
144
+ standalone: true,
145
+ imports: [
146
+ CommonModule,
147
+ DsMobileHandbookFolderMiniComponent,
148
+ DsMobileCardInlineFileComponent,
149
+ DsMobileCardInlineContactComponent,
150
+ DsMobileSwiperComponent,
151
+ DsMobileModalBaseComponent,
152
+ DsMobileSectionComponent,
153
+ ],
154
+ styleUrls: ['./ds-mobile-handbook-detail-modal.css'],
155
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
156
+ template: `
157
+ <ds-mobile-modal-base [loading]="loading" [error]="error" [headerTitle]="handbook().title" [headerMeta]="handbook().itemCount + ' emner'" closeButtonLabel="Luk">
158
+ <!-- Header Folder Icon -->
159
+ <ds-mobile-handbook-folder-mini header-leading [variant]="handbook().variant" [customColor]="handbook().customColor" [iconName]="handbook().iconName">
160
+ </ds-mobile-handbook-folder-mini>
161
+
162
+ <!-- Content (main content slot) -->
163
+ @if (handbook().items && handbook().items!.length > 0) {
164
+ @for (item of getDisplayItems(); track item.title + $index) {
165
+ <ds-mobile-section [headline]="item.title || ''" contentGap="20px">
166
+ @if (item.description) {
167
+ <div class="item-description" [innerHTML]="item.description"></div>
168
+ }
169
+
170
+ <!-- Images -->
171
+ @if (item.images && item.images.length > 0) {
172
+ <ds-mobile-swiper [slideWidth]="item.images.length === 1 ? '100%' : '85%'" [gap]="16">
173
+ @for (image of item.images; track image) {
174
+ <div class="swiper-slide">
175
+ <img [src]="image" [alt]="item.title" class="item-image" />
176
+ </div>
177
+ }
178
+ </ds-mobile-swiper>
179
+ }
180
+
181
+ <!-- Contacts and Attachments (grouped) -->
182
+ @if ((item.contacts && item.contacts.length > 0) || (item.attachments && item.attachments.length > 0)) {
183
+ <div class="contacts-attachments-group">
184
+ @if (item.contacts && item.contacts.length > 0) {
185
+ @for (contact of item.contacts; track contact.name) {
186
+ <ds-mobile-card-inline-contact
187
+ [name]="contact.name"
188
+ [initials]="contact.initials"
189
+ [contactPerson]="contact.contactPerson || ''"
190
+ [phoneNumber]="contact.phoneNumber || ''"
191
+ [clickable]="true"
192
+ (contactClick)="handleContactClick(contact)"
193
+ >
194
+ </ds-mobile-card-inline-contact>
195
+ }
196
+ }
197
+
198
+ @if (item.attachments && item.attachments.length > 0) {
199
+ @for (attachment of item.attachments; track attachment.name) {
200
+ <ds-mobile-card-inline-file
201
+ [fileName]="attachment.name"
202
+ [variant]="attachment.type === 'pdf' ? 'pdf' : 'doc'"
203
+ [fileUrl]="attachment.url"
204
+ (fileClick)="handleAttachmentClick(attachment)"
205
+ >
206
+ </ds-mobile-card-inline-file>
207
+ }
208
+ }
209
+ </div>
210
+ }
211
+ </ds-mobile-section>
212
+ }
213
+ } @else {
214
+ <!-- Empty State -->
215
+ <ds-mobile-section>
216
+ <div class="handbook-empty-state">
217
+ <img src="/Assets/Empty%20state-chat.png" alt="No items yet" class="empty-state-image" />
218
+ <h3 class="empty-state-title">No items yet</h3>
219
+ <p class="empty-state-description">This folder is empty</p>
220
+ </div>
221
+ </ds-mobile-section>
222
+ }
223
+ </ds-mobile-modal-base>
224
+ `,
225
+ })
226
+ export class DsMobileHandbookDetailModalComponent implements OnInit {
227
+ // Handbook data passed from service
228
+ @Input() handbookData!: HandbookDetailData;
229
+
230
+ /**
231
+ * Loading state - when true, shows loading indicator
232
+ */
233
+ @Input() loading: boolean = false;
234
+
235
+ /**
236
+ * Error state - when set, shows error message
237
+ */
238
+ @Input() error?: string;
239
+
240
+ // Signal for reactive handbook data
241
+ handbook = signal<HandbookDetailData>({
242
+ title: '',
243
+ variant: 'light-purple',
244
+ iconName: 'remixFolder3Line',
245
+ itemCount: 0,
246
+ items: [],
247
+ });
248
+
249
+ constructor(private modalController: ModalController) {}
250
+
251
+ ngOnInit(): void {
252
+ // Initialize handbook data from input
253
+ if (this.handbookData) {
254
+ this.handbook.set(this.handbookData);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Split handbook items to enforce content structure rules:
260
+ * - Never mix photos and documents (attachments) in the same section
261
+ * - Never mix contact persons and attachments together in the same section
262
+ *
263
+ * This method splits items that violate these rules into multiple display items.
264
+ * Each resulting item will have only compatible content types.
265
+ */
266
+ splitItemsByContentRules(item: HandbookItem): HandbookItem[] {
267
+ const displayItems: HandbookItem[] = [];
268
+
269
+ const hasImages = item.images && item.images.length > 0;
270
+ const hasContacts = item.contacts && item.contacts.length > 0;
271
+ const hasAttachments = item.attachments && item.attachments.length > 0;
272
+ const hasContent = hasContacts || hasAttachments;
273
+
274
+ // Case 1: Images AND other content -> Split Pictures from (Contacts + Attachments)
275
+ if (hasImages && hasContent) {
276
+ // Part 1: Images
277
+ displayItems.push({
278
+ title: item.title,
279
+ description: item.description,
280
+ images: item.images,
281
+ });
282
+
283
+ // Part 2: Contacts + Attachments
284
+ displayItems.push({
285
+ title: '', // No title for the continuation
286
+ description: undefined,
287
+ contacts: item.contacts,
288
+ attachments: item.attachments,
289
+ });
290
+
291
+ return displayItems;
292
+ }
293
+
294
+ // Case 2: No splitting needed (Images only, or No Images)
295
+ // This allows Contacts and Attachments to coexist in one item
296
+ return [item];
297
+ }
298
+
299
+ /**
300
+ * Get all display items with enforced content structure rules applied
301
+ */
302
+ getDisplayItems(): HandbookItem[] {
303
+ const items = this.handbook().items || [];
304
+ const displayItems: HandbookItem[] = [];
305
+
306
+ for (const item of items) {
307
+ const splitItems = this.splitItemsByContentRules(item);
308
+ displayItems.push(...splitItems);
309
+ }
310
+
311
+ return displayItems;
312
+ }
313
+
314
+ /**
315
+ * Handle contact click - shows bottom sheet with call and copy actions
316
+ */
317
+ async handleContactClick(contact: ContactItem): Promise<void> {
318
+ // Only show actions if there's a phone number
319
+ if (!contact.phoneNumber) {
320
+ return;
321
+ }
322
+
323
+ const actionGroups: ActionGroup[] = [
324
+ {
325
+ actions: [
326
+ {
327
+ action: 'call',
328
+ title: 'Ring',
329
+ icon: 'remixPhoneLine',
330
+ },
331
+ {
332
+ action: 'copy',
333
+ title: 'Kopier nummer',
334
+ icon: 'remixFileCopyLine',
335
+ },
336
+ ],
337
+ },
338
+ ];
339
+
340
+ const sheet = await this.modalController.create({
341
+ component: DsMobileActionsBottomSheetComponent,
342
+ componentProps: {
343
+ customActionGroups: actionGroups,
344
+ },
345
+ breakpoints: [0, 1],
346
+ initialBreakpoint: 1,
347
+ handle: true,
348
+ cssClass: ['ds-bottom-sheet', 'auto-height'],
349
+ });
350
+
351
+ await sheet.present();
352
+ disableModalShadowPointerEvents(sheet);
353
+
354
+ const result = await sheet.onWillDismiss();
355
+ if (result.data?.action) {
356
+ this.handleContactAction(result.data.action, contact);
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Handle the selected contact action
362
+ */
363
+ private async handleContactAction(action: string, contact: ContactItem): Promise<void> {
364
+ switch (action) {
365
+ case 'call':
366
+ // Open phone dialer with the contact's phone number
367
+ if (contact.phoneNumber) {
368
+ window.location.href = `tel:${contact.phoneNumber}`;
369
+ }
370
+ break;
371
+
372
+ case 'copy':
373
+ // Copy phone number to clipboard
374
+ if (contact.phoneNumber) {
375
+ try {
376
+ // Try modern Clipboard API first
377
+ await navigator.clipboard.writeText(contact.phoneNumber);
378
+ console.log('Phone number copied to clipboard:', contact.phoneNumber);
379
+ // TODO: Show toast notification if you have a toast service
380
+ } catch (err) {
381
+ console.error('Failed to copy phone number:', err);
382
+ // Fallback: Try older execCommand method
383
+ this.fallbackCopyToClipboard(contact.phoneNumber);
384
+ }
385
+ }
386
+ break;
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Fallback method for copying text to clipboard (for older browsers/webviews)
392
+ */
393
+ private fallbackCopyToClipboard(text: string): void {
394
+ const textArea = document.createElement('textarea');
395
+ textArea.value = text;
396
+ textArea.style.position = 'fixed';
397
+ textArea.style.left = '-999999px';
398
+ textArea.style.top = '-999999px';
399
+ document.body.appendChild(textArea);
400
+ textArea.focus();
401
+ textArea.select();
402
+
403
+ try {
404
+ const successful = document.execCommand('copy');
405
+ if (successful) {
406
+ console.log('Phone number copied to clipboard (fallback):', text);
407
+ } else {
408
+ console.error('Fallback copy failed');
409
+ }
410
+ } catch (err) {
411
+ console.error('Fallback copy error:', err);
412
+ }
413
+
414
+ document.body.removeChild(textArea);
415
+ }
416
+
417
+ /**
418
+ * Handle attachment click
419
+ */
420
+ handleAttachmentClick(attachment: AttachmentItem): void {
421
+ console.log('Attachment clicked:', attachment);
422
+ // Attachment action is now handled by DsMobileCardInlineFileComponent via fileUrl input
423
+ }
424
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ds-mobile-handbook-detail-modal';
2
+ export * from './ds-mobile-handbook-detail-modal.service';
3
+
@@ -0,0 +1,175 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsIconComponent } from '@propbinder/design-system';
4
+
5
+ /**
6
+ * DsMobileHandbookFolderMiniComponent
7
+ *
8
+ * A minimized folder icon component for use in headers and small spaces.
9
+ * Simplified version without animations or page sheets - just folder and icon.
10
+ *
11
+ * @example
12
+ * ```html
13
+ * <ds-mobile-handbook-folder-mini
14
+ * [variant]="'pink'"
15
+ * [iconName]="'remixLightbulbLine'">
16
+ * </ds-mobile-handbook-folder-mini>
17
+ * ```
18
+ */
19
+ @Component({
20
+ selector: 'ds-mobile-handbook-folder-mini',
21
+ standalone: true,
22
+ imports: [CommonModule, DsIconComponent],
23
+ styles: [
24
+ `
25
+ :host {
26
+ display: inline-block;
27
+ width: 32px;
28
+ height: 32px;
29
+ flex-shrink: 0;
30
+
31
+ /* Native support for extended API colors */
32
+ --color-red-base: #dc3545;
33
+ --color-red-strong: #ae1d3b;
34
+ --color-green-base: #28a745;
35
+ --color-green-strong: #058057;
36
+ --color-yellow-base: #ffc107;
37
+ --color-yellow-strong: #e4b200;
38
+ --color-purple-base: #6f42c1;
39
+ --color-purple-strong: #4204c5;
40
+ --color-indigo-base: #6610f2;
41
+ --color-indigo-strong: #a527a2;
42
+ --color-lime-base: #82c91e;
43
+ --color-lime-strong: #58a503;
44
+ --color-teal-base: #20c997;
45
+ --color-teal-strong: #0ca678;
46
+ --color-cyan-base: #17a2b8;
47
+ --color-cyan-strong: #1098ad;
48
+ --color-brown-base: #795548;
49
+ --color-brown-strong: #5c4033;
50
+ --color-light-blue-base: #add8e6;
51
+ --color-light-blue-strong: #87ceeb;
52
+ --color-light-green-base: #90ee90;
53
+ --color-light-green-strong: #32cd32;
54
+ --color-coral-base: #f08080;
55
+ --color-coral-strong: #cd5c5c;
56
+ --color-salmon-base: #ffa07a;
57
+ --color-salmon-strong: #fa8072;
58
+ --color-seagreen-base: #20b2aa;
59
+ --color-seagreen-strong: #2e8b57;
60
+ }
61
+
62
+ .mini-folder-container {
63
+ position: relative;
64
+ width: 100%;
65
+ height: 100%;
66
+ display: flex;
67
+ flex-direction: column;
68
+ }
69
+
70
+ .mini-folder-tab {
71
+ width: 50%;
72
+ height: auto;
73
+ display: block;
74
+ }
75
+
76
+ .mini-folder-back {
77
+ height: 28px;
78
+ border-radius: 0px 4px 4px 4px;
79
+ position: relative;
80
+ margin-top: -1px;
81
+ }
82
+
83
+ .mini-folder-front {
84
+ position: absolute;
85
+ bottom: 0;
86
+ left: 0;
87
+ right: 0;
88
+ height: 24px;
89
+ border-radius: 4px;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ z-index: 2;
94
+ box-shadow:
95
+ inset 0 8px 8px rgba(255, 255, 255, 0.2),
96
+ inset 0 0.5px 0.5px rgba(255, 255, 255, 0.3);
97
+ }
98
+ `,
99
+ ],
100
+ template: `
101
+ <div class="mini-folder-container">
102
+ <!-- Folder Tab SVG -->
103
+ <svg class="mini-folder-tab" width="101" height="24" viewBox="0 0 101 24" fill="none" xmlns="http://www.w3.org/2000/svg">
104
+ <path
105
+ d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
106
+ [attr.fill]="customColor || getColorVar('strong')"
107
+ />
108
+ </svg>
109
+
110
+ <!-- Folder Back -->
111
+ <div class="mini-folder-back" [style.background-color]="customColor || getColorVar('strong')">
112
+ <!-- Folder Front -->
113
+ <div class="mini-folder-front" [style.background-color]="customColor || getColorVar('base')">
114
+ <ds-icon [name]="iconName" [size]="'14px'" [style.color]="'white'" />
115
+ </div>
116
+ </div>
117
+ </div>
118
+ `,
119
+ })
120
+ export class DsMobileHandbookFolderMiniComponent {
121
+ /**
122
+ * Color variant for the folder
123
+ * Available variants: success, warning, destructive, blue, light-purple, pink, salmon-orange, orange, lime-green, grey
124
+ */
125
+ @Input() variant: string = 'light-purple';
126
+
127
+ /**
128
+ * Optional custom hex color for the folder.
129
+ * If provided, overrides the variant color.
130
+ */
131
+ @Input() customColor?: string;
132
+
133
+ /**
134
+ * Icon name from the design system icon library
135
+ */
136
+ @Input() iconName: string = 'remixFolder3Line';
137
+
138
+ /**
139
+ * Get the CSS variable name for the color variant
140
+ */
141
+ getColorVar(suffix: 'base' | 'strong'): string {
142
+ const variantMap: Record<string, string> = {
143
+ // Core design system variants
144
+ success: 'success',
145
+ warning: 'warning',
146
+ destructive: 'destructive',
147
+ blue: 'blue',
148
+ 'light-purple': 'light-purple',
149
+ pink: 'pink',
150
+ 'salmon-orange': 'salmon-orange',
151
+ orange: 'orange',
152
+ 'lime-green': 'lime-green',
153
+ grey: 'grey',
154
+
155
+ // Extended API colors mapping to their native exact counterparts now
156
+ red: 'red',
157
+ green: 'green',
158
+ yellow: 'yellow',
159
+ purple: 'purple',
160
+ indigo: 'indigo',
161
+ lime: 'lime',
162
+ teal: 'teal',
163
+ cyan: 'cyan',
164
+ brown: 'brown',
165
+ 'light-blue': 'light-blue',
166
+ 'light-green': 'light-green',
167
+ coral: 'coral',
168
+ salmon: 'salmon',
169
+ seagreen: 'seagreen',
170
+ };
171
+
172
+ const colorName = variantMap[this.variant] || 'light-purple';
173
+ return `var(--color-${colorName}-${suffix})`;
174
+ }
175
+ }