@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,460 +0,0 @@
1
- import { Component, signal, computed, input, CUSTOM_ELEMENTS_SCHEMA, OnInit, PLATFORM_ID, inject } from '@angular/core';
2
- import { CommonModule, isPlatformBrowser } from '@angular/common';
3
- import { FormsModule } from '@angular/forms';
4
- import { ModalController, IonPicker, IonPickerColumn, IonPickerColumnOption } from '@ionic/angular/standalone';
5
- import { DsMobileDropdownComponent } from '../../dropdown';
6
- import { DsInputTimeComponent, DsLabelComponent } from '@propbinder/design-system';
7
- import { DsMobileBottomSheetWrapperComponent } from '../../bottom-sheet/ds-mobile-bottom-sheet-wrapper';
8
- import { DsMobileBottomSheetHeaderComponent } from '../../bottom-sheet/ds-mobile-bottom-sheet-header';
9
-
10
- export interface WhenCanBookData {
11
- days: string[];
12
- timeRange: { start: string; end: string };
13
- duration: string;
14
- }
15
-
16
- /**
17
- * DsMobileWhenCanBookSheetComponent
18
- *
19
- * Bottom sheet for selecting when a facility can be booked (days, time range, duration).
20
- * "Vælg selv" opens a drum-roll IonPopover on mobile, or inline number inputs on desktop.
21
- */
22
- @Component({
23
- selector: 'ds-mobile-when-can-book-sheet',
24
- standalone: true,
25
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
26
- imports: [
27
- CommonModule,
28
- FormsModule,
29
- DsInputTimeComponent,
30
- DsLabelComponent,
31
- DsMobileBottomSheetWrapperComponent,
32
- DsMobileBottomSheetHeaderComponent,
33
- DsMobileDropdownComponent,
34
- IonPicker,
35
- IonPickerColumn,
36
- IonPickerColumnOption,
37
- ],
38
- template: `
39
- <ds-mobile-bottom-sheet-wrapper>
40
- <!-- Header with back and done buttons -->
41
- <ds-mobile-bottom-sheet-header
42
- title="Kan bookes"
43
- leftButtonLabel="Annuller"
44
- rightButtonLabel="Gem"
45
- [rightButtonDisabled]="!isValid()"
46
- (leftButtonClick)="dismiss()"
47
- (rightButtonClick)="confirmSelection()">
48
- </ds-mobile-bottom-sheet-header>
49
-
50
- <div class="form-content">
51
- <!-- Day chips -->
52
- <div class="section">
53
- <ds-label size="md" className="form-section-label">Kan bookes</ds-label>
54
- <div class="day-chips">
55
- @for (day of days; track day) {
56
- <button
57
- type="button"
58
- class="day-chip"
59
- [class.active]="selectedDays().has(day)"
60
- (click)="toggleDay(day)">
61
- {{ day }}
62
- </button>
63
- }
64
- </div>
65
- </div>
66
-
67
- <!-- Time range -->
68
- <div class="section">
69
- <ds-label size="md" className="form-section-label">I tidsrummet</ds-label>
70
- <div class="time-inputs">
71
- <ds-input-time size="lg" [(ngModel)]="startTime"></ds-input-time>
72
- <span class="separator">til</span>
73
- <ds-input-time size="lg" [(ngModel)]="endTime"></ds-input-time>
74
- </div>
75
- </div>
76
-
77
- <!-- Duration chips -->
78
- <div class="section">
79
- <ds-label size="md" className="form-section-label">Varighed</ds-label>
80
- <div class="duration-chips">
81
- @for (duration of durations; track duration.value) {
82
- <button
83
- type="button"
84
- class="duration-chip"
85
- [id]="duration.value === 'Vælg selv' ? 'vaelg-selv-chip' : null"
86
- [class.active]="selectedDuration() === duration.value"
87
- (click)="selectDuration(duration.value)">
88
- @if (duration.value === 'Vælg selv' && selectedDuration() === 'Vælg selv') {
89
- {{ customDurationLabel() || 'Vælg selv' }}
90
- } @else {
91
- {{ duration.label }}
92
- }
93
- </button>
94
- }
95
- </div>
96
-
97
- <!-- Desktop: inline inputs shown when "Vælg selv" is active -->
98
- @if (showInlineInputs()) {
99
- <div class="custom-duration-inputs">
100
- <input
101
- type="number"
102
- class="duration-number-input"
103
- min="0"
104
- [ngModel]="customHours()"
105
- (ngModelChange)="customHours.set(+$event)"
106
- (blur)="normalize()"
107
- />
108
- <span class="duration-unit">timer</span>
109
- <input
110
- type="number"
111
- class="duration-number-input"
112
- min="0"
113
- max="59"
114
- [ngModel]="customMinutes()"
115
- (ngModelChange)="customMinutes.set(+$event)"
116
- (blur)="normalize()"
117
- />
118
- <span class="duration-unit">min</span>
119
- </div>
120
- }
121
-
122
- <!-- Mobile: drum-roll popover anchored to the "Vælg selv" chip -->
123
- @if (!isDesktop()) {
124
- <ds-mobile-dropdown
125
- trigger="vaelg-selv-chip"
126
- [isOpen]="pickerOpen()"
127
- maxWidth="240px"
128
- position="above"
129
- (closed)="onPickerDismiss()">
130
- <ng-template #customContent>
131
- <ion-picker style="--background: transparent; --fade-background-rgb: transparent; --highlight-background: rgba(0, 0, 0, 0.05); --highlight-border-radius: 9999px;">
132
- <ion-picker-column
133
- [value]="customHours()"
134
- style="--padding-start: 4px; --padding-end: 4px;"
135
- (ionChange)="customHours.set(+($event.detail.value ?? customHours()))">
136
- <div slot="suffix" class="picker-suffix">t</div>
137
- @for (h of hours; track h) {
138
- <ion-picker-column-option [value]="h">{{ h }}</ion-picker-column-option>
139
- }
140
- </ion-picker-column>
141
- <ion-picker-column
142
- [value]="customMinutes()"
143
- style="--padding-start: 4px; --padding-end: 4px;"
144
- (ionChange)="customMinutes.set(+($event.detail.value ?? customMinutes()))">
145
- <div slot="suffix" class="picker-suffix">min</div>
146
- @for (m of minuteOptions; track m) {
147
- <ion-picker-column-option [value]="m">{{ m }}</ion-picker-column-option>
148
- }
149
- </ion-picker-column>
150
- </ion-picker>
151
- </ng-template>
152
- </ds-mobile-dropdown>
153
- }
154
- </div>
155
- </div>
156
- </ds-mobile-bottom-sheet-wrapper>
157
- `,
158
- styles: [`
159
- .form-content {
160
- padding: 16px;
161
- }
162
-
163
- .section {
164
- display: flex;
165
- flex-direction: column;
166
- gap: 8px;
167
- margin-bottom: 24px;
168
- }
169
-
170
- .section:last-child {
171
- margin-bottom: 0;
172
- }
173
-
174
- /* Label spacing */
175
- ::ng-deep ds-label.form-section-label {
176
- display: block;
177
- margin-bottom: 12px;
178
- }
179
-
180
- ::ng-deep .form-section-label {
181
- font-weight: 500;
182
- }
183
-
184
- /* Day chips */
185
- .day-chips {
186
- display: flex;
187
- gap: 8px;
188
- flex-wrap: wrap;
189
- width: 100%;
190
- }
191
-
192
- .day-chip {
193
- flex: none;
194
- width: 64px;
195
- padding: 10px 16px;
196
- border: 1px solid var(--border-color-default, #d1d5db);
197
- border-radius: 8px;
198
- background: transparent;
199
- color: var(--color-text-primary);
200
- font-size: 14px;
201
- font-weight: 500;
202
- cursor: pointer;
203
- transition: all 0.2s ease;
204
- outline: none;
205
- }
206
-
207
- .day-chip:hover {
208
- background: var(--color-bg-secondary);
209
- }
210
-
211
- .day-chip.active {
212
- background: var(--color-accent);
213
- color: white;
214
- border-color: var(--color-accent);
215
- }
216
-
217
- /* Time inputs */
218
- .time-inputs {
219
- display: flex;
220
- align-items: center;
221
- gap: 12px;
222
- }
223
-
224
- .separator {
225
- color: var(--color-text-secondary);
226
- font-size: 14px;
227
- }
228
-
229
- /* Duration chips */
230
- .duration-chips {
231
- display: flex;
232
- gap: 8px;
233
- flex-wrap: wrap;
234
- }
235
-
236
- .duration-chip {
237
- padding: 10px 16px;
238
- border: 1px solid var(--border-color-default, #d1d5db);
239
- border-radius: 8px;
240
- background: transparent;
241
- color: var(--color-text-primary);
242
- font-size: 14px;
243
- font-weight: 500;
244
- cursor: pointer;
245
- transition: all 0.2s ease;
246
- outline: none;
247
- }
248
-
249
- .duration-chip:hover {
250
- background: var(--color-bg-secondary);
251
- }
252
-
253
- .duration-chip.active {
254
- background: var(--color-accent);
255
- color: white;
256
- border-color: var(--color-accent);
257
- }
258
-
259
- /* Desktop inline custom duration inputs */
260
- .custom-duration-inputs {
261
- display: flex;
262
- align-items: center;
263
- gap: 8px;
264
- margin-top: 12px;
265
- animation: fadeSlideIn 0.15s ease;
266
- }
267
-
268
- @keyframes fadeSlideIn {
269
- from { opacity: 0; transform: translateY(-4px); }
270
- to { opacity: 1; transform: translateY(0); }
271
- }
272
-
273
- .duration-number-input {
274
- width: 64px;
275
- padding: 8px 10px;
276
- border: 1px solid var(--border-color-default, #d1d5db);
277
- border-radius: 8px;
278
- background: transparent;
279
- color: var(--color-text-primary);
280
- font-size: 14px;
281
- font-weight: 500;
282
- text-align: center;
283
- outline: none;
284
- transition: border-color 0.2s ease;
285
- }
286
-
287
- .duration-number-input:focus {
288
- border-color: var(--color-accent);
289
- }
290
-
291
- /* Remove browser spin buttons */
292
- .duration-number-input::-webkit-inner-spin-button,
293
- .duration-number-input::-webkit-outer-spin-button {
294
- -webkit-appearance: none;
295
- margin: 0;
296
- }
297
- .duration-number-input[type=number] {
298
- -moz-appearance: textfield;
299
- }
300
-
301
- .duration-unit {
302
- color: var(--color-text-secondary);
303
- font-size: 14px;
304
- }
305
-
306
- .picker-suffix {
307
- margin-left: -24px;
308
- font-size: 13px;
309
- color: var(--color-text-secondary);
310
- }
311
-
312
- `]
313
- })
314
- export class DsMobileWhenCanBookSheetComponent implements OnInit {
315
- private platformId = inject(PLATFORM_ID);
316
-
317
- // Platform
318
- isDesktop = signal<boolean>(false);
319
-
320
- // State
321
- selectedDays = signal<Set<string>>(new Set(['Fre', 'Lør', 'Søn']));
322
- startTime = signal('09:00');
323
- endTime = signal('17:30');
324
- selectedDuration = signal('1 time');
325
-
326
- /**
327
- * Maximum number of days available in the "Vælg selv" picker/inputs.
328
- * Defaults to 30 (one full month). Override via componentProps when opening the sheet.
329
- */
330
- maxDays = input<number>(30);
331
-
332
- // Custom duration state
333
- pickerOpen = signal<boolean>(false);
334
- customDays = signal<number>(0);
335
- customHours = signal<number>(1);
336
- customMinutes = signal<number>(0);
337
-
338
- customDurationLabel = computed(() => {
339
- const d = this.customDays();
340
- const h = this.customHours();
341
- const m = this.customMinutes();
342
- const parts: string[] = [];
343
- if (d > 0) parts.push(`${d} dage`);
344
- if (h > 0) parts.push(`${h}t`);
345
- if (m > 0) parts.push(`${m}min`);
346
- return parts.join(' ');
347
- });
348
-
349
- showInlineInputs = computed(() =>
350
- this.selectedDuration() === 'Vælg selv' && this.isDesktop()
351
- );
352
-
353
- // Options
354
- days = ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn'];
355
- durations = [
356
- { value: '30 min', label: '30 min' },
357
- { value: '1 time', label: '1 time' },
358
- { value: '2 timer', label: '2 timer' },
359
- { value: 'Hele dagen', label: 'Hele dagen' },
360
- { value: 'Vælg selv', label: 'Vælg selv' }
361
- ];
362
- daysOptions = computed(() => Array.from({ length: this.maxDays() + 1 }, (_, i) => i));
363
- hours = Array.from({ length: 24 }, (_, i) => i);
364
- minuteOptions = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
365
-
366
- // Validation
367
- isValid = computed(() => {
368
- if (this.selectedDuration() === 'Vælg selv' && this.customDurationLabel() === '') {
369
- return false;
370
- }
371
- return this.selectedDays().size > 0 &&
372
- this.startTime() &&
373
- this.endTime();
374
- });
375
-
376
- constructor(private modalController: ModalController) {}
377
-
378
- ngOnInit(): void {
379
- if (isPlatformBrowser(this.platformId)) {
380
- this.isDesktop.set(window.innerWidth >= 768);
381
- window.addEventListener('resize', () => {
382
- this.isDesktop.set(window.innerWidth >= 768);
383
- });
384
- }
385
- }
386
-
387
- /**
388
- * Handle duration chip selection.
389
- * On mobile, opening "Vælg selv" triggers the IonPopover via its trigger id.
390
- * On desktop, it shows the inline inputs.
391
- */
392
- selectDuration(value: string): void {
393
- this.selectedDuration.set(value);
394
- if (value === 'Vælg selv' && !this.isDesktop()) {
395
- this.pickerOpen.set(true);
396
- }
397
- }
398
-
399
- /**
400
- * Normalizes days/hours/minutes so values never overflow their units.
401
- * e.g. 0d 48t 0min → 2d 0t 0min; 0d 0t 75min → 0d 1t 15min.
402
- * Caps days at maxDays().
403
- */
404
- normalize(): void {
405
- const totalMinutes = this.customDays() * 24 * 60
406
- + this.customHours() * 60
407
- + this.customMinutes();
408
-
409
- const maxTotalMinutes = this.maxDays() * 24 * 60 + 23 * 60 + 59;
410
- const clamped = Math.max(0, Math.min(totalMinutes, maxTotalMinutes));
411
-
412
- const d = Math.floor(clamped / (24 * 60));
413
- const h = Math.floor((clamped % (24 * 60)) / 60);
414
- const m = clamped % 60;
415
-
416
- this.customDays.set(d);
417
- this.customHours.set(h);
418
- this.customMinutes.set(m);
419
- }
420
-
421
- /**
422
- * Called when the mobile picker popover is dismissed — normalize and commit.
423
- */
424
- onPickerDismiss(): void {
425
- this.normalize();
426
- this.pickerOpen.set(false);
427
- }
428
-
429
- /**
430
- * Toggle day selection
431
- */
432
- toggleDay(day: string): void {
433
- const current = new Set(this.selectedDays());
434
- current.has(day) ? current.delete(day) : current.add(day);
435
- this.selectedDays.set(current);
436
- }
437
-
438
- /**
439
- * Confirm selection and dismiss with data
440
- */
441
- confirmSelection(): void {
442
- const duration = this.selectedDuration() === 'Vælg selv'
443
- ? this.customDurationLabel()
444
- : this.selectedDuration();
445
-
446
- const data: WhenCanBookData = {
447
- days: Array.from(this.selectedDays()),
448
- timeRange: { start: this.startTime(), end: this.endTime() },
449
- duration
450
- };
451
- this.modalController.dismiss({ value: data }, 'select');
452
- }
453
-
454
- /**
455
- * Dismiss without saving
456
- */
457
- dismiss(): void {
458
- this.modalController.dismiss(null, 'cancel');
459
- }
460
- }
@@ -1,134 +0,0 @@
1
- import { Component, signal, computed, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { ModalController } from '@ionic/angular/standalone';
4
- import { DsCheckboxComponent } from '@propbinder/design-system';
5
- import { DsMobileActionListItemComponent } from '../../action-list-item/ds-mobile-action-list-item';
6
- import { DsMobileBottomSheetWrapperComponent } from '../../bottom-sheet/ds-mobile-bottom-sheet-wrapper';
7
- import { DsMobileBottomSheetHeaderComponent } from '../../bottom-sheet/ds-mobile-bottom-sheet-header';
8
-
9
- export interface WhoCanBookOption {
10
- value: string;
11
- label: string;
12
- isMaster?: boolean;
13
- }
14
-
15
- /**
16
- * DsMobileWhoCanBookSheetComponent
17
- *
18
- * Bottom sheet for selecting who can book a facility (multi-select with checkboxes).
19
- */
20
- @Component({
21
- selector: 'ds-mobile-who-can-book-sheet',
22
- standalone: true,
23
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
24
- imports: [
25
- CommonModule,
26
- DsCheckboxComponent,
27
- DsMobileActionListItemComponent,
28
- DsMobileBottomSheetWrapperComponent,
29
- DsMobileBottomSheetHeaderComponent
30
- ],
31
- template: `
32
- <ds-mobile-bottom-sheet-wrapper>
33
- <!-- Header with back and done buttons -->
34
- <ds-mobile-bottom-sheet-header
35
- title="Hvem kan booke"
36
- leftButtonLabel="Tilbage"
37
- rightButtonLabel="Færdig"
38
- [rightButtonDisabled]="!hasSelectionChanged()"
39
- (leftButtonClick)="dismiss()"
40
- (rightButtonClick)="confirmSelection()">
41
- </ds-mobile-bottom-sheet-header>
42
-
43
- <!-- Options list -->
44
- <div class="options-list">
45
- @for (option of options; track option.value; let isLast = $last) {
46
- <ds-mobile-action-list-item
47
- [title]="option.label"
48
- [showDivider]="!isLast"
49
- (itemClick)="toggleOption(option.value)">
50
- <div content-trailing>
51
- <ds-checkbox
52
- [checked]="selectedOptions().has(option.value)"
53
- [showLabel]="false"
54
- size="md"
55
- (click)="$event.stopPropagation()"
56
- (checkedChange)="toggleOption(option.value)">
57
- </ds-checkbox>
58
- </div>
59
- </ds-mobile-action-list-item>
60
- }
61
- </div>
62
- </ds-mobile-bottom-sheet-wrapper>
63
- `,
64
- styles: [`
65
- .options-list {
66
- display: flex;
67
- flex-direction: column;
68
- padding: 0 16px;
69
- }
70
- `]
71
- })
72
- export class DsMobileWhoCanBookSheetComponent {
73
- options: WhoCanBookOption[] = [
74
- { value: 'alle', label: 'Alle', isMaster: true },
75
- { value: 'faelleskab-a', label: 'Fælleskab A' },
76
- { value: 'faelleskab-b', label: 'Fælleskab B' },
77
- { value: 'faelleskab-c', label: 'Fælleskab C' }
78
- ];
79
-
80
- // Signal-based state for selected options - all selected by default
81
- selectedOptions = signal<Set<string>>(new Set(['alle', 'faelleskab-a', 'faelleskab-b', 'faelleskab-c']));
82
-
83
- // Computed signal to check if any selection has been made
84
- hasSelectionChanged = computed(() => this.selectedOptions().size > 0);
85
-
86
- constructor(private modalController: ModalController) {}
87
-
88
- /**
89
- * Toggle an option on/off
90
- * Special logic for 'alle': when toggled on, selects all options; when toggled off, deselects all
91
- */
92
- toggleOption(value: string): void {
93
- const current = new Set(this.selectedOptions());
94
-
95
- if (value === 'alle') {
96
- if (current.has('alle')) {
97
- // Deselect all
98
- current.clear();
99
- } else {
100
- // Select all
101
- this.options.forEach(opt => current.add(opt.value));
102
- }
103
- } else {
104
- if (current.has(value)) {
105
- current.delete(value);
106
- current.delete('alle'); // Uncheck "Alle" if any individual item is unchecked
107
- } else {
108
- current.add(value);
109
- // Check if all non-master items are selected
110
- const allSelected = this.options
111
- .filter(o => !o.isMaster)
112
- .every(o => current.has(o.value));
113
- if (allSelected) current.add('alle');
114
- }
115
- }
116
-
117
- this.selectedOptions.set(current);
118
- }
119
-
120
- /**
121
- * Confirm selection and dismiss with selected options
122
- */
123
- confirmSelection(): void {
124
- const selected = Array.from(this.selectedOptions());
125
- this.modalController.dismiss({ value: selected }, 'select');
126
- }
127
-
128
- /**
129
- * Dismiss without saving
130
- */
131
- dismiss(): void {
132
- this.modalController.dismiss(null, 'cancel');
133
- }
134
- }
@@ -1,69 +0,0 @@
1
- import { Injectable } from '@angular/core';
2
- import { ModalController } from '@ionic/angular/standalone';
3
- import { BaseModalService } from '../../services/base-modal.service';
4
- import {
5
- DsMobileFacilityDetailModalComponent,
6
- FacilityDetailData,
7
- } from './ds-mobile-facility-detail-modal';
8
-
9
- /**
10
- * DsMobileFacilityDetailModalService
11
- *
12
- * Service for displaying facility details in a full-screen modal.
13
- * Built on Ionic's modal system with native gestures and animations.
14
- * Follows the same pattern as DsMobilePostDetailModalService for consistent behavior.
15
- *
16
- * Features:
17
- * - Full facility information display
18
- * - Hero image with swiper
19
- * - Rich text description support
20
- * - Requirements and booking type display
21
- * - Restrictions list
22
- * - Fixed bottom booking button
23
- * - Native modal animations
24
- * - Safe area support
25
- *
26
- * @example
27
- * ```typescript
28
- * constructor(private facilityModal: DsMobileFacilityDetailModalService) {}
29
- *
30
- * async openFacility() {
31
- * await this.facilityModal.open({
32
- * id: 'facility-1',
33
- * facilityTitle: 'Festlokale på taget',
34
- * heroImage: '/Assets/Dummy-photos/balcony-view.jpg',
35
- * fullDescription: '<p>The rooftop terrace is designed...</p>',
36
- * requirements: ['Kræver nøglekort'],
37
- * bookingType: 'Instant booking',
38
- * expectations: '<p>The terrace is furnished...</p>',
39
- * restrictions: ['No smoking or vaping...']
40
- * });
41
- * }
42
- * ```
43
- */
44
- @Injectable({
45
- providedIn: 'root',
46
- })
47
- export class DsMobileFacilityDetailModalService extends BaseModalService {
48
- constructor(modalController: ModalController) {
49
- super(modalController);
50
- }
51
-
52
- /**
53
- * Open the facility detail modal
54
- *
55
- * @param facilityData Facility data to display
56
- * @returns Promise that resolves when the modal is presented
57
- */
58
- async open(facilityData: FacilityDetailData): Promise<void> {
59
- const modal = await this.createModal(
60
- DsMobileFacilityDetailModalComponent,
61
- { facilityData },
62
- {
63
- keyboardClose: true, // Allow keyboard close behavior
64
- }
65
- );
66
-
67
- await modal.present();
68
- }
69
- }