{"version":3,"file":"lightbox.flight.mjs","names":[],"sources":["../../../../package/common/lightbox/lightbox.flight.ts"],"sourcesContent":["/** A box on screen, in CSS pixels. */\nexport interface Rect {\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n}\n\n/** A picture in flight: the box the whole picture fills, the part of it on show, and that part's corner radius. */\nexport interface Frame {\n  box: Rect;\n  clip: Rect;\n  radius: number;\n}\n\nexport function contain(aspect: number, rect: Rect): Rect {\n  return fit(aspect, rect, Math.min);\n}\n\nexport function cover(aspect: number, rect: Rect): Rect {\n  return fit(aspect, rect, Math.max);\n}\n\nfunction fit(aspect: number, rect: Rect, pick: (a: number, b: number) => number): Rect {\n  const width = pick(rect.width, rect.height * aspect);\n  const height = width / aspect;\n  return { left: rect.left + (rect.width - width) / 2, top: rect.top + (rect.height - height) / 2, width, height };\n}\n\nexport function intersect(a: Rect, b: Rect): Rect | null {\n  const left = Math.max(a.left, b.left);\n  const top = Math.max(a.top, b.top);\n  const right = Math.min(a.left + a.width, b.left + b.width);\n  const bottom = Math.min(a.top + a.height, b.top + b.height);\n  return right > left && bottom > top ? { left, top, width: right - left, height: bottom - top } : null;\n}\n\nfunction rectOf(element: Element): Rect {\n  const { left, top, width, height } = element.getBoundingClientRect();\n  return { left, top, width, height };\n}\n\n/**\n * The image on show where it sits on screen, cut to the PanZoom around it, which a zoomed-in image\n * overflows. `figure` is a Lightbox slide, whose first child is that PanZoom.\n */\nexport function pictureFrame(figure: Element): { frame: Frame; src: string; aspect: number } | null {\n  const viewport = figure.firstElementChild;\n  // Image keeps the `<img>` that is not on show at no size.\n  const image = [...(viewport?.querySelectorAll('img') ?? [])].find(\n    (img) => img.naturalWidth > 0 && img.getBoundingClientRect().width > 0,\n  );\n  if (!viewport || !image) return null;\n  const aspect = image.naturalWidth / image.naturalHeight;\n  // A thumbnail standing in fills a box of the screen's shape, `object-fit: contain`.\n  const box = contain(aspect, rectOf(image));\n  const clip = intersect(box, rectOf(viewport));\n  return clip && { frame: { box, clip, radius: 0 }, src: image.currentSrc, aspect };\n}\n\n/** The whole picture around a thumbnail, which is usually a crop (`object-fit: cover`). Null off screen. */\nexport function thumbnailFrame(thumbnail: Element, aspect: number): Frame | null {\n  const clip = rectOf(thumbnail);\n  const screen = { left: 0, top: 0, width: window.innerWidth, height: window.innerHeight };\n  if (!intersect(clip, screen)) return null;\n  return { box: cover(aspect, clip), clip, radius: radiusOf(thumbnail, clip) };\n}\n\n/** The top left corner's, taken for all four. A percentage is of the shorter side, as on a round avatar. */\nfunction radiusOf(element: Element, rect: Rect): number {\n  const radius = getComputedStyle(element).borderTopLeftRadius;\n  const value = parseFloat(radius) || 0;\n  return radius.endsWith('%') ? (value / 100) * Math.min(rect.width, rect.height) : value;\n}\n\nconst hiddenThumbnails = new WeakMap<HTMLElement, { count: number; visibility: string }>();\n\n/** Hides a thumbnail until every caller that hid it has shown it again: the open viewer and a flight overlap. */\nexport function hideThumbnail(thumbnail: HTMLElement): () => void {\n  const entry = hiddenThumbnails.get(thumbnail) ?? { count: 0, visibility: thumbnail.style.visibility };\n  hiddenThumbnails.set(thumbnail, entry);\n  entry.count += 1;\n  thumbnail.style.visibility = 'hidden';\n  return () => {\n    entry.count -= 1;\n    if (entry.count > 0) return;\n    hiddenThumbnails.delete(thumbnail);\n    thumbnail.style.visibility = entry.visibility;\n  };\n}\n\n/** On past the edge of the screen, in the direction a swipe was moving it. */\nexport function flungFrame(frame: Frame, direction: number): Frame {\n  const distance = Math.sign(direction) * window.innerHeight;\n  const moved = (rect: Rect) => ({ ...rect, top: rect.top + distance });\n  return { box: moved(frame.box), clip: moved(frame.clip), radius: frame.radius };\n}\n\n/**\n * For an element laid out at `layout` with `transform-origin: 0 0`. Every box has the picture's\n * shape, so one scale fits it, and the clip is in the element's own pixels, before that scale.\n */\nexport function flightKeyframes(from: Frame, to: Frame, layout: Rect): Keyframe[] {\n  return [from, to].map(({ box, clip, radius }) => {\n    const scale = box.width / layout.width;\n    const inset = [\n      clip.top - box.top,\n      box.left + box.width - clip.left - clip.width,\n      box.top + box.height - clip.top - clip.height,\n      clip.left - box.left,\n    ];\n    return {\n      transform: `translate(${box.left - layout.left}px, ${box.top - layout.top}px) scale(${scale})`,\n      clipPath: `inset(${inset.map((side) => `${side / scale}px`).join(' ')} round ${radius / scale}px)`,\n    };\n  });\n}\n"],"mappings":"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"}