@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,156 @@
1
+ import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit, OnDestroy, ElementRef, inject } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ModalController } from '@ionic/angular/standalone';
4
+ import { Keyboard } from '@capacitor/keyboard';
5
+
6
+ /**
7
+ * DsMobileBottomSheetWrapperComponent
8
+ *
9
+ * A wrapper component that provides common layout styling for all bottom sheets.
10
+ * Handles safe area insets, provides consistent layout structure, and automatic
11
+ * keyboard handling using the same approach as modal base.
12
+ *
13
+ * Features:
14
+ * - Automatic keyboard push-up behavior (same as MobileModalBase)
15
+ * - Auto-height for bottom sheets
16
+ * - Safe area inset handling
17
+ * - Consistent styling across all bottom sheets
18
+ * - Only responds to keyboard when this bottom sheet is the top-most modal
19
+ *
20
+ * Usage:
21
+ * Wrap your bottom sheet content with this component using ng-content projection.
22
+ *
23
+ * @example
24
+ * ```html
25
+ * <ds-mobile-bottom-sheet-wrapper>
26
+ * <!-- Your bottom sheet content here -->
27
+ * </ds-mobile-bottom-sheet-wrapper>
28
+ * ```
29
+ */
30
+ @Component({
31
+ selector: 'ds-mobile-bottom-sheet-wrapper',
32
+ standalone: true,
33
+ imports: [CommonModule],
34
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
35
+ template: `
36
+ <div class="bottom-sheet-wrapper">
37
+ <ng-content></ng-content>
38
+ </div>
39
+ `,
40
+ styles: [`
41
+ :host {
42
+ display: block;
43
+ background: var(--color-background-neutral-primary, #ffffff);
44
+ background: #ffffff; /* Explicit fallback for iOS */
45
+ height: auto;
46
+ }
47
+
48
+ .bottom-sheet-wrapper {
49
+ display: flex;
50
+ flex-direction: column;
51
+ background: var(--color-background-neutral-primary, #ffffff);
52
+ background: #ffffff; /* Explicit fallback for iOS */
53
+ padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
54
+ overflow: hidden;
55
+ width: 100%;
56
+ height: auto;
57
+ min-height: unset;
58
+ }
59
+
60
+ :host ::ng-deep .view-container:not(.is-animating) {
61
+ height: auto !important;
62
+ min-height: 0 !important;
63
+ }
64
+ `]
65
+ })
66
+ export class DsMobileBottomSheetWrapperComponent implements OnInit, OnDestroy {
67
+ private modalController = inject(ModalController);
68
+
69
+ constructor(private elementRef: ElementRef) {}
70
+
71
+ ngOnInit(): void {
72
+ // Set up keyboard listeners using the same approach as MobileModalBase
73
+ this.setupKeyboardListeners();
74
+ }
75
+
76
+ ngOnDestroy(): void {
77
+ this.cleanupKeyboardListeners();
78
+ }
79
+
80
+ /**
81
+ * Set up keyboard event listeners to adjust bottom sheet position
82
+ * Uses --keyboard-height-sheet variable (separate from modal's --keyboard-height)
83
+ * Only responds if this bottom sheet is the currently active (top-most) modal
84
+ */
85
+ private setupKeyboardListeners(): void {
86
+ Keyboard.addListener('keyboardWillShow', async (info) => {
87
+ // Check if this bottom sheet is the top-most modal
88
+ const topModal = await this.modalController.getTop();
89
+ const thisModal = this.getModalElement();
90
+
91
+ if (topModal !== thisModal) {
92
+ console.log('[BottomSheetWrapper] Keyboard event ignored - not top modal');
93
+ return; // Not the active modal, ignore keyboard event
94
+ }
95
+
96
+ console.log('[BottomSheetWrapper] Keyboard showing, height:', info.keyboardHeight);
97
+
98
+ // Use a separate CSS variable for bottom sheets to avoid conflicts with modals
99
+ document.documentElement.style.setProperty('--keyboard-height-sheet', `${info.keyboardHeight}px`);
100
+
101
+ // Add class to the modal element for additional styling control
102
+ if (thisModal) {
103
+ thisModal.classList.add('keyboard-visible-sheet');
104
+ }
105
+ }).catch(() => {
106
+ console.log('[BottomSheetWrapper] Keyboard plugin not available (web)');
107
+ });
108
+
109
+ Keyboard.addListener('keyboardWillHide', async () => {
110
+ // Check if this bottom sheet is the top-most modal
111
+ const topModal = await this.modalController.getTop();
112
+ const thisModal = this.getModalElement();
113
+
114
+ if (topModal !== thisModal) {
115
+ console.log('[BottomSheetWrapper] Keyboard hide event ignored - not top modal');
116
+ return; // Not the active modal, ignore keyboard event
117
+ }
118
+
119
+ console.log('[BottomSheetWrapper] Keyboard hiding');
120
+
121
+ // Reset keyboard height for bottom sheets
122
+ document.documentElement.style.setProperty('--keyboard-height-sheet', '0px');
123
+
124
+ // Remove keyboard class
125
+ if (thisModal) {
126
+ thisModal.classList.remove('keyboard-visible-sheet');
127
+ }
128
+ }).catch(() => {
129
+ console.log('[BottomSheetWrapper] Keyboard plugin not available (web)');
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Clean up keyboard event listeners
135
+ */
136
+ private cleanupKeyboardListeners(): void {
137
+ Keyboard.removeAllListeners().catch(() => {
138
+ // Keyboard plugin not available
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Get the parent ion-modal element
144
+ */
145
+ private getModalElement(): HTMLIonModalElement | null {
146
+ let element = this.elementRef.nativeElement.parentElement;
147
+ while (element) {
148
+ if (element.tagName === 'ION-MODAL') {
149
+ return element as HTMLIonModalElement;
150
+ }
151
+ element = element.parentElement;
152
+ }
153
+ return null;
154
+ }
155
+ }
156
+
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Bottom Sheet Modal Styles
3
+ * Based on Ionic 6 bottom sheet presentation
4
+ */
5
+
6
+ /* Base bottom sheet styling */
7
+ .ds-bottom-sheet {
8
+ --border-radius: 16px;
9
+ --box-shadow: none;
10
+ --backdrop-opacity: 0.4;
11
+ transition: --backdrop-opacity 0.3s ease;
12
+ /* Modal at top:0 so backdrop covers full screen including status bar */
13
+ top: 0 !important;
14
+ height: 100% !important;
15
+ }
16
+
17
+ /* Offset only the sheet content, not the backdrop */
18
+ .ds-bottom-sheet::part(content) {
19
+ margin-top: var(--app-sheet-top-offset);
20
+ }
21
+
22
+ /* Faster animation for instant appearance */
23
+ .ds-bottom-sheet::part(content) {
24
+ transition: transform 150ms cubic-bezier(0.36, 0.66, 0.04, 1) !important;
25
+ }
26
+
27
+ .ds-bottom-sheet::part(backdrop) {
28
+ transition: opacity 150ms ease-out !important;
29
+ }
30
+
31
+ /* Backdrop blur effect */
32
+ .ds-bottom-sheet--blur {
33
+ --backdrop-opacity: 0.8;
34
+ }
35
+
36
+ .ds-bottom-sheet--blur::part(backdrop) {
37
+ backdrop-filter: blur(8px);
38
+ -webkit-backdrop-filter: blur(8px);
39
+ }
40
+
41
+ /* Custom backdrop styling */
42
+ .ds-bottom-sheet--custom-backdrop::part(backdrop) {
43
+ background: rgba(0, 0, 0, var(--backdrop-opacity, 0.6));
44
+ }
45
+
46
+ /* Backdrop styling */
47
+ .ds-bottom-sheet::part(backdrop) {
48
+ background: rgba(0, 0, 0, var(--backdrop-opacity, 0.6));
49
+ }
50
+
51
+ /* Handle customization */
52
+ .ds-bottom-sheet::part(handle) {
53
+ background: var(--border-color-default);
54
+ width: 36px;
55
+ height: 4px;
56
+ border-radius: 2px;
57
+ margin-top: 4px;
58
+ margin-bottom: 8px;
59
+ }
60
+
61
+ /* Content area styling - Override Ionic's bottom-based positioning */
62
+ .ds-bottom-sheet::part(content) {
63
+ border-radius: var(--border-radius);
64
+ background: var(--color-background-neutral-primary, #ffffff);
65
+ box-shadow: var(--box-shadow);
66
+ position: absolute !important;
67
+ top: 0 !important;
68
+ bottom: 0 !important;
69
+ height: 100% !important;
70
+ max-height: 100% !important;
71
+ }
72
+
73
+ /* Remove border radius when fully expanded */
74
+ @media (min-height: 768px) {
75
+ .ds-bottom-sheet.modal-sheet-expanded::part(content) {
76
+ border-radius: 0;
77
+ }
78
+ }
79
+
80
+
81
+
82
+ /* Ensure content scrolls properly */
83
+ .ds-bottom-sheet ion-content {
84
+ --background: transparent;
85
+ }
86
+
87
+ /* Better mobile experience */
88
+ @media (max-width: 768px) {
89
+ .ds-bottom-sheet::part(content) {
90
+ max-width: 100%;
91
+ }
92
+ }
93
+
94
+ /* Desktop centering */
95
+ @media (min-width: 769px) {
96
+ .ds-bottom-sheet::part(content) {
97
+ max-width: 640px;
98
+ margin: 0 auto;
99
+ }
100
+ }
101
+
@@ -0,0 +1,169 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { ModalController } from '@ionic/angular/standalone';
3
+ import { ComponentRef } from '@angular/core';
4
+ import { disableModalShadowPointerEvents } from './modal-shadow-fix';
5
+
6
+ /**
7
+ * Configuration options for the bottom sheet modal
8
+ */
9
+ export interface BottomSheetOptions {
10
+ /** The component to display in the bottom sheet */
11
+ component: any;
12
+ /** Component props to pass to the modal content */
13
+ componentProps?: { [key: string]: any };
14
+ /** Breakpoints for the bottom sheet (0-1 values representing percentage of screen) */
15
+ breakpoints?: number[];
16
+ /** Initial breakpoint to open the sheet at */
17
+ initialBreakpoint?: number;
18
+ /** Show/hide the drag handle */
19
+ handle?: boolean;
20
+ /** Custom CSS class for styling */
21
+ cssClass?: string | string[];
22
+ /** Whether backdrop dismisses the modal */
23
+ backdropDismiss?: boolean;
24
+ /** Backdrop opacity (0-1) */
25
+ backdropOpacity?: number;
26
+ /** Enable backdrop blur effect */
27
+ backdropBlur?: boolean;
28
+ /** Keyboard close behavior */
29
+ keyboardClose?: boolean;
30
+ /** Auto-height mode: sheet sizes to content instead of using fixed breakpoints */
31
+ autoHeight?: boolean;
32
+ }
33
+
34
+ /**
35
+ * DsMobileBottomSheetService
36
+ *
37
+ * Service for creating and managing Ionic 6 bottom sheet modals.
38
+ * Based on the Ionic blog article: https://ionic.io/blog/5-examples-of-the-new-ionic-6-bottom-sheet-modal
39
+ *
40
+ * Features:
41
+ * - Multiple breakpoints for snap-to positions
42
+ * - Customizable initial height
43
+ * - Optional drag handle
44
+ * - Backdrop blur effect
45
+ * - Custom styling support
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * constructor(private bottomSheet: DsMobileBottomSheetService) {}
50
+ *
51
+ * async openSheet() {
52
+ * const sheet = await this.bottomSheet.create({
53
+ * component: PostCreateComponent,
54
+ * breakpoints: [0, 0.5, 0.9],
55
+ * initialBreakpoint: 0.5,
56
+ * handle: true
57
+ * });
58
+ *
59
+ * const result = await sheet.onWillDismiss();
60
+ * console.log('Sheet dismissed with:', result.data);
61
+ * }
62
+ * ```
63
+ */
64
+ @Injectable({
65
+ providedIn: 'root'
66
+ })
67
+ export class DsMobileBottomSheetService {
68
+ constructor(private modalController: ModalController) {}
69
+
70
+ /**
71
+ * Create and present a bottom sheet modal
72
+ *
73
+ * @param options Configuration options for the bottom sheet
74
+ * @returns Promise that resolves to the modal instance
75
+ */
76
+ async create(options: BottomSheetOptions): Promise<HTMLIonModalElement> {
77
+ const {
78
+ component,
79
+ componentProps = {},
80
+ breakpoints = [0, 0.5, 0.9],
81
+ initialBreakpoint = 0.5,
82
+ handle = true,
83
+ cssClass = '',
84
+ backdropDismiss = true,
85
+ backdropOpacity,
86
+ backdropBlur = false,
87
+ keyboardClose = true,
88
+ autoHeight = false
89
+ } = options;
90
+
91
+ // Build CSS classes array
92
+ const cssClasses = ['ds-bottom-sheet'];
93
+ if (backdropBlur) {
94
+ cssClasses.push('ds-bottom-sheet--blur');
95
+ }
96
+ if (autoHeight) {
97
+ cssClasses.push('auto-height');
98
+ }
99
+ if (typeof cssClass === 'string' && cssClass) {
100
+ cssClasses.push(cssClass);
101
+ } else if (Array.isArray(cssClass)) {
102
+ cssClasses.push(...cssClass);
103
+ }
104
+
105
+ const modal = await this.modalController.create({
106
+ component,
107
+ componentProps,
108
+ breakpoints: autoHeight ? undefined : breakpoints,
109
+ initialBreakpoint: autoHeight ? undefined : initialBreakpoint,
110
+ handle: autoHeight ? false : handle,
111
+ cssClass: cssClasses,
112
+ backdropDismiss,
113
+ keyboardClose,
114
+ showBackdrop: true,
115
+ canDismiss: backdropDismiss,
116
+ // Remove animation delay for instant appearance
117
+ animated: true,
118
+ enterAnimation: undefined, // Use default but we'll customize via CSS
119
+ leaveAnimation: undefined,
120
+ ...(backdropOpacity !== undefined && {
121
+ cssClass: [...cssClasses, 'ds-bottom-sheet--custom-backdrop']
122
+ })
123
+ });
124
+
125
+ // Apply custom backdrop opacity if specified
126
+ if (backdropOpacity !== undefined) {
127
+ modal.style.setProperty('--backdrop-opacity', backdropOpacity.toString());
128
+ }
129
+
130
+ // Add ESC key listener to dismiss the modal
131
+ const escKeyHandler = (event: KeyboardEvent) => {
132
+ if (event.key === 'Escape') {
133
+ modal.dismiss(undefined, 'escape');
134
+ }
135
+ };
136
+
137
+ // Add listener when modal is presented
138
+ modal.addEventListener('ionModalDidPresent', () => {
139
+ document.addEventListener('keydown', escKeyHandler);
140
+ });
141
+
142
+ // Remove listener when modal is dismissed
143
+ modal.addEventListener('ionModalDidDismiss', () => {
144
+ document.removeEventListener('keydown', escKeyHandler);
145
+ });
146
+
147
+ await modal.present();
148
+ disableModalShadowPointerEvents(modal);
149
+
150
+ // Don't wait - return immediately so component can try to focus
151
+ // while still in user gesture context
152
+ return modal;
153
+ }
154
+
155
+ /**
156
+ * Dismiss all open modals
157
+ */
158
+ async dismiss(data?: any, role?: string): Promise<boolean> {
159
+ return this.modalController.dismiss(data, role);
160
+ }
161
+
162
+ /**
163
+ * Get the top-most modal overlay
164
+ */
165
+ async getTop(): Promise<HTMLIonModalElement | undefined> {
166
+ return this.modalController.getTop();
167
+ }
168
+ }
169
+
@@ -0,0 +1,211 @@
1
+ import { Component, Input, ContentChild, TemplateRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ModalController } from '@ionic/angular/standalone';
4
+ import { DsButtonComponent } from '@propbinder/design-system';
5
+ import { DsMobileBottomSheetWrapperComponent } from './ds-mobile-bottom-sheet-wrapper';
6
+ import { DsMobileIllustrationComponent } from '../illustration/ds-mobile-illustration';
7
+
8
+ /**
9
+ * DsMobileConfirmationSheetComponent
10
+ *
11
+ * Generic bottom sheet for displaying confirmation messages with optional summary content.
12
+ * Highly flexible and reusable across different confirmation scenarios.
13
+ *
14
+ * **Features:**
15
+ * - Customizable title and message
16
+ * - Optional illustration with variant selection
17
+ * - Content projection for custom summary section
18
+ * - Customizable button text and action
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Simple confirmation
23
+ * const sheet = await modalController.create({
24
+ * component: DsMobileConfirmationSheetComponent,
25
+ * componentProps: {
26
+ * title: 'Booking accepteret',
27
+ * message: 'Din booking er bekræftet.',
28
+ * buttonText: 'Luk',
29
+ * illustrationVariant: 'confirmation'
30
+ * },
31
+ * breakpoints: [0, 1],
32
+ * initialBreakpoint: 1,
33
+ * handle: true,
34
+ * cssClass: ['ds-bottom-sheet', 'auto-height']
35
+ * });
36
+ * await sheet.present();
37
+ * ```
38
+ *
39
+ * @example
40
+ * ```html
41
+ * <!-- With custom summary content -->
42
+ * <ds-mobile-confirmation-sheet
43
+ * title="Post created"
44
+ * message="Your post has been published successfully."
45
+ * buttonText="Done">
46
+ * <div summary>
47
+ * <!-- Custom summary content here -->
48
+ * </div>
49
+ * </ds-mobile-confirmation-sheet>
50
+ * ```
51
+ */
52
+ @Component({
53
+ selector: 'ds-mobile-confirmation-sheet',
54
+ standalone: true,
55
+ imports: [
56
+ CommonModule,
57
+ DsMobileBottomSheetWrapperComponent,
58
+ DsMobileIllustrationComponent,
59
+ DsButtonComponent
60
+ ],
61
+ styles: [`
62
+ .confirmation-content {
63
+ padding: 48px 20px 24px 20px;
64
+ display: flex;
65
+ flex-direction: column;
66
+ align-items: center;
67
+ gap: 24px;
68
+ }
69
+
70
+ .success-icon {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ margin-bottom: -32px;
75
+ z-index: 0;
76
+ }
77
+
78
+ .confirmation-text {
79
+ display: flex;
80
+ flex-direction: column;
81
+ align-items: center;
82
+ gap: 8px;
83
+ z-index: 1;
84
+ }
85
+
86
+ .confirmation-title {
87
+ font-family: 'Brockmann', sans-serif;
88
+ font-size: var(--font-size-xl, 20px);
89
+ font-weight: 600;
90
+ color: var(--text-color-default-primary, #202227);
91
+ text-align: center;
92
+ margin: 0;
93
+ }
94
+
95
+ .confirmation-message {
96
+ font-family: 'Brockmann', sans-serif;
97
+ font-size: var(--font-size-sm, 14px);
98
+ font-weight: 400;
99
+ line-height: 1.6;
100
+ color: var(--text-color-default-secondary, #71727a);
101
+ text-align: center;
102
+ margin: 0;
103
+ max-width: 320px;
104
+ }
105
+
106
+ .summary-section {
107
+ width: 100%;
108
+ }
109
+
110
+ .button-container {
111
+ width: 100%;
112
+ }
113
+
114
+ .button-container ::ng-deep ds-button {
115
+ display: block;
116
+ width: 100%;
117
+ }
118
+
119
+ .button-container ::ng-deep ds-button button {
120
+ width: 100%;
121
+ border-radius: 100px;
122
+ height: 48px;
123
+ }
124
+ `],
125
+ template: `
126
+ <ds-mobile-bottom-sheet-wrapper>
127
+ <div class="confirmation-content">
128
+ <!-- Illustration -->
129
+ @if (showIllustration) {
130
+ <div class="success-icon">
131
+ <ds-mobile-illustration
132
+ [variant]="illustrationVariant"
133
+ [size]="illustrationSize" />
134
+ </div>
135
+ }
136
+
137
+ <!-- Title and Message -->
138
+ <div class="confirmation-text">
139
+ <h2 class="confirmation-title">{{ title }}</h2>
140
+ <p class="confirmation-message">{{ message }}</p>
141
+ </div>
142
+
143
+ <!-- Optional Summary Content (projected) -->
144
+ @if (summaryTemplate) {
145
+ <div class="summary-section">
146
+ <ng-container *ngTemplateOutlet="summaryTemplate"></ng-container>
147
+ </div>
148
+ }
149
+
150
+ <!-- Action Button -->
151
+ <div class="button-container">
152
+ <ds-button
153
+ size="lg"
154
+ variant="primary"
155
+ (clicked)="handleButtonClick()">
156
+ {{ buttonText }}
157
+ </ds-button>
158
+ </div>
159
+ </div>
160
+ </ds-mobile-bottom-sheet-wrapper>
161
+ `
162
+ })
163
+ export class DsMobileConfirmationSheetComponent {
164
+ /**
165
+ * Confirmation title
166
+ */
167
+ @Input({ required: true }) title!: string;
168
+
169
+ /**
170
+ * Confirmation message
171
+ */
172
+ @Input({ required: true }) message!: string;
173
+
174
+ /**
175
+ * Button text
176
+ * @default 'Luk'
177
+ */
178
+ @Input() buttonText: string = 'Luk';
179
+
180
+ /**
181
+ * Whether to show the illustration
182
+ * @default true
183
+ */
184
+ @Input() showIllustration: boolean = true;
185
+
186
+ /**
187
+ * Illustration variant
188
+ * @default 'confirmation'
189
+ */
190
+ @Input() illustrationVariant: 'post' | 'inquiry' | 'confirmation' = 'confirmation';
191
+
192
+ /**
193
+ * Illustration size
194
+ * @default '120px'
195
+ */
196
+ @Input() illustrationSize: string = '120px';
197
+
198
+ /**
199
+ * Optional summary content template
200
+ */
201
+ @ContentChild('summary', { read: TemplateRef }) summaryTemplate?: TemplateRef<any>;
202
+
203
+ constructor(private modalController: ModalController) {}
204
+
205
+ /**
206
+ * Handle button click - dismisses the sheet
207
+ */
208
+ async handleButtonClick(): Promise<void> {
209
+ await this.modalController.dismiss();
210
+ }
211
+ }