@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,164 @@
|
|
|
1
|
+
import { Component, input, output } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DsAvatarComponent } from '@propbinder/design-system';
|
|
4
|
+
import { DsMobileLoaderOverlayComponent } from '../loader-overlay';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* File type for attachment preview
|
|
8
|
+
*/
|
|
9
|
+
export type AttachmentFileType = 'image' | 'pdf' | 'doc' | 'docx' | 'xls' | 'xlsx' | 'other';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Attachment data interface
|
|
13
|
+
*/
|
|
14
|
+
export interface AttachmentData {
|
|
15
|
+
id: string;
|
|
16
|
+
src: string;
|
|
17
|
+
type: AttachmentFileType;
|
|
18
|
+
name?: string;
|
|
19
|
+
size?: string;
|
|
20
|
+
isLoading?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* DsMobileAttachmentPreviewComponent
|
|
25
|
+
*
|
|
26
|
+
* Reusable component for displaying attachment previews.
|
|
27
|
+
* Supports both image previews and file type indicators (PDF, DOCX, etc.).
|
|
28
|
+
*
|
|
29
|
+
* Features:
|
|
30
|
+
* - Image preview for photos
|
|
31
|
+
* - File type indicator box for documents
|
|
32
|
+
* - Remove button overlay
|
|
33
|
+
* - Consistent 96x96 size
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```html
|
|
37
|
+
* <!-- Image attachment -->
|
|
38
|
+
* <ds-mobile-attachment-preview
|
|
39
|
+
* [attachment]="{ id: '1', src: 'photo.jpg', type: 'image' }"
|
|
40
|
+
* (remove)="handleRemove($event)">
|
|
41
|
+
* </ds-mobile-attachment-preview>
|
|
42
|
+
*
|
|
43
|
+
* <!-- PDF attachment -->
|
|
44
|
+
* <ds-mobile-attachment-preview
|
|
45
|
+
* [attachment]="{ id: '2', src: 'doc.pdf', type: 'pdf', name: 'Document.pdf' }"
|
|
46
|
+
* (remove)="handleRemove($event)">
|
|
47
|
+
* </ds-mobile-attachment-preview>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
@Component({
|
|
51
|
+
selector: 'ds-mobile-attachment-preview',
|
|
52
|
+
standalone: true,
|
|
53
|
+
imports: [CommonModule, DsAvatarComponent, DsMobileLoaderOverlayComponent],
|
|
54
|
+
styleUrls: ['./ds-mobile-attachment-preview.css'],
|
|
55
|
+
template: `
|
|
56
|
+
<div class="attachment-preview" [class.loading]="attachment().isLoading">
|
|
57
|
+
@if (attachment().type === 'image') {
|
|
58
|
+
<!-- Image Preview -->
|
|
59
|
+
<img
|
|
60
|
+
[src]="attachment().src"
|
|
61
|
+
[alt]="attachment().name || 'Attachment'"
|
|
62
|
+
class="preview-image"
|
|
63
|
+
/>
|
|
64
|
+
} @else {
|
|
65
|
+
<!-- File Type Preview -->
|
|
66
|
+
<div class="preview-file">
|
|
67
|
+
<div class="file-avatar">
|
|
68
|
+
<ds-avatar
|
|
69
|
+
type="icon"
|
|
70
|
+
[iconName]="getIconName()"
|
|
71
|
+
size="md"
|
|
72
|
+
[class]="'file-' + attachment().type"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="file-info">
|
|
76
|
+
<div class="file-name" [title]="attachment().name || 'Unknown file'">
|
|
77
|
+
{{ attachment().name || 'Unknown file' }}
|
|
78
|
+
</div>
|
|
79
|
+
<div class="file-type-label">{{ getFileTypeLabel() }}</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
<!-- Loading Overlay -->
|
|
85
|
+
@if (attachment().isLoading) {
|
|
86
|
+
<ds-mobile-loader-overlay [borderRadius]="12" />
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
<!-- Remove Button (hidden when loading) -->
|
|
90
|
+
@if (!attachment().isLoading) {
|
|
91
|
+
<button
|
|
92
|
+
class="remove-btn"
|
|
93
|
+
(mousedown)="handleRemove($event)"
|
|
94
|
+
type="button"
|
|
95
|
+
[attr.aria-label]="'Remove ' + (attachment().name || 'attachment')">
|
|
96
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
97
|
+
<path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
98
|
+
</svg>
|
|
99
|
+
</button>
|
|
100
|
+
}
|
|
101
|
+
</div>
|
|
102
|
+
`
|
|
103
|
+
})
|
|
104
|
+
export class DsMobileAttachmentPreviewComponent {
|
|
105
|
+
/**
|
|
106
|
+
* Attachment data to display
|
|
107
|
+
*/
|
|
108
|
+
attachment = input.required<AttachmentData>();
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Emits when the remove button is clicked
|
|
112
|
+
*/
|
|
113
|
+
remove = output<string>();
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get the file type label (PDF, DOCX, etc.)
|
|
117
|
+
*/
|
|
118
|
+
getFileTypeLabel(): string {
|
|
119
|
+
const type = this.attachment().type.toUpperCase();
|
|
120
|
+
|
|
121
|
+
// Map common types to their display labels
|
|
122
|
+
const labelMap: Record<string, string> = {
|
|
123
|
+
'PDF': 'PDF',
|
|
124
|
+
'DOC': 'DOC',
|
|
125
|
+
'DOCX': 'DOCX',
|
|
126
|
+
'XLS': 'XLS',
|
|
127
|
+
'XLSX': 'XLSX',
|
|
128
|
+
'OTHER': 'FILE'
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return labelMap[type] || type;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get the appropriate icon name based on file type
|
|
136
|
+
*/
|
|
137
|
+
getIconName(): string {
|
|
138
|
+
const type = this.attachment().type;
|
|
139
|
+
|
|
140
|
+
const iconMap: Record<AttachmentFileType, string> = {
|
|
141
|
+
'image': 'remixImageLine',
|
|
142
|
+
'pdf': 'remixFileTextLine',
|
|
143
|
+
'doc': 'remixFileTextLine',
|
|
144
|
+
'docx': 'remixFileTextLine',
|
|
145
|
+
'xls': 'remixFileList3Line',
|
|
146
|
+
'xlsx': 'remixFileList3Line',
|
|
147
|
+
'other': 'remixAttachmentLine'
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
return iconMap[type] || 'remixAttachmentLine';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Handle remove button click
|
|
155
|
+
* Uses mousedown to prevent keyboard from closing
|
|
156
|
+
*/
|
|
157
|
+
handleRemove(event?: MouseEvent): void {
|
|
158
|
+
if (event) {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
event.stopPropagation();
|
|
161
|
+
}
|
|
162
|
+
this.remove.emit(this.attachment().id);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ds-mobile-attachment-preview';
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Component, Input, computed, ViewEncapsulation, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DsAvatarComponent } from '@propbinder/design-system';
|
|
4
|
+
import { DsAppIconComponent, AppIconSize } from '../app-icon/ds-app-icon';
|
|
5
|
+
import { WhitelabelService } from '../../services/whitelabel.service';
|
|
6
|
+
|
|
7
|
+
export type AvatarType = 'initials' | 'photo' | 'icon';
|
|
8
|
+
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
+
export type BadgePosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* DsAvatarWithBadgeComponent
|
|
13
|
+
*
|
|
14
|
+
* Displays an avatar with a logomark badge overlay.
|
|
15
|
+
* Useful for showing user avatars with organization branding.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```html
|
|
19
|
+
* <ds-avatar-with-badge
|
|
20
|
+
* [type]="'initials'"
|
|
21
|
+
* [initials]="'JD'"
|
|
22
|
+
* [size]="'lg'"
|
|
23
|
+
* [badgePosition]="'bottom-right'"
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
@Component({
|
|
28
|
+
selector: 'ds-avatar-with-badge',
|
|
29
|
+
standalone: true,
|
|
30
|
+
imports: [CommonModule, DsAvatarComponent, DsAppIconComponent],
|
|
31
|
+
encapsulation: ViewEncapsulation.Emulated,
|
|
32
|
+
styles: [`
|
|
33
|
+
:host {
|
|
34
|
+
display: inline-block;
|
|
35
|
+
position: relative;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.avatar-badge-container {
|
|
39
|
+
position: relative;
|
|
40
|
+
display: inline-block;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.avatar-badge {
|
|
44
|
+
position: absolute;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
border-radius: 22%;
|
|
49
|
+
/* White shadow acting as a border */
|
|
50
|
+
box-shadow: 0 0 0 2px var(--color-background-primary, #ffffff);
|
|
51
|
+
/* Set background color for app icon inside */
|
|
52
|
+
--app-icon-background: var(--color-app-icon-surface, #5d5fef);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Prevent the app icon from stretching */
|
|
56
|
+
.avatar-badge ds-app-icon {
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/* Badge positioning */
|
|
62
|
+
.avatar-badge--bottom-right {
|
|
63
|
+
bottom: -4px;
|
|
64
|
+
right: -4px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.avatar-badge--bottom-left {
|
|
68
|
+
bottom: -6px;
|
|
69
|
+
left: -6px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.avatar-badge--top-right {
|
|
73
|
+
top: -6px;
|
|
74
|
+
right: -6px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.avatar-badge--top-left {
|
|
78
|
+
top: -6px;
|
|
79
|
+
left: -6px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Badge container sizes - matches the app-icon size inside
|
|
83
|
+
xs/sm/md/lg use 16px (app-icon sm)
|
|
84
|
+
xl uses 32px (app-icon md) */
|
|
85
|
+
.avatar-badge--xs { width: 16px; height: 16px; }
|
|
86
|
+
.avatar-badge--sm { width: 16px; height: 16px; }
|
|
87
|
+
.avatar-badge--md { width: 16px; height: 16px; }
|
|
88
|
+
.avatar-badge--lg { width: 16px; height: 16px; }
|
|
89
|
+
.avatar-badge--xl { width: 24px; height: 24px; }
|
|
90
|
+
`],
|
|
91
|
+
template: `
|
|
92
|
+
<div class="avatar-badge-container">
|
|
93
|
+
<ds-avatar
|
|
94
|
+
[type]="type"
|
|
95
|
+
[size]="size"
|
|
96
|
+
[initials]="initials"
|
|
97
|
+
[src]="src"
|
|
98
|
+
[iconName]="iconName"
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
@if (showBadge) {
|
|
102
|
+
<div [class]="badgeClasses()">
|
|
103
|
+
<ds-app-icon [size]="badgeIconSize()" />
|
|
104
|
+
</div>
|
|
105
|
+
}
|
|
106
|
+
</div>
|
|
107
|
+
`
|
|
108
|
+
})
|
|
109
|
+
export class DsAvatarWithBadgeComponent {
|
|
110
|
+
whitelabelService = inject(WhitelabelService);
|
|
111
|
+
|
|
112
|
+
// Avatar props
|
|
113
|
+
@Input() type: AvatarType = 'initials';
|
|
114
|
+
@Input() size: AvatarSize = 'md';
|
|
115
|
+
@Input() initials: string = '';
|
|
116
|
+
@Input() src: string = '';
|
|
117
|
+
@Input() iconName: string = 'remixUser3Fill';
|
|
118
|
+
|
|
119
|
+
// Badge props
|
|
120
|
+
@Input() showBadge: boolean = true;
|
|
121
|
+
@Input() badgePosition: BadgePosition = 'bottom-right';
|
|
122
|
+
|
|
123
|
+
badgeClasses = computed(() => {
|
|
124
|
+
return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Computed badge icon size that scales with avatar size
|
|
129
|
+
* Maps avatar sizes to appropriate app icon sizes
|
|
130
|
+
*/
|
|
131
|
+
badgeIconSize = computed((): AppIconSize => {
|
|
132
|
+
const sizeMap: Record<AvatarSize, AppIconSize> = {
|
|
133
|
+
xs: 'sm',
|
|
134
|
+
sm: 'sm',
|
|
135
|
+
md: 'sm',
|
|
136
|
+
lg: 'sm',
|
|
137
|
+
xl: 'md',
|
|
138
|
+
};
|
|
139
|
+
return sizeMap[this.size];
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Component, Input } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DsMobileConfirmationSheetComponent } from '../bottom-sheet/ds-mobile-confirmation-sheet';
|
|
4
|
+
import { DsMobileBookingSummaryComponent } from './ds-mobile-booking-summary';
|
|
5
|
+
import { BookingResult } from './ds-mobile-booking-modal';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DsMobileBookingConfirmationWrapperComponent
|
|
9
|
+
*
|
|
10
|
+
* Wrapper component that uses the generic confirmation sheet
|
|
11
|
+
* specifically for booking confirmations.
|
|
12
|
+
*
|
|
13
|
+
* This component handles the booking-specific formatting and
|
|
14
|
+
* uses content projection to pass the booking summary to the generic sheet.
|
|
15
|
+
*/
|
|
16
|
+
@Component({
|
|
17
|
+
selector: 'ds-mobile-booking-confirmation-wrapper',
|
|
18
|
+
standalone: true,
|
|
19
|
+
imports: [
|
|
20
|
+
CommonModule,
|
|
21
|
+
DsMobileConfirmationSheetComponent,
|
|
22
|
+
DsMobileBookingSummaryComponent
|
|
23
|
+
],
|
|
24
|
+
template: `
|
|
25
|
+
<ds-mobile-confirmation-sheet
|
|
26
|
+
[title]="'Booking accepteret'"
|
|
27
|
+
[message]="confirmationMessage"
|
|
28
|
+
[buttonText]="'Luk'"
|
|
29
|
+
[illustrationVariant]="'confirmation'">
|
|
30
|
+
<ng-template #summary>
|
|
31
|
+
<ds-mobile-booking-summary
|
|
32
|
+
[facilityTitle]="booking.facilityTitle"
|
|
33
|
+
[facilityThumbnail]="facilityThumbnail"
|
|
34
|
+
[date]="formattedDate"
|
|
35
|
+
[time]="formattedTime"
|
|
36
|
+
/>
|
|
37
|
+
</ng-template>
|
|
38
|
+
</ds-mobile-confirmation-sheet>
|
|
39
|
+
`
|
|
40
|
+
})
|
|
41
|
+
export class DsMobileBookingConfirmationWrapperComponent {
|
|
42
|
+
@Input() booking!: BookingResult;
|
|
43
|
+
@Input() facilityThumbnail?: string;
|
|
44
|
+
|
|
45
|
+
get confirmationMessage(): string {
|
|
46
|
+
const date = this.booking.selectedDate.fullDate;
|
|
47
|
+
const dateStr = this.formatFullDate(date);
|
|
48
|
+
return `Din booking til den ${dateStr} er bekræftet. Du kan annullere fra startdatoen.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get formattedDate(): string {
|
|
52
|
+
const { dayName, date, monthName } = this.booking.selectedDate;
|
|
53
|
+
return `${dayName} ${date} ${monthName}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get formattedTime(): string {
|
|
57
|
+
const { startTime, endTime } = this.booking.selectedTimeSlot;
|
|
58
|
+
return `${startTime} - ${endTime}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private formatFullDate(date: Date): string {
|
|
62
|
+
const day = date.getDate();
|
|
63
|
+
const monthNames = [
|
|
64
|
+
'januar', 'februar', 'marts', 'april', 'maj', 'juni',
|
|
65
|
+
'juli', 'august', 'september', 'oktober', 'november', 'december'
|
|
66
|
+
];
|
|
67
|
+
const month = monthNames[date.getMonth()];
|
|
68
|
+
const year = date.getFullYear();
|
|
69
|
+
return `${day}. ${month}, ${year}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { ModalController } from '@ionic/angular/standalone';
|
|
3
|
+
import { BaseModalService } from '../../services/base-modal.service';
|
|
4
|
+
import { DsMobileBookingModalComponent, BookingResult, DateOption, TimeSlot } from './ds-mobile-booking-modal';
|
|
5
|
+
import { DsMobileBookingConfirmationWrapperComponent } from './ds-mobile-booking-confirmation-wrapper';
|
|
6
|
+
import { disableModalShadowPointerEvents } from '../bottom-sheet/modal-shadow-fix';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* DsMobileBookingModalService
|
|
10
|
+
*
|
|
11
|
+
* Service for managing the booking modal flow:
|
|
12
|
+
* 1. Opens the booking modal for date/time selection
|
|
13
|
+
* 2. On confirmation, opens the confirmation bottom sheet
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* constructor(private bookingModalService: DsMobileBookingModalService) {}
|
|
18
|
+
*
|
|
19
|
+
* async openBooking() {
|
|
20
|
+
* await this.bookingModalService.open('facility-1', 'Boremaskinen');
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface BookingModalOptions {
|
|
25
|
+
facilityId: string;
|
|
26
|
+
facilityTitle: string;
|
|
27
|
+
facilityThumbnail?: string;
|
|
28
|
+
daysAhead?: number;
|
|
29
|
+
availableDates?: DateOption[];
|
|
30
|
+
availableTimeSlots?: Record<string, TimeSlot[]>;
|
|
31
|
+
labels?: {
|
|
32
|
+
selectFromCalendar?: string;
|
|
33
|
+
confirmBooking?: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Injectable({ providedIn: 'root' })
|
|
38
|
+
export class DsMobileBookingModalService extends BaseModalService {
|
|
39
|
+
constructor(modalController: ModalController) {
|
|
40
|
+
super(modalController);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Open the booking modal for a facility
|
|
45
|
+
*
|
|
46
|
+
* @param options The booking modal options including dynamic data and texts
|
|
47
|
+
* @returns Promise that resolves when the booking flow is complete
|
|
48
|
+
*/
|
|
49
|
+
async open(options: BookingModalOptions): Promise<void> {
|
|
50
|
+
const componentProps: any = {
|
|
51
|
+
facilityId: options.facilityId,
|
|
52
|
+
facilityTitle: options.facilityTitle,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (options.daysAhead !== undefined) componentProps.daysAhead = options.daysAhead;
|
|
56
|
+
if (options.availableDates !== undefined) componentProps.availableDates = options.availableDates;
|
|
57
|
+
if (options.availableTimeSlots !== undefined) componentProps.availableTimeSlots = options.availableTimeSlots;
|
|
58
|
+
if (options.labels?.selectFromCalendar) componentProps.selectFromCalendarText = options.labels.selectFromCalendar;
|
|
59
|
+
if (options.labels?.confirmBooking) componentProps.confirmBookingText = options.labels.confirmBooking;
|
|
60
|
+
|
|
61
|
+
const modal = await this.createModal(
|
|
62
|
+
DsMobileBookingModalComponent,
|
|
63
|
+
componentProps,
|
|
64
|
+
{ keyboardClose: true }
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
await modal.present();
|
|
68
|
+
|
|
69
|
+
const result = await modal.onWillDismiss<BookingResult>();
|
|
70
|
+
|
|
71
|
+
// If user confirmed the booking, dismiss all modals and show confirmation sheet
|
|
72
|
+
if (result.role === 'confirm' && result.data) {
|
|
73
|
+
// Dismiss all open modals (booking modal + facility detail modal)
|
|
74
|
+
await this.dismissAllModals();
|
|
75
|
+
|
|
76
|
+
// Show confirmation sheet after all modals are dismissed
|
|
77
|
+
await this.openConfirmationSheet(result.data, options.facilityThumbnail);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Dismiss all open modals
|
|
83
|
+
*/
|
|
84
|
+
private async dismissAllModals(): Promise<void> {
|
|
85
|
+
let topModal = await this.modalController.getTop();
|
|
86
|
+
while (topModal) {
|
|
87
|
+
await topModal.dismiss();
|
|
88
|
+
topModal = await this.modalController.getTop();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Open the confirmation bottom sheet after successful booking
|
|
94
|
+
*
|
|
95
|
+
* @param booking The booking result data
|
|
96
|
+
* @param facilityThumbnail Optional thumbnail image URL
|
|
97
|
+
* @returns Promise that resolves when the sheet is dismissed
|
|
98
|
+
*/
|
|
99
|
+
private async openConfirmationSheet(
|
|
100
|
+
booking: BookingResult,
|
|
101
|
+
facilityThumbnail?: string
|
|
102
|
+
): Promise<void> {
|
|
103
|
+
const sheet = await this.modalController.create({
|
|
104
|
+
component: DsMobileBookingConfirmationWrapperComponent,
|
|
105
|
+
componentProps: {
|
|
106
|
+
booking,
|
|
107
|
+
facilityThumbnail
|
|
108
|
+
},
|
|
109
|
+
breakpoints: [0, 1],
|
|
110
|
+
initialBreakpoint: 1,
|
|
111
|
+
handle: true,
|
|
112
|
+
cssClass: ['ds-bottom-sheet', 'auto-height'],
|
|
113
|
+
backdropDismiss: true,
|
|
114
|
+
showBackdrop: true,
|
|
115
|
+
mode: 'ios'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await sheet.present();
|
|
119
|
+
disableModalShadowPointerEvents(sheet);
|
|
120
|
+
}
|
|
121
|
+
}
|