@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.
- package/fesm2022/propbinder-mobile-design.mjs +12596 -0
- package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
- package/index.d.ts +3214 -0
- package/package.json +39 -12
- package/ng-package.json +0 -7
- package/src/animations/page-transitions.ts +0 -86
- package/src/assets/fonts/Brockmann-Bold.otf +0 -0
- package/src/assets/fonts/Brockmann-BoldItalic.otf +0 -0
- package/src/assets/fonts/Brockmann-Medium.otf +0 -0
- package/src/assets/fonts/Brockmann-MediumItalic.otf +0 -0
- package/src/assets/fonts/Brockmann-Regular.otf +0 -0
- package/src/assets/fonts/Brockmann-RegularItalic.otf +0 -0
- package/src/assets/fonts/Brockmann-SemiBold.otf +0 -0
- package/src/assets/fonts/Brockmann-SemiBoldItalic.otf +0 -0
- package/src/assets/fonts/Brockmann_desktop_license.pdf +0 -0
- package/src/assets/fonts/brockmann-medium-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-regular-webfont.woff2 +0 -0
- package/src/assets/fonts/brockmann-semibold-webfont.woff2 +0 -0
- package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -83
- package/src/components/action-list-item/index.ts +0 -2
- package/src/components/app-layout/ds-mobile-app-layout.css +0 -343
- package/src/components/app-layout/ds-mobile-app-layout.ts +0 -271
- package/src/components/app-layout/index.ts +0 -2
- package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -130
- package/src/components/avatar-with-badge/index.ts +0 -2
- package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -273
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -110
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -167
- package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -656
- package/src/components/bottom-sheet/index.ts +0 -3
- package/src/components/comment/ds-mobile-comment.ts +0 -516
- 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 -158
- package/src/components/content/index.ts +0 -2
- package/src/components/ds-mobile-tabs.css +0 -372
- package/src/components/ds-mobile-tabs.ts +0 -217
- 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.service.ts +0 -98
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -514
- package/src/components/handbook-detail-modal/index.ts +0 -3
- package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -130
- package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -444
- package/src/components/handbook-folder/index.ts +0 -4
- package/src/components/header-content/ds-mobile-header-content.ts +0 -211
- package/src/components/header-content/index.ts +0 -2
- package/src/components/index.ts +0 -45
- package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -269
- package/src/components/inline-photo/index.ts +0 -1
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.css +0 -60
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -280
- 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 -197
- package/src/components/interactive-list-item-message/index.ts +0 -2
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.css +0 -70
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -594
- 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 -331
- package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -173
- package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -464
- package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -375
- 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 -293
- 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 -499
- 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/logo/ds-logo.ts +0 -85
- package/src/components/logo/index.ts +0 -2
- 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/page-details/ds-mobile-page-details.css +0 -285
- package/src/components/page-details/ds-mobile-page-details.ts +0 -128
- package/src/components/page-details/index.ts +0 -2
- package/src/components/page-main/ds-mobile-page-main.css +0 -346
- package/src/components/page-main/ds-mobile-page-main.ts +0 -331
- package/src/components/page-main/index.ts +0 -2
- package/src/components/post-card/ds-mobile-post-card.ts +0 -685
- package/src/components/post-card/ds-mobile-post-pdf-attachment.ts +0 -124
- package/src/components/post-card/index.ts +0 -11
- 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.service.ts +0 -104
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -1273
- package/src/components/post-detail-modal/index.ts +0 -9
- package/src/components/shared/directives/index.ts +0 -2
- package/src/components/shared/directives/long-press.directive.ts +0 -208
- package/src/components/shared/index.ts +0 -3
- package/src/components/shared/mobile-common.css +0 -94
- package/src/components/shared/mobile-page-base.css +0 -315
- package/src/components/shared/mobile-page-base.ts +0 -70
- package/src/components/swiper/ds-mobile-swiper.ts +0 -123
- package/src/components/swiper/index.ts +0 -2
- package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -132
- package/src/components/tab-bar/index.ts +0 -2
- package/src/components/tabs/ds-mobile-tabs.css +0 -405
- package/src/components/tabs/ds-mobile-tabs.ts +0 -204
- package/src/components/tabs/index.ts +0 -2
- package/src/pages/community.page.ts +0 -768
- package/src/pages/handbook.page.ts +0 -298
- package/src/pages/home.page.ts +0 -192
- package/src/pages/index.ts +0 -9
- package/src/pages/inquiries.example.ts +0 -212
- package/src/pages/inquiry-detail.example.css +0 -434
- package/src/pages/inquiry-detail.example.ts +0 -416
- package/src/pages/mobile-tabs-example.component.ts +0 -146
- package/src/pages/post-create.page.ts +0 -311
- package/src/pages/post-detail.page.ts +0 -295
- package/src/pages/whitelabel-demo.page.ts +0 -548
- package/src/public-api.ts +0 -5
- package/src/services/user.service.ts +0 -35
- package/src/services/whitelabel.service.ts +0 -171
- package/src/styles/ionic.css +0 -673
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -13
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Component,
|
|
3
|
-
signal,
|
|
4
|
-
computed,
|
|
5
|
-
ViewChild,
|
|
6
|
-
ElementRef,
|
|
7
|
-
AfterViewInit,
|
|
8
|
-
OnDestroy,
|
|
9
|
-
OnInit,
|
|
10
|
-
CUSTOM_ELEMENTS_SCHEMA,
|
|
11
|
-
HostListener
|
|
12
|
-
} from '@angular/core';
|
|
13
|
-
import { CommonModule } from '@angular/common';
|
|
14
|
-
import {
|
|
15
|
-
IonSpinner,
|
|
16
|
-
GestureController,
|
|
17
|
-
Gesture
|
|
18
|
-
} from '@ionic/angular/standalone';
|
|
19
|
-
import { Share } from '@capacitor/share';
|
|
20
|
-
import { Haptics, ImpactStyle } from '@capacitor/haptics';
|
|
21
|
-
import { DsIconButtonComponent } from '@propbinder/design-system';
|
|
22
|
-
import { DsAvatarComponent } from '@propbinder/design-system';
|
|
23
|
-
import { DsMobileLightboxHeaderComponent } from './ds-mobile-lightbox-header';
|
|
24
|
-
import { DsMobileLightboxFooterComponent } from './ds-mobile-lightbox-footer';
|
|
25
|
-
import type { LightboxImage, LightboxAuthor } from './ds-mobile-lightbox.service';
|
|
26
|
-
import Swiper from 'swiper';
|
|
27
|
-
import type { SwiperOptions } from 'swiper/types';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* DsMobileLightboxImageComponent
|
|
31
|
-
*
|
|
32
|
-
* Full-screen image lightbox component with Swiper.js navigation and pinch-zoom.
|
|
33
|
-
*
|
|
34
|
-
* This component is typically not used directly - use DsMobileLightboxService instead.
|
|
35
|
-
*
|
|
36
|
-
* Features:
|
|
37
|
-
* - Swiper.js for smooth image navigation
|
|
38
|
-
* - Pinch to zoom in/out
|
|
39
|
-
* - Double-tap to toggle zoom
|
|
40
|
-
* - Swipe down to close (when not zoomed)
|
|
41
|
-
* - Image counter and navigation controls
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```typescript
|
|
45
|
-
* // Don't instantiate directly - use the service:
|
|
46
|
-
* constructor(private lightbox: DsMobileLightboxService) {}
|
|
47
|
-
*
|
|
48
|
-
* openImage() {
|
|
49
|
-
* this.lightbox.openImages({
|
|
50
|
-
* images: [{ type: 'image', src: 'image.jpg', title: 'My Image' }]
|
|
51
|
-
* });
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
@Component({
|
|
56
|
-
selector: 'ds-mobile-lightbox-image',
|
|
57
|
-
standalone: true,
|
|
58
|
-
imports: [
|
|
59
|
-
CommonModule,
|
|
60
|
-
IonSpinner,
|
|
61
|
-
DsIconButtonComponent,
|
|
62
|
-
DsAvatarComponent,
|
|
63
|
-
DsMobileLightboxHeaderComponent,
|
|
64
|
-
DsMobileLightboxFooterComponent
|
|
65
|
-
],
|
|
66
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
67
|
-
template: `
|
|
68
|
-
<div class="lightbox-overlay"
|
|
69
|
-
[class.zoomed]="isZoomed()">
|
|
70
|
-
|
|
71
|
-
<div class="lightbox-wrapper">
|
|
72
|
-
<!-- Header with author info and close button -->
|
|
73
|
-
<ds-mobile-lightbox-header
|
|
74
|
-
[author]="author"
|
|
75
|
-
(closeClick)="close()"
|
|
76
|
-
/>
|
|
77
|
-
|
|
78
|
-
<!-- Swiper container -->
|
|
79
|
-
<div class="swiper-container" #swiperContainer>
|
|
80
|
-
<div class="swiper-wrapper">
|
|
81
|
-
@for (image of images; track image.src; let i = $index) {
|
|
82
|
-
<div class="swiper-slide">
|
|
83
|
-
<div class="image-zoom-container" [attr.data-index]="i">
|
|
84
|
-
<img
|
|
85
|
-
[src]="image.src"
|
|
86
|
-
[alt]="image.alt || 'Lightbox image'"
|
|
87
|
-
class="lightbox-image"
|
|
88
|
-
(load)="onImageLoad(i)"
|
|
89
|
-
(error)="onImageError(i)">
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
}
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
<!-- Loading indicator -->
|
|
97
|
-
@if (isLoading()) {
|
|
98
|
-
<div class="loading-spinner">
|
|
99
|
-
<ion-spinner name="crescent"></ion-spinner>
|
|
100
|
-
</div>
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
<!-- Footer with navigation and actions -->
|
|
104
|
-
<ds-mobile-lightbox-footer
|
|
105
|
-
[showNavigation]="showControls && images.length > 1"
|
|
106
|
-
[currentIndex]="currentIndex()"
|
|
107
|
-
[totalImages]="images.length"
|
|
108
|
-
[isLiked]="isLiked()"
|
|
109
|
-
[likeCount]="likeCount()"
|
|
110
|
-
[commentCount]="commentCount()"
|
|
111
|
-
(prevClick)="previousImage()"
|
|
112
|
-
(nextClick)="nextImage()"
|
|
113
|
-
(likeClick)="onLikeToggle()"
|
|
114
|
-
(commentClick)="onReply()"
|
|
115
|
-
(shareClick)="onShare()"
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
`,
|
|
120
|
-
styleUrl: './ds-mobile-lightbox.css'
|
|
121
|
-
})
|
|
122
|
-
export class DsMobileLightboxImageComponent implements OnInit, AfterViewInit, OnDestroy {
|
|
123
|
-
// Inputs (passed from service as regular properties, not signals)
|
|
124
|
-
images!: LightboxImage[];
|
|
125
|
-
author?: LightboxAuthor;
|
|
126
|
-
initialIndex: number = 0;
|
|
127
|
-
enableZoom: boolean = true;
|
|
128
|
-
showControls: boolean = true;
|
|
129
|
-
enableSwipe: boolean = true;
|
|
130
|
-
showInfo: boolean = true;
|
|
131
|
-
animation: 'fade' | 'zoom' | 'slide' = 'fade';
|
|
132
|
-
onCloseRequested?: () => void;
|
|
133
|
-
|
|
134
|
-
// View children
|
|
135
|
-
@ViewChild('swiperContainer', { read: ElementRef }) swiperContainer!: ElementRef<HTMLDivElement>;
|
|
136
|
-
|
|
137
|
-
// State
|
|
138
|
-
currentIndex = signal(0);
|
|
139
|
-
scale = signal(1);
|
|
140
|
-
isZoomed = signal(false);
|
|
141
|
-
isLoading = signal(true);
|
|
142
|
-
hasError = signal(false);
|
|
143
|
-
|
|
144
|
-
// Action states
|
|
145
|
-
isLiked = signal(false);
|
|
146
|
-
likeCount = signal(0);
|
|
147
|
-
commentCount = signal(0);
|
|
148
|
-
|
|
149
|
-
// Computed
|
|
150
|
-
currentImage = computed(() => this.images[this.currentIndex()]);
|
|
151
|
-
|
|
152
|
-
// Swiper instance
|
|
153
|
-
private swiper?: Swiper;
|
|
154
|
-
private zoomData: Map<number, { scale: number; x: number; y: number }> = new Map();
|
|
155
|
-
|
|
156
|
-
constructor(
|
|
157
|
-
private gestureCtrl: GestureController
|
|
158
|
-
) {}
|
|
159
|
-
|
|
160
|
-
ngOnInit(): void {
|
|
161
|
-
// Set initial index from the passed property
|
|
162
|
-
if (this.initialIndex !== undefined) {
|
|
163
|
-
this.currentIndex.set(this.initialIndex);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Initialize action states from current image
|
|
167
|
-
const currentImg = this.images[this.currentIndex()];
|
|
168
|
-
if (currentImg) {
|
|
169
|
-
this.isLiked.set(currentImg.isLiked ?? false);
|
|
170
|
-
this.likeCount.set(currentImg.likeCount ?? 0);
|
|
171
|
-
this.commentCount.set(currentImg.commentCount ?? 0);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
ngAfterViewInit(): void {
|
|
176
|
-
setTimeout(() => {
|
|
177
|
-
this.initializeSwiper();
|
|
178
|
-
this.initializeZoomGestures();
|
|
179
|
-
}, 100);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
ngOnDestroy(): void {
|
|
183
|
-
// Clean up Swiper
|
|
184
|
-
if (this.swiper) {
|
|
185
|
-
this.swiper.destroy();
|
|
186
|
-
this.swiper = undefined;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Initialize Swiper for image navigation
|
|
191
|
-
*/
|
|
192
|
-
private initializeSwiper(): void {
|
|
193
|
-
if (!this.swiperContainer) {
|
|
194
|
-
console.error('[Lightbox] Swiper container not found');
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const swiperOptions: SwiperOptions = {
|
|
199
|
-
initialSlide: this.initialIndex,
|
|
200
|
-
speed: 300,
|
|
201
|
-
resistance: true,
|
|
202
|
-
resistanceRatio: 0.85,
|
|
203
|
-
slidesPerView: 1,
|
|
204
|
-
spaceBetween: 0,
|
|
205
|
-
touchRatio: 1,
|
|
206
|
-
longSwipesRatio: 0.5,
|
|
207
|
-
threshold: 10,
|
|
208
|
-
on: {
|
|
209
|
-
slideChange: (swiper) => {
|
|
210
|
-
this.currentIndex.set(swiper.activeIndex);
|
|
211
|
-
this.updateActionStates();
|
|
212
|
-
|
|
213
|
-
// Check if the image is already loaded
|
|
214
|
-
const currentSlide = swiper.slides[swiper.activeIndex];
|
|
215
|
-
const img = currentSlide?.querySelector('img');
|
|
216
|
-
if (img && img.complete && img.naturalHeight !== 0) {
|
|
217
|
-
// Image is already loaded
|
|
218
|
-
this.isLoading.set(false);
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
slideChangeTransitionStart: () => {
|
|
222
|
-
// Don't show loading spinner if image is already loaded
|
|
223
|
-
const currentSlide = this.swiper?.slides[this.swiper.activeIndex];
|
|
224
|
-
const img = currentSlide?.querySelector('img');
|
|
225
|
-
if (!img || !img.complete || img.naturalHeight === 0) {
|
|
226
|
-
this.isLoading.set(true);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
this.swiper = new Swiper(this.swiperContainer.nativeElement, swiperOptions);
|
|
233
|
-
|
|
234
|
-
// Check if the initial image is already loaded
|
|
235
|
-
setTimeout(() => {
|
|
236
|
-
const currentSlide = this.swiper?.slides[this.currentIndex()];
|
|
237
|
-
const img = currentSlide?.querySelector('img');
|
|
238
|
-
if (img && img.complete && img.naturalHeight !== 0) {
|
|
239
|
-
this.isLoading.set(false);
|
|
240
|
-
}
|
|
241
|
-
}, 0);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Initialize pinch-zoom gestures for all slides
|
|
246
|
-
*/
|
|
247
|
-
private initializeZoomGestures(): void {
|
|
248
|
-
if (!this.enableZoom) return;
|
|
249
|
-
|
|
250
|
-
const slides = this.swiperContainer.nativeElement.querySelectorAll('.image-zoom-container');
|
|
251
|
-
|
|
252
|
-
slides.forEach((slide, index) => {
|
|
253
|
-
this.initializeZoomForSlide(slide as HTMLElement, index);
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Initialize zoom gestures for a specific slide
|
|
259
|
-
*/
|
|
260
|
-
private initializeZoomForSlide(container: HTMLElement, index: number): void {
|
|
261
|
-
let initialDistance = 0;
|
|
262
|
-
let initialScale = 1;
|
|
263
|
-
let currentScale = 1;
|
|
264
|
-
let lastTap = 0;
|
|
265
|
-
|
|
266
|
-
// Double-tap to zoom
|
|
267
|
-
container.addEventListener('click', (event: MouseEvent) => {
|
|
268
|
-
const now = Date.now();
|
|
269
|
-
const timeSinceLastTap = now - lastTap;
|
|
270
|
-
|
|
271
|
-
if (timeSinceLastTap < 300 && timeSinceLastTap > 0) {
|
|
272
|
-
event.preventDefault();
|
|
273
|
-
this.toggleZoom(container, index);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
lastTap = now;
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
// Pinch to zoom
|
|
280
|
-
const getTouchDistance = (touches: TouchList) => {
|
|
281
|
-
const dx = touches[0].clientX - touches[1].clientX;
|
|
282
|
-
const dy = touches[0].clientY - touches[1].clientY;
|
|
283
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
container.addEventListener('touchstart', (event: TouchEvent) => {
|
|
287
|
-
if (event.touches.length === 2) {
|
|
288
|
-
event.preventDefault();
|
|
289
|
-
initialDistance = getTouchDistance(event.touches);
|
|
290
|
-
const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
|
|
291
|
-
initialScale = zoomData.scale;
|
|
292
|
-
|
|
293
|
-
// Disable Swiper when zooming
|
|
294
|
-
if (this.swiper) {
|
|
295
|
-
this.swiper.allowTouchMove = false;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}, { passive: false });
|
|
299
|
-
|
|
300
|
-
container.addEventListener('touchmove', (event: TouchEvent) => {
|
|
301
|
-
if (event.touches.length === 2) {
|
|
302
|
-
event.preventDefault();
|
|
303
|
-
|
|
304
|
-
const currentDistance = getTouchDistance(event.touches);
|
|
305
|
-
const pinchScale = currentDistance / initialDistance;
|
|
306
|
-
|
|
307
|
-
currentScale = Math.max(1, Math.min(initialScale * pinchScale, 4));
|
|
308
|
-
|
|
309
|
-
const img = container.querySelector('img');
|
|
310
|
-
if (img) {
|
|
311
|
-
img.style.transform = `scale(${currentScale})`;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (currentScale > 1) {
|
|
315
|
-
this.isZoomed.set(true);
|
|
316
|
-
} else {
|
|
317
|
-
this.isZoomed.set(false);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}, { passive: false });
|
|
321
|
-
|
|
322
|
-
container.addEventListener('touchend', (event: TouchEvent) => {
|
|
323
|
-
if (event.touches.length < 2) {
|
|
324
|
-
// Save zoom state
|
|
325
|
-
this.zoomData.set(index, { scale: currentScale, x: 0, y: 0 });
|
|
326
|
-
|
|
327
|
-
// Re-enable Swiper if not zoomed
|
|
328
|
-
if (this.swiper && currentScale <= 1) {
|
|
329
|
-
this.swiper.allowTouchMove = true;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Toggle zoom on double-tap
|
|
337
|
-
*/
|
|
338
|
-
private toggleZoom(container: HTMLElement, index: number): void {
|
|
339
|
-
const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
|
|
340
|
-
const img = container.querySelector('img');
|
|
341
|
-
|
|
342
|
-
if (!img) return;
|
|
343
|
-
|
|
344
|
-
if (zoomData.scale > 1) {
|
|
345
|
-
// Zoom out
|
|
346
|
-
img.style.transform = 'scale(1)';
|
|
347
|
-
this.zoomData.set(index, { scale: 1, x: 0, y: 0 });
|
|
348
|
-
this.isZoomed.set(false);
|
|
349
|
-
if (this.swiper) {
|
|
350
|
-
this.swiper.allowTouchMove = true;
|
|
351
|
-
}
|
|
352
|
-
} else {
|
|
353
|
-
// Zoom in
|
|
354
|
-
img.style.transform = 'scale(2)';
|
|
355
|
-
this.zoomData.set(index, { scale: 2, x: 0, y: 0 });
|
|
356
|
-
this.isZoomed.set(true);
|
|
357
|
-
if (this.swiper) {
|
|
358
|
-
this.swiper.allowTouchMove = false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Update action states (like, comments) when slide changes
|
|
365
|
-
*/
|
|
366
|
-
private updateActionStates(): void {
|
|
367
|
-
const currentImg = this.images[this.currentIndex()];
|
|
368
|
-
if (currentImg) {
|
|
369
|
-
this.isLiked.set(currentImg.isLiked ?? false);
|
|
370
|
-
this.likeCount.set(currentImg.likeCount ?? 0);
|
|
371
|
-
this.commentCount.set(currentImg.commentCount ?? 0);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Close the lightbox
|
|
377
|
-
*/
|
|
378
|
-
close(): void {
|
|
379
|
-
if (this.onCloseRequested) {
|
|
380
|
-
this.onCloseRequested();
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Handle share button click
|
|
386
|
-
*/
|
|
387
|
-
async onShare(): Promise<void> {
|
|
388
|
-
console.log('[Lightbox] Share button clicked');
|
|
389
|
-
const currentImg = this.currentImage();
|
|
390
|
-
|
|
391
|
-
try {
|
|
392
|
-
await Share.share({
|
|
393
|
-
title: currentImg.title || 'Shared Image',
|
|
394
|
-
url: currentImg.src,
|
|
395
|
-
dialogTitle: 'Share Image',
|
|
396
|
-
});
|
|
397
|
-
} catch (error) {
|
|
398
|
-
console.error('[Lightbox] Share failed:', error);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Handle like button toggle
|
|
404
|
-
*/
|
|
405
|
-
onLikeToggle(): void {
|
|
406
|
-
console.log('[Lightbox] Like button toggled');
|
|
407
|
-
this.isLiked.update(liked => !liked);
|
|
408
|
-
|
|
409
|
-
if (this.isLiked()) {
|
|
410
|
-
this.likeCount.update(count => count + 1);
|
|
411
|
-
} else {
|
|
412
|
-
this.likeCount.update(count => Math.max(0, count - 1));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Handle reply/comment button click
|
|
418
|
-
*/
|
|
419
|
-
onReply(): void {
|
|
420
|
-
console.log('[Lightbox] Reply button clicked');
|
|
421
|
-
if (this.onCloseRequested) {
|
|
422
|
-
this.onCloseRequested();
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Navigate to the next image
|
|
428
|
-
*/
|
|
429
|
-
nextImage(): void {
|
|
430
|
-
if (this.swiper && this.currentIndex() < this.images.length - 1) {
|
|
431
|
-
this.swiper.slideNext();
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Navigate to the previous image
|
|
437
|
-
*/
|
|
438
|
-
previousImage(): void {
|
|
439
|
-
if (this.swiper && this.currentIndex() > 0) {
|
|
440
|
-
this.swiper.slidePrev();
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Handle image load success
|
|
446
|
-
*/
|
|
447
|
-
onImageLoad(index: number): void {
|
|
448
|
-
if (index === this.currentIndex()) {
|
|
449
|
-
this.isLoading.set(false);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Handle image load error
|
|
455
|
-
*/
|
|
456
|
-
onImageError(index: number): void {
|
|
457
|
-
if (index === this.currentIndex()) {
|
|
458
|
-
console.error(`[Lightbox] Image ${index} failed to load`);
|
|
459
|
-
this.isLoading.set(false);
|
|
460
|
-
this.hasError.set(true);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|