@propbinder/mobile-design 0.2.15 → 0.2.16

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.
@@ -17,12 +17,12 @@ import { Camera, CameraSource, CameraResultType } from '@capacitor/camera';
17
17
  import { Subject } from 'rxjs';
18
18
  import { createAnimation } from '@ionic/core';
19
19
  import { filter } from 'rxjs/operators';
20
- import { Share } from '@capacitor/share';
21
20
  import Swiper from 'swiper';
21
+ import { Pagination } from 'swiper/modules';
22
+ import { Share } from '@capacitor/share';
22
23
  import { Filesystem, Directory } from '@capacitor/filesystem';
23
24
  import { Browser } from '@capacitor/browser';
24
25
  import { Capacitor } from '@capacitor/core';
25
- import { Pagination } from 'swiper/modules';
26
26
  import * as i1$3 from '@angular/platform-browser';
27
27
 
28
28
  const DEFAULT_CONFIG = {
@@ -9479,902 +9479,1211 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
9479
9479
  }] } });
9480
9480
 
9481
9481
  /**
9482
- * DsMobileLightboxHeaderComponent
9482
+ * DsMobileSwiperComponent
9483
9483
  *
9484
- * Shared header component for all lightbox types (image, PDF, etc.)
9485
- * Displays author information and close button with consistent styling.
9486
- */
9487
- class DsMobileLightboxHeaderComponent {
9488
- /**
9489
- * Author information to display
9490
- */
9491
- author = input(...(ngDevMode ? [undefined, { debugName: "author" }] : []));
9492
- /**
9493
- * Emitted when close button is clicked
9494
- */
9495
- closeClick = output();
9496
- /**
9497
- * Emitted when share button is clicked
9498
- */
9499
- shareClick = output();
9500
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9501
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxHeaderComponent, isStandalone: true, selector: "ds-mobile-lightbox-header", inputs: { author: { classPropertyName: "author", publicName: "author", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeClick: "closeClick", shareClick: "shareClick" }, ngImport: i0, template: `
9502
- <div class="lightbox-header lightbox-context">
9503
- <div class="header-content">
9504
- <!-- Post author info -->
9505
- @if (author()) {
9506
- <div class="post-author-info">
9507
- <ds-avatar
9508
- [initials]="author()!.avatarInitials ?? ''"
9509
- [type]="author()!.avatarType ?? 'initials'"
9510
- [src]="author()!.avatarSrc ?? ''"
9511
- size="md"
9512
- />
9513
- <div class="author-details">
9514
- <div class="author-name">{{ author()!.name }}</div>
9515
- <div class="author-meta">
9516
- @if (author()!.role) {
9517
- <span>{{ author()!.role }}</span>
9518
- }
9519
- @if (author()!.role && author()!.timestamp) {
9520
- <span class="separator">·</span>
9521
- }
9522
- @if (author()!.timestamp) {
9523
- <span>{{ author()!.timestamp }}</span>
9524
- }
9525
- </div>
9526
- </div>
9527
- </div>
9528
- }
9529
-
9530
- <!-- Action buttons - always visible -->
9531
- <div class="header-actions">
9532
- <ds-icon-button
9533
- icon="remixShare2Line"
9534
- variant="ghost"
9535
- size="md"
9536
- (clicked)="shareClick.emit()"
9537
- class="share-button"
9538
- [ariaLabel]="'Share'">
9539
- </ds-icon-button>
9540
-
9541
- <ds-icon-button
9542
- icon="remixCloseLine"
9543
- variant="ghost"
9544
- size="md"
9545
- (clicked)="closeClick.emit()"
9546
- class="close-button"
9547
- [ariaLabel]="'Close'">
9548
- </ds-icon-button>
9549
- </div>
9550
- </div>
9551
- </div>
9552
- `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:flex-end;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0;margin-right:auto}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.header-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.close-button,.share-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button,.share-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover,.share-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active,.share-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg,.share-button::ng-deep svg{color:#fff!important;fill:#fff!important}@supports (padding-top: env(safe-area-inset-top)){.lightbox-header{padding-top:calc(16px + env(safe-area-inset-top))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }] });
9553
- }
9554
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxHeaderComponent, decorators: [{
9555
- type: Component,
9556
- args: [{ selector: 'ds-mobile-lightbox-header', standalone: true, imports: [CommonModule, DsIconButtonComponent, DsAvatarComponent], template: `
9557
- <div class="lightbox-header lightbox-context">
9558
- <div class="header-content">
9559
- <!-- Post author info -->
9560
- @if (author()) {
9561
- <div class="post-author-info">
9562
- <ds-avatar
9563
- [initials]="author()!.avatarInitials ?? ''"
9564
- [type]="author()!.avatarType ?? 'initials'"
9565
- [src]="author()!.avatarSrc ?? ''"
9566
- size="md"
9567
- />
9568
- <div class="author-details">
9569
- <div class="author-name">{{ author()!.name }}</div>
9570
- <div class="author-meta">
9571
- @if (author()!.role) {
9572
- <span>{{ author()!.role }}</span>
9573
- }
9574
- @if (author()!.role && author()!.timestamp) {
9575
- <span class="separator">·</span>
9576
- }
9577
- @if (author()!.timestamp) {
9578
- <span>{{ author()!.timestamp }}</span>
9579
- }
9580
- </div>
9581
- </div>
9582
- </div>
9583
- }
9584
-
9585
- <!-- Action buttons - always visible -->
9586
- <div class="header-actions">
9587
- <ds-icon-button
9588
- icon="remixShare2Line"
9589
- variant="ghost"
9590
- size="md"
9591
- (clicked)="shareClick.emit()"
9592
- class="share-button"
9593
- [ariaLabel]="'Share'">
9594
- </ds-icon-button>
9595
-
9596
- <ds-icon-button
9597
- icon="remixCloseLine"
9598
- variant="ghost"
9599
- size="md"
9600
- (clicked)="closeClick.emit()"
9601
- class="close-button"
9602
- [ariaLabel]="'Close'">
9603
- </ds-icon-button>
9604
- </div>
9605
- </div>
9606
- </div>
9607
- `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:flex-end;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0;margin-right:auto}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.header-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.close-button,.share-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button,.share-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover,.share-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active,.share-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg,.share-button::ng-deep svg{color:#fff!important;fill:#fff!important}@supports (padding-top: env(safe-area-inset-top)){.lightbox-header{padding-top:calc(16px + env(safe-area-inset-top))}}\n"] }]
9608
- }], propDecorators: { author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], closeClick: [{ type: i0.Output, args: ["closeClick"] }], shareClick: [{ type: i0.Output, args: ["shareClick"] }] } });
9609
-
9610
- /**
9611
- * DsMobileLightboxFooterComponent
9484
+ * A reusable swiper/carousel component with configurable child width and spacing.
9612
9485
  *
9613
- * Shared footer component for all lightbox types (image, PDF, etc.)
9614
- * Displays navigation controls (for multiple images) and action buttons.
9486
+ * Features:
9487
+ * - First slide is left-aligned
9488
+ * - Middle slides are centered when active
9489
+ * - Last slide is right-aligned
9490
+ * - Configurable slide width and gap
9491
+ * - Content projection via ng-content
9492
+ *
9493
+ * Usage:
9494
+ * ```html
9495
+ * <ds-mobile-swiper [slideWidth]="'75vw'" [gap]="16">
9496
+ * <div class="swiper-slide">Slide 1</div>
9497
+ * <div class="swiper-slide">Slide 2</div>
9498
+ * <div class="swiper-slide">Slide 3</div>
9499
+ * </ds-mobile-swiper>
9500
+ * ```
9615
9501
  */
9616
- class DsMobileLightboxFooterComponent {
9617
- /**
9618
- * Whether to show navigation controls
9619
- */
9620
- showNavigation = input(false, ...(ngDevMode ? [{ debugName: "showNavigation" }] : []));
9621
- /**
9622
- * Current image index (0-based)
9623
- */
9624
- currentIndex = input(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
9625
- /**
9626
- * Total number of images
9627
- */
9628
- totalImages = input(1, ...(ngDevMode ? [{ debugName: "totalImages" }] : []));
9629
- /**
9630
- * Whether to show like & comment action buttons
9631
- * @default false
9632
- */
9633
- showActions = input(false, ...(ngDevMode ? [{ debugName: "showActions" }] : []));
9634
- /**
9635
- * Whether the content is liked
9636
- */
9637
- isLiked = input(false, ...(ngDevMode ? [{ debugName: "isLiked" }] : []));
9502
+ class DsMobileSwiperComponent {
9503
+ elementRef;
9638
9504
  /**
9639
- * Number of likes
9505
+ * Width of each slide (e.g., '75vw', '300px', '80%')
9640
9506
  */
9641
- likeCount = input(0, ...(ngDevMode ? [{ debugName: "likeCount" }] : []));
9507
+ slideWidth = input('75vw', ...(ngDevMode ? [{ debugName: "slideWidth" }] : []));
9642
9508
  /**
9643
- * Number of comments
9509
+ * Gap between slides in pixels
9644
9510
  */
9645
- commentCount = input(0, ...(ngDevMode ? [{ debugName: "commentCount" }] : []));
9511
+ gap = input(16, ...(ngDevMode ? [{ debugName: "gap" }] : []));
9646
9512
  /**
9647
- * Emitted when previous button is clicked
9513
+ * Enable pagination dots
9648
9514
  */
9649
- prevClick = output();
9515
+ pagination = input(false, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
9650
9516
  /**
9651
- * Emitted when next button is clicked
9517
+ * Enable auto height - container adapts to active slide's height
9652
9518
  */
9653
- nextClick = output();
9519
+ autoHeight = input(false, ...(ngDevMode ? [{ debugName: "autoHeight" }] : []));
9654
9520
  /**
9655
- * Emitted when like button is clicked
9521
+ * Enable progressive opacity based on slide position
9522
+ * Slides fade in/out smoothly as they move toward/away from center
9656
9523
  */
9657
- likeClick = output();
9524
+ progressiveOpacity = input(false, ...(ngDevMode ? [{ debugName: "progressiveOpacity" }] : []));
9658
9525
  /**
9659
- * Emitted when comment button is clicked
9526
+ * Enable progressive scale based on slide position
9527
+ * Slides scale down smoothly as they move away from center
9660
9528
  */
9661
- commentClick = output();
9662
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9663
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxFooterComponent, isStandalone: true, selector: "ds-mobile-lightbox-footer", inputs: { showNavigation: { classPropertyName: "showNavigation", publicName: "showNavigation", isSignal: true, isRequired: false, transformFunction: null }, currentIndex: { classPropertyName: "currentIndex", publicName: "currentIndex", isSignal: true, isRequired: false, transformFunction: null }, totalImages: { classPropertyName: "totalImages", publicName: "totalImages", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, isLiked: { classPropertyName: "isLiked", publicName: "isLiked", isSignal: true, isRequired: false, transformFunction: null }, likeCount: { classPropertyName: "likeCount", publicName: "likeCount", isSignal: true, isRequired: false, transformFunction: null }, commentCount: { classPropertyName: "commentCount", publicName: "commentCount", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { prevClick: "prevClick", nextClick: "nextClick", likeClick: "likeClick", commentClick: "commentClick" }, ngImport: i0, template: `
9664
- <div class="lightbox-footer">
9665
- <!-- Navigation controls (only shown for multiple images) -->
9666
- @if (showNavigation() && totalImages() > 1) {
9667
- <div class="footer-navigation">
9668
- <button
9669
- class="nav-button prev"
9670
- (click)="prevClick.emit()"
9671
- [disabled]="currentIndex() === 0"
9672
- aria-label="Previous image">
9673
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9674
- <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9675
- </svg>
9676
- </button>
9677
-
9678
- <div class="counter">
9679
- {{ currentIndex() + 1 }} / {{ totalImages() }}
9680
- </div>
9681
-
9682
- <button
9683
- class="nav-button next"
9684
- (click)="nextClick.emit()"
9685
- [disabled]="currentIndex() === totalImages() - 1"
9686
- aria-label="Next image">
9687
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9688
- <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9689
- </svg>
9690
- </button>
9691
- </div>
9692
- }
9693
-
9694
- <!-- Action buttons -->
9695
- @if (showActions()) {
9696
- <div class="footer-actions">
9697
- <div class="action-buttons-left">
9698
- <!-- Like button -->
9699
- <ds-icon-button
9700
- [icon]="isLiked() ? 'remixHeart3Fill' : 'remixHeart3Line'"
9701
- variant="ghost"
9702
- size="md"
9703
- (click)="likeClick.emit()"
9704
- [attr.data-liked]="isLiked()"
9705
- class="action-button-like"
9706
- [ariaLabel]="'Like'">
9707
- </ds-icon-button>
9708
-
9709
- <!-- Comment button -->
9710
- <ds-icon-button
9711
- icon="remixChat3Line"
9712
- variant="ghost"
9713
- size="md"
9714
- (click)="commentClick.emit()"
9715
- class="action-button-comment"
9716
- [ariaLabel]="'Comment'">
9717
- </ds-icon-button>
9718
- </div>
9719
- </div>
9720
- }
9721
- </div>
9722
- `, isInline: true, styles: [".lightbox-footer{position:fixed;bottom:0;left:0;right:0;z-index:100;padding:16px 20px 20px;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none;display:flex;flex-direction:column;gap:12px}.footer-navigation{display:flex;align-items:center;justify-content:center;gap:24px;pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding:0}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px;flex-shrink:0}.counter{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-radius:100px;padding:8px 16px;color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;letter-spacing:-.3px;pointer-events:auto;-webkit-user-select:none;user-select:none}.footer-actions{display:flex;align-items:center;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-footer{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
9723
- }
9724
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxFooterComponent, decorators: [{
9725
- type: Component,
9726
- args: [{ selector: 'ds-mobile-lightbox-footer', standalone: true, imports: [CommonModule, DsIconButtonComponent], template: `
9727
- <div class="lightbox-footer">
9728
- <!-- Navigation controls (only shown for multiple images) -->
9729
- @if (showNavigation() && totalImages() > 1) {
9730
- <div class="footer-navigation">
9731
- <button
9732
- class="nav-button prev"
9733
- (click)="prevClick.emit()"
9734
- [disabled]="currentIndex() === 0"
9735
- aria-label="Previous image">
9736
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9737
- <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9738
- </svg>
9739
- </button>
9740
-
9741
- <div class="counter">
9742
- {{ currentIndex() + 1 }} / {{ totalImages() }}
9743
- </div>
9744
-
9745
- <button
9746
- class="nav-button next"
9747
- (click)="nextClick.emit()"
9748
- [disabled]="currentIndex() === totalImages() - 1"
9749
- aria-label="Next image">
9750
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9751
- <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9752
- </svg>
9753
- </button>
9754
- </div>
9755
- }
9756
-
9757
- <!-- Action buttons -->
9758
- @if (showActions()) {
9759
- <div class="footer-actions">
9760
- <div class="action-buttons-left">
9761
- <!-- Like button -->
9762
- <ds-icon-button
9763
- [icon]="isLiked() ? 'remixHeart3Fill' : 'remixHeart3Line'"
9764
- variant="ghost"
9765
- size="md"
9766
- (click)="likeClick.emit()"
9767
- [attr.data-liked]="isLiked()"
9768
- class="action-button-like"
9769
- [ariaLabel]="'Like'">
9770
- </ds-icon-button>
9771
-
9772
- <!-- Comment button -->
9773
- <ds-icon-button
9774
- icon="remixChat3Line"
9775
- variant="ghost"
9776
- size="md"
9777
- (click)="commentClick.emit()"
9778
- class="action-button-comment"
9779
- [ariaLabel]="'Comment'">
9780
- </ds-icon-button>
9781
- </div>
9782
- </div>
9783
- }
9784
- </div>
9785
- `, styles: [".lightbox-footer{position:fixed;bottom:0;left:0;right:0;z-index:100;padding:16px 20px 20px;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none;display:flex;flex-direction:column;gap:12px}.footer-navigation{display:flex;align-items:center;justify-content:center;gap:24px;pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding:0}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px;flex-shrink:0}.counter{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-radius:100px;padding:8px 16px;color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;letter-spacing:-.3px;pointer-events:auto;-webkit-user-select:none;user-select:none}.footer-actions{display:flex;align-items:center;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-footer{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"] }]
9786
- }], propDecorators: { showNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNavigation", required: false }] }], currentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentIndex", required: false }] }], totalImages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalImages", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], isLiked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLiked", required: false }] }], likeCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "likeCount", required: false }] }], commentCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentCount", required: false }] }], prevClick: [{ type: i0.Output, args: ["prevClick"] }], nextClick: [{ type: i0.Output, args: ["nextClick"] }], likeClick: [{ type: i0.Output, args: ["likeClick"] }], commentClick: [{ type: i0.Output, args: ["commentClick"] }] } });
9787
-
9788
- /**
9789
- * DsMobileLightboxImageComponent
9790
- *
9791
- * Full-screen image lightbox component with Swiper.js navigation and pinch-zoom.
9792
- *
9793
- * This component is typically not used directly - use DsMobileLightboxService instead.
9794
- *
9795
- * Features:
9796
- * - Swiper.js for smooth image navigation
9797
- * - Pinch to zoom in/out
9798
- * - Double-tap to toggle zoom
9799
- * - Swipe down to close (when not zoomed)
9800
- * - Image counter and navigation controls
9801
- *
9802
- * @example
9803
- * ```typescript
9804
- * // Don't instantiate directly - use the service:
9805
- * constructor(private lightbox: DsMobileLightboxService) {}
9806
- *
9807
- * openImage() {
9808
- * this.lightbox.openImages({
9809
- * images: [{ type: 'image', src: 'image.jpg', title: 'My Image' }]
9810
- * });
9811
- * }
9812
- * ```
9813
- */
9814
- class DsMobileLightboxImageComponent {
9815
- gestureCtrl;
9816
- // Inputs (passed from service as regular properties, not signals)
9817
- images;
9818
- author;
9819
- initialIndex = 0;
9820
- enableZoom = true;
9821
- showControls = true;
9822
- enableSwipe = true;
9823
- showInfo = true;
9824
- showActions = false;
9825
- animation = 'fade';
9826
- onCloseRequested;
9827
- // View children
9529
+ progressiveScale = input(false, ...(ngDevMode ? [{ debugName: "progressiveScale" }] : []));
9828
9530
  swiperContainer;
9829
- // State
9830
- currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
9831
- scale = signal(1, ...(ngDevMode ? [{ debugName: "scale" }] : []));
9832
- isZoomed = signal(false, ...(ngDevMode ? [{ debugName: "isZoomed" }] : []));
9833
- isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
9834
- hasError = signal(false, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
9835
- // Action states
9836
- isLiked = signal(false, ...(ngDevMode ? [{ debugName: "isLiked" }] : []));
9837
- likeCount = signal(0, ...(ngDevMode ? [{ debugName: "likeCount" }] : []));
9838
- commentCount = signal(0, ...(ngDevMode ? [{ debugName: "commentCount" }] : []));
9839
- // Computed
9840
- currentImage = computed(() => this.images[this.currentIndex()], ...(ngDevMode ? [{ debugName: "currentImage" }] : []));
9841
- // Swiper instance
9842
- swiper;
9843
- zoomData = new Map();
9844
- constructor(gestureCtrl) {
9845
- this.gestureCtrl = gestureCtrl;
9846
- }
9847
- ngOnInit() {
9848
- // Set initial index from the passed property
9849
- if (this.initialIndex !== undefined) {
9850
- this.currentIndex.set(this.initialIndex);
9851
- }
9852
- // Initialize action states from current image
9853
- const currentImg = this.images[this.currentIndex()];
9854
- if (currentImg) {
9855
- this.isLiked.set(currentImg.isLiked ?? false);
9856
- this.likeCount.set(currentImg.likeCount ?? 0);
9857
- this.commentCount.set(currentImg.commentCount ?? 0);
9858
- }
9531
+ swiperInstance = null;
9532
+ constructor(elementRef) {
9533
+ this.elementRef = elementRef;
9859
9534
  }
9860
9535
  ngAfterViewInit() {
9536
+ // Add progressive-opacity class to host if enabled
9537
+ if (this.progressiveOpacity()) {
9538
+ this.elementRef.nativeElement.classList.add('progressive-opacity');
9539
+ }
9861
9540
  setTimeout(() => {
9862
9541
  this.initializeSwiper();
9863
- this.initializeZoomGestures();
9864
9542
  }, 100);
9865
9543
  }
9866
- ngOnDestroy() {
9867
- // Clean up Swiper
9868
- if (this.swiper) {
9869
- this.swiper.destroy();
9870
- this.swiper = undefined;
9871
- }
9872
- }
9873
- /**
9874
- * Initialize Swiper for image navigation
9875
- */
9876
9544
  initializeSwiper() {
9877
- if (!this.swiperContainer) {
9878
- console.error('[Lightbox] Swiper container not found');
9545
+ if (!this.swiperContainer)
9879
9546
  return;
9880
- }
9881
- const swiperOptions = {
9882
- initialSlide: this.initialIndex,
9547
+ // Apply slide width to all slides
9548
+ const slides = this.swiperContainer.nativeElement.querySelectorAll('.swiper-slide');
9549
+ slides.forEach((slide, index) => {
9550
+ slide.style.width = this.slideWidth();
9551
+ // Set initial opacity BEFORE Swiper initializes to prevent flash of inactive slides
9552
+ if (this.progressiveOpacity()) {
9553
+ // Hide all slides except the first one (active slide)
9554
+ slide.style.opacity = index === 0 ? '1' : '0';
9555
+ }
9556
+ });
9557
+ const config = {
9558
+ slidesPerView: 'auto',
9559
+ spaceBetween: this.gap(),
9560
+ centeredSlides: true,
9561
+ centeredSlidesBounds: true,
9883
9562
  speed: 300,
9884
9563
  resistance: true,
9885
9564
  resistanceRatio: 0.85,
9886
- slidesPerView: 1,
9887
- spaceBetween: 0,
9888
- touchRatio: 1,
9889
- longSwipesRatio: 0.5,
9890
- threshold: 10,
9891
- on: {
9892
- slideChange: (swiper) => {
9893
- this.currentIndex.set(swiper.activeIndex);
9894
- this.updateActionStates();
9895
- // Check if the image is already loaded
9896
- const currentSlide = swiper.slides[swiper.activeIndex];
9897
- const img = currentSlide?.querySelector('img');
9898
- if (img && img.complete && img.naturalHeight !== 0) {
9899
- // Image is already loaded
9900
- this.isLoading.set(false);
9565
+ autoHeight: this.autoHeight(),
9566
+ watchSlidesProgress: this.progressiveOpacity() || this.progressiveScale(), // Enable progress tracking
9567
+ };
9568
+ // Configure event handlers
9569
+ config.on = {};
9570
+ // Configure autoHeight animation
9571
+ if (this.autoHeight()) {
9572
+ config.autoHeight = true;
9573
+ // The height transition will use the speed value (300ms)
9574
+ config.on.slideChangeTransitionStart = () => {
9575
+ // Height transition happens automatically
9576
+ };
9577
+ }
9578
+ // Configure progressive effects (opacity and/or scale)
9579
+ if (this.progressiveOpacity() || this.progressiveScale()) {
9580
+ config.on.setTranslate = () => {
9581
+ if (!this.swiperInstance)
9582
+ return;
9583
+ this.swiperInstance.slides.forEach((slideEl) => {
9584
+ const progress = slideEl.progress || 0;
9585
+ const absProgress = Math.abs(progress);
9586
+ // Progressive opacity with sharper cutoff
9587
+ if (this.progressiveOpacity()) {
9588
+ // Make opacity drop off more aggressively
9589
+ // Slides with absProgress > 0.5 will be completely hidden
9590
+ let opacity;
9591
+ if (absProgress > 0.5) {
9592
+ opacity = 0;
9593
+ }
9594
+ else {
9595
+ opacity = 1 - (absProgress * 2); // 2x multiplier for faster fade
9596
+ }
9597
+ slideEl.style.opacity = Math.max(opacity, 0).toString();
9901
9598
  }
9902
- },
9903
- slideChangeTransitionStart: () => {
9904
- // Don't show loading spinner if image is already loaded
9905
- const currentSlide = this.swiper?.slides[this.swiper.activeIndex];
9906
- const img = currentSlide?.querySelector('img');
9907
- if (!img || !img.complete || img.naturalHeight === 0) {
9908
- this.isLoading.set(true);
9599
+ // Progressive scale
9600
+ if (this.progressiveScale()) {
9601
+ // Scale from 1 (center) to 0.9 (edges)
9602
+ const minScale = 0.9;
9603
+ const scale = 1 - (absProgress * (1 - minScale));
9604
+ slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
9909
9605
  }
9910
- }
9911
- }
9912
- };
9913
- this.swiper = new Swiper(this.swiperContainer.nativeElement, swiperOptions);
9914
- // Check if the initial image is already loaded
9915
- setTimeout(() => {
9916
- const currentSlide = this.swiper?.slides[this.currentIndex()];
9917
- const img = currentSlide?.querySelector('img');
9918
- if (img && img.complete && img.naturalHeight !== 0) {
9919
- this.isLoading.set(false);
9920
- }
9921
- }, 0);
9922
- }
9923
- /**
9924
- * Initialize pinch-zoom gestures for all slides
9925
- */
9926
- initializeZoomGestures() {
9927
- if (!this.enableZoom)
9928
- return;
9929
- const slides = this.swiperContainer.nativeElement.querySelectorAll('.image-zoom-container');
9930
- slides.forEach((slide, index) => {
9931
- this.initializeZoomForSlide(slide, index);
9932
- });
9933
- }
9934
- /**
9935
- * Initialize zoom gestures for a specific slide
9936
- */
9937
- initializeZoomForSlide(container, index) {
9938
- let initialDistance = 0;
9939
- let initialScale = 1;
9940
- let currentScale = 1;
9941
- let lastTap = 0;
9942
- // Double-tap to zoom
9943
- container.addEventListener('click', (event) => {
9944
- const now = Date.now();
9945
- const timeSinceLastTap = now - lastTap;
9946
- if (timeSinceLastTap < 300 && timeSinceLastTap > 0) {
9947
- event.preventDefault();
9948
- this.toggleZoom(container, index);
9949
- }
9950
- lastTap = now;
9951
- });
9952
- // Pinch to zoom
9953
- const getTouchDistance = (touches) => {
9954
- const dx = touches[0].clientX - touches[1].clientX;
9955
- const dy = touches[0].clientY - touches[1].clientY;
9956
- return Math.sqrt(dx * dx + dy * dy);
9957
- };
9958
- container.addEventListener('touchstart', (event) => {
9959
- if (event.touches.length === 2) {
9960
- event.preventDefault();
9961
- initialDistance = getTouchDistance(event.touches);
9962
- const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
9963
- initialScale = zoomData.scale;
9964
- // Disable Swiper when zooming
9965
- if (this.swiper) {
9966
- this.swiper.allowTouchMove = false;
9967
- }
9968
- }
9969
- }, { passive: false });
9970
- container.addEventListener('touchmove', (event) => {
9971
- if (event.touches.length === 2) {
9972
- event.preventDefault();
9973
- const currentDistance = getTouchDistance(event.touches);
9974
- const pinchScale = currentDistance / initialDistance;
9975
- currentScale = Math.max(1, Math.min(initialScale * pinchScale, 4));
9976
- const img = container.querySelector('img');
9977
- if (img) {
9978
- img.style.transform = `scale(${currentScale})`;
9979
- }
9980
- if (currentScale > 1) {
9981
- this.isZoomed.set(true);
9982
- }
9983
- else {
9984
- this.isZoomed.set(false);
9985
- }
9986
- }
9987
- }, { passive: false });
9988
- container.addEventListener('touchend', (event) => {
9989
- if (event.touches.length < 2) {
9990
- // Save zoom state
9991
- this.zoomData.set(index, { scale: currentScale, x: 0, y: 0 });
9992
- // Re-enable Swiper if not zoomed
9993
- if (this.swiper && currentScale <= 1) {
9994
- this.swiper.allowTouchMove = true;
9995
- }
9996
- }
9997
- });
9998
- }
9999
- /**
10000
- * Toggle zoom on double-tap
10001
- */
10002
- toggleZoom(container, index) {
10003
- const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
10004
- const img = container.querySelector('img');
10005
- if (!img)
10006
- return;
10007
- if (zoomData.scale > 1) {
10008
- // Zoom out
10009
- img.style.transform = 'scale(1)';
10010
- this.zoomData.set(index, { scale: 1, x: 0, y: 0 });
10011
- this.isZoomed.set(false);
10012
- if (this.swiper) {
10013
- this.swiper.allowTouchMove = true;
10014
- }
10015
- }
10016
- else {
10017
- // Zoom in
10018
- img.style.transform = 'scale(2)';
10019
- this.zoomData.set(index, { scale: 2, x: 0, y: 0 });
10020
- this.isZoomed.set(true);
10021
- if (this.swiper) {
10022
- this.swiper.allowTouchMove = false;
10023
- }
10024
- }
10025
- }
10026
- /**
10027
- * Update action states (like, comments) when slide changes
10028
- */
10029
- updateActionStates() {
10030
- const currentImg = this.images[this.currentIndex()];
10031
- if (currentImg) {
10032
- this.isLiked.set(currentImg.isLiked ?? false);
10033
- this.likeCount.set(currentImg.likeCount ?? 0);
10034
- this.commentCount.set(currentImg.commentCount ?? 0);
10035
- }
10036
- }
10037
- /**
10038
- * Close the lightbox
10039
- */
10040
- close() {
10041
- if (this.onCloseRequested) {
10042
- this.onCloseRequested();
10043
- }
10044
- }
10045
- /**
10046
- * Handle share button click
10047
- */
10048
- async onShare() {
10049
- console.log('[Lightbox] Share button clicked');
10050
- const currentImg = this.currentImage();
10051
- if (!currentImg?.src) {
10052
- console.warn('[Lightbox] No image to share');
10053
- return;
10054
- }
10055
- try {
10056
- // Check if Web Share API is available (for browser)
10057
- if (navigator.share) {
10058
- await navigator.share({
10059
- title: currentImg.title || 'Shared Image',
10060
- text: currentImg.description || '',
10061
- url: currentImg.src,
10062
9606
  });
10063
- console.log('[Lightbox] Shared via Web Share API');
10064
- }
10065
- else {
10066
- // Fallback to Capacitor Share API (for native apps)
10067
- await Share.share({
10068
- title: currentImg.title || 'Shared Image',
10069
- url: currentImg.src,
10070
- dialogTitle: 'Share Image',
9607
+ };
9608
+ // Also update on init
9609
+ config.on.init = () => {
9610
+ if (!this.swiperInstance)
9611
+ return;
9612
+ this.swiperInstance.slides.forEach((slideEl) => {
9613
+ const progress = slideEl.progress || 0;
9614
+ const absProgress = Math.abs(progress);
9615
+ // Set initial opacity with sharper cutoff
9616
+ if (this.progressiveOpacity()) {
9617
+ let opacity;
9618
+ if (absProgress > 0.5) {
9619
+ opacity = 0;
9620
+ }
9621
+ else {
9622
+ opacity = 1 - (absProgress * 2);
9623
+ }
9624
+ slideEl.style.opacity = Math.max(opacity, 0).toString();
9625
+ }
9626
+ // Set initial scale
9627
+ if (this.progressiveScale()) {
9628
+ const minScale = 0.9;
9629
+ const scale = 1 - (absProgress * (1 - minScale));
9630
+ slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
9631
+ }
10071
9632
  });
10072
- console.log('[Lightbox] Shared via Capacitor Share API');
10073
- }
10074
- }
10075
- catch (error) {
10076
- // User cancellation is expected and not an error
10077
- if (error?.message?.includes('cancel') || error?.code === 'USER_CANCELLED') {
10078
- console.log('[Lightbox] Share cancelled by user');
10079
- return;
10080
- }
10081
- console.error('[Lightbox] Share failed:', error);
10082
- }
10083
- }
10084
- /**
10085
- * Handle like button toggle
10086
- */
10087
- onLikeToggle() {
10088
- console.log('[Lightbox] Like button toggled');
10089
- this.isLiked.update(liked => !liked);
10090
- if (this.isLiked()) {
10091
- this.likeCount.update(count => count + 1);
9633
+ };
10092
9634
  }
10093
- else {
10094
- this.likeCount.update(count => Math.max(0, count - 1));
9635
+ // Add pagination if enabled
9636
+ if (this.pagination()) {
9637
+ config.modules = [Pagination];
9638
+ config.pagination = {
9639
+ el: '.swiper-pagination',
9640
+ clickable: true,
9641
+ dynamicBullets: false,
9642
+ };
10095
9643
  }
9644
+ this.swiperInstance = new Swiper(this.swiperContainer.nativeElement, config);
10096
9645
  }
10097
9646
  /**
10098
- * Handle reply/comment button click
9647
+ * Navigate to previous slide
10099
9648
  */
10100
- onReply() {
10101
- console.log('[Lightbox] Reply button clicked');
10102
- if (this.onCloseRequested) {
10103
- this.onCloseRequested();
10104
- }
9649
+ slidePrev() {
9650
+ this.swiperInstance?.slidePrev();
10105
9651
  }
10106
9652
  /**
10107
- * Navigate to the next image
9653
+ * Navigate to next slide
10108
9654
  */
10109
- nextImage() {
10110
- if (this.swiper && this.currentIndex() < this.images.length - 1) {
10111
- this.swiper.slideNext();
10112
- }
9655
+ slideNext() {
9656
+ this.swiperInstance?.slideNext();
10113
9657
  }
10114
9658
  /**
10115
- * Navigate to the previous image
9659
+ * Check if at the beginning
10116
9660
  */
10117
- previousImage() {
10118
- if (this.swiper && this.currentIndex() > 0) {
10119
- this.swiper.slidePrev();
10120
- }
9661
+ isBeginning() {
9662
+ return this.swiperInstance?.isBeginning ?? true;
10121
9663
  }
10122
9664
  /**
10123
- * Handle image load success
9665
+ * Check if at the end
10124
9666
  */
10125
- onImageLoad(index) {
10126
- if (index === this.currentIndex()) {
10127
- this.isLoading.set(false);
10128
- }
9667
+ isEnd() {
9668
+ return this.swiperInstance?.isEnd ?? true;
10129
9669
  }
10130
- /**
10131
- * Handle image load error
10132
- */
10133
- onImageError(index) {
10134
- if (index === this.currentIndex()) {
10135
- console.error(`[Lightbox] Image ${index} failed to load`);
10136
- this.isLoading.set(false);
10137
- this.hasError.set(true);
9670
+ ngOnDestroy() {
9671
+ if (this.swiperInstance) {
9672
+ this.swiperInstance.destroy();
9673
+ this.swiperInstance = null;
10138
9674
  }
10139
9675
  }
10140
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxImageComponent, deps: [{ token: i1.GestureController }], target: i0.ɵɵFactoryTarget.Component });
10141
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxImageComponent, isStandalone: true, selector: "ds-mobile-lightbox-image", viewQueries: [{ propertyName: "swiperContainer", first: true, predicate: ["swiperContainer"], descendants: true, read: ElementRef }], ngImport: i0, template: `
10142
- <div class="lightbox-overlay"
10143
- [class.zoomed]="isZoomed()">
10144
-
10145
- <div class="lightbox-wrapper">
10146
- <!-- Header with author info and action buttons -->
10147
- <ds-mobile-lightbox-header
10148
- [author]="author"
10149
- (closeClick)="close()"
10150
- (shareClick)="onShare()"
10151
- />
10152
-
10153
- <!-- Swiper container -->
10154
- <div class="swiper-container" #swiperContainer>
10155
- <div class="swiper-wrapper">
10156
- @for (image of images; track image.src; let i = $index) {
10157
- <div class="swiper-slide">
10158
- <div class="image-zoom-container" [attr.data-index]="i">
10159
- <img
10160
- [src]="image.src"
10161
- [alt]="image.alt || 'Lightbox image'"
10162
- class="lightbox-image"
10163
- (load)="onImageLoad(i)"
10164
- (error)="onImageError(i)">
10165
- </div>
10166
- </div>
10167
- }
10168
- </div>
10169
- </div>
10170
-
10171
- <!-- Loading indicator -->
10172
- @if (isLoading()) {
10173
- <div class="loading-spinner">
10174
- <ion-spinner name="crescent"></ion-spinner>
10175
- </div>
10176
- }
10177
-
10178
- <!-- Footer with navigation and actions -->
10179
- <ds-mobile-lightbox-footer
10180
- [showNavigation]="showControls && images.length > 1"
10181
- [showActions]="showActions"
10182
- [currentIndex]="currentIndex()"
10183
- [totalImages]="images.length"
10184
- [isLiked]="isLiked()"
10185
- [likeCount]="likeCount()"
10186
- [commentCount]="commentCount()"
10187
- (prevClick)="previousImage()"
10188
- (nextClick)="nextImage()"
10189
- (likeClick)="onLikeToggle()"
10190
- (commentClick)="onReply()"
10191
- />
9676
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSwiperComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
9677
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileSwiperComponent, isStandalone: true, selector: "ds-mobile-swiper", inputs: { slideWidth: { classPropertyName: "slideWidth", publicName: "slideWidth", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, autoHeight: { classPropertyName: "autoHeight", publicName: "autoHeight", isSignal: true, isRequired: false, transformFunction: null }, progressiveOpacity: { classPropertyName: "progressiveOpacity", publicName: "progressiveOpacity", isSignal: true, isRequired: false, transformFunction: null }, progressiveScale: { classPropertyName: "progressiveScale", publicName: "progressiveScale", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "swiperContainer", first: true, predicate: ["swiperContainer"], descendants: true }], ngImport: i0, template: `
9678
+ <div class="swiper-container" #swiperContainer>
9679
+ <div class="swiper-wrapper">
9680
+ <ng-content></ng-content>
10192
9681
  </div>
9682
+ @if (pagination()) {
9683
+ <div class="swiper-pagination"></div>
9684
+ }
10193
9685
  </div>
10194
- `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}:host{display:block;position:fixed;inset:0;width:100vw;height:100vh;z-index:10000}.lightbox-overlay{position:fixed;inset:0;width:100vw;height:100vh;background:#000000fa;z-index:10000;display:flex;flex-direction:column;touch-action:none;animation:fadeIn .2s ease-out}.lightbox-overlay.zoomed{overflow:hidden}.lightbox-content{display:block;height:100vh;width:100vw;position:absolute;inset:0}.lightbox-content::part(scroll){display:flex;flex-direction:column;height:100%;overflow:hidden}.lightbox-wrapper{position:absolute;inset:0;display:flex;flex-direction:column;width:100%;height:100%}.lightbox-content.zoomed{overflow:hidden}.lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.close-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg{color:#fff!important;fill:#fff!important}.swiper-container{position:absolute;inset:0;width:100%;height:100%;z-index:1}.swiper-wrapper{width:100%;height:100%}.swiper-slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.image-zoom-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}.lightbox-image{max-width:min(640px,100%);max-height:100%;width:auto;height:auto;-o-object-fit:contain;object-fit:contain;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none;transition:transform .3s ease-out;transform-origin:center center}.lightbox-overlay.zoomed .swiper-container{touch-action:none}.loading-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10}.loading-spinner ion-spinner{--color: rgba(255, 255, 255, .8);width:48px;height:48px}.error-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;text-align:center;color:#fffc;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:500;padding:20px;background:#00000080;border-radius:12px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.lightbox-bottom-section{position:fixed;bottom:0;left:0;right:0;z-index:100;display:flex;flex-direction:column;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none}.lightbox-controls{display:flex;align-items:center;justify-content:center;gap:24px;padding:16px 20px 12px;pointer-events:none}.nav-button,.counter{pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);color:#fff;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease;padding:0;margin:0;outline:none}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px}.counter{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;padding:10px 16px;background:#00000080;border-radius:100px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);white-space:nowrap}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-bottom-section{padding-bottom:env(safe-area-inset-bottom)}}.lightbox-footer{display:flex;padding:12px 20px 20px;pointer-events:none}.footer-actions{display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-with-count::ng-deep button,.action-button-share::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover,.action-button-with-count::ng-deep button:hover,.action-button-share::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active,.action-button-with-count::ng-deep button:active,.action-button-share::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-with-count::ng-deep button svg,.action-button-share::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content,.action-button-with-count::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg,.action-button-with-count::ng-deep button .btn__icon svg,.action-button-share::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment,.action-button-share{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-share::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@media (min-width: 768px){.lightbox-header{padding:24px}.close-button{width:48px;height:48px}.lightbox-controls{padding:20px 24px 16px}.nav-button{width:48px;height:48px}.counter{font-size:var(--font-size-base);padding:12px 20px}.lightbox-footer{padding:16px 24px 24px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{height:48px;padding:0 20px}.action-button-share::ng-deep button{width:48px!important;height:48px!important;min-width:48px!important;min-height:48px!important}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes zoomIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.image-wrapper,.nav-button,.lightbox-caption{transition:none}}.nav-button:focus-visible{outline:2px solid white;outline-offset:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DsMobileLightboxHeaderComponent, selector: "ds-mobile-lightbox-header", inputs: ["author"], outputs: ["closeClick", "shareClick"] }, { kind: "component", type: DsMobileLightboxFooterComponent, selector: "ds-mobile-lightbox-footer", inputs: ["showNavigation", "currentIndex", "totalImages", "showActions", "isLiked", "likeCount", "commentCount"], outputs: ["prevClick", "nextClick", "likeClick", "commentClick"] }] });
9686
+ `, isInline: true, styles: [":host{display:block;width:100%}.swiper-container{width:100%;position:relative;overflow:visible}.swiper-wrapper{display:flex;box-sizing:content-box}:host ::ng-deep .swiper-slide{flex-shrink:0;height:auto;position:relative;display:flex;align-items:flex-start;justify-content:center;transition:opacity .3s ease,transform .3s ease}:host(.progressive-opacity) ::ng-deep .swiper-slide{opacity:0}:host(.progressive-opacity) ::ng-deep .swiper-slide:first-child{opacity:1}.swiper-pagination{position:relative;text-align:center;display:flex;justify-content:center;margin-top:20px;margin-bottom:-16px}:host ::ng-deep .swiper-pagination-bullet{width:6px;height:6px;border-radius:50%;background:var(--color-accent);opacity:.25;transition:all .3s ease;cursor:pointer}:host ::ng-deep .swiper-pagination-bullet-active{opacity:1;background:var(--color-accent);width:20px;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
10195
9687
  }
10196
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxImageComponent, decorators: [{
9688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSwiperComponent, decorators: [{
10197
9689
  type: Component,
10198
- args: [{ selector: 'ds-mobile-lightbox-image', standalone: true, imports: [
10199
- CommonModule,
10200
- IonSpinner,
10201
- DsMobileLightboxHeaderComponent,
10202
- DsMobileLightboxFooterComponent
10203
- ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
10204
- <div class="lightbox-overlay"
10205
- [class.zoomed]="isZoomed()">
10206
-
10207
- <div class="lightbox-wrapper">
10208
- <!-- Header with author info and action buttons -->
10209
- <ds-mobile-lightbox-header
10210
- [author]="author"
10211
- (closeClick)="close()"
10212
- (shareClick)="onShare()"
10213
- />
10214
-
10215
- <!-- Swiper container -->
10216
- <div class="swiper-container" #swiperContainer>
10217
- <div class="swiper-wrapper">
10218
- @for (image of images; track image.src; let i = $index) {
10219
- <div class="swiper-slide">
10220
- <div class="image-zoom-container" [attr.data-index]="i">
10221
- <img
10222
- [src]="image.src"
10223
- [alt]="image.alt || 'Lightbox image'"
10224
- class="lightbox-image"
10225
- (load)="onImageLoad(i)"
10226
- (error)="onImageError(i)">
10227
- </div>
10228
- </div>
10229
- }
10230
- </div>
10231
- </div>
10232
-
10233
- <!-- Loading indicator -->
10234
- @if (isLoading()) {
10235
- <div class="loading-spinner">
10236
- <ion-spinner name="crescent"></ion-spinner>
10237
- </div>
10238
- }
10239
-
10240
- <!-- Footer with navigation and actions -->
10241
- <ds-mobile-lightbox-footer
10242
- [showNavigation]="showControls && images.length > 1"
10243
- [showActions]="showActions"
10244
- [currentIndex]="currentIndex()"
10245
- [totalImages]="images.length"
10246
- [isLiked]="isLiked()"
10247
- [likeCount]="likeCount()"
10248
- [commentCount]="commentCount()"
10249
- (prevClick)="previousImage()"
10250
- (nextClick)="nextImage()"
10251
- (likeClick)="onLikeToggle()"
10252
- (commentClick)="onReply()"
10253
- />
9690
+ args: [{ selector: 'ds-mobile-swiper', standalone: true, imports: [CommonModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
9691
+ <div class="swiper-container" #swiperContainer>
9692
+ <div class="swiper-wrapper">
9693
+ <ng-content></ng-content>
10254
9694
  </div>
9695
+ @if (pagination()) {
9696
+ <div class="swiper-pagination"></div>
9697
+ }
10255
9698
  </div>
10256
- `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}:host{display:block;position:fixed;inset:0;width:100vw;height:100vh;z-index:10000}.lightbox-overlay{position:fixed;inset:0;width:100vw;height:100vh;background:#000000fa;z-index:10000;display:flex;flex-direction:column;touch-action:none;animation:fadeIn .2s ease-out}.lightbox-overlay.zoomed{overflow:hidden}.lightbox-content{display:block;height:100vh;width:100vw;position:absolute;inset:0}.lightbox-content::part(scroll){display:flex;flex-direction:column;height:100%;overflow:hidden}.lightbox-wrapper{position:absolute;inset:0;display:flex;flex-direction:column;width:100%;height:100%}.lightbox-content.zoomed{overflow:hidden}.lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.close-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg{color:#fff!important;fill:#fff!important}.swiper-container{position:absolute;inset:0;width:100%;height:100%;z-index:1}.swiper-wrapper{width:100%;height:100%}.swiper-slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.image-zoom-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}.lightbox-image{max-width:min(640px,100%);max-height:100%;width:auto;height:auto;-o-object-fit:contain;object-fit:contain;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none;transition:transform .3s ease-out;transform-origin:center center}.lightbox-overlay.zoomed .swiper-container{touch-action:none}.loading-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10}.loading-spinner ion-spinner{--color: rgba(255, 255, 255, .8);width:48px;height:48px}.error-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;text-align:center;color:#fffc;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:500;padding:20px;background:#00000080;border-radius:12px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.lightbox-bottom-section{position:fixed;bottom:0;left:0;right:0;z-index:100;display:flex;flex-direction:column;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none}.lightbox-controls{display:flex;align-items:center;justify-content:center;gap:24px;padding:16px 20px 12px;pointer-events:none}.nav-button,.counter{pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);color:#fff;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease;padding:0;margin:0;outline:none}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px}.counter{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;padding:10px 16px;background:#00000080;border-radius:100px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);white-space:nowrap}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-bottom-section{padding-bottom:env(safe-area-inset-bottom)}}.lightbox-footer{display:flex;padding:12px 20px 20px;pointer-events:none}.footer-actions{display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-with-count::ng-deep button,.action-button-share::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover,.action-button-with-count::ng-deep button:hover,.action-button-share::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active,.action-button-with-count::ng-deep button:active,.action-button-share::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-with-count::ng-deep button svg,.action-button-share::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content,.action-button-with-count::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg,.action-button-with-count::ng-deep button .btn__icon svg,.action-button-share::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment,.action-button-share{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-share::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@media (min-width: 768px){.lightbox-header{padding:24px}.close-button{width:48px;height:48px}.lightbox-controls{padding:20px 24px 16px}.nav-button{width:48px;height:48px}.counter{font-size:var(--font-size-base);padding:12px 20px}.lightbox-footer{padding:16px 24px 24px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{height:48px;padding:0 20px}.action-button-share::ng-deep button{width:48px!important;height:48px!important;min-width:48px!important;min-height:48px!important}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes zoomIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.image-wrapper,.nav-button,.lightbox-caption{transition:none}}.nav-button:focus-visible{outline:2px solid white;outline-offset:2px}\n"] }]
10257
- }], ctorParameters: () => [{ type: i1.GestureController }], propDecorators: { swiperContainer: [{
9699
+ `, styles: [":host{display:block;width:100%}.swiper-container{width:100%;position:relative;overflow:visible}.swiper-wrapper{display:flex;box-sizing:content-box}:host ::ng-deep .swiper-slide{flex-shrink:0;height:auto;position:relative;display:flex;align-items:flex-start;justify-content:center;transition:opacity .3s ease,transform .3s ease}:host(.progressive-opacity) ::ng-deep .swiper-slide{opacity:0}:host(.progressive-opacity) ::ng-deep .swiper-slide:first-child{opacity:1}.swiper-pagination{position:relative;text-align:center;display:flex;justify-content:center;margin-top:20px;margin-bottom:-16px}:host ::ng-deep .swiper-pagination-bullet{width:6px;height:6px;border-radius:50%;background:var(--color-accent);opacity:.25;transition:all .3s ease;cursor:pointer}:host ::ng-deep .swiper-pagination-bullet-active{opacity:1;background:var(--color-accent);width:20px;border-radius:3px}\n"] }]
9700
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { slideWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideWidth", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }], autoHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHeight", required: false }] }], progressiveOpacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressiveOpacity", required: false }] }], progressiveScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressiveScale", required: false }] }], swiperContainer: [{
10258
9701
  type: ViewChild,
10259
- args: ['swiperContainer', { read: ElementRef }]
9702
+ args: ['swiperContainer', { static: false }]
10260
9703
  }] } });
10261
9704
 
10262
9705
  /**
10263
- * DsMobileLightboxPdfComponent
10264
- *
10265
- * PDF viewer component that displays PDF info and allows users to open PDFs in the native device viewer.
10266
- * Shows a lightbox with PDF details first, then user can choose to open in native viewer.
9706
+ * DsMobileSystemMessageBannerComponent
10267
9707
  *
10268
- * This component is typically not used directly - use DsMobileLightboxService instead.
9708
+ * Full-width centered banner component for displaying system messages in chat conversations.
9709
+ * Uses the same text styling as message bubbles for consistency.
10269
9710
  *
10270
9711
  * Features:
10271
- * - PDF info preview (title, size, icon)
10272
- * - User-initiated native PDF viewing (iOS/Android)
10273
- * - Download and cache support
10274
- * - Share functionality
10275
- * - Loading states
9712
+ * - Full-width centered layout
9713
+ * - Subtle background with theming support
9714
+ * - Same typography as message bubbles
9715
+ * - Optional icon support
9716
+ *
9717
+ * Common use cases:
9718
+ * - Inquiry status updates ("Your inquiry has been assigned to...")
9719
+ * - System notifications ("This inquiry is marked as resolved")
9720
+ * - Auto-replies ("We aim to respond within 24 hours...")
9721
+ * - Time/date separators
10276
9722
  *
10277
9723
  * @example
10278
- * ```typescript
10279
- * // Don't instantiate directly - use the service:
10280
- * constructor(private lightbox: DsMobileLightboxService) {}
9724
+ * ```html
9725
+ * <!-- Simple system message -->
9726
+ * <ds-mobile-system-message-banner
9727
+ * [message]="'Ricki Meihlen har overtaget din henvendelse og vil kontakte dig snart.'">
9728
+ * </ds-mobile-system-message-banner>
10281
9729
  *
10282
- * openPdf() {
10283
- * this.lightbox.openPdf({
10284
- * pdf: { type: 'pdf', src: 'document.pdf', title: 'My Document' }
10285
- * });
10286
- * }
9730
+ * <!-- With icon -->
9731
+ * <ds-mobile-system-message-banner
9732
+ * [message]="'Vi bestræber os at svare inden for 24 timer på hverdage.'"
9733
+ * [iconName]="'remixInformationLine'">
9734
+ * </ds-mobile-system-message-banner>
10287
9735
  * ```
10288
9736
  */
10289
- class DsMobileLightboxPdfComponent {
10290
- // Inputs (passed from service as regular properties)
10291
- pdf;
10292
- author;
10293
- onCloseRequested;
10294
- // State
10295
- isLoading = false;
10296
- hasError = false;
10297
- errorMessage = '';
10298
- cachedFilePath;
10299
- constructor() { }
10300
- ngOnInit() {
10301
- console.log('[PDF Lightbox] Initializing with PDF:', this.pdf);
10302
- // Don't automatically open - let user click the "Open" button
10303
- // this.openPdfInNativeViewer();
10304
- }
9737
+ class DsMobileSystemMessageBannerComponent {
10305
9738
  /**
10306
- * Open the PDF in the native device viewer
9739
+ * System message text to display
10307
9740
  */
10308
- async openPdfInNativeViewer() {
10309
- if (!this.pdf?.src) {
10310
- console.error('[PDF Lightbox] No PDF source provided');
10311
- this.hasError = true;
10312
- this.errorMessage = 'No PDF file provided';
10313
- return;
10314
- }
10315
- this.isLoading = true;
10316
- this.hasError = false;
10317
- this.errorMessage = '';
10318
- try {
10319
- console.log('[PDF Lightbox] Opening PDF:', this.pdf.src);
10320
- // Check if it's already a full URL
10321
- let pdfUrl;
10322
- if (this.pdf.src.startsWith('http://') || this.pdf.src.startsWith('https://')) {
10323
- // Already a full URL
10324
- pdfUrl = this.pdf.src;
10325
- }
10326
- else {
10327
- // Relative path - construct full URL
10328
- // Use current origin (which includes the dev server URL in Capacitor)
10329
- // Remove leading slash if present to avoid double slashes
10330
- const cleanPath = this.pdf.src.startsWith('/') ? this.pdf.src.slice(1) : this.pdf.src;
10331
- pdfUrl = `${window.location.origin}/${cleanPath}`;
10332
- }
10333
- console.log('[PDF Lightbox] Opening PDF at URL:', pdfUrl);
10334
- // Use Browser to open the PDF
10335
- await Browser.open({
10336
- url: pdfUrl,
10337
- presentationStyle: 'fullscreen'
10338
- });
10339
- this.isLoading = false;
10340
- // Close the modal after opening browser
10341
- setTimeout(() => {
10342
- this.close();
10343
- }, 500);
10344
- }
10345
- catch (error) {
10346
- console.error('[PDF Lightbox] Error opening PDF:', error);
10347
- this.isLoading = false;
10348
- this.hasError = true;
10349
- this.errorMessage = error?.message || 'Failed to open PDF';
10350
- }
10351
- }
9741
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
10352
9742
  /**
10353
- * Download a remote PDF and open it
9743
+ * Optional icon name (currently using inline SVG for info icon)
9744
+ * Can be extended to support full icon library integration
10354
9745
  */
10355
- async downloadAndOpenPdf() {
10356
- try {
10357
- console.log('[PDF Lightbox] Downloading PDF from:', this.pdf.src);
10358
- // Fetch the PDF
10359
- const response = await fetch(this.pdf.src);
10360
- if (!response.ok) {
10361
- throw new Error(`Failed to download PDF: ${response.statusText}`);
10362
- }
10363
- const blob = await response.blob();
10364
- const base64Data = await this.blobToBase64(blob);
10365
- // Generate a filename
10366
- const fileName = this.pdf.title
10367
- ? `${this.pdf.title.replace(/[^a-z0-9]/gi, '_')}.pdf`
10368
- : 'document.pdf';
10369
- // Save to cache directory
10370
- const result = await Filesystem.writeFile({
10371
- path: fileName,
10372
- data: base64Data,
10373
- directory: Directory.Cache
10374
- });
10375
- console.log('[PDF Lightbox] PDF saved to cache:', result.uri);
10376
- this.cachedFilePath = result.uri;
10377
- // Open using Browser
9746
+ iconName = input('', ...(ngDevMode ? [{ debugName: "iconName" }] : []));
9747
+ /**
9748
+ * Whether this system message appears directly after a timestamp
9749
+ * When true, removes top padding to reduce spacing
9750
+ */
9751
+ afterTimestamp = input(false, ...(ngDevMode ? [{ debugName: "afterTimestamp" }] : []));
9752
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSystemMessageBannerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9753
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileSystemMessageBannerComponent, isStandalone: true, selector: "ds-mobile-system-message-banner", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, iconName: { classPropertyName: "iconName", publicName: "iconName", isSignal: true, isRequired: false, transformFunction: null }, afterTimestamp: { classPropertyName: "afterTimestamp", publicName: "afterTimestamp", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.after-timestamp": "afterTimestamp()" } }, ngImport: i0, template: `
9754
+ <div class="system-message-container">
9755
+ <div class="system-message-content">
9756
+ @if (iconName()) {
9757
+ <span class="system-message-icon">
9758
+ <!-- Icon slot - you can add ds-icon component here if needed -->
9759
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
9760
+ <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"/>
9761
+ </svg>
9762
+ </span>
9763
+ }
9764
+ <p class="system-message-text">{{ message() }}</p>
9765
+ </div>
9766
+ </div>
9767
+ `, isInline: true, styles: [":host{display:block;width:100%;padding:12px 0}:host(.after-timestamp){padding-top:0}.system-message-container{display:flex;justify-content:center;align-items:center;width:100%;padding:0 16px}.system-message-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;max-width:85%;text-align:center}.system-message-icon{flex-shrink:0;width:16px;height:16px;color:var(--color-text-tertiary, #a0a0a0)}.system-message-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--color-text-secondary, #666666);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
9768
+ }
9769
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSystemMessageBannerComponent, decorators: [{
9770
+ type: Component,
9771
+ args: [{ selector: 'ds-mobile-system-message-banner', standalone: true, imports: [CommonModule], host: {
9772
+ '[class.after-timestamp]': 'afterTimestamp()'
9773
+ }, template: `
9774
+ <div class="system-message-container">
9775
+ <div class="system-message-content">
9776
+ @if (iconName()) {
9777
+ <span class="system-message-icon">
9778
+ <!-- Icon slot - you can add ds-icon component here if needed -->
9779
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
9780
+ <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"/>
9781
+ </svg>
9782
+ </span>
9783
+ }
9784
+ <p class="system-message-text">{{ message() }}</p>
9785
+ </div>
9786
+ </div>
9787
+ `, styles: [":host{display:block;width:100%;padding:12px 0}:host(.after-timestamp){padding-top:0}.system-message-container{display:flex;justify-content:center;align-items:center;width:100%;padding:0 16px}.system-message-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;max-width:85%;text-align:center}.system-message-icon{flex-shrink:0;width:16px;height:16px;color:var(--color-text-tertiary, #a0a0a0)}.system-message-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--color-text-secondary, #666666);margin:0}\n"] }]
9788
+ }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], iconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconName", required: false }] }], afterTimestamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "afterTimestamp", required: false }] }] } });
9789
+
9790
+ /**
9791
+ * DsMobileLightboxHeaderComponent
9792
+ *
9793
+ * Shared header component for all lightbox types (image, PDF, etc.)
9794
+ * Displays author information and close button with consistent styling.
9795
+ */
9796
+ class DsMobileLightboxHeaderComponent {
9797
+ /**
9798
+ * Author information to display
9799
+ */
9800
+ author = input(...(ngDevMode ? [undefined, { debugName: "author" }] : []));
9801
+ /**
9802
+ * Emitted when close button is clicked
9803
+ */
9804
+ closeClick = output();
9805
+ /**
9806
+ * Emitted when share button is clicked
9807
+ */
9808
+ shareClick = output();
9809
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9810
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxHeaderComponent, isStandalone: true, selector: "ds-mobile-lightbox-header", inputs: { author: { classPropertyName: "author", publicName: "author", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeClick: "closeClick", shareClick: "shareClick" }, ngImport: i0, template: `
9811
+ <div class="lightbox-header lightbox-context">
9812
+ <div class="header-content">
9813
+ <!-- Post author info -->
9814
+ @if (author()) {
9815
+ <div class="post-author-info">
9816
+ <ds-avatar
9817
+ [initials]="author()!.avatarInitials ?? ''"
9818
+ [type]="author()!.avatarType ?? 'initials'"
9819
+ [src]="author()!.avatarSrc ?? ''"
9820
+ size="md"
9821
+ />
9822
+ <div class="author-details">
9823
+ <div class="author-name">{{ author()!.name }}</div>
9824
+ <div class="author-meta">
9825
+ @if (author()!.role) {
9826
+ <span>{{ author()!.role }}</span>
9827
+ }
9828
+ @if (author()!.role && author()!.timestamp) {
9829
+ <span class="separator">·</span>
9830
+ }
9831
+ @if (author()!.timestamp) {
9832
+ <span>{{ author()!.timestamp }}</span>
9833
+ }
9834
+ </div>
9835
+ </div>
9836
+ </div>
9837
+ }
9838
+
9839
+ <!-- Action buttons - always visible -->
9840
+ <div class="header-actions">
9841
+ <ds-icon-button
9842
+ icon="remixShare2Line"
9843
+ variant="ghost"
9844
+ size="md"
9845
+ (clicked)="shareClick.emit()"
9846
+ class="share-button"
9847
+ [ariaLabel]="'Share'">
9848
+ </ds-icon-button>
9849
+
9850
+ <ds-icon-button
9851
+ icon="remixCloseLine"
9852
+ variant="ghost"
9853
+ size="md"
9854
+ (clicked)="closeClick.emit()"
9855
+ class="close-button"
9856
+ [ariaLabel]="'Close'">
9857
+ </ds-icon-button>
9858
+ </div>
9859
+ </div>
9860
+ </div>
9861
+ `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:flex-end;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0;margin-right:auto}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.header-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.close-button,.share-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button,.share-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover,.share-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active,.share-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg,.share-button::ng-deep svg{color:#fff!important;fill:#fff!important}@supports (padding-top: env(safe-area-inset-top)){.lightbox-header{padding-top:calc(16px + env(safe-area-inset-top))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }] });
9862
+ }
9863
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxHeaderComponent, decorators: [{
9864
+ type: Component,
9865
+ args: [{ selector: 'ds-mobile-lightbox-header', standalone: true, imports: [CommonModule, DsIconButtonComponent, DsAvatarComponent], template: `
9866
+ <div class="lightbox-header lightbox-context">
9867
+ <div class="header-content">
9868
+ <!-- Post author info -->
9869
+ @if (author()) {
9870
+ <div class="post-author-info">
9871
+ <ds-avatar
9872
+ [initials]="author()!.avatarInitials ?? ''"
9873
+ [type]="author()!.avatarType ?? 'initials'"
9874
+ [src]="author()!.avatarSrc ?? ''"
9875
+ size="md"
9876
+ />
9877
+ <div class="author-details">
9878
+ <div class="author-name">{{ author()!.name }}</div>
9879
+ <div class="author-meta">
9880
+ @if (author()!.role) {
9881
+ <span>{{ author()!.role }}</span>
9882
+ }
9883
+ @if (author()!.role && author()!.timestamp) {
9884
+ <span class="separator">·</span>
9885
+ }
9886
+ @if (author()!.timestamp) {
9887
+ <span>{{ author()!.timestamp }}</span>
9888
+ }
9889
+ </div>
9890
+ </div>
9891
+ </div>
9892
+ }
9893
+
9894
+ <!-- Action buttons - always visible -->
9895
+ <div class="header-actions">
9896
+ <ds-icon-button
9897
+ icon="remixShare2Line"
9898
+ variant="ghost"
9899
+ size="md"
9900
+ (clicked)="shareClick.emit()"
9901
+ class="share-button"
9902
+ [ariaLabel]="'Share'">
9903
+ </ds-icon-button>
9904
+
9905
+ <ds-icon-button
9906
+ icon="remixCloseLine"
9907
+ variant="ghost"
9908
+ size="md"
9909
+ (clicked)="closeClick.emit()"
9910
+ class="close-button"
9911
+ [ariaLabel]="'Close'">
9912
+ </ds-icon-button>
9913
+ </div>
9914
+ </div>
9915
+ </div>
9916
+ `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:flex-end;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0;margin-right:auto}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.header-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}.close-button,.share-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button,.share-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover,.share-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active,.share-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg,.share-button::ng-deep svg{color:#fff!important;fill:#fff!important}@supports (padding-top: env(safe-area-inset-top)){.lightbox-header{padding-top:calc(16px + env(safe-area-inset-top))}}\n"] }]
9917
+ }], propDecorators: { author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], closeClick: [{ type: i0.Output, args: ["closeClick"] }], shareClick: [{ type: i0.Output, args: ["shareClick"] }] } });
9918
+
9919
+ /**
9920
+ * DsMobileLightboxFooterComponent
9921
+ *
9922
+ * Shared footer component for all lightbox types (image, PDF, etc.)
9923
+ * Displays navigation controls (for multiple images) and action buttons.
9924
+ */
9925
+ class DsMobileLightboxFooterComponent {
9926
+ /**
9927
+ * Whether to show navigation controls
9928
+ */
9929
+ showNavigation = input(false, ...(ngDevMode ? [{ debugName: "showNavigation" }] : []));
9930
+ /**
9931
+ * Current image index (0-based)
9932
+ */
9933
+ currentIndex = input(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
9934
+ /**
9935
+ * Total number of images
9936
+ */
9937
+ totalImages = input(1, ...(ngDevMode ? [{ debugName: "totalImages" }] : []));
9938
+ /**
9939
+ * Whether to show like & comment action buttons
9940
+ * @default false
9941
+ */
9942
+ showActions = input(false, ...(ngDevMode ? [{ debugName: "showActions" }] : []));
9943
+ /**
9944
+ * Whether the content is liked
9945
+ */
9946
+ isLiked = input(false, ...(ngDevMode ? [{ debugName: "isLiked" }] : []));
9947
+ /**
9948
+ * Number of likes
9949
+ */
9950
+ likeCount = input(0, ...(ngDevMode ? [{ debugName: "likeCount" }] : []));
9951
+ /**
9952
+ * Number of comments
9953
+ */
9954
+ commentCount = input(0, ...(ngDevMode ? [{ debugName: "commentCount" }] : []));
9955
+ /**
9956
+ * Emitted when previous button is clicked
9957
+ */
9958
+ prevClick = output();
9959
+ /**
9960
+ * Emitted when next button is clicked
9961
+ */
9962
+ nextClick = output();
9963
+ /**
9964
+ * Emitted when like button is clicked
9965
+ */
9966
+ likeClick = output();
9967
+ /**
9968
+ * Emitted when comment button is clicked
9969
+ */
9970
+ commentClick = output();
9971
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9972
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxFooterComponent, isStandalone: true, selector: "ds-mobile-lightbox-footer", inputs: { showNavigation: { classPropertyName: "showNavigation", publicName: "showNavigation", isSignal: true, isRequired: false, transformFunction: null }, currentIndex: { classPropertyName: "currentIndex", publicName: "currentIndex", isSignal: true, isRequired: false, transformFunction: null }, totalImages: { classPropertyName: "totalImages", publicName: "totalImages", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, isLiked: { classPropertyName: "isLiked", publicName: "isLiked", isSignal: true, isRequired: false, transformFunction: null }, likeCount: { classPropertyName: "likeCount", publicName: "likeCount", isSignal: true, isRequired: false, transformFunction: null }, commentCount: { classPropertyName: "commentCount", publicName: "commentCount", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { prevClick: "prevClick", nextClick: "nextClick", likeClick: "likeClick", commentClick: "commentClick" }, ngImport: i0, template: `
9973
+ <div class="lightbox-footer">
9974
+ <!-- Navigation controls (only shown for multiple images) -->
9975
+ @if (showNavigation() && totalImages() > 1) {
9976
+ <div class="footer-navigation">
9977
+ <button
9978
+ class="nav-button prev"
9979
+ (click)="prevClick.emit()"
9980
+ [disabled]="currentIndex() === 0"
9981
+ aria-label="Previous image">
9982
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9983
+ <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9984
+ </svg>
9985
+ </button>
9986
+
9987
+ <div class="counter">
9988
+ {{ currentIndex() + 1 }} / {{ totalImages() }}
9989
+ </div>
9990
+
9991
+ <button
9992
+ class="nav-button next"
9993
+ (click)="nextClick.emit()"
9994
+ [disabled]="currentIndex() === totalImages() - 1"
9995
+ aria-label="Next image">
9996
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
9997
+ <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
9998
+ </svg>
9999
+ </button>
10000
+ </div>
10001
+ }
10002
+
10003
+ <!-- Action buttons -->
10004
+ @if (showActions()) {
10005
+ <div class="footer-actions">
10006
+ <div class="action-buttons-left">
10007
+ <!-- Like button -->
10008
+ <ds-icon-button
10009
+ [icon]="isLiked() ? 'remixHeart3Fill' : 'remixHeart3Line'"
10010
+ variant="ghost"
10011
+ size="md"
10012
+ (click)="likeClick.emit()"
10013
+ [attr.data-liked]="isLiked()"
10014
+ class="action-button-like"
10015
+ [ariaLabel]="'Like'">
10016
+ </ds-icon-button>
10017
+
10018
+ <!-- Comment button -->
10019
+ <ds-icon-button
10020
+ icon="remixChat3Line"
10021
+ variant="ghost"
10022
+ size="md"
10023
+ (click)="commentClick.emit()"
10024
+ class="action-button-comment"
10025
+ [ariaLabel]="'Comment'">
10026
+ </ds-icon-button>
10027
+ </div>
10028
+ </div>
10029
+ }
10030
+ </div>
10031
+ `, isInline: true, styles: [".lightbox-footer{position:fixed;bottom:0;left:0;right:0;z-index:100;padding:16px 20px 20px;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none;display:flex;flex-direction:column;gap:12px}.footer-navigation{display:flex;align-items:center;justify-content:center;gap:24px;pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding:0}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px;flex-shrink:0}.counter{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-radius:100px;padding:8px 16px;color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;letter-spacing:-.3px;pointer-events:auto;-webkit-user-select:none;user-select:none}.footer-actions{display:flex;align-items:center;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-footer{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconButtonComponent, selector: "ds-icon-button", inputs: ["variant", "size", "icon", "disabled", "loading", "pressed", "expanded", "ariaLabel", "tooltip", "tooltipDisabled", "tooltipPlacement"], outputs: ["clicked", "focused", "blurred"] }] });
10032
+ }
10033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxFooterComponent, decorators: [{
10034
+ type: Component,
10035
+ args: [{ selector: 'ds-mobile-lightbox-footer', standalone: true, imports: [CommonModule, DsIconButtonComponent], template: `
10036
+ <div class="lightbox-footer">
10037
+ <!-- Navigation controls (only shown for multiple images) -->
10038
+ @if (showNavigation() && totalImages() > 1) {
10039
+ <div class="footer-navigation">
10040
+ <button
10041
+ class="nav-button prev"
10042
+ (click)="prevClick.emit()"
10043
+ [disabled]="currentIndex() === 0"
10044
+ aria-label="Previous image">
10045
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
10046
+ <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
10047
+ </svg>
10048
+ </button>
10049
+
10050
+ <div class="counter">
10051
+ {{ currentIndex() + 1 }} / {{ totalImages() }}
10052
+ </div>
10053
+
10054
+ <button
10055
+ class="nav-button next"
10056
+ (click)="nextClick.emit()"
10057
+ [disabled]="currentIndex() === totalImages() - 1"
10058
+ aria-label="Next image">
10059
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
10060
+ <path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
10061
+ </svg>
10062
+ </button>
10063
+ </div>
10064
+ }
10065
+
10066
+ <!-- Action buttons -->
10067
+ @if (showActions()) {
10068
+ <div class="footer-actions">
10069
+ <div class="action-buttons-left">
10070
+ <!-- Like button -->
10071
+ <ds-icon-button
10072
+ [icon]="isLiked() ? 'remixHeart3Fill' : 'remixHeart3Line'"
10073
+ variant="ghost"
10074
+ size="md"
10075
+ (click)="likeClick.emit()"
10076
+ [attr.data-liked]="isLiked()"
10077
+ class="action-button-like"
10078
+ [ariaLabel]="'Like'">
10079
+ </ds-icon-button>
10080
+
10081
+ <!-- Comment button -->
10082
+ <ds-icon-button
10083
+ icon="remixChat3Line"
10084
+ variant="ghost"
10085
+ size="md"
10086
+ (click)="commentClick.emit()"
10087
+ class="action-button-comment"
10088
+ [ariaLabel]="'Comment'">
10089
+ </ds-icon-button>
10090
+ </div>
10091
+ </div>
10092
+ }
10093
+ </div>
10094
+ `, styles: [".lightbox-footer{position:fixed;bottom:0;left:0;right:0;z-index:100;padding:16px 20px 20px;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none;display:flex;flex-direction:column;gap:12px}.footer-navigation{display:flex;align-items:center;justify-content:center;gap:24px;pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);border-radius:50%;color:#fff;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding:0}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px;flex-shrink:0}.counter{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-radius:100px;padding:8px 16px;color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;letter-spacing:-.3px;pointer-events:auto;-webkit-user-select:none;user-select:none}.footer-actions{display:flex;align-items:center;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-footer{padding-bottom:calc(20px + env(safe-area-inset-bottom))}}\n"] }]
10095
+ }], propDecorators: { showNavigation: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNavigation", required: false }] }], currentIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentIndex", required: false }] }], totalImages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalImages", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], isLiked: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLiked", required: false }] }], likeCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "likeCount", required: false }] }], commentCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "commentCount", required: false }] }], prevClick: [{ type: i0.Output, args: ["prevClick"] }], nextClick: [{ type: i0.Output, args: ["nextClick"] }], likeClick: [{ type: i0.Output, args: ["likeClick"] }], commentClick: [{ type: i0.Output, args: ["commentClick"] }] } });
10096
+
10097
+ /**
10098
+ * DsMobileLightboxImageComponent
10099
+ *
10100
+ * Full-screen image lightbox component with Swiper.js navigation and pinch-zoom.
10101
+ *
10102
+ * This component is typically not used directly - use DsMobileLightboxService instead.
10103
+ *
10104
+ * Features:
10105
+ * - Swiper.js for smooth image navigation
10106
+ * - Pinch to zoom in/out
10107
+ * - Double-tap to toggle zoom
10108
+ * - Swipe down to close (when not zoomed)
10109
+ * - Image counter and navigation controls
10110
+ *
10111
+ * @example
10112
+ * ```typescript
10113
+ * // Don't instantiate directly - use the service:
10114
+ * constructor(private lightbox: DsMobileLightboxService) {}
10115
+ *
10116
+ * openImage() {
10117
+ * this.lightbox.openImages({
10118
+ * images: [{ type: 'image', src: 'image.jpg', title: 'My Image' }]
10119
+ * });
10120
+ * }
10121
+ * ```
10122
+ */
10123
+ class DsMobileLightboxImageComponent {
10124
+ gestureCtrl;
10125
+ // Inputs (passed from service as regular properties, not signals)
10126
+ images;
10127
+ author;
10128
+ initialIndex = 0;
10129
+ enableZoom = true;
10130
+ showControls = true;
10131
+ enableSwipe = true;
10132
+ showInfo = true;
10133
+ showActions = false;
10134
+ animation = 'fade';
10135
+ onCloseRequested;
10136
+ // View children
10137
+ swiperContainer;
10138
+ // State
10139
+ currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : []));
10140
+ scale = signal(1, ...(ngDevMode ? [{ debugName: "scale" }] : []));
10141
+ isZoomed = signal(false, ...(ngDevMode ? [{ debugName: "isZoomed" }] : []));
10142
+ isLoading = signal(true, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
10143
+ hasError = signal(false, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
10144
+ // Action states
10145
+ isLiked = signal(false, ...(ngDevMode ? [{ debugName: "isLiked" }] : []));
10146
+ likeCount = signal(0, ...(ngDevMode ? [{ debugName: "likeCount" }] : []));
10147
+ commentCount = signal(0, ...(ngDevMode ? [{ debugName: "commentCount" }] : []));
10148
+ // Computed
10149
+ currentImage = computed(() => this.images[this.currentIndex()], ...(ngDevMode ? [{ debugName: "currentImage" }] : []));
10150
+ // Swiper instance
10151
+ swiper;
10152
+ zoomData = new Map();
10153
+ constructor(gestureCtrl) {
10154
+ this.gestureCtrl = gestureCtrl;
10155
+ }
10156
+ ngOnInit() {
10157
+ // Set initial index from the passed property
10158
+ if (this.initialIndex !== undefined) {
10159
+ this.currentIndex.set(this.initialIndex);
10160
+ }
10161
+ // Initialize action states from current image
10162
+ const currentImg = this.images[this.currentIndex()];
10163
+ if (currentImg) {
10164
+ this.isLiked.set(currentImg.isLiked ?? false);
10165
+ this.likeCount.set(currentImg.likeCount ?? 0);
10166
+ this.commentCount.set(currentImg.commentCount ?? 0);
10167
+ }
10168
+ }
10169
+ ngAfterViewInit() {
10170
+ setTimeout(() => {
10171
+ this.initializeSwiper();
10172
+ this.initializeZoomGestures();
10173
+ }, 100);
10174
+ }
10175
+ ngOnDestroy() {
10176
+ // Clean up Swiper
10177
+ if (this.swiper) {
10178
+ this.swiper.destroy();
10179
+ this.swiper = undefined;
10180
+ }
10181
+ }
10182
+ /**
10183
+ * Initialize Swiper for image navigation
10184
+ */
10185
+ initializeSwiper() {
10186
+ if (!this.swiperContainer) {
10187
+ console.error('[Lightbox] Swiper container not found');
10188
+ return;
10189
+ }
10190
+ const swiperOptions = {
10191
+ initialSlide: this.initialIndex,
10192
+ speed: 300,
10193
+ resistance: true,
10194
+ resistanceRatio: 0.85,
10195
+ slidesPerView: 1,
10196
+ spaceBetween: 0,
10197
+ touchRatio: 1,
10198
+ longSwipesRatio: 0.5,
10199
+ threshold: 10,
10200
+ on: {
10201
+ slideChange: (swiper) => {
10202
+ this.currentIndex.set(swiper.activeIndex);
10203
+ this.updateActionStates();
10204
+ // Check if the image is already loaded
10205
+ const currentSlide = swiper.slides[swiper.activeIndex];
10206
+ const img = currentSlide?.querySelector('img');
10207
+ if (img && img.complete && img.naturalHeight !== 0) {
10208
+ // Image is already loaded
10209
+ this.isLoading.set(false);
10210
+ }
10211
+ },
10212
+ slideChangeTransitionStart: () => {
10213
+ // Don't show loading spinner if image is already loaded
10214
+ const currentSlide = this.swiper?.slides[this.swiper.activeIndex];
10215
+ const img = currentSlide?.querySelector('img');
10216
+ if (!img || !img.complete || img.naturalHeight === 0) {
10217
+ this.isLoading.set(true);
10218
+ }
10219
+ }
10220
+ }
10221
+ };
10222
+ this.swiper = new Swiper(this.swiperContainer.nativeElement, swiperOptions);
10223
+ // Check if the initial image is already loaded
10224
+ setTimeout(() => {
10225
+ const currentSlide = this.swiper?.slides[this.currentIndex()];
10226
+ const img = currentSlide?.querySelector('img');
10227
+ if (img && img.complete && img.naturalHeight !== 0) {
10228
+ this.isLoading.set(false);
10229
+ }
10230
+ }, 0);
10231
+ }
10232
+ /**
10233
+ * Initialize pinch-zoom gestures for all slides
10234
+ */
10235
+ initializeZoomGestures() {
10236
+ if (!this.enableZoom)
10237
+ return;
10238
+ const slides = this.swiperContainer.nativeElement.querySelectorAll('.image-zoom-container');
10239
+ slides.forEach((slide, index) => {
10240
+ this.initializeZoomForSlide(slide, index);
10241
+ });
10242
+ }
10243
+ /**
10244
+ * Initialize zoom gestures for a specific slide
10245
+ */
10246
+ initializeZoomForSlide(container, index) {
10247
+ let initialDistance = 0;
10248
+ let initialScale = 1;
10249
+ let currentScale = 1;
10250
+ let lastTap = 0;
10251
+ // Double-tap to zoom
10252
+ container.addEventListener('click', (event) => {
10253
+ const now = Date.now();
10254
+ const timeSinceLastTap = now - lastTap;
10255
+ if (timeSinceLastTap < 300 && timeSinceLastTap > 0) {
10256
+ event.preventDefault();
10257
+ this.toggleZoom(container, index);
10258
+ }
10259
+ lastTap = now;
10260
+ });
10261
+ // Pinch to zoom
10262
+ const getTouchDistance = (touches) => {
10263
+ const dx = touches[0].clientX - touches[1].clientX;
10264
+ const dy = touches[0].clientY - touches[1].clientY;
10265
+ return Math.sqrt(dx * dx + dy * dy);
10266
+ };
10267
+ container.addEventListener('touchstart', (event) => {
10268
+ if (event.touches.length === 2) {
10269
+ event.preventDefault();
10270
+ initialDistance = getTouchDistance(event.touches);
10271
+ const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
10272
+ initialScale = zoomData.scale;
10273
+ // Disable Swiper when zooming
10274
+ if (this.swiper) {
10275
+ this.swiper.allowTouchMove = false;
10276
+ }
10277
+ }
10278
+ }, { passive: false });
10279
+ container.addEventListener('touchmove', (event) => {
10280
+ if (event.touches.length === 2) {
10281
+ event.preventDefault();
10282
+ const currentDistance = getTouchDistance(event.touches);
10283
+ const pinchScale = currentDistance / initialDistance;
10284
+ currentScale = Math.max(1, Math.min(initialScale * pinchScale, 4));
10285
+ const img = container.querySelector('img');
10286
+ if (img) {
10287
+ img.style.transform = `scale(${currentScale})`;
10288
+ }
10289
+ if (currentScale > 1) {
10290
+ this.isZoomed.set(true);
10291
+ }
10292
+ else {
10293
+ this.isZoomed.set(false);
10294
+ }
10295
+ }
10296
+ }, { passive: false });
10297
+ container.addEventListener('touchend', (event) => {
10298
+ if (event.touches.length < 2) {
10299
+ // Save zoom state
10300
+ this.zoomData.set(index, { scale: currentScale, x: 0, y: 0 });
10301
+ // Re-enable Swiper if not zoomed
10302
+ if (this.swiper && currentScale <= 1) {
10303
+ this.swiper.allowTouchMove = true;
10304
+ }
10305
+ }
10306
+ });
10307
+ }
10308
+ /**
10309
+ * Toggle zoom on double-tap
10310
+ */
10311
+ toggleZoom(container, index) {
10312
+ const zoomData = this.zoomData.get(index) || { scale: 1, x: 0, y: 0 };
10313
+ const img = container.querySelector('img');
10314
+ if (!img)
10315
+ return;
10316
+ if (zoomData.scale > 1) {
10317
+ // Zoom out
10318
+ img.style.transform = 'scale(1)';
10319
+ this.zoomData.set(index, { scale: 1, x: 0, y: 0 });
10320
+ this.isZoomed.set(false);
10321
+ if (this.swiper) {
10322
+ this.swiper.allowTouchMove = true;
10323
+ }
10324
+ }
10325
+ else {
10326
+ // Zoom in
10327
+ img.style.transform = 'scale(2)';
10328
+ this.zoomData.set(index, { scale: 2, x: 0, y: 0 });
10329
+ this.isZoomed.set(true);
10330
+ if (this.swiper) {
10331
+ this.swiper.allowTouchMove = false;
10332
+ }
10333
+ }
10334
+ }
10335
+ /**
10336
+ * Update action states (like, comments) when slide changes
10337
+ */
10338
+ updateActionStates() {
10339
+ const currentImg = this.images[this.currentIndex()];
10340
+ if (currentImg) {
10341
+ this.isLiked.set(currentImg.isLiked ?? false);
10342
+ this.likeCount.set(currentImg.likeCount ?? 0);
10343
+ this.commentCount.set(currentImg.commentCount ?? 0);
10344
+ }
10345
+ }
10346
+ /**
10347
+ * Close the lightbox
10348
+ */
10349
+ close() {
10350
+ if (this.onCloseRequested) {
10351
+ this.onCloseRequested();
10352
+ }
10353
+ }
10354
+ /**
10355
+ * Handle share button click
10356
+ */
10357
+ async onShare() {
10358
+ console.log('[Lightbox] Share button clicked');
10359
+ const currentImg = this.currentImage();
10360
+ if (!currentImg?.src) {
10361
+ console.warn('[Lightbox] No image to share');
10362
+ return;
10363
+ }
10364
+ try {
10365
+ // Check if Web Share API is available (for browser)
10366
+ if (navigator.share) {
10367
+ await navigator.share({
10368
+ title: currentImg.title || 'Shared Image',
10369
+ text: currentImg.description || '',
10370
+ url: currentImg.src,
10371
+ });
10372
+ console.log('[Lightbox] Shared via Web Share API');
10373
+ }
10374
+ else {
10375
+ // Fallback to Capacitor Share API (for native apps)
10376
+ await Share.share({
10377
+ title: currentImg.title || 'Shared Image',
10378
+ url: currentImg.src,
10379
+ dialogTitle: 'Share Image',
10380
+ });
10381
+ console.log('[Lightbox] Shared via Capacitor Share API');
10382
+ }
10383
+ }
10384
+ catch (error) {
10385
+ // User cancellation is expected and not an error
10386
+ if (error?.message?.includes('cancel') || error?.code === 'USER_CANCELLED') {
10387
+ console.log('[Lightbox] Share cancelled by user');
10388
+ return;
10389
+ }
10390
+ console.error('[Lightbox] Share failed:', error);
10391
+ }
10392
+ }
10393
+ /**
10394
+ * Handle like button toggle
10395
+ */
10396
+ onLikeToggle() {
10397
+ console.log('[Lightbox] Like button toggled');
10398
+ this.isLiked.update(liked => !liked);
10399
+ if (this.isLiked()) {
10400
+ this.likeCount.update(count => count + 1);
10401
+ }
10402
+ else {
10403
+ this.likeCount.update(count => Math.max(0, count - 1));
10404
+ }
10405
+ }
10406
+ /**
10407
+ * Handle reply/comment button click
10408
+ */
10409
+ onReply() {
10410
+ console.log('[Lightbox] Reply button clicked');
10411
+ if (this.onCloseRequested) {
10412
+ this.onCloseRequested();
10413
+ }
10414
+ }
10415
+ /**
10416
+ * Navigate to the next image
10417
+ */
10418
+ nextImage() {
10419
+ if (this.swiper && this.currentIndex() < this.images.length - 1) {
10420
+ this.swiper.slideNext();
10421
+ }
10422
+ }
10423
+ /**
10424
+ * Navigate to the previous image
10425
+ */
10426
+ previousImage() {
10427
+ if (this.swiper && this.currentIndex() > 0) {
10428
+ this.swiper.slidePrev();
10429
+ }
10430
+ }
10431
+ /**
10432
+ * Handle image load success
10433
+ */
10434
+ onImageLoad(index) {
10435
+ if (index === this.currentIndex()) {
10436
+ this.isLoading.set(false);
10437
+ }
10438
+ }
10439
+ /**
10440
+ * Handle image load error
10441
+ */
10442
+ onImageError(index) {
10443
+ if (index === this.currentIndex()) {
10444
+ console.error(`[Lightbox] Image ${index} failed to load`);
10445
+ this.isLoading.set(false);
10446
+ this.hasError.set(true);
10447
+ }
10448
+ }
10449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxImageComponent, deps: [{ token: i1.GestureController }], target: i0.ɵɵFactoryTarget.Component });
10450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileLightboxImageComponent, isStandalone: true, selector: "ds-mobile-lightbox-image", viewQueries: [{ propertyName: "swiperContainer", first: true, predicate: ["swiperContainer"], descendants: true, read: ElementRef }], ngImport: i0, template: `
10451
+ <div class="lightbox-overlay"
10452
+ [class.zoomed]="isZoomed()">
10453
+
10454
+ <div class="lightbox-wrapper">
10455
+ <!-- Header with author info and action buttons -->
10456
+ <ds-mobile-lightbox-header
10457
+ [author]="author"
10458
+ (closeClick)="close()"
10459
+ (shareClick)="onShare()"
10460
+ />
10461
+
10462
+ <!-- Swiper container -->
10463
+ <div class="swiper-container" #swiperContainer>
10464
+ <div class="swiper-wrapper">
10465
+ @for (image of images; track image.src; let i = $index) {
10466
+ <div class="swiper-slide">
10467
+ <div class="image-zoom-container" [attr.data-index]="i">
10468
+ <img
10469
+ [src]="image.src"
10470
+ [alt]="image.alt || 'Lightbox image'"
10471
+ class="lightbox-image"
10472
+ (load)="onImageLoad(i)"
10473
+ (error)="onImageError(i)">
10474
+ </div>
10475
+ </div>
10476
+ }
10477
+ </div>
10478
+ </div>
10479
+
10480
+ <!-- Loading indicator -->
10481
+ @if (isLoading()) {
10482
+ <div class="loading-spinner">
10483
+ <ion-spinner name="crescent"></ion-spinner>
10484
+ </div>
10485
+ }
10486
+
10487
+ <!-- Footer with navigation and actions -->
10488
+ <ds-mobile-lightbox-footer
10489
+ [showNavigation]="showControls && images.length > 1"
10490
+ [showActions]="showActions"
10491
+ [currentIndex]="currentIndex()"
10492
+ [totalImages]="images.length"
10493
+ [isLiked]="isLiked()"
10494
+ [likeCount]="likeCount()"
10495
+ [commentCount]="commentCount()"
10496
+ (prevClick)="previousImage()"
10497
+ (nextClick)="nextImage()"
10498
+ (likeClick)="onLikeToggle()"
10499
+ (commentClick)="onReply()"
10500
+ />
10501
+ </div>
10502
+ </div>
10503
+ `, isInline: true, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}:host{display:block;position:fixed;inset:0;width:100vw;height:100vh;z-index:10000}.lightbox-overlay{position:fixed;inset:0;width:100vw;height:100vh;background:#000000fa;z-index:10000;display:flex;flex-direction:column;touch-action:none;animation:fadeIn .2s ease-out}.lightbox-overlay.zoomed{overflow:hidden}.lightbox-content{display:block;height:100vh;width:100vw;position:absolute;inset:0}.lightbox-content::part(scroll){display:flex;flex-direction:column;height:100%;overflow:hidden}.lightbox-wrapper{position:absolute;inset:0;display:flex;flex-direction:column;width:100%;height:100%}.lightbox-content.zoomed{overflow:hidden}.lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.close-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg{color:#fff!important;fill:#fff!important}.swiper-container{position:absolute;inset:0;width:100%;height:100%;z-index:1}.swiper-wrapper{width:100%;height:100%}.swiper-slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.image-zoom-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}.lightbox-image{max-width:min(640px,100%);max-height:100%;width:auto;height:auto;-o-object-fit:contain;object-fit:contain;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none;transition:transform .3s ease-out;transform-origin:center center}.lightbox-overlay.zoomed .swiper-container{touch-action:none}.loading-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10}.loading-spinner ion-spinner{--color: rgba(255, 255, 255, .8);width:48px;height:48px}.error-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;text-align:center;color:#fffc;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:500;padding:20px;background:#00000080;border-radius:12px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.lightbox-bottom-section{position:fixed;bottom:0;left:0;right:0;z-index:100;display:flex;flex-direction:column;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none}.lightbox-controls{display:flex;align-items:center;justify-content:center;gap:24px;padding:16px 20px 12px;pointer-events:none}.nav-button,.counter{pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);color:#fff;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease;padding:0;margin:0;outline:none}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px}.counter{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;padding:10px 16px;background:#00000080;border-radius:100px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);white-space:nowrap}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-bottom-section{padding-bottom:env(safe-area-inset-bottom)}}.lightbox-footer{display:flex;padding:12px 20px 20px;pointer-events:none}.footer-actions{display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-with-count::ng-deep button,.action-button-share::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover,.action-button-with-count::ng-deep button:hover,.action-button-share::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active,.action-button-with-count::ng-deep button:active,.action-button-share::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-with-count::ng-deep button svg,.action-button-share::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content,.action-button-with-count::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg,.action-button-with-count::ng-deep button .btn__icon svg,.action-button-share::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment,.action-button-share{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-share::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@media (min-width: 768px){.lightbox-header{padding:24px}.close-button{width:48px;height:48px}.lightbox-controls{padding:20px 24px 16px}.nav-button{width:48px;height:48px}.counter{font-size:var(--font-size-base);padding:12px 20px}.lightbox-footer{padding:16px 24px 24px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{height:48px;padding:0 20px}.action-button-share::ng-deep button{width:48px!important;height:48px!important;min-width:48px!important;min-height:48px!important}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes zoomIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.image-wrapper,.nav-button,.lightbox-caption{transition:none}}.nav-button:focus-visible{outline:2px solid white;outline-offset:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DsMobileLightboxHeaderComponent, selector: "ds-mobile-lightbox-header", inputs: ["author"], outputs: ["closeClick", "shareClick"] }, { kind: "component", type: DsMobileLightboxFooterComponent, selector: "ds-mobile-lightbox-footer", inputs: ["showNavigation", "currentIndex", "totalImages", "showActions", "isLiked", "likeCount", "commentCount"], outputs: ["prevClick", "nextClick", "likeClick", "commentClick"] }] });
10504
+ }
10505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileLightboxImageComponent, decorators: [{
10506
+ type: Component,
10507
+ args: [{ selector: 'ds-mobile-lightbox-image', standalone: true, imports: [
10508
+ CommonModule,
10509
+ IonSpinner,
10510
+ DsMobileLightboxHeaderComponent,
10511
+ DsMobileLightboxFooterComponent
10512
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
10513
+ <div class="lightbox-overlay"
10514
+ [class.zoomed]="isZoomed()">
10515
+
10516
+ <div class="lightbox-wrapper">
10517
+ <!-- Header with author info and action buttons -->
10518
+ <ds-mobile-lightbox-header
10519
+ [author]="author"
10520
+ (closeClick)="close()"
10521
+ (shareClick)="onShare()"
10522
+ />
10523
+
10524
+ <!-- Swiper container -->
10525
+ <div class="swiper-container" #swiperContainer>
10526
+ <div class="swiper-wrapper">
10527
+ @for (image of images; track image.src; let i = $index) {
10528
+ <div class="swiper-slide">
10529
+ <div class="image-zoom-container" [attr.data-index]="i">
10530
+ <img
10531
+ [src]="image.src"
10532
+ [alt]="image.alt || 'Lightbox image'"
10533
+ class="lightbox-image"
10534
+ (load)="onImageLoad(i)"
10535
+ (error)="onImageError(i)">
10536
+ </div>
10537
+ </div>
10538
+ }
10539
+ </div>
10540
+ </div>
10541
+
10542
+ <!-- Loading indicator -->
10543
+ @if (isLoading()) {
10544
+ <div class="loading-spinner">
10545
+ <ion-spinner name="crescent"></ion-spinner>
10546
+ </div>
10547
+ }
10548
+
10549
+ <!-- Footer with navigation and actions -->
10550
+ <ds-mobile-lightbox-footer
10551
+ [showNavigation]="showControls && images.length > 1"
10552
+ [showActions]="showActions"
10553
+ [currentIndex]="currentIndex()"
10554
+ [totalImages]="images.length"
10555
+ [isLiked]="isLiked()"
10556
+ [likeCount]="likeCount()"
10557
+ [commentCount]="commentCount()"
10558
+ (prevClick)="previousImage()"
10559
+ (nextClick)="nextImage()"
10560
+ (likeClick)="onLikeToggle()"
10561
+ (commentClick)="onReply()"
10562
+ />
10563
+ </div>
10564
+ </div>
10565
+ `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}:host{display:block;position:fixed;inset:0;width:100vw;height:100vh;z-index:10000}.lightbox-overlay{position:fixed;inset:0;width:100vw;height:100vh;background:#000000fa;z-index:10000;display:flex;flex-direction:column;touch-action:none;animation:fadeIn .2s ease-out}.lightbox-overlay.zoomed{overflow:hidden}.lightbox-content{display:block;height:100vh;width:100vw;position:absolute;inset:0}.lightbox-content::part(scroll){display:flex;flex-direction:column;height:100%;overflow:hidden}.lightbox-wrapper{position:absolute;inset:0;display:flex;flex-direction:column;width:100%;height:100%}.lightbox-content.zoomed{overflow:hidden}.lightbox-header{position:fixed;top:0;left:0;right:0;z-index:1000;padding:0 16px;background:linear-gradient(to bottom,rgba(0,0,0,.8) 0%,rgba(0,0,0,.4) 80%,transparent 100%);pointer-events:none}.header-content{display:flex;align-items:center;justify-content:space-between;gap:12px;pointer-events:auto}.post-author-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.author-details{display:flex;flex-direction:column;min-width:0;flex:1}.author-name{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px}.author-meta{color:#ffffffb3;font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;display:flex;align-items:center;gap:6px}.author-meta .separator{color:#ffffff80}.close-button{pointer-events:auto;flex-shrink:0;border-radius:50%}.close-button::ng-deep button{color:#fff!important;background:#ffffff1a!important;border-radius:50%;transition:background .2s ease;border:none;width:36px!important;height:36px!important;min-width:36px!important;min-height:36px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.close-button::ng-deep button:hover{background:#ffffff26!important}.close-button::ng-deep button:active{background:#ffffff26!important}.close-button::ng-deep svg{color:#fff!important;fill:#fff!important}.swiper-container{position:absolute;inset:0;width:100%;height:100%;z-index:1}.swiper-wrapper{width:100%;height:100%}.swiper-slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.image-zoom-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}.lightbox-image{max-width:min(640px,100%);max-height:100%;width:auto;height:auto;-o-object-fit:contain;object-fit:contain;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none;transition:transform .3s ease-out;transform-origin:center center}.lightbox-overlay.zoomed .swiper-container{touch-action:none}.loading-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10}.loading-spinner ion-spinner{--color: rgba(255, 255, 255, .8);width:48px;height:48px}.error-message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;text-align:center;color:#fffc;font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:500;padding:20px;background:#00000080;border-radius:12px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.lightbox-bottom-section{position:fixed;bottom:0;left:0;right:0;z-index:100;display:flex;flex-direction:column;background:linear-gradient(to top,rgba(0,0,0,.8) 0%,rgba(0,0,0,.6) 50%,rgba(0,0,0,.4) 75%,transparent 100%);pointer-events:none}.lightbox-controls{display:flex;align-items:center;justify-content:center;gap:24px;padding:16px 20px 12px;pointer-events:none}.nav-button,.counter{pointer-events:auto}.nav-button{background:#ffffff1a;border:1px solid rgba(255,255,255,.2);color:#fff;width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease;padding:0;margin:0;outline:none}.nav-button:hover:not(:disabled){background:#fff3;transform:scale(1.05)}.nav-button:active:not(:disabled){transform:scale(.95)}.nav-button:disabled{opacity:.3;cursor:not-allowed}.nav-button svg{width:24px;height:24px}.counter{color:#fff;font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:500;line-height:1;padding:10px 16px;background:#00000080;border-radius:100px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);white-space:nowrap}@supports (padding-bottom: env(safe-area-inset-bottom)){.lightbox-bottom-section{padding-bottom:env(safe-area-inset-bottom)}}.lightbox-footer{display:flex;padding:12px 20px 20px;pointer-events:none}.footer-actions{display:flex;align-items:center;justify-content:space-between;gap:16px;pointer-events:auto}.action-buttons-left{display:flex;align-items:center;gap:16px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-with-count::ng-deep button,.action-button-share::ng-deep button{background:#ffffff1a!important;border:1px solid rgba(255,255,255,.2)!important;color:#fff!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);transition:all .2s ease}.action-button-like::ng-deep button:hover,.action-button-comment::ng-deep button:hover,.action-button-with-count::ng-deep button:hover,.action-button-share::ng-deep button:hover{background:#fff3!important;transform:scale(1.02)}.action-button-like::ng-deep button:active,.action-button-comment::ng-deep button:active,.action-button-with-count::ng-deep button:active,.action-button-share::ng-deep button:active{transform:scale(.98)}.action-button-like::ng-deep button svg,.action-button-comment::ng-deep button svg,.action-button-with-count::ng-deep button svg,.action-button-share::ng-deep button svg,.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon,.action-button-like::ng-deep button .btn__content,.action-button-comment::ng-deep button .btn__content,.action-button-with-count::ng-deep button .btn__content{color:#fff!important;fill:#fff!important}.action-button-like::ng-deep button .btn__icon svg,.action-button-comment::ng-deep button .btn__icon svg,.action-button-with-count::ng-deep button .btn__icon svg,.action-button-share::ng-deep button .btn__icon svg{color:#fff!important;fill:#fff!important;display:block!important;opacity:1!important;visibility:visible!important;width:20px!important;height:20px!important}.action-button-like::ng-deep button .btn__icon,.action-button-comment::ng-deep button .btn__icon,.action-button-with-count::ng-deep button .btn__icon,.action-button-share::ng-deep button .btn__icon{display:flex!important;align-items:center!important;justify-content:center!important;flex-shrink:0!important}.action-button-like[data-liked=true]::ng-deep button svg{fill:#f91880!important;color:#f91880!important}.action-button-like[data-liked=true]::ng-deep button{border-color:#f918804d!important}.action-button-like,.action-button-comment,.action-button-share{flex-shrink:0;border-radius:50%}.action-button-like::ng-deep button,.action-button-comment::ng-deep button,.action-button-share::ng-deep button{border-radius:50%!important;width:44px!important;height:44px!important;min-width:44px!important;min-height:44px!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}@media (min-width: 768px){.lightbox-header{padding:24px}.close-button{width:48px;height:48px}.lightbox-controls{padding:20px 24px 16px}.nav-button{width:48px;height:48px}.counter{font-size:var(--font-size-base);padding:12px 20px}.lightbox-footer{padding:16px 24px 24px}.action-button-like::ng-deep button,.action-button-comment::ng-deep button{height:48px;padding:0 20px}.action-button-share::ng-deep button{width:48px!important;height:48px!important;min-width:48px!important;min-height:48px!important}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes zoomIn{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion: reduce){.image-wrapper,.nav-button,.lightbox-caption{transition:none}}.nav-button:focus-visible{outline:2px solid white;outline-offset:2px}\n"] }]
10566
+ }], ctorParameters: () => [{ type: i1.GestureController }], propDecorators: { swiperContainer: [{
10567
+ type: ViewChild,
10568
+ args: ['swiperContainer', { read: ElementRef }]
10569
+ }] } });
10570
+
10571
+ /**
10572
+ * DsMobileLightboxPdfComponent
10573
+ *
10574
+ * PDF viewer component that displays PDF info and allows users to open PDFs in the native device viewer.
10575
+ * Shows a lightbox with PDF details first, then user can choose to open in native viewer.
10576
+ *
10577
+ * This component is typically not used directly - use DsMobileLightboxService instead.
10578
+ *
10579
+ * Features:
10580
+ * - PDF info preview (title, size, icon)
10581
+ * - User-initiated native PDF viewing (iOS/Android)
10582
+ * - Download and cache support
10583
+ * - Share functionality
10584
+ * - Loading states
10585
+ *
10586
+ * @example
10587
+ * ```typescript
10588
+ * // Don't instantiate directly - use the service:
10589
+ * constructor(private lightbox: DsMobileLightboxService) {}
10590
+ *
10591
+ * openPdf() {
10592
+ * this.lightbox.openPdf({
10593
+ * pdf: { type: 'pdf', src: 'document.pdf', title: 'My Document' }
10594
+ * });
10595
+ * }
10596
+ * ```
10597
+ */
10598
+ class DsMobileLightboxPdfComponent {
10599
+ // Inputs (passed from service as regular properties)
10600
+ pdf;
10601
+ author;
10602
+ onCloseRequested;
10603
+ // State
10604
+ isLoading = false;
10605
+ hasError = false;
10606
+ errorMessage = '';
10607
+ cachedFilePath;
10608
+ constructor() { }
10609
+ ngOnInit() {
10610
+ console.log('[PDF Lightbox] Initializing with PDF:', this.pdf);
10611
+ // Don't automatically open - let user click the "Open" button
10612
+ // this.openPdfInNativeViewer();
10613
+ }
10614
+ /**
10615
+ * Open the PDF in the native device viewer
10616
+ */
10617
+ async openPdfInNativeViewer() {
10618
+ if (!this.pdf?.src) {
10619
+ console.error('[PDF Lightbox] No PDF source provided');
10620
+ this.hasError = true;
10621
+ this.errorMessage = 'No PDF file provided';
10622
+ return;
10623
+ }
10624
+ this.isLoading = true;
10625
+ this.hasError = false;
10626
+ this.errorMessage = '';
10627
+ try {
10628
+ console.log('[PDF Lightbox] Opening PDF:', this.pdf.src);
10629
+ // Check if it's already a full URL
10630
+ let pdfUrl;
10631
+ if (this.pdf.src.startsWith('http://') || this.pdf.src.startsWith('https://')) {
10632
+ // Already a full URL
10633
+ pdfUrl = this.pdf.src;
10634
+ }
10635
+ else {
10636
+ // Relative path - construct full URL
10637
+ // Use current origin (which includes the dev server URL in Capacitor)
10638
+ // Remove leading slash if present to avoid double slashes
10639
+ const cleanPath = this.pdf.src.startsWith('/') ? this.pdf.src.slice(1) : this.pdf.src;
10640
+ pdfUrl = `${window.location.origin}/${cleanPath}`;
10641
+ }
10642
+ console.log('[PDF Lightbox] Opening PDF at URL:', pdfUrl);
10643
+ // Use Browser to open the PDF
10644
+ await Browser.open({
10645
+ url: pdfUrl,
10646
+ presentationStyle: 'fullscreen'
10647
+ });
10648
+ this.isLoading = false;
10649
+ // Close the modal after opening browser
10650
+ setTimeout(() => {
10651
+ this.close();
10652
+ }, 500);
10653
+ }
10654
+ catch (error) {
10655
+ console.error('[PDF Lightbox] Error opening PDF:', error);
10656
+ this.isLoading = false;
10657
+ this.hasError = true;
10658
+ this.errorMessage = error?.message || 'Failed to open PDF';
10659
+ }
10660
+ }
10661
+ /**
10662
+ * Download a remote PDF and open it
10663
+ */
10664
+ async downloadAndOpenPdf() {
10665
+ try {
10666
+ console.log('[PDF Lightbox] Downloading PDF from:', this.pdf.src);
10667
+ // Fetch the PDF
10668
+ const response = await fetch(this.pdf.src);
10669
+ if (!response.ok) {
10670
+ throw new Error(`Failed to download PDF: ${response.statusText}`);
10671
+ }
10672
+ const blob = await response.blob();
10673
+ const base64Data = await this.blobToBase64(blob);
10674
+ // Generate a filename
10675
+ const fileName = this.pdf.title
10676
+ ? `${this.pdf.title.replace(/[^a-z0-9]/gi, '_')}.pdf`
10677
+ : 'document.pdf';
10678
+ // Save to cache directory
10679
+ const result = await Filesystem.writeFile({
10680
+ path: fileName,
10681
+ data: base64Data,
10682
+ directory: Directory.Cache
10683
+ });
10684
+ console.log('[PDF Lightbox] PDF saved to cache:', result.uri);
10685
+ this.cachedFilePath = result.uri;
10686
+ // Open using Browser
10378
10687
  await Browser.open({
10379
10688
  url: result.uri,
10380
10689
  presentationStyle: 'fullscreen'
@@ -13496,98 +13805,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
13496
13805
 
13497
13806
  <ds-icon
13498
13807
  content-trailing
13499
- name="remixArrowRightSLine"
13500
- size="20px"
13501
- class="item-trailing"
13502
- />
13503
- </ds-mobile-card-inline>
13504
- `, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3b82f6)!important}\n"] }]
13505
- }], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], fileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileSize", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], fileUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileUrl", required: false }] }], fileClick: [{ type: i0.Output, args: ["fileClick"] }] } });
13506
-
13507
- /**
13508
- * DsMobileSystemMessageBannerComponent
13509
- *
13510
- * Full-width centered banner component for displaying system messages in chat conversations.
13511
- * Uses the same text styling as message bubbles for consistency.
13512
- *
13513
- * Features:
13514
- * - Full-width centered layout
13515
- * - Subtle background with theming support
13516
- * - Same typography as message bubbles
13517
- * - Optional icon support
13518
- *
13519
- * Common use cases:
13520
- * - Inquiry status updates ("Your inquiry has been assigned to...")
13521
- * - System notifications ("This inquiry is marked as resolved")
13522
- * - Auto-replies ("We aim to respond within 24 hours...")
13523
- * - Time/date separators
13524
- *
13525
- * @example
13526
- * ```html
13527
- * <!-- Simple system message -->
13528
- * <ds-mobile-system-message-banner
13529
- * [message]="'Ricki Meihlen har overtaget din henvendelse og vil kontakte dig snart.'">
13530
- * </ds-mobile-system-message-banner>
13531
- *
13532
- * <!-- With icon -->
13533
- * <ds-mobile-system-message-banner
13534
- * [message]="'Vi bestræber os på at svare inden for 24 timer på hverdage.'"
13535
- * [iconName]="'remixInformationLine'">
13536
- * </ds-mobile-system-message-banner>
13537
- * ```
13538
- */
13539
- class DsMobileSystemMessageBannerComponent {
13540
- /**
13541
- * System message text to display
13542
- */
13543
- message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
13544
- /**
13545
- * Optional icon name (currently using inline SVG for info icon)
13546
- * Can be extended to support full icon library integration
13547
- */
13548
- iconName = input('', ...(ngDevMode ? [{ debugName: "iconName" }] : []));
13549
- /**
13550
- * Whether this system message appears directly after a timestamp
13551
- * When true, removes top padding to reduce spacing
13552
- */
13553
- afterTimestamp = input(false, ...(ngDevMode ? [{ debugName: "afterTimestamp" }] : []));
13554
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSystemMessageBannerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13555
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileSystemMessageBannerComponent, isStandalone: true, selector: "ds-mobile-system-message-banner", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, iconName: { classPropertyName: "iconName", publicName: "iconName", isSignal: true, isRequired: false, transformFunction: null }, afterTimestamp: { classPropertyName: "afterTimestamp", publicName: "afterTimestamp", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.after-timestamp": "afterTimestamp()" } }, ngImport: i0, template: `
13556
- <div class="system-message-container">
13557
- <div class="system-message-content">
13558
- @if (iconName()) {
13559
- <span class="system-message-icon">
13560
- <!-- Icon slot - you can add ds-icon component here if needed -->
13561
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
13562
- <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"/>
13563
- </svg>
13564
- </span>
13565
- }
13566
- <p class="system-message-text">{{ message() }}</p>
13567
- </div>
13568
- </div>
13569
- `, isInline: true, styles: [":host{display:block;width:100%;padding:12px 0}:host(.after-timestamp){padding-top:0}.system-message-container{display:flex;justify-content:center;align-items:center;width:100%;padding:0 16px}.system-message-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;max-width:85%;text-align:center}.system-message-icon{flex-shrink:0;width:16px;height:16px;color:var(--color-text-tertiary, #a0a0a0)}.system-message-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--color-text-secondary, #666666);margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
13570
- }
13571
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSystemMessageBannerComponent, decorators: [{
13572
- type: Component,
13573
- args: [{ selector: 'ds-mobile-system-message-banner', standalone: true, imports: [CommonModule], host: {
13574
- '[class.after-timestamp]': 'afterTimestamp()'
13575
- }, template: `
13576
- <div class="system-message-container">
13577
- <div class="system-message-content">
13578
- @if (iconName()) {
13579
- <span class="system-message-icon">
13580
- <!-- Icon slot - you can add ds-icon component here if needed -->
13581
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
13582
- <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"/>
13583
- </svg>
13584
- </span>
13585
- }
13586
- <p class="system-message-text">{{ message() }}</p>
13587
- </div>
13588
- </div>
13589
- `, styles: [":host{display:block;width:100%;padding:12px 0}:host(.after-timestamp){padding-top:0}.system-message-container{display:flex;justify-content:center;align-items:center;width:100%;padding:0 16px}.system-message-content{display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 16px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;max-width:85%;text-align:center}.system-message-icon{flex-shrink:0;width:16px;height:16px;color:var(--color-text-tertiary, #a0a0a0)}.system-message-text{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:20px;letter-spacing:-.3px;color:var(--color-text-secondary, #666666);margin:0}\n"] }]
13590
- }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], iconName: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconName", required: false }] }], afterTimestamp: [{ type: i0.Input, args: [{ isSignal: true, alias: "afterTimestamp", required: false }] }] } });
13808
+ name="remixArrowRightSLine"
13809
+ size="20px"
13810
+ class="item-trailing"
13811
+ />
13812
+ </ds-mobile-card-inline>
13813
+ `, styles: [".item-avatar.pdf ::ng-deep .avatar--icon{background-color:#ff5757!important}.item-avatar.doc ::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3b82f6)!important}\n"] }]
13814
+ }], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], fileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileSize", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], fileUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileUrl", required: false }] }], fileClick: [{ type: i0.Output, args: ["fileClick"] }] } });
13591
13815
 
13592
13816
  /**
13593
13817
  * DsMobileChatModalComponent
@@ -14934,487 +15158,263 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
14934
15158
  [rows]="1"
14935
15159
  placeholder="Tell us what this inquiry is about..."
14936
15160
  class="inquiry-description-input ghost-input-clean"
14937
- (valueChange)="validateForm()"
14938
- />
14939
- </ds-mobile-section>
14940
-
14941
- <!-- Fixed Bottom Container (Slides with keyboard) -->
14942
- <div footer class="fixed-bottom-container">
14943
- <!-- Attachment Previews (if any) -->
14944
- @if (attachments().length > 0) {
14945
- <div class="attachment-previews-section">
14946
- <div class="image-previews">
14947
- @for (attachment of attachments(); track attachment.id) {
14948
- <ds-mobile-attachment-preview
14949
- [attachment]="attachment"
14950
- (remove)="removeAttachment($event)"
14951
- />
14952
- }
14953
- </div>
14954
- </div>
14955
- }
14956
-
14957
- <!-- Submit Actions Container -->
14958
- <div class="submit-container">
14959
- <div class="submit-content">
14960
- <!-- Upload Actions (Left) -->
14961
- <div class="upload-actions">
14962
- <ds-icon-button
14963
- icon="remixImageLine"
14964
- variant="secondary"
14965
- size="md"
14966
- (clicked)="addPhoto()"
14967
- [disabled]="attachments().length >= 6"
14968
- aria-label="Add photo">
14969
- </ds-icon-button>
14970
- <ds-icon-button
14971
- icon="remixAttachmentLine"
14972
- variant="secondary"
14973
- size="md"
14974
- (clicked)="handleAddAttachment()"
14975
- [disabled]="attachments().length >= 6"
14976
- aria-label="Add attachment">
14977
- </ds-icon-button>
14978
-
14979
- <!-- Hidden file input for file selection -->
14980
- <input
14981
- #fileInput
14982
- type="file"
14983
- accept="*/*"
14984
- multiple
14985
- (change)="handleFileSelect($event)"
14986
- style="display: none;"
14987
- aria-hidden="true"
14988
- />
14989
- </div>
14990
-
14991
- <!-- Submit Button (Right) -->
14992
- <ds-button
14993
- variant="primary"
14994
- size="lg"
14995
- [disabled]="!isFormValid() || isSubmitting()"
14996
- (clicked)="handleSubmit()">
14997
- Submit
14998
- </ds-button>
14999
- </div>
15000
- </div>
15001
- </div>
15002
- </ds-mobile-modal-base>
15003
- `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".inquiry-title-input{flex:0 0 auto}.inquiry-title-input ::ng-deep textarea{font-size:var(--font-size-2xl);font-weight:400;line-height:1.2;max-height:200px;overflow-y:auto;resize:none;box-sizing:border-box;padding-right:52px}.fixed-bottom-container{background:var(--color-background-neutral-primary);border-top:1px solid var(--border-color-default)}.attachment-previews-section{padding:16px 20px;border-bottom:1px solid var(--border-color-default)}.image-previews{display:flex;flex-wrap:wrap;gap:8px}.submit-container{padding:16px 20px}.submit-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.upload-actions{display:flex;align-items:center;gap:8px}.upload-actions ds-icon-button::ng-deep button{width:44px;height:44px;border-radius:50%}.submit-content ds-button::ng-deep button{border-radius:100px}.inquiry-description-input{display:flex;flex:1;min-height:0}.inquiry-description-input ::ng-deep .textarea-container{display:flex;flex:1;min-height:0}.inquiry-description-input ::ng-deep textarea{flex:1;min-height:200px;resize:none}@media (min-width: 768px){.submit-container{padding:20px 32px}}\n"] }]
15004
- }], propDecorators: { titleInputRef: [{
15005
- type: ViewChild,
15006
- args: ['titleInput', { read: ElementRef }]
15007
- }], titleInput: [{
15008
- type: ViewChild,
15009
- args: ['titleInput']
15010
- }], fileInput: [{
15011
- type: ViewChild,
15012
- args: ['fileInput']
15013
- }], loading: [{
15014
- type: Input
15015
- }], error: [{
15016
- type: Input
15017
- }], onSubmit: [{
15018
- type: Input
15019
- }] } });
15020
-
15021
- /**
15022
- * DsMobileNewInquiryModalService
15023
- *
15024
- * Service for displaying the new inquiry creation modal.
15025
- * Built on Ionic's modal system with native gestures and animations.
15026
- *
15027
- * Features:
15028
- * - Full-screen modal with form
15029
- * - Title and description inputs
15030
- * - Photo upload with camera/gallery
15031
- * - Form validation
15032
- * - Submit handling
15033
- * - Loading and error states
15034
- *
15035
- * @example
15036
- * ```typescript
15037
- * constructor(private inquiryModal: DsMobileNewInquiryModalService) {}
15038
- *
15039
- * async createNewInquiry() {
15040
- * await this.inquiryModal.open({
15041
- * onSubmit: async (data) => {
15042
- * console.log('Creating inquiry:', data);
15043
- * // Call your API to create the inquiry
15044
- * await this.apiService.createInquiry(data);
15045
- * // Close the modal
15046
- * await this.inquiryModal.close();
15047
- * }
15048
- * });
15049
- * }
15050
- * ```
15051
- */
15052
- class DsMobileNewInquiryModalService extends BaseModalService {
15053
- constructor(modalController) {
15054
- super(modalController);
15055
- }
15056
- /**
15057
- * Open the new inquiry modal
15058
- *
15059
- * @param options Modal options including onSubmit callback
15060
- * @returns Promise that resolves when the modal is presented
15061
- */
15062
- async open(options) {
15063
- // console.log('[NewInquiryModal] Opening modal with options:', options);
15064
- const modal = await this.createModal(DsMobileNewInquiryModalComponent, {
15065
- onSubmit: options?.onSubmit,
15066
- loading: options?.loading ?? false,
15067
- error: options?.error,
15068
- }, {
15069
- keyboardClose: false, // Don't close on keyboard hide for this modal
15070
- });
15071
- // console.log('[NewInquiryModal] Modal created, presenting...');
15072
- await modal.present();
15073
- // console.log('[NewInquiryModal] Modal presented');
15074
- }
15075
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Injectable });
15076
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, providedIn: 'root' });
15077
- }
15078
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, decorators: [{
15079
- type: Injectable,
15080
- args: [{
15081
- providedIn: 'root',
15082
- }]
15083
- }], ctorParameters: () => [{ type: i1.ModalController }] });
15084
-
15085
- /**
15086
- * DsMobileHandbookFolderMiniComponent
15087
- *
15088
- * A minimized folder icon component for use in headers and small spaces.
15089
- * Simplified version without animations or page sheets - just folder and icon.
15090
- *
15091
- * @example
15092
- * ```html
15093
- * <ds-mobile-handbook-folder-mini
15094
- * [variant]="'pink'"
15095
- * [iconName]="'remixLightbulbLine'">
15096
- * </ds-mobile-handbook-folder-mini>
15097
- * ```
15098
- */
15099
- class DsMobileHandbookFolderMiniComponent {
15100
- /**
15101
- * Color variant for the folder
15102
- * Available variants: success, warning, destructive, blue, light-purple, pink, salmon-orange, orange, lime-green, grey
15103
- */
15104
- variant = 'light-purple';
15105
- /**
15106
- * Icon name from the design system icon library
15107
- */
15108
- iconName = 'remixFolder3Line';
15109
- /**
15110
- * Get the CSS variable name for the color variant
15111
- */
15112
- getColorVar(suffix) {
15113
- const variantMap = {
15114
- 'success': 'success',
15115
- 'warning': 'warning',
15116
- 'destructive': 'destructive',
15117
- 'blue': 'blue',
15118
- 'light-purple': 'light-purple',
15119
- 'pink': 'pink',
15120
- 'salmon-orange': 'salmon-orange',
15121
- 'orange': 'orange',
15122
- 'lime-green': 'lime-green',
15123
- 'grey': 'grey'
15124
- };
15125
- const colorName = variantMap[this.variant] || 'light-purple';
15126
- return `var(--color-${colorName}-${suffix})`;
15127
- }
15128
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileHandbookFolderMiniComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15129
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DsMobileHandbookFolderMiniComponent, isStandalone: true, selector: "ds-mobile-handbook-folder-mini", inputs: { variant: "variant", iconName: "iconName" }, ngImport: i0, template: `
15130
- <div class="mini-folder-container">
15131
- <!-- Folder Tab SVG -->
15132
- <svg
15133
- class="mini-folder-tab"
15134
- width="101"
15135
- height="24"
15136
- viewBox="0 0 101 24"
15137
- fill="none"
15138
- xmlns="http://www.w3.org/2000/svg">
15139
- <path
15140
- d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
15141
- [attr.fill]="getColorVar('strong')"/>
15142
- </svg>
15143
-
15144
- <!-- Folder Back -->
15145
- <div class="mini-folder-back" [style.background-color]="getColorVar('strong')">
15146
- <!-- Folder Front -->
15147
- <div
15148
- class="mini-folder-front"
15149
- [style.background-color]="getColorVar('base')">
15150
- <ds-icon
15151
- [name]="iconName"
15152
- [size]="'14px'"
15153
- [style.color]="'white'" />
15154
- </div>
15155
- </div>
15156
- </div>
15157
- `, isInline: true, styles: [":host{display:inline-block;width:32px;height:32px;flex-shrink:0}.mini-folder-container{position:relative;width:100%;height:100%;display:flex;flex-direction:column}.mini-folder-tab{width:50%;height:auto;display:block}.mini-folder-back{height:28px;border-radius:0 4px 4px;position:relative;margin-top:-1px}.mini-folder-front{position:absolute;bottom:0;left:0;right:0;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;z-index:2;box-shadow:inset 0 8px 8px #fff3,inset 0 .5px .5px #ffffff4d}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
15158
- }
15159
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileHandbookFolderMiniComponent, decorators: [{
15160
- type: Component,
15161
- args: [{ selector: 'ds-mobile-handbook-folder-mini', standalone: true, imports: [CommonModule, DsIconComponent], template: `
15162
- <div class="mini-folder-container">
15163
- <!-- Folder Tab SVG -->
15164
- <svg
15165
- class="mini-folder-tab"
15166
- width="101"
15167
- height="24"
15168
- viewBox="0 0 101 24"
15169
- fill="none"
15170
- xmlns="http://www.w3.org/2000/svg">
15171
- <path
15172
- d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
15173
- [attr.fill]="getColorVar('strong')"/>
15174
- </svg>
15175
-
15176
- <!-- Folder Back -->
15177
- <div class="mini-folder-back" [style.background-color]="getColorVar('strong')">
15178
- <!-- Folder Front -->
15179
- <div
15180
- class="mini-folder-front"
15181
- [style.background-color]="getColorVar('base')">
15182
- <ds-icon
15183
- [name]="iconName"
15184
- [size]="'14px'"
15185
- [style.color]="'white'" />
15161
+ (valueChange)="validateForm()"
15162
+ />
15163
+ </ds-mobile-section>
15164
+
15165
+ <!-- Fixed Bottom Container (Slides with keyboard) -->
15166
+ <div footer class="fixed-bottom-container">
15167
+ <!-- Attachment Previews (if any) -->
15168
+ @if (attachments().length > 0) {
15169
+ <div class="attachment-previews-section">
15170
+ <div class="image-previews">
15171
+ @for (attachment of attachments(); track attachment.id) {
15172
+ <ds-mobile-attachment-preview
15173
+ [attachment]="attachment"
15174
+ (remove)="removeAttachment($event)"
15175
+ />
15176
+ }
15177
+ </div>
15178
+ </div>
15179
+ }
15180
+
15181
+ <!-- Submit Actions Container -->
15182
+ <div class="submit-container">
15183
+ <div class="submit-content">
15184
+ <!-- Upload Actions (Left) -->
15185
+ <div class="upload-actions">
15186
+ <ds-icon-button
15187
+ icon="remixImageLine"
15188
+ variant="secondary"
15189
+ size="md"
15190
+ (clicked)="addPhoto()"
15191
+ [disabled]="attachments().length >= 6"
15192
+ aria-label="Add photo">
15193
+ </ds-icon-button>
15194
+ <ds-icon-button
15195
+ icon="remixAttachmentLine"
15196
+ variant="secondary"
15197
+ size="md"
15198
+ (clicked)="handleAddAttachment()"
15199
+ [disabled]="attachments().length >= 6"
15200
+ aria-label="Add attachment">
15201
+ </ds-icon-button>
15202
+
15203
+ <!-- Hidden file input for file selection -->
15204
+ <input
15205
+ #fileInput
15206
+ type="file"
15207
+ accept="*/*"
15208
+ multiple
15209
+ (change)="handleFileSelect($event)"
15210
+ style="display: none;"
15211
+ aria-hidden="true"
15212
+ />
15213
+ </div>
15214
+
15215
+ <!-- Submit Button (Right) -->
15216
+ <ds-button
15217
+ variant="primary"
15218
+ size="lg"
15219
+ [disabled]="!isFormValid() || isSubmitting()"
15220
+ (clicked)="handleSubmit()">
15221
+ Submit
15222
+ </ds-button>
15223
+ </div>
15186
15224
  </div>
15187
15225
  </div>
15188
- </div>
15189
- `, styles: [":host{display:inline-block;width:32px;height:32px;flex-shrink:0}.mini-folder-container{position:relative;width:100%;height:100%;display:flex;flex-direction:column}.mini-folder-tab{width:50%;height:auto;display:block}.mini-folder-back{height:28px;border-radius:0 4px 4px;position:relative;margin-top:-1px}.mini-folder-front{position:absolute;bottom:0;left:0;right:0;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;z-index:2;box-shadow:inset 0 8px 8px #fff3,inset 0 .5px .5px #ffffff4d}\n"] }]
15190
- }], propDecorators: { variant: [{
15226
+ </ds-mobile-modal-base>
15227
+ `, styles: [".author-details{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.author-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.author-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373);display:flex;align-items:center;gap:6px}.author-meta .separator{color:var(--color-text-tertiary, #a0a0a0)}.lightbox-context .author-name,.overlay-context .author-name{color:#fffffff2}.lightbox-context .author-meta,.overlay-context .author-meta{color:#ffffffb3}.lightbox-context .author-meta .separator,.overlay-context .author-meta .separator{color:#ffffff80}.section-headline{font-size:var(--font-size-base);font-weight:600;color:var(--text-color-default-primary);padding:16px 0;margin:0;letter-spacing:-.2px;display:flex;align-items:center;gap:6px}.empty-state-title{font-family:Brockmann,sans-serif;font-size:var(--font-size-base);font-weight:600;line-height:1.3;color:var(--text-color-default-primary, #202227);margin:0 0 8px}.empty-state-description{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--text-color-default-secondary, #545B66);margin:0}.ghost-input-clean ::ng-deep .ds-input,.ghost-input-clean ::ng-deep .ds-textarea,.ghost-input-clean ::ng-deep .textarea-container{outline:none!important;border:none!important}.ghost-input-clean ::ng-deep .ds-input:hover,.ghost-input-clean ::ng-deep .ds-textarea:hover,.ghost-input-clean ::ng-deep .textarea-container:hover,.ghost-input-clean ::ng-deep .ds-input:focus,.ghost-input-clean ::ng-deep .ds-textarea:focus,.ghost-input-clean ::ng-deep .textarea-container:focus,.ghost-input-clean ::ng-deep .ds-input:focus-within,.ghost-input-clean ::ng-deep .ds-textarea:focus-within,.ghost-input-clean ::ng-deep .textarea-container:focus-within{outline:none!important;border:none!important;box-shadow:none!important}.ghost-input-clean ::ng-deep textarea{outline:none!important;border:none!important;box-shadow:none!important;resize:none!important}.ghost-input-clean ::ng-deep textarea:hover,.ghost-input-clean ::ng-deep textarea:focus{outline:none!important;border:none!important;box-shadow:none!important}\n", ".inquiry-title-input{flex:0 0 auto}.inquiry-title-input ::ng-deep textarea{font-size:var(--font-size-2xl);font-weight:400;line-height:1.2;max-height:200px;overflow-y:auto;resize:none;box-sizing:border-box;padding-right:52px}.fixed-bottom-container{background:var(--color-background-neutral-primary);border-top:1px solid var(--border-color-default)}.attachment-previews-section{padding:16px 20px;border-bottom:1px solid var(--border-color-default)}.image-previews{display:flex;flex-wrap:wrap;gap:8px}.submit-container{padding:16px 20px}.submit-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.upload-actions{display:flex;align-items:center;gap:8px}.upload-actions ds-icon-button::ng-deep button{width:44px;height:44px;border-radius:50%}.submit-content ds-button::ng-deep button{border-radius:100px}.inquiry-description-input{display:flex;flex:1;min-height:0}.inquiry-description-input ::ng-deep .textarea-container{display:flex;flex:1;min-height:0}.inquiry-description-input ::ng-deep textarea{flex:1;min-height:200px;resize:none}@media (min-width: 768px){.submit-container{padding:20px 32px}}\n"] }]
15228
+ }], propDecorators: { titleInputRef: [{
15229
+ type: ViewChild,
15230
+ args: ['titleInput', { read: ElementRef }]
15231
+ }], titleInput: [{
15232
+ type: ViewChild,
15233
+ args: ['titleInput']
15234
+ }], fileInput: [{
15235
+ type: ViewChild,
15236
+ args: ['fileInput']
15237
+ }], loading: [{
15191
15238
  type: Input
15192
- }], iconName: [{
15239
+ }], error: [{
15240
+ type: Input
15241
+ }], onSubmit: [{
15193
15242
  type: Input
15194
15243
  }] } });
15195
15244
 
15196
15245
  /**
15197
- * DsMobileSwiperComponent
15246
+ * DsMobileNewInquiryModalService
15198
15247
  *
15199
- * A reusable swiper/carousel component with configurable child width and spacing.
15248
+ * Service for displaying the new inquiry creation modal.
15249
+ * Built on Ionic's modal system with native gestures and animations.
15200
15250
  *
15201
15251
  * Features:
15202
- * - First slide is left-aligned
15203
- * - Middle slides are centered when active
15204
- * - Last slide is right-aligned
15205
- * - Configurable slide width and gap
15206
- * - Content projection via ng-content
15252
+ * - Full-screen modal with form
15253
+ * - Title and description inputs
15254
+ * - Photo upload with camera/gallery
15255
+ * - Form validation
15256
+ * - Submit handling
15257
+ * - Loading and error states
15207
15258
  *
15208
- * Usage:
15209
- * ```html
15210
- * <ds-mobile-swiper [slideWidth]="'75vw'" [gap]="16">
15211
- * <div class="swiper-slide">Slide 1</div>
15212
- * <div class="swiper-slide">Slide 2</div>
15213
- * <div class="swiper-slide">Slide 3</div>
15214
- * </ds-mobile-swiper>
15215
- * ```
15216
- */
15217
- class DsMobileSwiperComponent {
15218
- elementRef;
15219
- /**
15220
- * Width of each slide (e.g., '75vw', '300px', '80%')
15221
- */
15222
- slideWidth = input('75vw', ...(ngDevMode ? [{ debugName: "slideWidth" }] : []));
15223
- /**
15224
- * Gap between slides in pixels
15225
- */
15226
- gap = input(16, ...(ngDevMode ? [{ debugName: "gap" }] : []));
15227
- /**
15228
- * Enable pagination dots
15229
- */
15230
- pagination = input(false, ...(ngDevMode ? [{ debugName: "pagination" }] : []));
15231
- /**
15232
- * Enable auto height - container adapts to active slide's height
15233
- */
15234
- autoHeight = input(false, ...(ngDevMode ? [{ debugName: "autoHeight" }] : []));
15235
- /**
15236
- * Enable progressive opacity based on slide position
15237
- * Slides fade in/out smoothly as they move toward/away from center
15238
- */
15239
- progressiveOpacity = input(false, ...(ngDevMode ? [{ debugName: "progressiveOpacity" }] : []));
15240
- /**
15241
- * Enable progressive scale based on slide position
15242
- * Slides scale down smoothly as they move away from center
15243
- */
15244
- progressiveScale = input(false, ...(ngDevMode ? [{ debugName: "progressiveScale" }] : []));
15245
- swiperContainer;
15246
- swiperInstance = null;
15247
- constructor(elementRef) {
15248
- this.elementRef = elementRef;
15249
- }
15250
- ngAfterViewInit() {
15251
- // Add progressive-opacity class to host if enabled
15252
- if (this.progressiveOpacity()) {
15253
- this.elementRef.nativeElement.classList.add('progressive-opacity');
15254
- }
15255
- setTimeout(() => {
15256
- this.initializeSwiper();
15257
- }, 100);
15258
- }
15259
- initializeSwiper() {
15260
- if (!this.swiperContainer)
15261
- return;
15262
- // Apply slide width to all slides
15263
- const slides = this.swiperContainer.nativeElement.querySelectorAll('.swiper-slide');
15264
- slides.forEach((slide, index) => {
15265
- slide.style.width = this.slideWidth();
15266
- // Set initial opacity BEFORE Swiper initializes to prevent flash of inactive slides
15267
- if (this.progressiveOpacity()) {
15268
- // Hide all slides except the first one (active slide)
15269
- slide.style.opacity = index === 0 ? '1' : '0';
15270
- }
15271
- });
15272
- const config = {
15273
- slidesPerView: 'auto',
15274
- spaceBetween: this.gap(),
15275
- centeredSlides: true,
15276
- centeredSlidesBounds: true,
15277
- speed: 300,
15278
- resistance: true,
15279
- resistanceRatio: 0.85,
15280
- autoHeight: this.autoHeight(),
15281
- watchSlidesProgress: this.progressiveOpacity() || this.progressiveScale(), // Enable progress tracking
15282
- };
15283
- // Configure event handlers
15284
- config.on = {};
15285
- // Configure autoHeight animation
15286
- if (this.autoHeight()) {
15287
- config.autoHeight = true;
15288
- // The height transition will use the speed value (300ms)
15289
- config.on.slideChangeTransitionStart = () => {
15290
- // Height transition happens automatically
15291
- };
15292
- }
15293
- // Configure progressive effects (opacity and/or scale)
15294
- if (this.progressiveOpacity() || this.progressiveScale()) {
15295
- config.on.setTranslate = () => {
15296
- if (!this.swiperInstance)
15297
- return;
15298
- this.swiperInstance.slides.forEach((slideEl) => {
15299
- const progress = slideEl.progress || 0;
15300
- const absProgress = Math.abs(progress);
15301
- // Progressive opacity with sharper cutoff
15302
- if (this.progressiveOpacity()) {
15303
- // Make opacity drop off more aggressively
15304
- // Slides with absProgress > 0.5 will be completely hidden
15305
- let opacity;
15306
- if (absProgress > 0.5) {
15307
- opacity = 0;
15308
- }
15309
- else {
15310
- opacity = 1 - (absProgress * 2); // 2x multiplier for faster fade
15311
- }
15312
- slideEl.style.opacity = Math.max(opacity, 0).toString();
15313
- }
15314
- // Progressive scale
15315
- if (this.progressiveScale()) {
15316
- // Scale from 1 (center) to 0.9 (edges)
15317
- const minScale = 0.9;
15318
- const scale = 1 - (absProgress * (1 - minScale));
15319
- slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
15320
- }
15321
- });
15322
- };
15323
- // Also update on init
15324
- config.on.init = () => {
15325
- if (!this.swiperInstance)
15326
- return;
15327
- this.swiperInstance.slides.forEach((slideEl) => {
15328
- const progress = slideEl.progress || 0;
15329
- const absProgress = Math.abs(progress);
15330
- // Set initial opacity with sharper cutoff
15331
- if (this.progressiveOpacity()) {
15332
- let opacity;
15333
- if (absProgress > 0.5) {
15334
- opacity = 0;
15335
- }
15336
- else {
15337
- opacity = 1 - (absProgress * 2);
15338
- }
15339
- slideEl.style.opacity = Math.max(opacity, 0).toString();
15340
- }
15341
- // Set initial scale
15342
- if (this.progressiveScale()) {
15343
- const minScale = 0.9;
15344
- const scale = 1 - (absProgress * (1 - minScale));
15345
- slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
15346
- }
15347
- });
15348
- };
15349
- }
15350
- // Add pagination if enabled
15351
- if (this.pagination()) {
15352
- config.modules = [Pagination];
15353
- config.pagination = {
15354
- el: '.swiper-pagination',
15355
- clickable: true,
15356
- dynamicBullets: false,
15357
- };
15358
- }
15359
- this.swiperInstance = new Swiper(this.swiperContainer.nativeElement, config);
15259
+ * @example
15260
+ * ```typescript
15261
+ * constructor(private inquiryModal: DsMobileNewInquiryModalService) {}
15262
+ *
15263
+ * async createNewInquiry() {
15264
+ * await this.inquiryModal.open({
15265
+ * onSubmit: async (data) => {
15266
+ * console.log('Creating inquiry:', data);
15267
+ * // Call your API to create the inquiry
15268
+ * await this.apiService.createInquiry(data);
15269
+ * // Close the modal
15270
+ * await this.inquiryModal.close();
15271
+ * }
15272
+ * });
15273
+ * }
15274
+ * ```
15275
+ */
15276
+ class DsMobileNewInquiryModalService extends BaseModalService {
15277
+ constructor(modalController) {
15278
+ super(modalController);
15360
15279
  }
15361
15280
  /**
15362
- * Navigate to previous slide
15281
+ * Open the new inquiry modal
15282
+ *
15283
+ * @param options Modal options including onSubmit callback
15284
+ * @returns Promise that resolves when the modal is presented
15363
15285
  */
15364
- slidePrev() {
15365
- this.swiperInstance?.slidePrev();
15286
+ async open(options) {
15287
+ // console.log('[NewInquiryModal] Opening modal with options:', options);
15288
+ const modal = await this.createModal(DsMobileNewInquiryModalComponent, {
15289
+ onSubmit: options?.onSubmit,
15290
+ loading: options?.loading ?? false,
15291
+ error: options?.error,
15292
+ }, {
15293
+ keyboardClose: false, // Don't close on keyboard hide for this modal
15294
+ });
15295
+ // console.log('[NewInquiryModal] Modal created, presenting...');
15296
+ await modal.present();
15297
+ // console.log('[NewInquiryModal] Modal presented');
15366
15298
  }
15299
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, deps: [{ token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Injectable });
15300
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, providedIn: 'root' });
15301
+ }
15302
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileNewInquiryModalService, decorators: [{
15303
+ type: Injectable,
15304
+ args: [{
15305
+ providedIn: 'root',
15306
+ }]
15307
+ }], ctorParameters: () => [{ type: i1.ModalController }] });
15308
+
15309
+ /**
15310
+ * DsMobileHandbookFolderMiniComponent
15311
+ *
15312
+ * A minimized folder icon component for use in headers and small spaces.
15313
+ * Simplified version without animations or page sheets - just folder and icon.
15314
+ *
15315
+ * @example
15316
+ * ```html
15317
+ * <ds-mobile-handbook-folder-mini
15318
+ * [variant]="'pink'"
15319
+ * [iconName]="'remixLightbulbLine'">
15320
+ * </ds-mobile-handbook-folder-mini>
15321
+ * ```
15322
+ */
15323
+ class DsMobileHandbookFolderMiniComponent {
15367
15324
  /**
15368
- * Navigate to next slide
15325
+ * Color variant for the folder
15326
+ * Available variants: success, warning, destructive, blue, light-purple, pink, salmon-orange, orange, lime-green, grey
15369
15327
  */
15370
- slideNext() {
15371
- this.swiperInstance?.slideNext();
15372
- }
15328
+ variant = 'light-purple';
15373
15329
  /**
15374
- * Check if at the beginning
15330
+ * Icon name from the design system icon library
15375
15331
  */
15376
- isBeginning() {
15377
- return this.swiperInstance?.isBeginning ?? true;
15378
- }
15332
+ iconName = 'remixFolder3Line';
15379
15333
  /**
15380
- * Check if at the end
15334
+ * Get the CSS variable name for the color variant
15381
15335
  */
15382
- isEnd() {
15383
- return this.swiperInstance?.isEnd ?? true;
15384
- }
15385
- ngOnDestroy() {
15386
- if (this.swiperInstance) {
15387
- this.swiperInstance.destroy();
15388
- this.swiperInstance = null;
15389
- }
15336
+ getColorVar(suffix) {
15337
+ const variantMap = {
15338
+ 'success': 'success',
15339
+ 'warning': 'warning',
15340
+ 'destructive': 'destructive',
15341
+ 'blue': 'blue',
15342
+ 'light-purple': 'light-purple',
15343
+ 'pink': 'pink',
15344
+ 'salmon-orange': 'salmon-orange',
15345
+ 'orange': 'orange',
15346
+ 'lime-green': 'lime-green',
15347
+ 'grey': 'grey'
15348
+ };
15349
+ const colorName = variantMap[this.variant] || 'light-purple';
15350
+ return `var(--color-${colorName}-${suffix})`;
15390
15351
  }
15391
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSwiperComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
15392
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileSwiperComponent, isStandalone: true, selector: "ds-mobile-swiper", inputs: { slideWidth: { classPropertyName: "slideWidth", publicName: "slideWidth", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, autoHeight: { classPropertyName: "autoHeight", publicName: "autoHeight", isSignal: true, isRequired: false, transformFunction: null }, progressiveOpacity: { classPropertyName: "progressiveOpacity", publicName: "progressiveOpacity", isSignal: true, isRequired: false, transformFunction: null }, progressiveScale: { classPropertyName: "progressiveScale", publicName: "progressiveScale", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "swiperContainer", first: true, predicate: ["swiperContainer"], descendants: true }], ngImport: i0, template: `
15393
- <div class="swiper-container" #swiperContainer>
15394
- <div class="swiper-wrapper">
15395
- <ng-content></ng-content>
15352
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileHandbookFolderMiniComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15353
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DsMobileHandbookFolderMiniComponent, isStandalone: true, selector: "ds-mobile-handbook-folder-mini", inputs: { variant: "variant", iconName: "iconName" }, ngImport: i0, template: `
15354
+ <div class="mini-folder-container">
15355
+ <!-- Folder Tab SVG -->
15356
+ <svg
15357
+ class="mini-folder-tab"
15358
+ width="101"
15359
+ height="24"
15360
+ viewBox="0 0 101 24"
15361
+ fill="none"
15362
+ xmlns="http://www.w3.org/2000/svg">
15363
+ <path
15364
+ d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
15365
+ [attr.fill]="getColorVar('strong')"/>
15366
+ </svg>
15367
+
15368
+ <!-- Folder Back -->
15369
+ <div class="mini-folder-back" [style.background-color]="getColorVar('strong')">
15370
+ <!-- Folder Front -->
15371
+ <div
15372
+ class="mini-folder-front"
15373
+ [style.background-color]="getColorVar('base')">
15374
+ <ds-icon
15375
+ [name]="iconName"
15376
+ [size]="'14px'"
15377
+ [style.color]="'white'" />
15378
+ </div>
15396
15379
  </div>
15397
- @if (pagination()) {
15398
- <div class="swiper-pagination"></div>
15399
- }
15400
15380
  </div>
15401
- `, isInline: true, styles: [":host{display:block;width:100%}.swiper-container{width:100%;position:relative;overflow:visible}.swiper-wrapper{display:flex;box-sizing:content-box}:host ::ng-deep .swiper-slide{flex-shrink:0;height:auto;position:relative;display:flex;align-items:flex-start;justify-content:center;transition:opacity .3s ease,transform .3s ease}:host(.progressive-opacity) ::ng-deep .swiper-slide{opacity:0}:host(.progressive-opacity) ::ng-deep .swiper-slide:first-child{opacity:1}.swiper-pagination{position:relative;text-align:center;display:flex;justify-content:center;margin-top:20px;margin-bottom:-16px}:host ::ng-deep .swiper-pagination-bullet{width:6px;height:6px;border-radius:50%;background:var(--color-accent);opacity:.25;transition:all .3s ease;cursor:pointer}:host ::ng-deep .swiper-pagination-bullet-active{opacity:1;background:var(--color-accent);width:20px;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
15381
+ `, isInline: true, styles: [":host{display:inline-block;width:32px;height:32px;flex-shrink:0}.mini-folder-container{position:relative;width:100%;height:100%;display:flex;flex-direction:column}.mini-folder-tab{width:50%;height:auto;display:block}.mini-folder-back{height:28px;border-radius:0 4px 4px;position:relative;margin-top:-1px}.mini-folder-front{position:absolute;bottom:0;left:0;right:0;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;z-index:2;box-shadow:inset 0 8px 8px #fff3,inset 0 .5px .5px #ffffff4d}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
15402
15382
  }
15403
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileSwiperComponent, decorators: [{
15383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileHandbookFolderMiniComponent, decorators: [{
15404
15384
  type: Component,
15405
- args: [{ selector: 'ds-mobile-swiper', standalone: true, imports: [CommonModule], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `
15406
- <div class="swiper-container" #swiperContainer>
15407
- <div class="swiper-wrapper">
15408
- <ng-content></ng-content>
15385
+ args: [{ selector: 'ds-mobile-handbook-folder-mini', standalone: true, imports: [CommonModule, DsIconComponent], template: `
15386
+ <div class="mini-folder-container">
15387
+ <!-- Folder Tab SVG -->
15388
+ <svg
15389
+ class="mini-folder-tab"
15390
+ width="101"
15391
+ height="24"
15392
+ viewBox="0 0 101 24"
15393
+ fill="none"
15394
+ xmlns="http://www.w3.org/2000/svg">
15395
+ <path
15396
+ d="M100.037 23.9999L100.5 24L0 24.0001V10.7646C0 4.80853 4.91797 -0.0234985 11 -0.0196688L66.4213 -0.0322266C69.3519 -0.0115886 72.197 1.20548 74.2473 3.29947L90.6765 20.0951C93.1218 22.5925 96.5417 23.9999 100.037 23.9999Z"
15397
+ [attr.fill]="getColorVar('strong')"/>
15398
+ </svg>
15399
+
15400
+ <!-- Folder Back -->
15401
+ <div class="mini-folder-back" [style.background-color]="getColorVar('strong')">
15402
+ <!-- Folder Front -->
15403
+ <div
15404
+ class="mini-folder-front"
15405
+ [style.background-color]="getColorVar('base')">
15406
+ <ds-icon
15407
+ [name]="iconName"
15408
+ [size]="'14px'"
15409
+ [style.color]="'white'" />
15410
+ </div>
15409
15411
  </div>
15410
- @if (pagination()) {
15411
- <div class="swiper-pagination"></div>
15412
- }
15413
15412
  </div>
15414
- `, styles: [":host{display:block;width:100%}.swiper-container{width:100%;position:relative;overflow:visible}.swiper-wrapper{display:flex;box-sizing:content-box}:host ::ng-deep .swiper-slide{flex-shrink:0;height:auto;position:relative;display:flex;align-items:flex-start;justify-content:center;transition:opacity .3s ease,transform .3s ease}:host(.progressive-opacity) ::ng-deep .swiper-slide{opacity:0}:host(.progressive-opacity) ::ng-deep .swiper-slide:first-child{opacity:1}.swiper-pagination{position:relative;text-align:center;display:flex;justify-content:center;margin-top:20px;margin-bottom:-16px}:host ::ng-deep .swiper-pagination-bullet{width:6px;height:6px;border-radius:50%;background:var(--color-accent);opacity:.25;transition:all .3s ease;cursor:pointer}:host ::ng-deep .swiper-pagination-bullet-active{opacity:1;background:var(--color-accent);width:20px;border-radius:3px}\n"] }]
15415
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { slideWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideWidth", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }], autoHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHeight", required: false }] }], progressiveOpacity: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressiveOpacity", required: false }] }], progressiveScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressiveScale", required: false }] }], swiperContainer: [{
15416
- type: ViewChild,
15417
- args: ['swiperContainer', { static: false }]
15413
+ `, styles: [":host{display:inline-block;width:32px;height:32px;flex-shrink:0}.mini-folder-container{position:relative;width:100%;height:100%;display:flex;flex-direction:column}.mini-folder-tab{width:50%;height:auto;display:block}.mini-folder-back{height:28px;border-radius:0 4px 4px;position:relative;margin-top:-1px}.mini-folder-front{position:absolute;bottom:0;left:0;right:0;height:24px;border-radius:4px;display:flex;align-items:center;justify-content:center;z-index:2;box-shadow:inset 0 8px 8px #fff3,inset 0 .5px .5px #ffffff4d}\n"] }]
15414
+ }], propDecorators: { variant: [{
15415
+ type: Input
15416
+ }], iconName: [{
15417
+ type: Input
15418
15418
  }] } });
15419
15419
 
15420
15420
  /**
@@ -16351,6 +16351,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
16351
16351
  `, styles: [":host{display:block;width:100%}.text-input-wrapper{position:relative;width:100%}.text-input{width:100%;height:48px;padding:0 16px;box-sizing:border-box;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-size:var(--font-size-base);font-weight:400;line-height:1.4;color:var(--text-color-default-primary);background-color:var(--color-background-neutral-primary);border:1px solid var(--border-color-default);border-radius:8px;transition:border-color var(--transition-duration-fast) var(--ease-smooth),background-color var(--transition-duration-fast) var(--ease-smooth),box-shadow var(--transition-duration-fast) var(--ease-smooth);outline:none;-webkit-appearance:none;appearance:none}.text-input::placeholder{color:var(--text-color-default-tertiary)}.text-input:hover:not(:disabled):not(:focus){border-color:var(--border-color-default);background-color:var(--color-background-neutral-primary-hover)}.text-input:focus{border-color:var(--color-accent);background-color:var(--color-background-neutral-primary);box-shadow:0 0 0 3px var(--outline-color-default)}.text-input.error{border-color:var(--color-destructive-base)}.text-input.error:focus{border-color:var(--color-destructive-base);box-shadow:0 0 0 3px #dc26261a}.text-input:disabled{background-color:var(--color-background-neutral-disabled);border-color:var(--border-color-default);color:var(--text-color-default-disabled);cursor:not-allowed}.text-input:disabled::placeholder{color:var(--text-color-default-disabled)}.error-message{margin-top:8px;font-family:Brockmann,system-ui,-apple-system,sans-serif;font-size:var(--font-size-sm);font-weight:400;line-height:1.4;color:var(--color-destructive-base)}\n"] }]
16352
16352
  }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], hasError: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasError", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], inputmode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputmode", required: false }] }], autoClearError: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoClearError", required: false }] }], validator: [{ type: i0.Input, args: [{ isSignal: true, alias: "validator", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], blur: [{ type: i0.Output, args: ["blur"] }], focus: [{ type: i0.Output, args: ["focus"] }], errorCleared: [{ type: i0.Output, args: ["errorCleared"] }] } });
16353
16353
 
16354
+ /**
16355
+ * DsMobileFileAttachmentComponent
16356
+ *
16357
+ * File attachment display for various document types.
16358
+ * Shows file info card with icon, filename, and file size.
16359
+ * Supports PDF and generic document formats.
16360
+ * Emits click event to open file in viewer.
16361
+ *
16362
+ * @example
16363
+ * ```html
16364
+ * <ds-mobile-file-attachment
16365
+ * [fileName]="'Document.pdf'"
16366
+ * [fileSize]="'1.2 MB'"
16367
+ * [variant]="'pdf'"
16368
+ * (fileClick)="openFile()">
16369
+ * </ds-mobile-file-attachment>
16370
+ * ```
16371
+ */
16372
+ class DsMobileFileAttachmentComponent {
16373
+ /**
16374
+ * File name
16375
+ */
16376
+ fileName = input('Document', ...(ngDevMode ? [{ debugName: "fileName" }] : []));
16377
+ /**
16378
+ * File size display (e.g., "1.2 MB")
16379
+ */
16380
+ fileSize = input('', ...(ngDevMode ? [{ debugName: "fileSize" }] : []));
16381
+ /**
16382
+ * File type variant
16383
+ * - 'pdf' - PDF document (red icon)
16384
+ * - 'doc' - Generic document (blue icon)
16385
+ */
16386
+ variant = input('doc', ...(ngDevMode ? [{ debugName: "variant" }] : []));
16387
+ /**
16388
+ * Emits when the file attachment is clicked
16389
+ */
16390
+ fileClick = output();
16391
+ /**
16392
+ * Get the appropriate icon name based on variant
16393
+ */
16394
+ getIconName() {
16395
+ return this.variant() === 'pdf' ? 'remixFileTextLine' : 'remixAttachmentLine';
16396
+ }
16397
+ /**
16398
+ * Get the file type label based on variant
16399
+ */
16400
+ getFileTypeLabel() {
16401
+ return this.variant() === 'pdf' ? 'PDF' : 'DOC';
16402
+ }
16403
+ handleClick(event) {
16404
+ event.stopPropagation();
16405
+ this.fileClick.emit();
16406
+ }
16407
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileFileAttachmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16408
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DsMobileFileAttachmentComponent, isStandalone: true, selector: "ds-mobile-file-attachment", inputs: { fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, fileSize: { classPropertyName: "fileSize", publicName: "fileSize", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fileClick: "fileClick" }, host: { listeners: { "click": "handleClick($event)" } }, ngImport: i0, template: `
16409
+ <div class="file-avatar" [class.pdf]="variant() === 'pdf'" [class.doc]="variant() === 'doc'">
16410
+ <ds-avatar
16411
+ type="icon"
16412
+ [iconName]="getIconName()"
16413
+ size="lg"
16414
+ />
16415
+ </div>
16416
+
16417
+ <div class="file-info">
16418
+ <div class="file-name">{{ fileName() }}</div>
16419
+ @if (fileSize()) {
16420
+ <div class="file-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
16421
+ } @else {
16422
+ <div class="file-meta">{{ getFileTypeLabel() }}</div>
16423
+ }
16424
+ </div>
16425
+
16426
+ <ds-icon
16427
+ name="remixArrowRightSLine"
16428
+ size="20px"
16429
+ class="open-icon"
16430
+ />
16431
+ `, isInline: true, styles: [":host{display:flex;align-items:center;gap:12px;padding:10px 12px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;cursor:pointer;transition:all .2s ease}:host:hover{background:var(--color-background-neutral-secondary-hover, #ebebeb)}:host:active{transform:scale(.98)}.file-avatar{flex-shrink:0}.file-avatar.pdf::ng-deep .avatar--icon{background-color:#ff5757!important}.file-avatar.doc::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3B82F6)!important}.file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.file-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373)}.open-icon{color:var(--color-text-tertiary, #a3a3a3);flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: DsAvatarComponent, selector: "ds-avatar", inputs: ["type", "size", "initials", "src", "alt", "iconName", "iconColor"] }, { kind: "component", type: DsIconComponent, selector: "ds-icon", inputs: ["name", "size", "color", "interactive"] }] });
16432
+ }
16433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DsMobileFileAttachmentComponent, decorators: [{
16434
+ type: Component,
16435
+ args: [{ selector: 'ds-mobile-file-attachment', standalone: true, imports: [CommonModule, DsAvatarComponent, DsIconComponent], host: {
16436
+ '(click)': 'handleClick($event)'
16437
+ }, template: `
16438
+ <div class="file-avatar" [class.pdf]="variant() === 'pdf'" [class.doc]="variant() === 'doc'">
16439
+ <ds-avatar
16440
+ type="icon"
16441
+ [iconName]="getIconName()"
16442
+ size="lg"
16443
+ />
16444
+ </div>
16445
+
16446
+ <div class="file-info">
16447
+ <div class="file-name">{{ fileName() }}</div>
16448
+ @if (fileSize()) {
16449
+ <div class="file-meta">{{ getFileTypeLabel() }} · {{ fileSize() }}</div>
16450
+ } @else {
16451
+ <div class="file-meta">{{ getFileTypeLabel() }}</div>
16452
+ }
16453
+ </div>
16454
+
16455
+ <ds-icon
16456
+ name="remixArrowRightSLine"
16457
+ size="20px"
16458
+ class="open-icon"
16459
+ />
16460
+ `, styles: [":host{display:flex;align-items:center;gap:12px;padding:10px 12px;background:var(--color-background-neutral-secondary, #f5f5f5);border-radius:16px;cursor:pointer;transition:all .2s ease}:host:hover{background:var(--color-background-neutral-secondary-hover, #ebebeb)}:host:active{transform:scale(.98)}.file-avatar{flex-shrink:0}.file-avatar.pdf::ng-deep .avatar--icon{background-color:#ff5757!important}.file-avatar.doc::ng-deep .avatar--icon{background-color:var(--color-blue-base, #3B82F6)!important}.file-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.file-name{font-family:Brockmann,sans-serif;font-size:var(--font-size-sm);font-weight:600;line-height:20px;letter-spacing:-.3px;color:var(--color-text-primary, #1a1a1a);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-meta{font-family:Brockmann,sans-serif;font-size:var(--font-size-xs);font-weight:400;line-height:1.2;letter-spacing:-.26px;color:var(--color-text-tertiary, #737373)}.open-icon{color:var(--color-text-tertiary, #a3a3a3);flex-shrink:0}\n"] }]
16461
+ }], propDecorators: { fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], fileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileSize", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], fileClick: [{ type: i0.Output, args: ["fileClick"] }] } });
16462
+
16354
16463
  /**
16355
16464
  * DsMobileFabComponent
16356
16465
  *
@@ -20606,5 +20715,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
20606
20715
  * Generated bundle index. Do not edit.
20607
20716
  */
20608
20717
 
20609
- export { ActionCommentComponent, ActionLikeComponent, ContentRowComponent, DsAppIconComponent, DsAvatarWithBadgeComponent, DsLogoComponent, DsMobileActionsBottomSheetComponent, DsMobileAttachmentPreviewComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileDropdownComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileIllustrationComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileLoaderOverlayComponent, DsMobileLongPressDirective, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobileOfflineBannerComponent, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileProfileActionsSheetComponent, DsMobilePropertyBannerComponent, DsMobileSectionComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsTextInputComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, SectionHeaderComponent, SignInPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, UserService, WhitelabelDemoModalComponent, WhitelabelDemoModalService, WhitelabelService, customBackTransition, customPageTransition };
20718
+ export { ActionCommentComponent, ActionLikeComponent, ContentRowComponent, DsAppIconComponent, DsAvatarWithBadgeComponent, DsLogoComponent, DsMobileActionListItemComponent, DsMobileActionsBottomSheetComponent, DsMobileAttachmentPreviewComponent, DsMobileBottomSheetService, DsMobileBottomSheetWrapperComponent, DsMobileCardInlineBannerComponent, DsMobileCardInlineComponent, DsMobileCardInlineContactComponent, DsMobileCardInlineFileComponent, DsMobileChatModalComponent, DsMobileChatModalService, DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent, DsMobileCommentComponent, DsMobileContactListItemComponent, DsMobileContentComponent, DsMobileDropdownComponent, DsMobileEmptyStateComponent, DsMobileFabComponent, DsMobileFileAttachmentComponent, DsMobileHandbookDetailModalComponent, DsMobileHandbookDetailModalService, DsMobileHandbookFolderComponent, DsMobileHandbookFolderMiniComponent, DsMobileHeaderContentComponent, DsMobileHeaderContentTileComponent, DsMobileIllustrationComponent, DsMobileInlinePhotoComponent, DsMobileInlineTabsComponent, DsMobileInteractiveListItemInquiryComponent, DsMobileInteractiveListItemMessageComponent, DsMobileInteractiveListItemPostComponent, DsMobileLightboxImageComponent as DsMobileLightboxComponent, DsMobileLightboxFooterComponent, DsMobileLightboxHeaderComponent, DsMobileLightboxImageComponent, DsMobileLightboxPdfComponent, DsMobileLightboxService, DsMobileListItemComponent, DsMobileListItemStaticComponent, DsMobileLoaderOverlayComponent, DsMobileLongPressDirective, DsMobileMessageBubbleComponent, DsMobileMessageComposerComponent, DsMobileModalBaseComponent, DsMobileModalService, DsMobileNewInquiryModalComponent, DsMobileNewInquiryModalService, DsMobileOfflineBannerComponent, DsMobilePageDetailsComponent, DsMobilePageMainComponent, DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent, DsMobilePostComposerComponent, DsMobilePostCreateBottomSheetComponent, DsMobilePostDetailModalComponent, DsMobilePostDetailModalService, DsMobileProfileActionsSheetComponent, DsMobilePropertyBannerComponent, DsMobileSectionComponent, DsMobileSwiperComponent, DsMobileSystemMessageBannerComponent, DsMobileTabBarComponent, DsMobileTabsComponent, DsTextInputComponent, MobileCommunityPageComponent, MobileHandbookPageComponent, MobileHomePageComponent, MobileInquiriesPageComponent, MobileInquiryDetailPageComponent, MobileModalBase, MobilePageBase, MobilePostDetailPageComponent, MobileTabsExampleComponent, PostActionsComponent, PostAttachmentsComponent, PostContentComponent, PostCreatePageComponent, PostMediaComponent, PostPdfAttachmentComponent, PostTextComponent, SectionHeaderComponent, SignInPageComponent, TileContentComponent, TileIconComponent, TileLabelComponent, TileValueComponent, UserService, WhitelabelDemoModalComponent, WhitelabelDemoModalService, WhitelabelService, customBackTransition, customPageTransition };
20610
20719
  //# sourceMappingURL=propbinder-mobile-design.mjs.map