@propbinder/mobile-design 0.0.2 → 0.0.21

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