@propbinder/mobile-design 0.2.50 → 0.2.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/fesm2022/propbinder-mobile-design.mjs +26206 -0
  2. package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
  3. package/index.d.ts +8193 -0
  4. package/package.json +39 -3
  5. package/ng-package.json +0 -24
  6. package/src/animations/page-transitions.ts +0 -165
  7. package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -102
  8. package/src/components/action-list-item/index.ts +0 -2
  9. package/src/components/app-icon/ds-app-icon.ts +0 -133
  10. package/src/components/app-icon/index.ts +0 -2
  11. package/src/components/attachment-preview/ds-mobile-attachment-preview.css +0 -139
  12. package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +0 -164
  13. package/src/components/attachment-preview/index.ts +0 -1
  14. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -142
  15. package/src/components/avatar-with-badge/index.ts +0 -2
  16. package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +0 -71
  17. package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +0 -121
  18. package/src/components/booking-modal/ds-mobile-booking-modal.ts +0 -598
  19. package/src/components/booking-modal/ds-mobile-booking-summary.ts +0 -161
  20. package/src/components/booking-modal/index.ts +0 -4
  21. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -266
  22. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +0 -146
  23. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +0 -156
  24. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -101
  25. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -169
  26. package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +0 -211
  27. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -578
  28. package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +0 -614
  29. package/src/components/bottom-sheet/index.ts +0 -8
  30. package/src/components/bottom-sheet/modal-shadow-fix.ts +0 -42
  31. package/src/components/card-inline/ds-mobile-card-inline.ts +0 -301
  32. package/src/components/card-inline/index.ts +0 -2
  33. package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +0 -118
  34. package/src/components/card-inline-banner/index.ts +0 -1
  35. package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +0 -120
  36. package/src/components/card-inline-contact/index.ts +0 -1
  37. package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +0 -141
  38. package/src/components/card-inline-file/index.ts +0 -1
  39. package/src/components/chat-modal/ds-mobile-chat-modal.css +0 -159
  40. package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +0 -105
  41. package/src/components/chat-modal/ds-mobile-chat-modal.ts +0 -918
  42. package/src/components/chat-modal/index.ts +0 -8
  43. package/src/components/comment/ds-mobile-comment.ts +0 -568
  44. package/src/components/comment/index.ts +0 -2
  45. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
  46. package/src/components/contact-list-item/index.ts +0 -2
  47. package/src/components/content/ds-mobile-content.ts +0 -139
  48. package/src/components/content/index.ts +0 -2
  49. package/src/components/dropdown/ds-mobile-dropdown.css +0 -199
  50. package/src/components/dropdown/ds-mobile-dropdown.ts +0 -340
  51. package/src/components/dropdown/index.ts +0 -2
  52. package/src/components/ds-mobile-tabs.css +0 -407
  53. package/src/components/ds-mobile-tabs.ts +0 -216
  54. package/src/components/empty-state/ds-mobile-empty-state.ts +0 -120
  55. package/src/components/empty-state/index.ts +0 -2
  56. package/src/components/fab/ds-mobile-fab.ts +0 -315
  57. package/src/components/fab/index.ts +0 -1
  58. package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +0 -121
  59. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +0 -189
  60. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +0 -135
  61. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +0 -656
  62. package/src/components/facility-creation-modal/index.ts +0 -9
  63. package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +0 -105
  64. package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +0 -188
  65. package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +0 -460
  66. package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +0 -134
  67. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +0 -69
  68. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +0 -379
  69. package/src/components/facility-detail-modal/index.ts +0 -2
  70. package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
  71. package/src/components/file-attachment/index.ts +0 -2
  72. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +0 -214
  73. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -84
  74. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -424
  75. package/src/components/handbook-detail-modal/index.ts +0 -3
  76. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -175
  77. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -533
  78. package/src/components/handbook-folder/index.ts +0 -4
  79. package/src/components/header-content/ds-mobile-header-content.ts +0 -222
  80. package/src/components/header-content/index.ts +0 -2
  81. package/src/components/illustration/ds-mobile-illustration.ts +0 -124
  82. package/src/components/illustration/index.ts +0 -2
  83. package/src/components/index.ts +0 -124
  84. package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -361
  85. package/src/components/inline-photo/index.ts +0 -1
  86. package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +0 -132
  87. package/src/components/inline-tabs/index.ts +0 -2
  88. package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +0 -350
  89. package/src/components/interactive-list-item-booking/index.ts +0 -1
  90. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -321
  91. package/src/components/interactive-list-item-inquiry/index.ts +0 -2
  92. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -237
  93. package/src/components/interactive-list-item-message/index.ts +0 -2
  94. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -549
  95. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
  96. package/src/components/interactive-list-item-post/index.ts +0 -13
  97. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -315
  98. package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -202
  99. package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -484
  100. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -377
  101. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
  102. package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
  103. package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -296
  104. package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
  105. package/src/components/lightbox/index.ts +0 -22
  106. package/src/components/list-item/ds-mobile-list-item.ts +0 -603
  107. package/src/components/list-item/index.ts +0 -2
  108. package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
  109. package/src/components/list-item-static/index.ts +0 -2
  110. package/src/components/loader-overlay/ds-mobile-loader-overlay.css +0 -49
  111. package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +0 -77
  112. package/src/components/loader-overlay/index.ts +0 -1
  113. package/src/components/logo/ds-logo.ts +0 -95
  114. package/src/components/logo/index.ts +0 -2
  115. package/src/components/message-bubble/ds-mobile-message-bubble.ts +0 -633
  116. package/src/components/message-bubble/index.ts +0 -7
  117. package/src/components/message-composer/ds-mobile-message-composer.ts +0 -1146
  118. package/src/components/message-composer/index.ts +0 -7
  119. package/src/components/modal/ds-mobile-modal.css +0 -163
  120. package/src/components/modal/ds-mobile-modal.service.ts +0 -329
  121. package/src/components/modal/index.ts +0 -8
  122. package/src/components/modal-base/ds-mobile-modal-base.css +0 -378
  123. package/src/components/modal-base/ds-mobile-modal-base.ts +0 -261
  124. package/src/components/modal-base/index.ts +0 -2
  125. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +0 -112
  126. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +0 -93
  127. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +0 -442
  128. package/src/components/new-inquiry-modal/index.ts +0 -4
  129. package/src/components/offline-banner/ds-mobile-offline-banner.ts +0 -135
  130. package/src/components/offline-banner/index.ts +0 -1
  131. package/src/components/page-details/ds-mobile-page-details.css +0 -83
  132. package/src/components/page-details/ds-mobile-page-details.ts +0 -282
  133. package/src/components/page-details/index.ts +0 -2
  134. package/src/components/page-main/ds-mobile-page-main.css +0 -68
  135. package/src/components/page-main/ds-mobile-page-main.ts +0 -421
  136. package/src/components/page-main/index.ts +0 -2
  137. package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
  138. package/src/components/post-composer/index.ts +0 -2
  139. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +0 -390
  140. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -108
  141. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -722
  142. package/src/components/post-detail-modal/index.ts +0 -9
  143. package/src/components/property-banner/ds-mobile-property-banner.ts +0 -95
  144. package/src/components/property-banner/index.ts +0 -2
  145. package/src/components/section/ds-mobile-section.ts +0 -263
  146. package/src/components/section/index.ts +0 -2
  147. package/src/components/shared/directives/index.ts +0 -2
  148. package/src/components/shared/directives/long-press.directive.ts +0 -212
  149. package/src/components/shared/index.ts +0 -3
  150. package/src/components/shared/mobile-modal-base.ts +0 -457
  151. package/src/components/shared/mobile-page-base.ts +0 -204
  152. package/src/components/swiper/ds-mobile-swiper-with-nav.ts +0 -160
  153. package/src/components/swiper/ds-mobile-swiper.ts +0 -327
  154. package/src/components/swiper/index.ts +0 -3
  155. package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +0 -129
  156. package/src/components/system-message-banner/index.ts +0 -2
  157. package/src/components/tab-bar/ds-mobile-tab-bar.css +0 -533
  158. package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -735
  159. package/src/components/tab-bar/index.ts +0 -2
  160. package/src/components/tabs/ds-mobile-tabs.css +0 -25
  161. package/src/components/tabs/ds-mobile-tabs.ts +0 -89
  162. package/src/components/tabs/index.ts +0 -2
  163. package/src/components/text-input/ds-text-input.ts +0 -287
  164. package/src/components/text-input/index.ts +0 -2
  165. package/src/examples/booking.page.ts +0 -434
  166. package/src/examples/community.page.ts +0 -776
  167. package/src/examples/handbook.page.ts +0 -324
  168. package/src/examples/home.page.ts +0 -347
  169. package/src/examples/index.ts +0 -12
  170. package/src/examples/inquiries.example.ts +0 -273
  171. package/src/examples/inquiry-detail.example.css +0 -189
  172. package/src/examples/inquiry-detail.example.ts +0 -415
  173. package/src/examples/mobile-tabs-example.component.ts +0 -208
  174. package/src/examples/post-create.page.ts +0 -311
  175. package/src/examples/post-detail.page.ts +0 -296
  176. package/src/examples/sign-in.page.ts +0 -291
  177. package/src/examples/whitelabel-demo-modal.component.ts +0 -1094
  178. package/src/examples/whitelabel-demo-modal.service.ts +0 -77
  179. package/src/models/index.ts +0 -7
  180. package/src/models/post.model.ts +0 -41
  181. package/src/pages/community.page.ts +0 -769
  182. package/src/pages/handbook.page.ts +0 -388
  183. package/src/pages/home.page.ts +0 -303
  184. package/src/pages/index.ts +0 -11
  185. package/src/pages/inquiries.example.ts +0 -273
  186. package/src/pages/inquiry-detail.example.css +0 -189
  187. package/src/pages/inquiry-detail.example.ts +0 -415
  188. package/src/pages/mobile-tabs-example.component.ts +0 -179
  189. package/src/pages/post-create.page.ts +0 -311
  190. package/src/pages/post-detail.page.ts +0 -296
  191. package/src/pages/sign-in.page.ts +0 -291
  192. package/src/pages/whitelabel-demo-modal.component.ts +0 -1094
  193. package/src/pages/whitelabel-demo-modal.service.ts +0 -77
  194. package/src/public-api.ts +0 -6
  195. package/src/services/base-modal.service.ts +0 -101
  196. package/src/services/index.ts +0 -11
  197. package/src/services/posts.service.ts +0 -542
  198. package/src/services/tracking-permission.service.ts +0 -88
  199. package/src/services/user.service.ts +0 -60
  200. package/src/services/whitelabel.service.ts +0 -675
  201. package/tsconfig.lib.json +0 -17
  202. package/tsconfig.lib.prod.json +0 -9
  203. package/tsconfig.spec.json +0 -13
  204. /package/{src/assets → assets}/fonts/Brockmann-Bold.otf +0 -0
  205. /package/{src/assets → assets}/fonts/Brockmann-BoldItalic.otf +0 -0
  206. /package/{src/assets → assets}/fonts/Brockmann-Medium.otf +0 -0
  207. /package/{src/assets → assets}/fonts/Brockmann-MediumItalic.otf +0 -0
  208. /package/{src/assets → assets}/fonts/Brockmann-Regular.otf +0 -0
  209. /package/{src/assets → assets}/fonts/Brockmann-RegularItalic.otf +0 -0
  210. /package/{src/assets → assets}/fonts/Brockmann-SemiBold.otf +0 -0
  211. /package/{src/assets → assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  212. /package/{src/assets → assets}/fonts/Brockmann_desktop_license.pdf +0 -0
  213. /package/{src/assets → assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
  214. /package/{src/assets → assets}/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
  215. /package/{src/assets → assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
  216. /package/{src/assets → assets}/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
  217. /package/{src/assets → assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
  218. /package/{src/assets → assets}/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
  219. /package/{src/styles → styles}/ionic.css +0 -0
  220. /package/{src/components/shared → styles}/mobile-common.css +0 -0
  221. /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
-