@propbinder/mobile-design 0.2.50 → 0.2.53
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/fesm2022/propbinder-mobile-design.mjs +26206 -0
- package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
- package/index.d.ts +8193 -0
- package/package.json +39 -3
- package/ng-package.json +0 -24
- package/src/animations/page-transitions.ts +0 -165
- package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -102
- package/src/components/action-list-item/index.ts +0 -2
- package/src/components/app-icon/ds-app-icon.ts +0 -133
- package/src/components/app-icon/index.ts +0 -2
- package/src/components/attachment-preview/ds-mobile-attachment-preview.css +0 -139
- package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +0 -164
- package/src/components/attachment-preview/index.ts +0 -1
- package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -142
- package/src/components/avatar-with-badge/index.ts +0 -2
- package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +0 -71
- package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +0 -121
- package/src/components/booking-modal/ds-mobile-booking-modal.ts +0 -598
- package/src/components/booking-modal/ds-mobile-booking-summary.ts +0 -161
- package/src/components/booking-modal/index.ts +0 -4
- package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -266
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +0 -146
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +0 -156
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -101
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -169
- package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +0 -211
- package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -578
- package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +0 -614
- package/src/components/bottom-sheet/index.ts +0 -8
- package/src/components/bottom-sheet/modal-shadow-fix.ts +0 -42
- package/src/components/card-inline/ds-mobile-card-inline.ts +0 -301
- package/src/components/card-inline/index.ts +0 -2
- package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +0 -118
- package/src/components/card-inline-banner/index.ts +0 -1
- package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +0 -120
- package/src/components/card-inline-contact/index.ts +0 -1
- package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +0 -141
- package/src/components/card-inline-file/index.ts +0 -1
- package/src/components/chat-modal/ds-mobile-chat-modal.css +0 -159
- package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +0 -105
- package/src/components/chat-modal/ds-mobile-chat-modal.ts +0 -918
- package/src/components/chat-modal/index.ts +0 -8
- package/src/components/comment/ds-mobile-comment.ts +0 -568
- package/src/components/comment/index.ts +0 -2
- package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
- package/src/components/contact-list-item/index.ts +0 -2
- package/src/components/content/ds-mobile-content.ts +0 -139
- package/src/components/content/index.ts +0 -2
- package/src/components/dropdown/ds-mobile-dropdown.css +0 -199
- package/src/components/dropdown/ds-mobile-dropdown.ts +0 -340
- package/src/components/dropdown/index.ts +0 -2
- package/src/components/ds-mobile-tabs.css +0 -407
- package/src/components/ds-mobile-tabs.ts +0 -216
- package/src/components/empty-state/ds-mobile-empty-state.ts +0 -120
- package/src/components/empty-state/index.ts +0 -2
- package/src/components/fab/ds-mobile-fab.ts +0 -315
- package/src/components/fab/index.ts +0 -1
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +0 -121
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +0 -189
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +0 -135
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +0 -656
- package/src/components/facility-creation-modal/index.ts +0 -9
- package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +0 -105
- package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +0 -188
- package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +0 -460
- package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +0 -134
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +0 -69
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +0 -379
- package/src/components/facility-detail-modal/index.ts +0 -2
- package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
- package/src/components/file-attachment/index.ts +0 -2
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +0 -214
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -84
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -424
- package/src/components/handbook-detail-modal/index.ts +0 -3
- package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -175
- package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -533
- package/src/components/handbook-folder/index.ts +0 -4
- package/src/components/header-content/ds-mobile-header-content.ts +0 -222
- package/src/components/header-content/index.ts +0 -2
- package/src/components/illustration/ds-mobile-illustration.ts +0 -124
- package/src/components/illustration/index.ts +0 -2
- package/src/components/index.ts +0 -124
- package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -361
- package/src/components/inline-photo/index.ts +0 -1
- package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +0 -132
- package/src/components/inline-tabs/index.ts +0 -2
- package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +0 -350
- package/src/components/interactive-list-item-booking/index.ts +0 -1
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -321
- package/src/components/interactive-list-item-inquiry/index.ts +0 -2
- package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -237
- package/src/components/interactive-list-item-message/index.ts +0 -2
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -549
- package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
- package/src/components/interactive-list-item-post/index.ts +0 -13
- package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -315
- package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -202
- package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -484
- package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -377
- package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
- package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
- package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -296
- package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
- package/src/components/lightbox/index.ts +0 -22
- package/src/components/list-item/ds-mobile-list-item.ts +0 -603
- package/src/components/list-item/index.ts +0 -2
- package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
- package/src/components/list-item-static/index.ts +0 -2
- package/src/components/loader-overlay/ds-mobile-loader-overlay.css +0 -49
- package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +0 -77
- package/src/components/loader-overlay/index.ts +0 -1
- package/src/components/logo/ds-logo.ts +0 -95
- package/src/components/logo/index.ts +0 -2
- package/src/components/message-bubble/ds-mobile-message-bubble.ts +0 -633
- package/src/components/message-bubble/index.ts +0 -7
- package/src/components/message-composer/ds-mobile-message-composer.ts +0 -1146
- package/src/components/message-composer/index.ts +0 -7
- package/src/components/modal/ds-mobile-modal.css +0 -163
- package/src/components/modal/ds-mobile-modal.service.ts +0 -329
- package/src/components/modal/index.ts +0 -8
- package/src/components/modal-base/ds-mobile-modal-base.css +0 -378
- package/src/components/modal-base/ds-mobile-modal-base.ts +0 -261
- package/src/components/modal-base/index.ts +0 -2
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +0 -112
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +0 -93
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +0 -442
- package/src/components/new-inquiry-modal/index.ts +0 -4
- package/src/components/offline-banner/ds-mobile-offline-banner.ts +0 -135
- package/src/components/offline-banner/index.ts +0 -1
- package/src/components/page-details/ds-mobile-page-details.css +0 -83
- package/src/components/page-details/ds-mobile-page-details.ts +0 -282
- package/src/components/page-details/index.ts +0 -2
- package/src/components/page-main/ds-mobile-page-main.css +0 -68
- package/src/components/page-main/ds-mobile-page-main.ts +0 -421
- package/src/components/page-main/index.ts +0 -2
- package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
- package/src/components/post-composer/index.ts +0 -2
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +0 -390
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -108
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -722
- package/src/components/post-detail-modal/index.ts +0 -9
- package/src/components/property-banner/ds-mobile-property-banner.ts +0 -95
- package/src/components/property-banner/index.ts +0 -2
- package/src/components/section/ds-mobile-section.ts +0 -263
- package/src/components/section/index.ts +0 -2
- package/src/components/shared/directives/index.ts +0 -2
- package/src/components/shared/directives/long-press.directive.ts +0 -212
- package/src/components/shared/index.ts +0 -3
- package/src/components/shared/mobile-modal-base.ts +0 -457
- package/src/components/shared/mobile-page-base.ts +0 -204
- package/src/components/swiper/ds-mobile-swiper-with-nav.ts +0 -160
- package/src/components/swiper/ds-mobile-swiper.ts +0 -327
- package/src/components/swiper/index.ts +0 -3
- package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +0 -129
- package/src/components/system-message-banner/index.ts +0 -2
- package/src/components/tab-bar/ds-mobile-tab-bar.css +0 -533
- package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -735
- package/src/components/tab-bar/index.ts +0 -2
- package/src/components/tabs/ds-mobile-tabs.css +0 -25
- package/src/components/tabs/ds-mobile-tabs.ts +0 -89
- package/src/components/tabs/index.ts +0 -2
- package/src/components/text-input/ds-text-input.ts +0 -287
- package/src/components/text-input/index.ts +0 -2
- package/src/examples/booking.page.ts +0 -434
- package/src/examples/community.page.ts +0 -776
- package/src/examples/handbook.page.ts +0 -324
- package/src/examples/home.page.ts +0 -347
- package/src/examples/index.ts +0 -12
- package/src/examples/inquiries.example.ts +0 -273
- package/src/examples/inquiry-detail.example.css +0 -189
- package/src/examples/inquiry-detail.example.ts +0 -415
- package/src/examples/mobile-tabs-example.component.ts +0 -208
- package/src/examples/post-create.page.ts +0 -311
- package/src/examples/post-detail.page.ts +0 -296
- package/src/examples/sign-in.page.ts +0 -291
- package/src/examples/whitelabel-demo-modal.component.ts +0 -1094
- package/src/examples/whitelabel-demo-modal.service.ts +0 -77
- package/src/models/index.ts +0 -7
- package/src/models/post.model.ts +0 -41
- package/src/pages/community.page.ts +0 -769
- package/src/pages/handbook.page.ts +0 -388
- package/src/pages/home.page.ts +0 -303
- package/src/pages/index.ts +0 -11
- package/src/pages/inquiries.example.ts +0 -273
- package/src/pages/inquiry-detail.example.css +0 -189
- package/src/pages/inquiry-detail.example.ts +0 -415
- package/src/pages/mobile-tabs-example.component.ts +0 -179
- package/src/pages/post-create.page.ts +0 -311
- package/src/pages/post-detail.page.ts +0 -296
- package/src/pages/sign-in.page.ts +0 -291
- package/src/pages/whitelabel-demo-modal.component.ts +0 -1094
- package/src/pages/whitelabel-demo-modal.service.ts +0 -77
- package/src/public-api.ts +0 -6
- package/src/services/base-modal.service.ts +0 -101
- package/src/services/index.ts +0 -11
- package/src/services/posts.service.ts +0 -542
- package/src/services/tracking-permission.service.ts +0 -88
- package/src/services/user.service.ts +0 -60
- package/src/services/whitelabel.service.ts +0 -675
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -13
- /package/{src/assets → assets}/fonts/Brockmann-Bold.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-BoldItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-Medium.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-MediumItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-Regular.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-RegularItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-SemiBold.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann_desktop_license.pdf +0 -0
- /package/{src/assets → assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
- /package/{src/styles → styles}/ionic.css +0 -0
- /package/{src/components/shared → styles}/mobile-common.css +0 -0
- /package/{src/components/shared → styles}/mobile-page-base.css +0 -0
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
import { Component, signal, ViewChild, ElementRef, AfterViewInit, OnInit, inject } from '@angular/core';
|
|
2
|
-
import { CommonModule } from '@angular/common';
|
|
3
|
-
import { FormsModule } from '@angular/forms';
|
|
4
|
-
import { ModalController, IonContent } from '@ionic/angular/standalone';
|
|
5
|
-
import { Capacitor } from '@capacitor/core';
|
|
6
|
-
import { Keyboard } from '@capacitor/keyboard';
|
|
7
|
-
import { FilePicker } from '@capawesome/capacitor-file-picker';
|
|
8
|
-
import { StatusBar } from '@capacitor/status-bar';
|
|
9
|
-
import { DsIconButtonComponent } from '@propbinder/design-system';
|
|
10
|
-
import { WhitelabelService } from '../../services/whitelabel.service';
|
|
11
|
-
import { DsMobileBottomSheetHeaderComponent } from './ds-mobile-bottom-sheet-header';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* DsMobilePostCreateBottomSheetComponent
|
|
15
|
-
*
|
|
16
|
-
* Bottom sheet modal for creating new posts in the community feed.
|
|
17
|
-
* This is the modal content that gets displayed in the bottom sheet.
|
|
18
|
-
* Features Threads-inspired interface with rich text editing capabilities.
|
|
19
|
-
*
|
|
20
|
-
* Auto-focuses the textarea and brings up the keyboard when opened.
|
|
21
|
-
*
|
|
22
|
-
* Usage: Use with DsMobileBottomSheetService to present as a bottom sheet
|
|
23
|
-
*/
|
|
24
|
-
@Component({
|
|
25
|
-
selector: 'ds-mobile-post-create-bottom-sheet',
|
|
26
|
-
standalone: true,
|
|
27
|
-
imports: [
|
|
28
|
-
CommonModule,
|
|
29
|
-
FormsModule,
|
|
30
|
-
IonContent,
|
|
31
|
-
DsIconButtonComponent,
|
|
32
|
-
DsMobileBottomSheetHeaderComponent
|
|
33
|
-
],
|
|
34
|
-
styles: [`
|
|
35
|
-
:host {
|
|
36
|
-
display: flex;
|
|
37
|
-
flex-direction: column;
|
|
38
|
-
height: 100%;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* ============================================
|
|
42
|
-
CONTENT AREA
|
|
43
|
-
============================================ */
|
|
44
|
-
|
|
45
|
-
ion-content {
|
|
46
|
-
--background: var(--color-background-neutral-primary, #ffffff);
|
|
47
|
-
--padding-top: 0;
|
|
48
|
-
--padding-bottom: 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.post-create-container {
|
|
52
|
-
padding: 24px 16px 16px;
|
|
53
|
-
max-width: 640px;
|
|
54
|
-
margin: 0 auto;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.post-composer {
|
|
58
|
-
display: flex;
|
|
59
|
-
gap: 12px;
|
|
60
|
-
align-items: flex-start;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.post-composer__main {
|
|
64
|
-
flex: 1;
|
|
65
|
-
min-width: 0;
|
|
66
|
-
display: flex;
|
|
67
|
-
flex-direction: column;
|
|
68
|
-
gap: 12px;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.post-composer__header {
|
|
72
|
-
display: flex;
|
|
73
|
-
align-items: center;
|
|
74
|
-
gap: 8px;
|
|
75
|
-
height: 32px;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.post-composer__username {
|
|
79
|
-
font-family: 'Brockmann', sans-serif;
|
|
80
|
-
font-size: var(--font-size-base);
|
|
81
|
-
font-weight: 600;
|
|
82
|
-
line-height: 20px;
|
|
83
|
-
letter-spacing: -0.3px;
|
|
84
|
-
color: var(--color-text-primary, #1a1a1a);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.post-composer__textarea {
|
|
88
|
-
width: 100%;
|
|
89
|
-
min-height: 60px;
|
|
90
|
-
max-height: 400px;
|
|
91
|
-
border: none;
|
|
92
|
-
outline: none;
|
|
93
|
-
resize: none;
|
|
94
|
-
font-family: 'Brockmann', sans-serif;
|
|
95
|
-
font-size: var(--font-size-base);
|
|
96
|
-
font-weight: 400;
|
|
97
|
-
line-height: 22px;
|
|
98
|
-
letter-spacing: -0.3px;
|
|
99
|
-
color: var(--color-text-primary, #1a1a1a);
|
|
100
|
-
background: transparent;
|
|
101
|
-
padding: 0;
|
|
102
|
-
cursor: text;
|
|
103
|
-
overflow-y: auto;
|
|
104
|
-
/* Auto-resize as user types */
|
|
105
|
-
field-sizing: content;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.post-composer__textarea::placeholder {
|
|
109
|
-
color: var(--color-text-tertiary, #999999);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* Visual focus indicator - helps users see the textarea is ready */
|
|
113
|
-
.post-composer__textarea:focus {
|
|
114
|
-
outline: none;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* Subtle animation to draw attention when empty */
|
|
118
|
-
@keyframes gentlePulse {
|
|
119
|
-
0%, 100% { opacity: 1; }
|
|
120
|
-
50% { opacity: 0.6; }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
.post-composer__textarea:not(:focus):empty + .focus-hint {
|
|
124
|
-
animation: gentlePulse 2s ease-in-out 1;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.post-composer__actions {
|
|
128
|
-
display: flex;
|
|
129
|
-
align-items: center;
|
|
130
|
-
gap: 8px;
|
|
131
|
-
padding-top: 12px;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.post-composer__actions ds-icon-button::ng-deep button {
|
|
135
|
-
width: 44px;
|
|
136
|
-
height: 44px;
|
|
137
|
-
border-radius: 50%;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/* ============================================
|
|
141
|
-
IMAGE PREVIEW
|
|
142
|
-
============================================ */
|
|
143
|
-
|
|
144
|
-
.image-previews {
|
|
145
|
-
display: flex;
|
|
146
|
-
flex-wrap: wrap;
|
|
147
|
-
gap: 8px;
|
|
148
|
-
margin-top: 12px;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.image-preview {
|
|
152
|
-
position: relative;
|
|
153
|
-
width: 96px;
|
|
154
|
-
height: 96px;
|
|
155
|
-
border-radius: 12px;
|
|
156
|
-
overflow: visible;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.preview-image {
|
|
160
|
-
width: 100%;
|
|
161
|
-
height: 100%;
|
|
162
|
-
display: block;
|
|
163
|
-
border-radius: 12px;
|
|
164
|
-
border: 1px solid var(--border-color-default);
|
|
165
|
-
object-fit: cover;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.remove-image-btn {
|
|
169
|
-
position: absolute;
|
|
170
|
-
top: -8px;
|
|
171
|
-
right: -8px;
|
|
172
|
-
width: 24px;
|
|
173
|
-
height: 24px;
|
|
174
|
-
border-radius: 50%;
|
|
175
|
-
background: rgba(0, 0, 0, 0.6);
|
|
176
|
-
backdrop-filter: blur(8px);
|
|
177
|
-
border: 2px solid white;
|
|
178
|
-
color: white;
|
|
179
|
-
display: flex;
|
|
180
|
-
align-items: center;
|
|
181
|
-
justify-content: center;
|
|
182
|
-
cursor: pointer;
|
|
183
|
-
transition: all 0.2s ease;
|
|
184
|
-
padding: 0;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.remove-image-btn:hover {
|
|
188
|
-
background: rgba(0, 0, 0, 0.8);
|
|
189
|
-
transform: scale(1.05);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.remove-image-btn:active {
|
|
193
|
-
transform: scale(0.95);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/* ============================================
|
|
197
|
-
MOBILE OPTIMIZATIONS
|
|
198
|
-
============================================ */
|
|
199
|
-
|
|
200
|
-
@media (max-width: 768px) {
|
|
201
|
-
.post-create-container {
|
|
202
|
-
padding: 12px 16px 24px;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.post-composer__textarea {
|
|
206
|
-
min-height: 60px;
|
|
207
|
-
max-height: 300px;
|
|
208
|
-
/* Make tap target larger on mobile */
|
|
209
|
-
padding: 8px;
|
|
210
|
-
margin: -8px;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
`],
|
|
214
|
-
template: `
|
|
215
|
-
<!-- Header with cancel and post buttons -->
|
|
216
|
-
<ds-mobile-bottom-sheet-header
|
|
217
|
-
[title]="modalTitle()"
|
|
218
|
-
leftButtonLabel="Annuller"
|
|
219
|
-
[rightButtonLabel]="submitButtonLabel()"
|
|
220
|
-
[rightButtonDisabled]="!canPost()"
|
|
221
|
-
(leftButtonClick)="handleCancel()"
|
|
222
|
-
(rightButtonClick)="handlePost()">
|
|
223
|
-
</ds-mobile-bottom-sheet-header>
|
|
224
|
-
|
|
225
|
-
<!-- Content -->
|
|
226
|
-
<ion-content>
|
|
227
|
-
<div class="post-create-container">
|
|
228
|
-
<div class="post-composer">
|
|
229
|
-
<div class="post-composer__main">
|
|
230
|
-
<textarea
|
|
231
|
-
#textareaInput
|
|
232
|
-
class="post-composer__textarea"
|
|
233
|
-
[(ngModel)]="postContent"
|
|
234
|
-
[placeholder]="placeholder()"
|
|
235
|
-
[readonly]="isReadonly"
|
|
236
|
-
(input)="handleInput()"
|
|
237
|
-
(focus)="handleFocus()"
|
|
238
|
-
inputmode="text"
|
|
239
|
-
enterkeyhint="done"
|
|
240
|
-
rows="1">
|
|
241
|
-
</textarea>
|
|
242
|
-
|
|
243
|
-
<!-- Image Previews -->
|
|
244
|
-
@if (selectedImages().length > 0) {
|
|
245
|
-
<div class="image-previews">
|
|
246
|
-
@for (image of selectedImages(); track image; let i = $index) {
|
|
247
|
-
<div class="image-preview">
|
|
248
|
-
<img [src]="image" alt="Selected image" class="preview-image" />
|
|
249
|
-
<button
|
|
250
|
-
class="remove-image-btn"
|
|
251
|
-
(click)="handleRemoveImage(i)"
|
|
252
|
-
type="button"
|
|
253
|
-
aria-label="Fjern billede">
|
|
254
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
255
|
-
<path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
|
256
|
-
</svg>
|
|
257
|
-
</button>
|
|
258
|
-
</div>
|
|
259
|
-
}
|
|
260
|
-
</div>
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
<div class="post-composer__actions">
|
|
264
|
-
<ds-icon-button
|
|
265
|
-
icon="remixImageLine"
|
|
266
|
-
variant="secondary"
|
|
267
|
-
size="md"
|
|
268
|
-
(clicked)="handleAddImage()"
|
|
269
|
-
aria-label="Tilføj billede">
|
|
270
|
-
</ds-icon-button>
|
|
271
|
-
<ds-icon-button
|
|
272
|
-
icon="remixAttachmentLine"
|
|
273
|
-
variant="secondary"
|
|
274
|
-
size="md"
|
|
275
|
-
(clicked)="handleAddAttachment()"
|
|
276
|
-
aria-label="Tilføj vedhæftning">
|
|
277
|
-
</ds-icon-button>
|
|
278
|
-
|
|
279
|
-
<!-- Hidden file input for file selection -->
|
|
280
|
-
<input
|
|
281
|
-
#fileInput
|
|
282
|
-
type="file"
|
|
283
|
-
accept="*/*"
|
|
284
|
-
multiple
|
|
285
|
-
(change)="handleFileSelect($event)"
|
|
286
|
-
style="display: none;"
|
|
287
|
-
aria-hidden="true"
|
|
288
|
-
/>
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
</div>
|
|
292
|
-
</div>
|
|
293
|
-
</ion-content>
|
|
294
|
-
`
|
|
295
|
-
})
|
|
296
|
-
export class DsMobilePostCreateBottomSheetComponent implements AfterViewInit, OnInit {
|
|
297
|
-
@ViewChild('textareaInput') textareaInput?: ElementRef<HTMLTextAreaElement>;
|
|
298
|
-
@ViewChild('fileInput') fileInput?: ElementRef<HTMLInputElement>;
|
|
299
|
-
|
|
300
|
-
private whitelabelService = inject(WhitelabelService);
|
|
301
|
-
|
|
302
|
-
// Optional input to control auto-focus behavior
|
|
303
|
-
autoFocus = true;
|
|
304
|
-
|
|
305
|
-
// Control readonly state for keyboard trick
|
|
306
|
-
isReadonly = true;
|
|
307
|
-
|
|
308
|
-
// Edit mode properties - can be set via componentProps
|
|
309
|
-
isEditMode = false;
|
|
310
|
-
postId?: string;
|
|
311
|
-
initialContent = '';
|
|
312
|
-
|
|
313
|
-
postContent = '';
|
|
314
|
-
selectedImages = signal<string[]>([]);
|
|
315
|
-
username = signal('Lars Mikkelsen');
|
|
316
|
-
placeholder = signal("Hvad er nyt?");
|
|
317
|
-
modalTitle = signal('Nyt opslag');
|
|
318
|
-
submitButtonLabel = signal('Slå op');
|
|
319
|
-
|
|
320
|
-
constructor(
|
|
321
|
-
private modalController: ModalController,
|
|
322
|
-
private elementRef: ElementRef
|
|
323
|
-
) {}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Ensure toolbar doesn't have unnecessary padding
|
|
327
|
-
* Modal is already positioned below status bar, so no extra safe area needed
|
|
328
|
-
*/
|
|
329
|
-
private applySafeAreaToToolbar(): void {
|
|
330
|
-
try {
|
|
331
|
-
const hostElement = this.elementRef?.nativeElement;
|
|
332
|
-
if (hostElement) {
|
|
333
|
-
const header = hostElement.querySelector('ion-header');
|
|
334
|
-
if (header) {
|
|
335
|
-
const toolbar = header.querySelector('ion-toolbar');
|
|
336
|
-
if (toolbar) {
|
|
337
|
-
const toolbarElement = toolbar as HTMLElement;
|
|
338
|
-
// Ensure toolbar uses standard padding (no safe area since modal is already offset)
|
|
339
|
-
toolbarElement.style.setProperty('--padding-top', '12px', 'important');
|
|
340
|
-
toolbarElement.style.setProperty('--min-height', '56px', 'important');
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
} catch (e) {
|
|
345
|
-
console.log('[SafeArea] Failed to apply to toolbar:', e);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
ngOnInit(): void {
|
|
350
|
-
// Initialize edit mode if provided
|
|
351
|
-
if (this.isEditMode && this.initialContent) {
|
|
352
|
-
this.postContent = this.initialContent;
|
|
353
|
-
this.modalTitle.set('Rediger opslag');
|
|
354
|
-
this.submitButtonLabel.set('Gem');
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
ngAfterViewInit(): void {
|
|
359
|
-
// Apply safe area padding immediately to prevent corruption
|
|
360
|
-
this.applySafeAreaToToolbar();
|
|
361
|
-
|
|
362
|
-
// Auto-resize textarea if there's initial content (edit mode)
|
|
363
|
-
if (this.postContent && this.textareaInput) {
|
|
364
|
-
setTimeout(() => {
|
|
365
|
-
this.resizeTextarea();
|
|
366
|
-
}, 0);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Try to focus IMMEDIATELY - no delay
|
|
370
|
-
// This maximizes our chance of being in user gesture context
|
|
371
|
-
if (this.autoFocus && this.textareaInput) {
|
|
372
|
-
const textarea = this.textareaInput.nativeElement;
|
|
373
|
-
|
|
374
|
-
// Remove readonly immediately
|
|
375
|
-
this.isReadonly = false;
|
|
376
|
-
|
|
377
|
-
// Try focusing with minimal delay
|
|
378
|
-
setTimeout(() => {
|
|
379
|
-
textarea.focus();
|
|
380
|
-
textarea.click();
|
|
381
|
-
|
|
382
|
-
// Explicitly show keyboard
|
|
383
|
-
Keyboard.show().catch(e => console.log('Keyboard.show() not available'));
|
|
384
|
-
|
|
385
|
-
// iOS sometimes needs a second attempt
|
|
386
|
-
setTimeout(() => {
|
|
387
|
-
textarea.focus();
|
|
388
|
-
Keyboard.show().catch(e => console.log('Keyboard.show() not available'));
|
|
389
|
-
}, 100);
|
|
390
|
-
}, 10);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Ionic lifecycle hook - called when modal enters view
|
|
396
|
-
* At 95% height, this acts more like a page than a modal
|
|
397
|
-
* which might allow keyboard to open
|
|
398
|
-
*/
|
|
399
|
-
ionViewDidEnter(): void {
|
|
400
|
-
// Resize textarea in case initial attempt didn't work
|
|
401
|
-
if (this.postContent && this.textareaInput) {
|
|
402
|
-
this.resizeTextarea();
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Final focus attempt when view fully enters
|
|
406
|
-
if (this.autoFocus && this.textareaInput) {
|
|
407
|
-
this.isReadonly = false;
|
|
408
|
-
const textarea = this.textareaInput.nativeElement;
|
|
409
|
-
|
|
410
|
-
// Try to focus as if this was a page navigation
|
|
411
|
-
textarea.focus();
|
|
412
|
-
textarea.click();
|
|
413
|
-
|
|
414
|
-
// Explicitly show keyboard
|
|
415
|
-
Keyboard.show().catch(e => console.log('Keyboard.show() not available'));
|
|
416
|
-
|
|
417
|
-
// Set cursor position
|
|
418
|
-
const length = textarea.value.length;
|
|
419
|
-
textarea.setSelectionRange(length, length);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
handleFocus(): void {
|
|
424
|
-
// When user focuses (or we focus programmatically), remove readonly
|
|
425
|
-
this.isReadonly = false;
|
|
426
|
-
// Explicitly show keyboard
|
|
427
|
-
Keyboard.show().catch(e => console.log('Keyboard.show() not available'));
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
handleInput(): void {
|
|
431
|
-
this.resizeTextarea();
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Auto-resize textarea based on content
|
|
436
|
-
*/
|
|
437
|
-
private resizeTextarea(): void {
|
|
438
|
-
if (this.textareaInput) {
|
|
439
|
-
const textarea = this.textareaInput.nativeElement;
|
|
440
|
-
// Reset height to auto to get the correct scrollHeight
|
|
441
|
-
textarea.style.height = 'auto';
|
|
442
|
-
// Set height based on content, respecting min/max from CSS
|
|
443
|
-
textarea.style.height = Math.min(textarea.scrollHeight, 400) + 'px';
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
canPost(): boolean {
|
|
448
|
-
return this.postContent.trim().length > 0 || this.selectedImages().length > 0;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
async handleCancel(): Promise<void> {
|
|
452
|
-
if (this.postContent.trim().length > 0 || this.selectedImages().length > 0) {
|
|
453
|
-
// Show confirmation
|
|
454
|
-
const confirmed = confirm('Kassér dette opslag?');
|
|
455
|
-
if (confirmed) {
|
|
456
|
-
await this.modalController.dismiss(null, 'cancel');
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
await this.modalController.dismiss(null, 'cancel');
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
async handlePost(): Promise<void> {
|
|
464
|
-
if (!this.canPost()) return;
|
|
465
|
-
|
|
466
|
-
if (this.isEditMode) {
|
|
467
|
-
console.log('Updating post:', this.postId, this.postContent);
|
|
468
|
-
} else {
|
|
469
|
-
console.log('Creating post:', this.postContent, 'with images:', this.selectedImages().length);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Pass the post content, images, and edit info back to the parent
|
|
473
|
-
await this.modalController.dismiss(
|
|
474
|
-
{
|
|
475
|
-
content: this.postContent,
|
|
476
|
-
images: this.selectedImages(),
|
|
477
|
-
timestamp: new Date(),
|
|
478
|
-
isEdit: this.isEditMode,
|
|
479
|
-
postId: this.postId
|
|
480
|
-
},
|
|
481
|
-
'post'
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
async handleAddImage(): Promise<void> {
|
|
486
|
-
console.log('Add image button clicked');
|
|
487
|
-
|
|
488
|
-
// Re-apply safe area padding before opening camera (preventive)
|
|
489
|
-
// This ensures the value is locked in before iOS corrupts it
|
|
490
|
-
this.applySafeAreaToToolbar();
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
console.log('Requesting photo from library...');
|
|
494
|
-
|
|
495
|
-
const result = await FilePicker.pickImages({
|
|
496
|
-
limit: 1,
|
|
497
|
-
});
|
|
498
|
-
const image = result.files?.[0];
|
|
499
|
-
|
|
500
|
-
console.log('Photo selected successfully:', image);
|
|
501
|
-
|
|
502
|
-
// Add the image path to the array
|
|
503
|
-
if (image) {
|
|
504
|
-
const imageSrc = image.path ? Capacitor.convertFileSrc(image.path) : (image.blob ? URL.createObjectURL(image.blob) : '');
|
|
505
|
-
if (imageSrc) {
|
|
506
|
-
this.selectedImages.update(images => [...images, imageSrc]);
|
|
507
|
-
console.log('Image added to preview:', imageSrc);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Re-apply safe area padding immediately after returning
|
|
512
|
-
// Since we're using fixed values, this won't cause flickering
|
|
513
|
-
requestAnimationFrame(() => {
|
|
514
|
-
this.applySafeAreaToToolbar();
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
// NOTE: Removed restoreStatusBar() call - WhitelabelService now handles
|
|
518
|
-
// status bar state globally, no need to restore after photo selection
|
|
519
|
-
|
|
520
|
-
} catch (error) {
|
|
521
|
-
console.error('Photo selection error:', error);
|
|
522
|
-
// Only show alert for non-cancellation errors
|
|
523
|
-
if (error && typeof error === 'object' && 'message' in error) {
|
|
524
|
-
const errorMessage = (error as any).message;
|
|
525
|
-
if (!errorMessage.includes('cancel')) {
|
|
526
|
-
alert(`Error selecting photo: ${JSON.stringify(error)}`);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
handleRemoveImage(index: number): void {
|
|
534
|
-
console.log('Removing image at index:', index);
|
|
535
|
-
this.selectedImages.update(images => images.filter((_, i) => i !== index));
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
handleAddAttachment(): void {
|
|
539
|
-
console.log('Add attachment button clicked');
|
|
540
|
-
// Trigger the hidden file input
|
|
541
|
-
if (this.fileInput) {
|
|
542
|
-
this.fileInput.nativeElement.click();
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
handleFileSelect(event: Event): void {
|
|
547
|
-
const input = event.target as HTMLInputElement;
|
|
548
|
-
const files = input.files;
|
|
549
|
-
|
|
550
|
-
if (!files || files.length === 0) {
|
|
551
|
-
console.log('No files selected');
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
console.log('Files selected:', files.length);
|
|
556
|
-
|
|
557
|
-
// Process each selected file
|
|
558
|
-
Array.from(files).forEach(file => {
|
|
559
|
-
console.log('File:', file.name, file.type, file.size);
|
|
560
|
-
|
|
561
|
-
// Create a data URL for preview (for images and other files)
|
|
562
|
-
const reader = new FileReader();
|
|
563
|
-
reader.onload = (e) => {
|
|
564
|
-
const result = e.target?.result as string;
|
|
565
|
-
if (result) {
|
|
566
|
-
// Add to selectedImages array for preview
|
|
567
|
-
this.selectedImages.update(images => [...images, result]);
|
|
568
|
-
console.log('File added to preview:', file.name);
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
reader.readAsDataURL(file);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
// Reset the input so the same file can be selected again
|
|
575
|
-
input.value = '';
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|