{"version":3,"file":"ngwr-lightbox.mjs","sources":["../../../projects/lib/lightbox/lightbox.ts","../../../projects/lib/lightbox/lightbox.html","../../../projects/lib/lightbox/ngwr-lightbox.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { type OverlayRef } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport type { TemplateRef } from '@angular/core';\nimport {\n  Component,\n  DestroyRef,\n  ViewContainerRef,\n  ViewEncapsulation,\n  computed,\n  effect,\n  inject,\n  input,\n  signal,\n  viewChild,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { WR_OVERLAY } from 'ngwr/overlay';\n\n/**\n * Image with click-to-zoom lightbox. The thumbnail is a plain `<img>`\n * with whatever sizing/`object-fit` you give it; clicking opens a CDK\n * overlay showing the full image, with Escape + close button + backdrop\n * to dismiss.\n *\n * @example\n * ```html\n * <wr-lightbox\n *   src=\"/photo.jpg\"\n *   alt=\"Mountain\"\n *   style=\"width: 16rem; height: 10rem; object-fit: cover\"\n * />\n * ```\n *\n * @see https://ngwr.dev/components/lightbox\n */\n@Component({\n  selector: 'wr-lightbox',\n  templateUrl: './lightbox.html',\n  styleUrl: './lightbox.scss',\n  encapsulation: ViewEncapsulation.None,\n  host: {\n    '[class]': 'classes()',\n    '[style.aspect-ratio]': 'aspectRatio()',\n  },\n})\nexport class WrLightbox {\n  /** Image source. */\n  readonly src = input.required<string>();\n\n  /** Alt text. Required for a11y; falls back to an empty string. @default '' */\n  readonly alt = input<string>('');\n\n  /** Optional preview src — shown until the full image loads. */\n  readonly preview = input<string | null>(null);\n\n  /** Disable the click-to-zoom lightbox. @default false */\n  readonly disablePreview = input(false, { transform: coerceBooleanProperty });\n\n  /** Caption shown under the full image in the lightbox. */\n  readonly caption = input<string>('');\n\n  /**\n   * Reserve space before the image resolves by fixing the thumbnail's\n   * `aspect-ratio` (e.g. `'16 / 9'`, `'4 / 3'`, or a number like `1.5`).\n   * Prevents the layout jump when the intrinsic image size isn't known up\n   * front — pair it with a `width` and the box keeps its height from first\n   * paint. @default null\n   */\n  readonly aspectRatio = input<string | number | null>(null);\n\n  protected readonly open = signal(false);\n  protected readonly loaded = signal(false);\n\n  protected readonly classes = computed(() => {\n    const parts = ['wr-lightbox'];\n    if (this.open()) parts.push('wr-lightbox--open');\n    if (!this.loaded()) parts.push('wr-lightbox--loading');\n    return parts.join(' ');\n  });\n\n  // Query by name — `@if` blocks register their own TemplateRefs, so an\n  // unqualified TemplateRef query would grab the thumbnail branch instead.\n  protected readonly panelTpl = viewChild.required<TemplateRef<unknown>>('viewerTpl');\n\n  private readonly overlay = inject(WR_OVERLAY);\n  private readonly vcr = inject(ViewContainerRef);\n  private readonly destroyRef = inject(DestroyRef);\n  private overlayRef: OverlayRef | null = null;\n\n  constructor() {\n    effect(() => {\n      if (this.open()) this.openOverlay();\n      else this.closeOverlay();\n    });\n  }\n\n  protected onClick(): void {\n    if (this.disablePreview()) return;\n    this.open.set(true);\n  }\n\n  protected onClose(): void {\n    this.open.set(false);\n  }\n\n  // Swipe-down-to-dismiss on the open viewer. The image follows the finger and\n  // fades; releasing past ~20% of the viewer height closes it.\n  private viewerStartY = 0;\n  private viewerSwiping = false;\n\n  protected onViewerSwipeStart(event: TouchEvent): void {\n    const touch = event.touches[0];\n    if (!touch) return;\n    this.viewerStartY = touch.clientY;\n    this.viewerSwiping = true;\n  }\n\n  protected onViewerSwipeMove(event: TouchEvent, viewer: HTMLElement): void {\n    if (!this.viewerSwiping) return;\n    const touch = event.touches[0];\n    if (!touch) return;\n    const dy = touch.clientY - this.viewerStartY;\n    if (dy <= 0) {\n      viewer.style.transform = '';\n      viewer.style.opacity = '';\n      return;\n    }\n    event.preventDefault();\n    viewer.style.transition = 'none';\n    viewer.style.transform = `translateY(${dy}px)`;\n    viewer.style.opacity = String(Math.max(0.3, 1 - dy / (viewer.offsetHeight || 1)));\n  }\n\n  protected onViewerSwipeEnd(event: TouchEvent, viewer: HTMLElement): void {\n    if (!this.viewerSwiping) return;\n    this.viewerSwiping = false;\n    const touch = event.changedTouches[0];\n    const dy = touch ? touch.clientY - this.viewerStartY : 0;\n    if (dy > (viewer.offsetHeight || 0) * 0.2) {\n      this.onClose();\n    } else {\n      viewer.style.transition = '';\n      viewer.style.transform = '';\n      viewer.style.opacity = '';\n    }\n  }\n\n  protected onLoad(): void {\n    this.loaded.set(true);\n  }\n\n  // Overlay\n\n  private openOverlay(): void {\n    if (this.overlayRef) return;\n\n    this.overlayRef = this.overlay.create({\n      hasBackdrop: true,\n      backdropClass: 'wr-lightbox-backdrop',\n      positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),\n      scrollStrategy: this.overlay.scrollStrategies.block(),\n      panelClass: 'wr-lightbox-overlay',\n    });\n\n    const portal = new TemplatePortal(this.panelTpl(), this.vcr);\n    this.overlayRef.attach(portal);\n\n    this.overlayRef\n      .backdropClick()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe(() => this.open.set(false));\n\n    this.overlayRef\n      .keydownEvents()\n      .pipe(takeUntilDestroyed(this.destroyRef))\n      .subscribe(event => {\n        if (event.key === 'Escape') {\n          event.preventDefault();\n          this.open.set(false);\n        }\n      });\n  }\n\n  private closeOverlay(): void {\n    if (!this.overlayRef) return;\n    this.overlayRef.dispose();\n    this.overlayRef = null;\n  }\n}\n","@if (disablePreview()) {\n  <img\n    class=\"wr-lightbox__thumb\"\n    [src]=\"preview() || src()\"\n    [alt]=\"alt()\"\n    (load)=\"onLoad()\"\n    loading=\"lazy\"\n    decoding=\"async\"\n  />\n} @else {\n  <button type=\"button\" class=\"wr-lightbox__trigger\" [attr.aria-label]=\"alt() || 'Open preview'\" (click)=\"onClick()\">\n    <img\n      class=\"wr-lightbox__thumb\"\n      [src]=\"preview() || src()\"\n      [alt]=\"alt()\"\n      (load)=\"onLoad()\"\n      loading=\"lazy\"\n      decoding=\"async\"\n    />\n  </button>\n}\n\n<ng-template #viewerTpl>\n  <div\n    #viewer\n    class=\"wr-lightbox-viewer\"\n    role=\"dialog\"\n    aria-modal=\"true\"\n    [attr.aria-label]=\"alt() || 'Image preview'\"\n    (touchstart)=\"onViewerSwipeStart($event)\"\n    (touchmove)=\"onViewerSwipeMove($event, viewer)\"\n    (touchend)=\"onViewerSwipeEnd($event, viewer)\"\n    (touchcancel)=\"onViewerSwipeEnd($event, viewer)\"\n  >\n    <button\n      type=\"button\"\n      class=\"wr-lightbox-viewer__close\"\n      aria-label=\"Close preview\"\n      (click)=\"onClose()\"\n      (keydown.enter)=\"onClose()\"\n      (keydown.space)=\"onClose()\"\n    >\n      <svg\n        class=\"wr-icon__svg\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n        viewBox=\"0 0 24 24\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        stroke-width=\"2\"\n        stroke-linecap=\"round\"\n        stroke-linejoin=\"round\"\n        aria-hidden=\"true\"\n      >\n        <path d=\"M18 6 6 18\" />\n        <path d=\"m6 6 12 12\" />\n      </svg>\n    </button>\n    <img class=\"wr-lightbox-viewer__full\" [src]=\"src()\" [alt]=\"alt()\" />\n    @if (caption()) {\n      <div class=\"wr-lightbox-viewer__caption\">{{ caption() }}</div>\n    }\n  </div>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}