@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.
- 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,457 +0,0 @@
|
|
|
1
|
-
import { input, output, Directive, OnInit, OnDestroy, inject, ViewChild } from '@angular/core';
|
|
2
|
-
import { ModalController, IonContent } from '@ionic/angular/standalone';
|
|
3
|
-
import { Keyboard } from '@capacitor/keyboard';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* MobileModalBase
|
|
7
|
-
*
|
|
8
|
-
* Shared base class for mobile modal components.
|
|
9
|
-
* Provides consistent modal behavior, state management, and keyboard handling.
|
|
10
|
-
*
|
|
11
|
-
* **Key Features:**
|
|
12
|
-
* - Loading and error state management
|
|
13
|
-
* - Header configuration (title, meta)
|
|
14
|
-
* - Automatic keyboard height tracking (iOS/Android)
|
|
15
|
-
* - Fixed bottom component support
|
|
16
|
-
* - Consistent close behavior
|
|
17
|
-
*
|
|
18
|
-
* **Usage:**
|
|
19
|
-
* ```typescript
|
|
20
|
-
* export class MyModalComponent extends MobileModalBase {
|
|
21
|
-
* constructor() {
|
|
22
|
-
* super();
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* @internal This is a base class and should not be used directly.
|
|
28
|
-
*/
|
|
29
|
-
@Directive()
|
|
30
|
-
export abstract class MobileModalBase implements OnInit, OnDestroy {
|
|
31
|
-
protected modalController = inject(ModalController);
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Reference to IonContent for accessing scroll element
|
|
35
|
-
*/
|
|
36
|
-
@ViewChild(IonContent, { read: IonContent }) protected ionContent?: IonContent;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* ResizeObserver for tracking fixed bottom height
|
|
40
|
-
*/
|
|
41
|
-
private fixedBottomObserver?: ResizeObserver;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Flag to prevent ResizeObserver from updating padding during keyboard animations
|
|
45
|
-
*/
|
|
46
|
-
private isKeyboardAnimating = false;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Loading state - when true, shows loading indicator
|
|
50
|
-
* @default false
|
|
51
|
-
*/
|
|
52
|
-
loading = input<boolean>(false);
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Error state - when set, shows error message
|
|
56
|
-
* @default undefined
|
|
57
|
-
*/
|
|
58
|
-
error = input<string | undefined>();
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Modal header title
|
|
62
|
-
* @default ''
|
|
63
|
-
*/
|
|
64
|
-
headerTitle = input<string>('');
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Modal header metadata (subtitle/secondary text)
|
|
68
|
-
* @default ''
|
|
69
|
-
*/
|
|
70
|
-
headerMeta = input<string>('');
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Accessibility label for close button
|
|
74
|
-
* @default 'Close'
|
|
75
|
-
*/
|
|
76
|
-
closeButtonLabel = input<string>('Close');
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Enable automatic keyboard height tracking
|
|
80
|
-
* When enabled, sets --keyboard-height CSS variable for sliding content
|
|
81
|
-
* @default false
|
|
82
|
-
*/
|
|
83
|
-
enableKeyboardHandling = input<boolean>(false);
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Whether modal has a fixed bottom component
|
|
87
|
-
* Used to manage spacing and keyboard interactions
|
|
88
|
-
* @default false
|
|
89
|
-
*/
|
|
90
|
-
hasFixedBottom = input<boolean>(false);
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Content padding for modal content
|
|
94
|
-
* Controls padding inside .modal-main-content
|
|
95
|
-
* - '0' (default) - No padding, use ds-mobile-section for content organization
|
|
96
|
-
* - '20px' - Legacy padding for content without sections
|
|
97
|
-
* - Any custom CSS padding value
|
|
98
|
-
*
|
|
99
|
-
* @default '0'
|
|
100
|
-
*/
|
|
101
|
-
contentPadding = input<string>('0');
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Enable auto-height behavior for bottom sheets
|
|
105
|
-
* When true, sets [fullscreen]="false" on ion-content and enforces flex: 0 0 auto
|
|
106
|
-
* @default false
|
|
107
|
-
*/
|
|
108
|
-
isAutoHeight = input<boolean>(false);
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Controls how modal content behaves when the keyboard opens.
|
|
112
|
-
* - 'follow': content is pushed to follow keyboard movement
|
|
113
|
-
* - 'overlay': keyboard/footer overlays lower content (no auto scroll push)
|
|
114
|
-
* @default 'follow'
|
|
115
|
-
*/
|
|
116
|
-
keyboardContentBehavior = input<'follow' | 'overlay'>('follow');
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Emitted when modal is closed
|
|
120
|
-
*/
|
|
121
|
-
closed = output<void>();
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Emitted when keyboard is about to show
|
|
125
|
-
* Provides keyboard height in pixels
|
|
126
|
-
* Child components can listen to this to react to keyboard appearance
|
|
127
|
-
*/
|
|
128
|
-
keyboardWillShow = output<number>();
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Emitted when keyboard is about to hide
|
|
132
|
-
* Child components can listen to this to react to keyboard dismissal
|
|
133
|
-
*/
|
|
134
|
-
keyboardWillHide = output<void>();
|
|
135
|
-
|
|
136
|
-
ngOnInit(): void {
|
|
137
|
-
if (this.enableKeyboardHandling()) {
|
|
138
|
-
this.setupKeyboardListeners();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (this.hasFixedBottom()) {
|
|
142
|
-
this.setupFixedBottomObserver();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
ngOnDestroy(): void {
|
|
147
|
-
if (this.enableKeyboardHandling()) {
|
|
148
|
-
this.cleanupKeyboardListeners();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (this.fixedBottomObserver) {
|
|
152
|
-
this.fixedBottomObserver.disconnect();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Close the modal
|
|
158
|
-
* Emits closed event and dismisses the modal
|
|
159
|
-
*/
|
|
160
|
-
close(): void {
|
|
161
|
-
this.closed.emit();
|
|
162
|
-
this.modalController.dismiss();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Set up keyboard event listeners to adjust component position
|
|
167
|
-
* Uses --keyboard-height for fixed bottom composer and adds padding to scroll area.
|
|
168
|
-
* Adjusts scroll position so content smoothly follows the keyboard up (like Messenger/Telegram).
|
|
169
|
-
* Only responds if this modal is the currently active (top-most) modal.
|
|
170
|
-
* @protected
|
|
171
|
-
*/
|
|
172
|
-
protected setupKeyboardListeners(): void {
|
|
173
|
-
Keyboard.addListener('keyboardWillShow', async (info) => {
|
|
174
|
-
// Check if this modal is the top-most modal
|
|
175
|
-
const topModal = await this.modalController.getTop();
|
|
176
|
-
if (!topModal || !this.isThisModal(topModal)) {
|
|
177
|
-
// console.log('[MobileModalBase] Keyboard event ignored - not top modal');
|
|
178
|
-
return; // Not the active modal, ignore keyboard event
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// console.log('[MobileModalBase] 🎹 Keyboard showing, height:', info.keyboardHeight);
|
|
182
|
-
// Set flag to prevent ResizeObserver from interfering
|
|
183
|
-
this.isKeyboardAnimating = true;
|
|
184
|
-
|
|
185
|
-
// Set global keyboard height FIRST so ResizeObserver can use it
|
|
186
|
-
document.documentElement.style.setProperty('--keyboard-height', `${info.keyboardHeight}px`);
|
|
187
|
-
|
|
188
|
-
// Update padding immediately to include keyboard height
|
|
189
|
-
if (this.ionContent) {
|
|
190
|
-
try {
|
|
191
|
-
const scrollElement = await this.ionContent.getScrollElement();
|
|
192
|
-
if (scrollElement) {
|
|
193
|
-
// Get current fixed bottom height and add keyboard height
|
|
194
|
-
const fixedBottomHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--fixed-bottom-height') || '0');
|
|
195
|
-
const totalPadding = this.getScrollPadding(fixedBottomHeight, info.keyboardHeight);
|
|
196
|
-
scrollElement.style.paddingBottom = `${totalPadding}px`;
|
|
197
|
-
// console.log('[MobileModalBase] Updated padding:', totalPadding, '(fixed:', fixedBottomHeight, '+ keyboard:', info.keyboardHeight, ')');
|
|
198
|
-
|
|
199
|
-
// In overlay mode, keep content stationary and let keyboard/footer overlap.
|
|
200
|
-
if (this.isOverlayBehavior()) {
|
|
201
|
-
this.isKeyboardAnimating = false;
|
|
202
|
-
} else {
|
|
203
|
-
// Store current scroll position before keyboard animation
|
|
204
|
-
const currentScrollTop = scrollElement.scrollTop;
|
|
205
|
-
// Animate scroll position to match keyboard
|
|
206
|
-
const startTime = performance.now();
|
|
207
|
-
const duration = 300; // Match keyboard animation duration
|
|
208
|
-
|
|
209
|
-
const animateScroll = (currentTime: number) => {
|
|
210
|
-
const elapsed = currentTime - startTime;
|
|
211
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
212
|
-
|
|
213
|
-
// Ease-out cubic (matches iOS/Android keyboard animation curve)
|
|
214
|
-
const easeProgress = 1 - Math.pow(1 - progress, 3);
|
|
215
|
-
|
|
216
|
-
// Scroll down by keyboard height so content "follows" keyboard up
|
|
217
|
-
scrollElement.scrollTop = currentScrollTop + info.keyboardHeight * easeProgress;
|
|
218
|
-
|
|
219
|
-
if (progress < 1) {
|
|
220
|
-
requestAnimationFrame(animateScroll);
|
|
221
|
-
} else {
|
|
222
|
-
// Animation complete - re-enable ResizeObserver
|
|
223
|
-
// console.log('[MobileModalBase] ✅ Keyboard animation complete');
|
|
224
|
-
this.isKeyboardAnimating = false;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
requestAnimationFrame(animateScroll);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
} catch (e) {
|
|
232
|
-
// console.log('[MobileModalBase] Could not access scroll element:', e);
|
|
233
|
-
this.isKeyboardAnimating = false;
|
|
234
|
-
}
|
|
235
|
-
} else {
|
|
236
|
-
this.isKeyboardAnimating = false;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Emit event for child components to react to keyboard appearance
|
|
240
|
-
this.keyboardWillShow.emit(info.keyboardHeight);
|
|
241
|
-
}).catch((e) => {
|
|
242
|
-
// console.log('[MobileModalBase] Keyboard listeners not available:', e);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
Keyboard.addListener('keyboardWillHide', async () => {
|
|
246
|
-
// Check if this modal is the top-most modal
|
|
247
|
-
const topModal = await this.modalController.getTop();
|
|
248
|
-
if (!topModal || !this.isThisModal(topModal)) {
|
|
249
|
-
// console.log('[MobileModalBase] Keyboard hide event ignored - not top modal');
|
|
250
|
-
return; // Not the active modal, ignore keyboard event
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// console.log('[MobileModalBase] 🎹 Keyboard hiding');
|
|
254
|
-
// Set flag to prevent ResizeObserver from interfering
|
|
255
|
-
this.isKeyboardAnimating = true;
|
|
256
|
-
|
|
257
|
-
// Get keyboard height before resetting (for scroll position adjustment)
|
|
258
|
-
const keyboardHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--keyboard-height') || '0');
|
|
259
|
-
|
|
260
|
-
// Reset keyboard height variable FIRST
|
|
261
|
-
document.documentElement.style.setProperty('--keyboard-height', '0px');
|
|
262
|
-
|
|
263
|
-
// Update padding immediately to remove keyboard height
|
|
264
|
-
if (this.ionContent) {
|
|
265
|
-
try {
|
|
266
|
-
const scrollElement = await this.ionContent.getScrollElement();
|
|
267
|
-
if (scrollElement) {
|
|
268
|
-
// Get current fixed bottom height (without keyboard)
|
|
269
|
-
const fixedBottomHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--fixed-bottom-height') || '0');
|
|
270
|
-
const totalPadding = this.getScrollPadding(fixedBottomHeight, 0);
|
|
271
|
-
scrollElement.style.paddingBottom = `${totalPadding}px`;
|
|
272
|
-
// console.log('[MobileModalBase] Removed keyboard padding, now:', fixedBottomHeight);
|
|
273
|
-
|
|
274
|
-
// In overlay mode, keep content stationary and let keyboard/footer overlap.
|
|
275
|
-
if (this.isOverlayBehavior()) {
|
|
276
|
-
this.isKeyboardAnimating = false;
|
|
277
|
-
} else {
|
|
278
|
-
// Store current scroll position
|
|
279
|
-
const currentScrollTop = scrollElement.scrollTop;
|
|
280
|
-
// Animate scroll position back
|
|
281
|
-
const startTime = performance.now();
|
|
282
|
-
const duration = 300;
|
|
283
|
-
|
|
284
|
-
const animateScroll = (currentTime: number) => {
|
|
285
|
-
const elapsed = currentTime - startTime;
|
|
286
|
-
const progress = Math.min(elapsed / duration, 1);
|
|
287
|
-
|
|
288
|
-
// Ease-out cubic
|
|
289
|
-
const easeProgress = 1 - Math.pow(1 - progress, 3);
|
|
290
|
-
|
|
291
|
-
// Scroll up by keyboard height (reverse the push)
|
|
292
|
-
const newScrollTop = currentScrollTop - keyboardHeight * easeProgress;
|
|
293
|
-
scrollElement.scrollTop = Math.max(0, newScrollTop);
|
|
294
|
-
|
|
295
|
-
if (progress < 1) {
|
|
296
|
-
requestAnimationFrame(animateScroll);
|
|
297
|
-
} else {
|
|
298
|
-
// Animation complete - re-enable ResizeObserver
|
|
299
|
-
// console.log('[MobileModalBase] ✅ Keyboard hide animation complete');
|
|
300
|
-
this.isKeyboardAnimating = false;
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
requestAnimationFrame(animateScroll);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
} catch (e) {
|
|
308
|
-
// console.log('[MobileModalBase] Could not access scroll element:', e);
|
|
309
|
-
this.isKeyboardAnimating = false;
|
|
310
|
-
}
|
|
311
|
-
} else {
|
|
312
|
-
this.isKeyboardAnimating = false;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Emit event for child components to react to keyboard dismissal
|
|
316
|
-
this.keyboardWillHide.emit();
|
|
317
|
-
}).catch((e) => {
|
|
318
|
-
// console.log('[MobileModalBase] Keyboard listeners not available:', e);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Set up ResizeObserver to track fixed bottom height and apply as CSS variable
|
|
324
|
-
* This allows dynamic bottom padding that adjusts to content
|
|
325
|
-
* @protected
|
|
326
|
-
*/
|
|
327
|
-
protected async setupFixedBottomObserver(): Promise<void> {
|
|
328
|
-
// Use measured fixed-bottom height directly; safe-area tuning is handled in CSS.
|
|
329
|
-
const offset = 0;
|
|
330
|
-
|
|
331
|
-
// Small delay to ensure DOM is ready
|
|
332
|
-
setTimeout(async () => {
|
|
333
|
-
const fixedBottom = document.querySelector('.modal-fixed-bottom');
|
|
334
|
-
// console.log('[MobileModalBase] Fixed bottom element:', fixedBottom, 'Offset:', offset);
|
|
335
|
-
|
|
336
|
-
if (fixedBottom) {
|
|
337
|
-
this.fixedBottomObserver = new ResizeObserver(async (entries) => {
|
|
338
|
-
// Skip updates during keyboard animations to prevent conflicts
|
|
339
|
-
if (this.isKeyboardAnimating) {
|
|
340
|
-
// console.log('[MobileModalBase] 🚫 ResizeObserver - skipping update during keyboard animation');
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// console.log('[MobileModalBase] ✅ ResizeObserver - processing update');
|
|
345
|
-
for (const entry of entries) {
|
|
346
|
-
// Use getBoundingClientRect to get full border-box height (includes padding)
|
|
347
|
-
const height = (entry.target as HTMLElement).getBoundingClientRect().height;
|
|
348
|
-
// Add platform-specific offset
|
|
349
|
-
const totalHeight = height + offset;
|
|
350
|
-
|
|
351
|
-
// CRITICAL: Include keyboard height if keyboard is visible!
|
|
352
|
-
const keyboardHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--keyboard-height') || '0');
|
|
353
|
-
const paddingWithKeyboard = this.getScrollPadding(totalHeight, keyboardHeight);
|
|
354
|
-
|
|
355
|
-
// console.log('[MobileModalBase] ResizeObserver - height:', height, 'totalHeight:', totalHeight, 'keyboard:', keyboardHeight, 'finalPadding:', paddingWithKeyboard);
|
|
356
|
-
document.documentElement.style.setProperty('--fixed-bottom-height', `${totalHeight}px`);
|
|
357
|
-
|
|
358
|
-
// Also update scroll element padding (including keyboard height!)
|
|
359
|
-
if (this.ionContent) {
|
|
360
|
-
try {
|
|
361
|
-
const scrollElement = await this.ionContent.getScrollElement();
|
|
362
|
-
if (scrollElement) {
|
|
363
|
-
// Get PREVIOUS padding to calculate the difference
|
|
364
|
-
const previousPadding = parseFloat(scrollElement.style.paddingBottom || '0');
|
|
365
|
-
const paddingDifference = paddingWithKeyboard - previousPadding;
|
|
366
|
-
|
|
367
|
-
// Update padding
|
|
368
|
-
scrollElement.style.paddingBottom = `${paddingWithKeyboard}px`;
|
|
369
|
-
|
|
370
|
-
// CRITICAL: Adjust scroll to maintain visual position
|
|
371
|
-
// When padding increases, we need to scroll down by the same amount
|
|
372
|
-
if (!this.isOverlayBehavior() && paddingDifference !== 0) {
|
|
373
|
-
const currentScrollTop = scrollElement.scrollTop;
|
|
374
|
-
scrollElement.scrollTop = currentScrollTop + paddingDifference;
|
|
375
|
-
// console.log('[MobileModalBase] Adjusted scroll by', paddingDifference, 'px (from', currentScrollTop, 'to', scrollElement.scrollTop, ')');
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
} catch (e) {
|
|
379
|
-
// console.log('[MobileModalBase] Could not update scroll element padding:', e);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
this.fixedBottomObserver.observe(fixedBottom);
|
|
386
|
-
|
|
387
|
-
// Set initial height immediately (with platform-specific offset)
|
|
388
|
-
const initialHeight = fixedBottom.getBoundingClientRect().height;
|
|
389
|
-
const initialTotal = initialHeight + offset;
|
|
390
|
-
// console.log('[MobileModalBase] Initial height:', initialHeight, 'initialTotal:', initialTotal);
|
|
391
|
-
document.documentElement.style.setProperty('--fixed-bottom-height', `${initialTotal}px`);
|
|
392
|
-
|
|
393
|
-
// Set initial scroll element padding
|
|
394
|
-
if (this.ionContent) {
|
|
395
|
-
try {
|
|
396
|
-
const scrollElement = await this.ionContent.getScrollElement();
|
|
397
|
-
if (scrollElement) {
|
|
398
|
-
// Include keyboard height if present
|
|
399
|
-
const keyboardHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--keyboard-height') || '0');
|
|
400
|
-
const paddingWithKeyboard = this.getScrollPadding(initialTotal, keyboardHeight);
|
|
401
|
-
scrollElement.style.paddingBottom = `${paddingWithKeyboard}px`;
|
|
402
|
-
}
|
|
403
|
-
} catch (e) {
|
|
404
|
-
// console.log('[MobileModalBase] Could not set initial scroll element padding:', e);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}, 100);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Clean up keyboard event listeners
|
|
413
|
-
* @protected
|
|
414
|
-
*/
|
|
415
|
-
protected cleanupKeyboardListeners(): void {
|
|
416
|
-
Keyboard.removeAllListeners().catch((e) => {
|
|
417
|
-
// console.log('[MobileModalBase] Keyboard cleanup not available:', e);
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Check if the given modal element is this modal component's parent modal
|
|
423
|
-
* @protected
|
|
424
|
-
*/
|
|
425
|
-
protected isThisModal(modalElement: HTMLIonModalElement): boolean {
|
|
426
|
-
// The modal element should contain this component
|
|
427
|
-
// We traverse up from the component to find the ion-modal
|
|
428
|
-
let element = document.querySelector('ds-mobile-modal-base')?.parentElement;
|
|
429
|
-
while (element) {
|
|
430
|
-
if (element === modalElement) {
|
|
431
|
-
return true;
|
|
432
|
-
}
|
|
433
|
-
if (element.tagName === 'ION-MODAL') {
|
|
434
|
-
return element === modalElement;
|
|
435
|
-
}
|
|
436
|
-
element = element.parentElement;
|
|
437
|
-
}
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* Returns true when keyboard should overlay content without push-scrolling.
|
|
443
|
-
*/
|
|
444
|
-
private isOverlayBehavior(): boolean {
|
|
445
|
-
return this.keyboardContentBehavior() === 'overlay';
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
* Computes scroll bottom inset for current keyboard behavior.
|
|
450
|
-
*/
|
|
451
|
-
private getScrollPadding(fixedBottomHeight: number, keyboardHeight: number): number {
|
|
452
|
-
if (this.isOverlayBehavior()) {
|
|
453
|
-
return fixedBottomHeight;
|
|
454
|
-
}
|
|
455
|
-
return fixedBottomHeight + keyboardHeight;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import { input, computed, signal, Directive, OnDestroy } from '@angular/core';
|
|
2
|
-
import { Network } from '@capacitor/network';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Content width preset values
|
|
6
|
-
* - 'narrow' - 640px max width (reading content)
|
|
7
|
-
* - 'standard' - 1024px max width (default)
|
|
8
|
-
* - 'wide' - 1440px max width (dashboards)
|
|
9
|
-
* - 'full' - 100% width (no max)
|
|
10
|
-
*/
|
|
11
|
-
export type ContentWidth = 'narrow' | 'standard' | 'wide' | 'full';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Network status type
|
|
15
|
-
*/
|
|
16
|
-
export type NetworkStatus = 'online' | 'offline' | 'unknown';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* MobilePageBase
|
|
20
|
-
*
|
|
21
|
-
* Shared base class for mobile page components (ds-mobile-page-main, ds-mobile-page-details).
|
|
22
|
-
* Provides consistent content width control and network status monitoring across all page types.
|
|
23
|
-
*
|
|
24
|
-
* **Padding Strategy:**
|
|
25
|
-
* - All pages use 20px horizontal padding globally
|
|
26
|
-
* - For tappable lists, use negative margins (e.g., margin: 0 -8px) to create full-width sections
|
|
27
|
-
* - This approach simplifies padding management and provides consistency
|
|
28
|
-
*
|
|
29
|
-
* **Network Monitoring:**
|
|
30
|
-
* - Tracks online/offline status via Capacitor Network plugin (native) or browser API (web)
|
|
31
|
-
* - Exposes `isOffline()` computed signal for easy consumption by child components
|
|
32
|
-
* - Pages can use this to conditionally disable features or show offline indicators
|
|
33
|
-
*
|
|
34
|
-
* @internal This is a base class and should not be used directly.
|
|
35
|
-
*/
|
|
36
|
-
@Directive()
|
|
37
|
-
export abstract class MobilePageBase implements OnDestroy {
|
|
38
|
-
/**
|
|
39
|
-
* Shows a loading overlay above page content area.
|
|
40
|
-
*
|
|
41
|
-
* Non-breaking: defaults to false, so existing pages are unchanged
|
|
42
|
-
* until they explicitly opt in.
|
|
43
|
-
*/
|
|
44
|
-
contentLoading = input<boolean>(false);
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Maximum content width (desktop only)
|
|
48
|
-
*
|
|
49
|
-
* **Options:**
|
|
50
|
-
* - `'narrow'` (640px) - For reading content, forms
|
|
51
|
-
* - `'standard'` (1024px) - Default for most pages
|
|
52
|
-
* - `'wide'` (1440px) - For dashboards, tables
|
|
53
|
-
* - `'full'` - No max-width constraint
|
|
54
|
-
*
|
|
55
|
-
* **Note:** Only applies on desktop (>= 768px). Mobile is always full width.
|
|
56
|
-
*
|
|
57
|
-
* @default 'standard'
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```html
|
|
61
|
-
* <!-- Narrow reading layout -->
|
|
62
|
-
* <ds-mobile-page-main title="Article" contentWidth="narrow">
|
|
63
|
-
*
|
|
64
|
-
* <!-- Wide dashboard -->
|
|
65
|
-
* <ds-mobile-page-main title="Dashboard" contentWidth="wide">
|
|
66
|
-
* ```
|
|
67
|
-
*/
|
|
68
|
-
contentWidth = input<ContentWidth>('standard');
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Resolved max-width value (computed)
|
|
72
|
-
* Maps preset values to pixel values
|
|
73
|
-
*
|
|
74
|
-
* @internal
|
|
75
|
-
*/
|
|
76
|
-
protected maxWidthValue = computed(() => {
|
|
77
|
-
const w = this.contentWidth();
|
|
78
|
-
|
|
79
|
-
const widthMap: Record<ContentWidth, string> = {
|
|
80
|
-
'narrow': '640px',
|
|
81
|
-
'standard': '1024px',
|
|
82
|
-
'wide': '1440px',
|
|
83
|
-
'full': '100%'
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
return widthMap[w];
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Network status signal
|
|
91
|
-
* Tracks current online/offline state
|
|
92
|
-
*
|
|
93
|
-
* @internal
|
|
94
|
-
*/
|
|
95
|
-
protected networkStatus = signal<NetworkStatus>('unknown');
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Is the device currently offline?
|
|
99
|
-
* Public computed signal for consumption by child components and pages
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```typescript
|
|
103
|
-
* // In a page component
|
|
104
|
-
* @ViewChild(DsMobilePageMainComponent) pageComponent!: DsMobilePageMainComponent;
|
|
105
|
-
*
|
|
106
|
-
* get isOffline() {
|
|
107
|
-
* return this.pageComponent?.isOffline();
|
|
108
|
-
* }
|
|
109
|
-
* ```
|
|
110
|
-
*/
|
|
111
|
-
public isOffline = computed(() => this.networkStatus() === 'offline');
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Is the device currently online?
|
|
115
|
-
* Public computed signal for consumption by child components and pages
|
|
116
|
-
*/
|
|
117
|
-
public isOnline = computed(() => this.networkStatus() === 'online');
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Network listener ID for Capacitor Network plugin
|
|
121
|
-
* Used to clean up listener on destroy
|
|
122
|
-
*
|
|
123
|
-
* @internal
|
|
124
|
-
*/
|
|
125
|
-
private networkListenerId?: string;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Browser API event handlers
|
|
129
|
-
* Stored as class properties for proper cleanup
|
|
130
|
-
*
|
|
131
|
-
* @internal
|
|
132
|
-
*/
|
|
133
|
-
private handleOnline = () => {
|
|
134
|
-
this.networkStatus.set('online');
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
private handleOffline = () => {
|
|
138
|
-
this.networkStatus.set('offline');
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Initialize network status monitoring
|
|
143
|
-
* Uses Capacitor Network plugin for native apps, browser API for web
|
|
144
|
-
*
|
|
145
|
-
* @param isNative - Whether running on native platform (iOS/Android)
|
|
146
|
-
* @internal Called by child components in ngAfterViewInit
|
|
147
|
-
*/
|
|
148
|
-
protected async initNetworkMonitoring(isNative: boolean): Promise<void> {
|
|
149
|
-
if (isNative) {
|
|
150
|
-
try {
|
|
151
|
-
// Try to use Capacitor Network plugin for native apps
|
|
152
|
-
const status = await Network.getStatus();
|
|
153
|
-
this.networkStatus.set(status.connected ? 'online' : 'offline');
|
|
154
|
-
|
|
155
|
-
// Listen for network status changes
|
|
156
|
-
const listener = await Network.addListener('networkStatusChange', (status: any) => {
|
|
157
|
-
this.networkStatus.set(status.connected ? 'online' : 'offline');
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Store listener ID for cleanup
|
|
161
|
-
this.networkListenerId = 'networkStatusChange';
|
|
162
|
-
} catch (error) {
|
|
163
|
-
// Fallback to browser API if Capacitor plugin fails
|
|
164
|
-
console.warn('Capacitor Network plugin not available, falling back to browser API', error);
|
|
165
|
-
this.initBrowserNetworkMonitoring();
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
// Use browser API for web/PWA
|
|
169
|
-
this.initBrowserNetworkMonitoring();
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Initialize browser-based network monitoring
|
|
175
|
-
* Uses navigator.onLine and window events
|
|
176
|
-
*
|
|
177
|
-
* @internal
|
|
178
|
-
*/
|
|
179
|
-
private initBrowserNetworkMonitoring(): void {
|
|
180
|
-
// Set initial status from browser
|
|
181
|
-
this.networkStatus.set(navigator.onLine ? 'online' : 'offline');
|
|
182
|
-
|
|
183
|
-
// Listen for online/offline events
|
|
184
|
-
window.addEventListener('online', this.handleOnline);
|
|
185
|
-
window.addEventListener('offline', this.handleOffline);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Cleanup network monitoring listeners
|
|
190
|
-
* Called automatically on component destroy
|
|
191
|
-
*/
|
|
192
|
-
ngOnDestroy(): void {
|
|
193
|
-
// Clean up Capacitor listener if it exists
|
|
194
|
-
if (this.networkListenerId) {
|
|
195
|
-
Network.removeAllListeners().catch((err: any) => {
|
|
196
|
-
console.warn('Failed to remove network listeners', err);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Clean up browser event listeners
|
|
201
|
-
window.removeEventListener('online', this.handleOnline);
|
|
202
|
-
window.removeEventListener('offline', this.handleOffline);
|
|
203
|
-
}
|
|
204
|
-
}
|