@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,120 @@
|
|
|
1
|
+
import { Component, input } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DsMobileEmptyStateComponent
|
|
6
|
+
*
|
|
7
|
+
* Reusable empty state component for displaying when there's no content.
|
|
8
|
+
* Used in chat modals, comment sections, and other list-based views.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Optional image display
|
|
12
|
+
* - Customizable title and description
|
|
13
|
+
* - Consistent styling across the app
|
|
14
|
+
* - Flexible image sizing
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```html
|
|
18
|
+
* <!-- With image -->
|
|
19
|
+
* <ds-mobile-empty-state
|
|
20
|
+
* [imageSrc]="'/Assets/empty-state-inquiry.svg'"
|
|
21
|
+
* [imageAlt]="'No messages'"
|
|
22
|
+
* [title]="'Ingen beskeder endnu'"
|
|
23
|
+
* [description]="'Start samtalen ved at sende en besked'">
|
|
24
|
+
* </ds-mobile-empty-state>
|
|
25
|
+
*
|
|
26
|
+
* <!-- Without image -->
|
|
27
|
+
* <ds-mobile-empty-state
|
|
28
|
+
* [title]="'Ingen resultater'"
|
|
29
|
+
* [description]="'Prøv at justere dine søgekriterier'">
|
|
30
|
+
* </ds-mobile-empty-state>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'ds-mobile-empty-state',
|
|
35
|
+
standalone: true,
|
|
36
|
+
imports: [CommonModule],
|
|
37
|
+
styles: [`
|
|
38
|
+
:host {
|
|
39
|
+
display: block;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.empty-state {
|
|
43
|
+
display: flex;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
padding: 60px 20px;
|
|
48
|
+
text-align: center;
|
|
49
|
+
gap: 24px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.empty-state-image {
|
|
53
|
+
width: 96px;
|
|
54
|
+
height: 96px;
|
|
55
|
+
margin: 0 auto;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.empty-state-text {
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
gap: 8px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.empty-state-title {
|
|
65
|
+
font-family: 'Brockmann', sans-serif;
|
|
66
|
+
font-size: var(--font-size-base);
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
line-height: 1.3;
|
|
69
|
+
color: var(--color-text-primary, #1a1a1a);
|
|
70
|
+
margin: 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.empty-state-description {
|
|
74
|
+
font-family: 'Brockmann', sans-serif;
|
|
75
|
+
font-size: var(--font-size-sm);
|
|
76
|
+
font-weight: 400;
|
|
77
|
+
line-height: 1.4;
|
|
78
|
+
color: var(--color-text-secondary, #737373);
|
|
79
|
+
margin: 0;
|
|
80
|
+
}
|
|
81
|
+
`],
|
|
82
|
+
template: `
|
|
83
|
+
<div class="empty-state">
|
|
84
|
+
@if (imageSrc()) {
|
|
85
|
+
<img
|
|
86
|
+
[src]="imageSrc()"
|
|
87
|
+
[alt]="imageAlt()"
|
|
88
|
+
class="empty-state-image" />
|
|
89
|
+
}
|
|
90
|
+
<div class="empty-state-text">
|
|
91
|
+
<h3 class="empty-state-title">{{ title() }}</h3>
|
|
92
|
+
@if (description()) {
|
|
93
|
+
<p class="empty-state-description">{{ description() }}</p>
|
|
94
|
+
}
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
`
|
|
98
|
+
})
|
|
99
|
+
export class DsMobileEmptyStateComponent {
|
|
100
|
+
/**
|
|
101
|
+
* Source URL for the empty state image (optional)
|
|
102
|
+
*/
|
|
103
|
+
imageSrc = input<string>('');
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Alt text for the image
|
|
107
|
+
*/
|
|
108
|
+
imageAlt = input<string>('Empty state');
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Title text for the empty state
|
|
112
|
+
*/
|
|
113
|
+
title = input.required<string>();
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Description text for the empty state (optional)
|
|
117
|
+
*/
|
|
118
|
+
description = input<string>('');
|
|
119
|
+
}
|
|
120
|
+
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { Component, input, output, ElementRef, OnInit, OnDestroy, AfterViewInit, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DsIconButtonComponent } from '@propbinder/design-system';
|
|
4
|
+
import { Platform } from '@ionic/angular/standalone';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DsMobileFabComponent
|
|
8
|
+
*
|
|
9
|
+
* Floating Action Button component for mobile interfaces.
|
|
10
|
+
* A prominent circular button that floats above the content.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Configurable positioning (bottom-right, bottom-left, bottom-center)
|
|
14
|
+
* - Uses design system theming variables
|
|
15
|
+
* - Dynamically calculates position above tab bar (including safe areas)
|
|
16
|
+
* - Maintains consistent 20px gap from tab bar
|
|
17
|
+
* - Handles iOS safe areas
|
|
18
|
+
* - Accessible with ARIA labels
|
|
19
|
+
* - Smooth entrance animation
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```html
|
|
23
|
+
* <ds-mobile-fab
|
|
24
|
+
* icon="remixAddLine"
|
|
25
|
+
* [position]="'bottom-right'"
|
|
26
|
+
* (fabClick)="handleAddClick()"
|
|
27
|
+
* ariaLabel="Add new inquiry">
|
|
28
|
+
* </ds-mobile-fab>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
@Component({
|
|
32
|
+
selector: 'ds-mobile-fab',
|
|
33
|
+
standalone: true,
|
|
34
|
+
imports: [CommonModule, DsIconButtonComponent],
|
|
35
|
+
host: {
|
|
36
|
+
'[class.fab-bottom-right]': 'position() === "bottom-right"',
|
|
37
|
+
'[class.fab-bottom-left]': 'position() === "bottom-left"',
|
|
38
|
+
'[class.fab-bottom-center]': 'position() === "bottom-center"'
|
|
39
|
+
},
|
|
40
|
+
template: `
|
|
41
|
+
<div class="fab-container">
|
|
42
|
+
<ds-icon-button
|
|
43
|
+
[icon]="icon()"
|
|
44
|
+
variant="primary"
|
|
45
|
+
[size]="size()"
|
|
46
|
+
[ariaLabel]="ariaLabel()"
|
|
47
|
+
[disabled]="disabled()"
|
|
48
|
+
(clicked)="handleClick()"
|
|
49
|
+
class="fab-button">
|
|
50
|
+
</ds-icon-button>
|
|
51
|
+
</div>
|
|
52
|
+
`,
|
|
53
|
+
styles: [`
|
|
54
|
+
:host {
|
|
55
|
+
display: block;
|
|
56
|
+
position: fixed;
|
|
57
|
+
z-index: 1000;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.fab-container {
|
|
62
|
+
position: relative;
|
|
63
|
+
pointer-events: none;
|
|
64
|
+
animation: fabEnter 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes fabEnter {
|
|
68
|
+
from {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
transform: scale(0.8);
|
|
71
|
+
}
|
|
72
|
+
to {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
transform: scale(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Position variants - now applied to :host */
|
|
79
|
+
|
|
80
|
+
/* MOBILE: Use dynamically calculated position based on actual tab bar height */
|
|
81
|
+
/* The JavaScript calculates the actual tab bar height (including padding) and sets --fab-calculated-bottom */
|
|
82
|
+
/* Fallback to 84px (64px base + 20px gap) if calculation hasn't run yet */
|
|
83
|
+
:host(.fab-bottom-right) {
|
|
84
|
+
bottom: var(--fab-calculated-bottom, 84px);
|
|
85
|
+
right: 20px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
:host(.fab-bottom-left) {
|
|
89
|
+
bottom: var(--fab-calculated-bottom, 84px);
|
|
90
|
+
left: 20px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
:host(.fab-bottom-center) {
|
|
94
|
+
bottom: var(--fab-calculated-bottom, 84px);
|
|
95
|
+
left: 50%;
|
|
96
|
+
transform: translateX(-50%);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Horizontal safe area support (all platforms) */
|
|
100
|
+
@supports (padding-right: env(safe-area-inset-right)) {
|
|
101
|
+
:host(.fab-bottom-right) {
|
|
102
|
+
right: calc(20px + env(safe-area-inset-right));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@supports (padding-left: env(safe-area-inset-left)) {
|
|
107
|
+
:host(.fab-bottom-left) {
|
|
108
|
+
left: calc(20px + env(safe-area-inset-left));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* DESKTOP: Larger spacing for desktop screens */
|
|
113
|
+
@media (min-width: 768px) {
|
|
114
|
+
:host-context(.plt-desktop).fab-bottom-right {
|
|
115
|
+
bottom: 40px;
|
|
116
|
+
right: 40px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
:host-context(.plt-desktop).fab-bottom-left {
|
|
120
|
+
bottom: 40px;
|
|
121
|
+
left: 40px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
:host-context(.plt-desktop).fab-bottom-center {
|
|
125
|
+
bottom: 40px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@supports (padding-right: env(safe-area-inset-right)) {
|
|
129
|
+
:host-context(.plt-desktop).fab-bottom-right {
|
|
130
|
+
right: calc(40px + env(safe-area-inset-right));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@supports (padding-left: env(safe-area-inset-left)) {
|
|
135
|
+
:host-context(.plt-desktop).fab-bottom-left {
|
|
136
|
+
left: calc(40px + env(safe-area-inset-left));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* FAB Button Styling */
|
|
142
|
+
.fab-button {
|
|
143
|
+
pointer-events: auto;
|
|
144
|
+
display: block;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Override icon button to be circular and use brand colors */
|
|
148
|
+
.fab-button::ng-deep button {
|
|
149
|
+
width: 56px !important;
|
|
150
|
+
height: 56px !important;
|
|
151
|
+
min-width: 56px !important;
|
|
152
|
+
min-height: 56px !important;
|
|
153
|
+
border-radius: 50% !important;
|
|
154
|
+
padding: 0 !important;
|
|
155
|
+
display: flex !important;
|
|
156
|
+
align-items: center !important;
|
|
157
|
+
justify-content: center !important;
|
|
158
|
+
|
|
159
|
+
/* Use whitelabel theming variables */
|
|
160
|
+
background: var(--color-accent) !important;
|
|
161
|
+
color: var(--color-on-accent) !important;
|
|
162
|
+
border: none !important;
|
|
163
|
+
|
|
164
|
+
/* Elevation shadow for prominence */
|
|
165
|
+
box-shadow:
|
|
166
|
+
0 3px 5px -1px rgba(0, 0, 0, 0.2),
|
|
167
|
+
0 6px 10px 0 rgba(0, 0, 0, 0.14),
|
|
168
|
+
0 1px 18px 0 rgba(0, 0, 0, 0.12);
|
|
169
|
+
|
|
170
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Hover state */
|
|
174
|
+
.fab-button::ng-deep button:hover:not(:disabled) {
|
|
175
|
+
background: var(--color-accent-hover) !important;
|
|
176
|
+
box-shadow:
|
|
177
|
+
0 5px 7px -2px rgba(0, 0, 0, 0.2),
|
|
178
|
+
0 8px 14px 0 rgba(0, 0, 0, 0.14),
|
|
179
|
+
0 2px 22px 0 rgba(0, 0, 0, 0.12);
|
|
180
|
+
transform: scale(1.05);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Active state */
|
|
184
|
+
.fab-button::ng-deep button:active:not(:disabled) {
|
|
185
|
+
background: var(--color-accent-active) !important;
|
|
186
|
+
box-shadow:
|
|
187
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.2),
|
|
188
|
+
0 4px 8px 0 rgba(0, 0, 0, 0.14),
|
|
189
|
+
0 1px 14px 0 rgba(0, 0, 0, 0.12);
|
|
190
|
+
transform: scale(0.95);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Disabled state */
|
|
194
|
+
.fab-button::ng-deep button:disabled {
|
|
195
|
+
opacity: 0.5;
|
|
196
|
+
cursor: not-allowed;
|
|
197
|
+
box-shadow:
|
|
198
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.1),
|
|
199
|
+
0 4px 8px 0 rgba(0, 0, 0, 0.08),
|
|
200
|
+
0 1px 14px 0 rgba(0, 0, 0, 0.06);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Icon styling */
|
|
204
|
+
.fab-button::ng-deep button .btn__icon,
|
|
205
|
+
.fab-button::ng-deep button .btn__icon svg {
|
|
206
|
+
color: var(--color-on-accent) !important;
|
|
207
|
+
fill: var(--color-on-accent) !important;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Ensure icon is centered and sized properly */
|
|
211
|
+
.fab-button::ng-deep button .btn__icon {
|
|
212
|
+
display: flex !important;
|
|
213
|
+
align-items: center !important;
|
|
214
|
+
justify-content: center !important;
|
|
215
|
+
flex-shrink: 0 !important;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.fab-button::ng-deep button .btn__icon svg {
|
|
219
|
+
width: 24px !important;
|
|
220
|
+
height: 24px !important;
|
|
221
|
+
}
|
|
222
|
+
`]
|
|
223
|
+
})
|
|
224
|
+
export class DsMobileFabComponent implements AfterViewInit, OnDestroy {
|
|
225
|
+
private elementRef = inject(ElementRef);
|
|
226
|
+
private platform = inject(Platform);
|
|
227
|
+
private resizeObserver?: ResizeObserver;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Icon name from the design system icon library
|
|
231
|
+
* @default 'remixAddLine'
|
|
232
|
+
*/
|
|
233
|
+
icon = input<string>('remixAddLine');
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Position of the FAB on the screen
|
|
237
|
+
* @default 'bottom-right'
|
|
238
|
+
*/
|
|
239
|
+
position = input<'bottom-right' | 'bottom-left' | 'bottom-center'>('bottom-right');
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Size of the FAB button
|
|
243
|
+
* Note: FAB is always 56px circular, but this affects the icon size
|
|
244
|
+
* @default 'md'
|
|
245
|
+
*/
|
|
246
|
+
size = input<'sm' | 'md' | 'lg'>('md');
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* ARIA label for accessibility
|
|
250
|
+
* @required - Always provide a descriptive label
|
|
251
|
+
*/
|
|
252
|
+
ariaLabel = input.required<string>();
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Whether the FAB is disabled
|
|
256
|
+
* @default false
|
|
257
|
+
*/
|
|
258
|
+
disabled = input<boolean>(false);
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Emitted when the FAB is clicked
|
|
262
|
+
*/
|
|
263
|
+
clicked = output<void>();
|
|
264
|
+
|
|
265
|
+
ngAfterViewInit(): void {
|
|
266
|
+
// Only calculate position on mobile (not desktop)
|
|
267
|
+
if (!this.platform.is('desktop')) {
|
|
268
|
+
this.calculatePosition();
|
|
269
|
+
this.setupResizeObserver();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Calculate the FAB position dynamically based on actual tab bar height
|
|
275
|
+
*/
|
|
276
|
+
private calculatePosition(): void {
|
|
277
|
+
const tabBar = document.querySelector('ion-tab-bar[slot="bottom"]');
|
|
278
|
+
if (tabBar) {
|
|
279
|
+
const rect = tabBar.getBoundingClientRect();
|
|
280
|
+
const actualHeight = rect.height;
|
|
281
|
+
const gap = 20;
|
|
282
|
+
const bottom = actualHeight + gap;
|
|
283
|
+
|
|
284
|
+
const host = this.elementRef.nativeElement as HTMLElement;
|
|
285
|
+
host.style.setProperty('--fab-calculated-bottom', `${bottom}px`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Setup ResizeObserver to recalculate position when tab bar size changes
|
|
291
|
+
* (e.g., when device rotates or safe area changes)
|
|
292
|
+
*/
|
|
293
|
+
private setupResizeObserver(): void {
|
|
294
|
+
const tabBar = document.querySelector('ion-tab-bar[slot="bottom"]');
|
|
295
|
+
if (tabBar && 'ResizeObserver' in window) {
|
|
296
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
297
|
+
this.calculatePosition();
|
|
298
|
+
});
|
|
299
|
+
this.resizeObserver.observe(tabBar);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Handle button click
|
|
305
|
+
*/
|
|
306
|
+
handleClick(): void {
|
|
307
|
+
if (!this.disabled()) {
|
|
308
|
+
this.clicked.emit();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
ngOnDestroy(): void {
|
|
313
|
+
this.resizeObserver?.disconnect();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DsMobileFabComponent } from './ds-mobile-fab';
|
package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
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 { DsIconComponent } from '@propbinder/design-system';
|
|
5
|
+
import { NewFacilityData } from './ds-mobile-facility-creation-modal';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DsMobileFacilityCreationConfirmationWrapperComponent
|
|
9
|
+
*
|
|
10
|
+
* Wrapper component that uses the generic confirmation sheet
|
|
11
|
+
* specifically for facility creation confirmations.
|
|
12
|
+
*
|
|
13
|
+
* This component displays a summary of the created facility.
|
|
14
|
+
*/
|
|
15
|
+
@Component({
|
|
16
|
+
selector: 'ds-mobile-facility-creation-confirmation-wrapper',
|
|
17
|
+
standalone: true,
|
|
18
|
+
imports: [
|
|
19
|
+
CommonModule,
|
|
20
|
+
DsMobileConfirmationSheetComponent,
|
|
21
|
+
DsIconComponent
|
|
22
|
+
],
|
|
23
|
+
template: `
|
|
24
|
+
<ds-mobile-confirmation-sheet
|
|
25
|
+
[title]="'Facilitet oprettet'"
|
|
26
|
+
[message]="'Din nye facilitet er nu oprettet og klar til booking.'"
|
|
27
|
+
[buttonText]="'Luk'"
|
|
28
|
+
[illustrationVariant]="'confirmation'">
|
|
29
|
+
<ng-template #summary>
|
|
30
|
+
<div class="facility-summary">
|
|
31
|
+
<div class="summary-item">
|
|
32
|
+
<div class="summary-details">
|
|
33
|
+
<h3 class="summary-title">{{ facilityData.title }}</h3>
|
|
34
|
+
<p class="summary-description">{{ facilityData.description }}</p>
|
|
35
|
+
|
|
36
|
+
<div class="summary-info-list">
|
|
37
|
+
<div class="info-row">
|
|
38
|
+
<ds-icon name="remixGroupLine" size="16px" color="tertiary" />
|
|
39
|
+
<span class="info-label">{{ facilityData.whoCanBook }}</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="info-row">
|
|
42
|
+
<ds-icon name="remixCalendarLine" size="16px" color="tertiary" />
|
|
43
|
+
<span class="info-label">{{ facilityData.whenCanBook }}</span>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="info-row">
|
|
46
|
+
<ds-icon name="remixPriceTag3Line" size="16px" color="tertiary" />
|
|
47
|
+
<span class="info-label">{{ facilityData.price }}</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="info-row">
|
|
50
|
+
<ds-icon name="remixKeyLine" size="16px" color="tertiary" />
|
|
51
|
+
<span class="info-label">{{ facilityData.accessRequirements }}</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</ng-template>
|
|
58
|
+
</ds-mobile-confirmation-sheet>
|
|
59
|
+
`,
|
|
60
|
+
styles: [`
|
|
61
|
+
.facility-summary {
|
|
62
|
+
width: 100%;
|
|
63
|
+
background: var(--color-background-neutral-secondary, #f5f5f5);
|
|
64
|
+
border-radius: 16px;
|
|
65
|
+
padding: 16px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.summary-item {
|
|
69
|
+
display: flex;
|
|
70
|
+
gap: 12px;
|
|
71
|
+
align-items: flex-start;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.summary-details {
|
|
75
|
+
flex: 1;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
gap: 12px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.summary-title {
|
|
82
|
+
font-family: 'Brockmann', sans-serif;
|
|
83
|
+
font-size: var(--font-size-base, 16px);
|
|
84
|
+
font-weight: 600;
|
|
85
|
+
color: var(--text-color-default-primary, #202227);
|
|
86
|
+
margin: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.summary-description {
|
|
90
|
+
font-family: 'Brockmann', sans-serif;
|
|
91
|
+
font-size: var(--font-size-sm, 14px);
|
|
92
|
+
font-weight: 400;
|
|
93
|
+
line-height: 1.5;
|
|
94
|
+
color: var(--text-color-default-secondary, #545b66);
|
|
95
|
+
margin: 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.summary-info-list {
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
gap: 8px;
|
|
102
|
+
margin-top: 4px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.info-row {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 8px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.info-label {
|
|
112
|
+
font-family: 'Brockmann', sans-serif;
|
|
113
|
+
font-size: var(--font-size-sm, 14px);
|
|
114
|
+
font-weight: 400;
|
|
115
|
+
color: var(--text-color-default-secondary, #545b66);
|
|
116
|
+
}
|
|
117
|
+
`]
|
|
118
|
+
})
|
|
119
|
+
export class DsMobileFacilityCreationConfirmationWrapperComponent {
|
|
120
|
+
@Input() facilityData!: NewFacilityData;
|
|
121
|
+
}
|