@propbinder/mobile-design 0.2.48 → 0.2.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ng-package.json +24 -0
- package/package.json +3 -39
- package/src/animations/page-transitions.ts +165 -0
- package/src/assets/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
- package/src/components/action-list-item/ds-mobile-action-list-item.ts +102 -0
- package/src/components/action-list-item/index.ts +2 -0
- package/src/components/app-icon/ds-app-icon.ts +133 -0
- package/src/components/app-icon/index.ts +2 -0
- package/src/components/attachment-preview/ds-mobile-attachment-preview.css +139 -0
- package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +164 -0
- package/src/components/attachment-preview/index.ts +1 -0
- package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +142 -0
- package/src/components/avatar-with-badge/index.ts +2 -0
- package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +71 -0
- package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +121 -0
- package/src/components/booking-modal/ds-mobile-booking-modal.ts +598 -0
- package/src/components/booking-modal/ds-mobile-booking-summary.ts +161 -0
- package/src/components/booking-modal/index.ts +4 -0
- package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +266 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +146 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +156 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +101 -0
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +169 -0
- package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +211 -0
- package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +578 -0
- package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +614 -0
- package/src/components/bottom-sheet/index.ts +8 -0
- package/src/components/bottom-sheet/modal-shadow-fix.ts +42 -0
- package/src/components/card-inline/ds-mobile-card-inline.ts +301 -0
- package/src/components/card-inline/index.ts +2 -0
- package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +118 -0
- package/src/components/card-inline-banner/index.ts +1 -0
- package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +120 -0
- package/src/components/card-inline-contact/index.ts +1 -0
- package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +141 -0
- package/src/components/card-inline-file/index.ts +1 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.css +159 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +105 -0
- package/src/components/chat-modal/ds-mobile-chat-modal.ts +918 -0
- package/src/components/chat-modal/index.ts +8 -0
- package/src/components/comment/ds-mobile-comment.ts +568 -0
- package/src/components/comment/index.ts +2 -0
- package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +182 -0
- package/src/components/contact-list-item/index.ts +2 -0
- package/src/components/content/ds-mobile-content.ts +139 -0
- package/src/components/content/index.ts +2 -0
- package/src/components/dropdown/ds-mobile-dropdown.css +199 -0
- package/src/components/dropdown/ds-mobile-dropdown.ts +340 -0
- package/src/components/dropdown/index.ts +2 -0
- package/src/components/ds-mobile-tabs.css +407 -0
- package/src/components/ds-mobile-tabs.ts +216 -0
- package/src/components/empty-state/ds-mobile-empty-state.ts +120 -0
- package/src/components/empty-state/index.ts +2 -0
- package/src/components/fab/ds-mobile-fab.ts +315 -0
- package/src/components/fab/index.ts +1 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +121 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +189 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +135 -0
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +656 -0
- package/src/components/facility-creation-modal/index.ts +9 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +105 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +188 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +460 -0
- package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +134 -0
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +69 -0
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +379 -0
- package/src/components/facility-detail-modal/index.ts +2 -0
- package/src/components/file-attachment/ds-mobile-file-attachment.ts +164 -0
- package/src/components/file-attachment/index.ts +2 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +214 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +84 -0
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +424 -0
- package/src/components/handbook-detail-modal/index.ts +3 -0
- package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +175 -0
- package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +533 -0
- package/src/components/handbook-folder/index.ts +4 -0
- package/src/components/header-content/ds-mobile-header-content.ts +222 -0
- package/src/components/header-content/index.ts +2 -0
- package/src/components/illustration/ds-mobile-illustration.ts +124 -0
- package/src/components/illustration/index.ts +2 -0
- package/src/components/index.ts +124 -0
- package/src/components/inline-photo/ds-mobile-inline-photo.ts +361 -0
- package/src/components/inline-photo/index.ts +1 -0
- package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +132 -0
- package/src/components/inline-tabs/index.ts +2 -0
- package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +350 -0
- package/src/components/interactive-list-item-booking/index.ts +1 -0
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +321 -0
- package/src/components/interactive-list-item-inquiry/index.ts +2 -0
- package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +237 -0
- package/src/components/interactive-list-item-message/index.ts +2 -0
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +549 -0
- package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +124 -0
- package/src/components/interactive-list-item-post/index.ts +13 -0
- package/src/components/lightbox/ds-mobile-lightbox-footer.ts +315 -0
- package/src/components/lightbox/ds-mobile-lightbox-header.ts +202 -0
- package/src/components/lightbox/ds-mobile-lightbox-image.ts +484 -0
- package/src/components/lightbox/ds-mobile-lightbox-pdf.css +377 -0
- package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +374 -0
- package/src/components/lightbox/ds-mobile-lightbox.css +587 -0
- package/src/components/lightbox/ds-mobile-lightbox.service.ts +296 -0
- package/src/components/lightbox/ds-mobile-lightbox.ts +529 -0
- package/src/components/lightbox/index.ts +22 -0
- package/src/components/list-item/ds-mobile-list-item.ts +603 -0
- package/src/components/list-item/index.ts +2 -0
- package/src/components/list-item-static/ds-mobile-list-item-static.ts +133 -0
- package/src/components/list-item-static/index.ts +2 -0
- package/src/components/loader-overlay/ds-mobile-loader-overlay.css +49 -0
- package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +77 -0
- package/src/components/loader-overlay/index.ts +1 -0
- package/src/components/logo/ds-logo.ts +95 -0
- package/src/components/logo/index.ts +2 -0
- package/src/components/message-bubble/ds-mobile-message-bubble.ts +633 -0
- package/src/components/message-bubble/index.ts +7 -0
- package/src/components/message-composer/ds-mobile-message-composer.ts +1146 -0
- package/src/components/message-composer/index.ts +7 -0
- package/src/components/modal/ds-mobile-modal.css +163 -0
- package/src/components/modal/ds-mobile-modal.service.ts +329 -0
- package/src/components/modal/index.ts +8 -0
- package/src/components/modal-base/ds-mobile-modal-base.css +378 -0
- package/src/components/modal-base/ds-mobile-modal-base.ts +261 -0
- package/src/components/modal-base/index.ts +2 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +112 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +93 -0
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +442 -0
- package/src/components/new-inquiry-modal/index.ts +4 -0
- package/src/components/offline-banner/ds-mobile-offline-banner.ts +135 -0
- package/src/components/offline-banner/index.ts +1 -0
- package/src/components/page-details/ds-mobile-page-details.css +83 -0
- package/src/components/page-details/ds-mobile-page-details.ts +282 -0
- package/src/components/page-details/index.ts +2 -0
- package/src/components/page-main/ds-mobile-page-main.css +68 -0
- package/src/components/page-main/ds-mobile-page-main.ts +421 -0
- package/src/components/page-main/index.ts +2 -0
- package/src/components/post-composer/ds-mobile-post-composer.ts +140 -0
- package/src/components/post-composer/index.ts +2 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +390 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +108 -0
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +722 -0
- package/src/components/post-detail-modal/index.ts +9 -0
- package/src/components/property-banner/ds-mobile-property-banner.ts +95 -0
- package/src/components/property-banner/index.ts +2 -0
- package/src/components/section/ds-mobile-section.ts +263 -0
- package/src/components/section/index.ts +2 -0
- package/src/components/shared/directives/index.ts +2 -0
- package/src/components/shared/directives/long-press.directive.ts +212 -0
- package/src/components/shared/index.ts +3 -0
- package/src/components/shared/mobile-modal-base.ts +457 -0
- package/src/components/shared/mobile-page-base.ts +204 -0
- package/src/components/swiper/ds-mobile-swiper-with-nav.ts +160 -0
- package/src/components/swiper/ds-mobile-swiper.ts +327 -0
- package/src/components/swiper/index.ts +3 -0
- package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +129 -0
- package/src/components/system-message-banner/index.ts +2 -0
- package/src/components/tab-bar/ds-mobile-tab-bar.css +533 -0
- package/src/components/tab-bar/ds-mobile-tab-bar.ts +735 -0
- package/src/components/tab-bar/index.ts +2 -0
- package/src/components/tabs/ds-mobile-tabs.css +25 -0
- package/src/components/tabs/ds-mobile-tabs.ts +89 -0
- package/src/components/tabs/index.ts +2 -0
- package/src/components/text-input/ds-text-input.ts +287 -0
- package/src/components/text-input/index.ts +2 -0
- package/src/examples/booking.page.ts +434 -0
- package/src/examples/community.page.ts +776 -0
- package/src/examples/handbook.page.ts +324 -0
- package/src/examples/home.page.ts +347 -0
- package/src/examples/index.ts +12 -0
- package/src/examples/inquiries.example.ts +273 -0
- package/src/examples/inquiry-detail.example.css +189 -0
- package/src/examples/inquiry-detail.example.ts +415 -0
- package/src/examples/mobile-tabs-example.component.ts +208 -0
- package/src/examples/post-create.page.ts +311 -0
- package/src/examples/post-detail.page.ts +296 -0
- package/src/examples/sign-in.page.ts +291 -0
- package/src/examples/whitelabel-demo-modal.component.ts +1094 -0
- package/src/examples/whitelabel-demo-modal.service.ts +77 -0
- package/src/models/index.ts +7 -0
- package/src/models/post.model.ts +41 -0
- package/src/pages/community.page.ts +769 -0
- package/src/pages/handbook.page.ts +388 -0
- package/src/pages/home.page.ts +303 -0
- package/src/pages/index.ts +11 -0
- package/src/pages/inquiries.example.ts +273 -0
- package/src/pages/inquiry-detail.example.css +189 -0
- package/src/pages/inquiry-detail.example.ts +415 -0
- package/src/pages/mobile-tabs-example.component.ts +179 -0
- package/src/pages/post-create.page.ts +311 -0
- package/src/pages/post-detail.page.ts +296 -0
- package/src/pages/sign-in.page.ts +291 -0
- package/src/pages/whitelabel-demo-modal.component.ts +1094 -0
- package/src/pages/whitelabel-demo-modal.service.ts +77 -0
- package/src/public-api.ts +6 -0
- package/src/services/base-modal.service.ts +101 -0
- package/src/services/index.ts +11 -0
- package/src/services/posts.service.ts +542 -0
- package/src/services/tracking-permission.service.ts +88 -0
- package/src/services/user.service.ts +60 -0
- package/src/services/whitelabel.service.ts +675 -0
- package/{styles → src/styles}/ionic.css +25 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +13 -0
- package/fesm2022/propbinder-mobile-design.mjs +0 -26168
- package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
- package/index.d.ts +0 -8169
- /package/{assets → src/assets}/fonts/Brockmann-Bold.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-BoldItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-Medium.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-MediumItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-Regular.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-RegularItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-SemiBold.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
- /package/{assets → src/assets}/fonts/Brockmann_desktop_license.pdf +0 -0
- /package/{assets → src/assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
- /package/{assets → src/assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
- /package/{assets → src/assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
- /package/{styles → src/components/shared}/mobile-common.css +0 -0
- /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
|
+
}
|