@simple-photo-gallery/common 2.1.4 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +25 -49
- package/dist/client.js.map +1 -1
- package/dist/styles/photoswipe/photoswipe.css +26 -0
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -289,52 +289,36 @@ var PhotoSwipeVideoPlugin = class {
|
|
|
289
289
|
};
|
|
290
290
|
|
|
291
291
|
// src/client/photoswipe/lightbox.ts
|
|
292
|
-
var CAPTION_SAMPLE_SIZE = 50;
|
|
293
292
|
var CAPTION_SAMPLE_HEIGHT_RATIO = 0.15;
|
|
294
|
-
var CAPTION_BG_DARK =
|
|
295
|
-
var CAPTION_BG_LIGHT =
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const g = Math.round(CAPTION_BG_DARK.g + (CAPTION_BG_LIGHT.g - CAPTION_BG_DARK.g) * t);
|
|
300
|
-
const b = Math.round(CAPTION_BG_DARK.b + (CAPTION_BG_LIGHT.b - CAPTION_BG_DARK.b) * t);
|
|
301
|
-
const a = +(CAPTION_BG_DARK.a + (CAPTION_BG_LIGHT.a - CAPTION_BG_DARK.a) * t).toFixed(3);
|
|
302
|
-
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
293
|
+
var CAPTION_BG_DARK = "rgba(255, 255, 255, 0.5)";
|
|
294
|
+
var CAPTION_BG_LIGHT = "rgba(0, 0, 0, 0.8)";
|
|
295
|
+
var BRIGHTNESS_THRESHOLD = 80;
|
|
296
|
+
function captionBgForBrightness(brightness) {
|
|
297
|
+
return brightness >= BRIGHTNESS_THRESHOLD ? CAPTION_BG_LIGHT : CAPTION_BG_DARK;
|
|
303
298
|
}
|
|
304
|
-
function
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const sampleHeight = Math.max(1, Math.round(srcHeight * CAPTION_SAMPLE_HEIGHT_RATIO));
|
|
315
|
-
const srcY = Math.max(0, srcHeight - sampleHeight);
|
|
316
|
-
try {
|
|
317
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
318
|
-
ctx.drawImage(img, 0, srcY, srcWidth, sampleHeight, 0, 0, canvas.width, canvas.height);
|
|
319
|
-
const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
320
|
-
let sum = 0;
|
|
321
|
-
let count = 0;
|
|
322
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
323
|
-
sum += 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
|
|
299
|
+
function sampleBlurHashBrightness(blurHash, width = 32, height = 32) {
|
|
300
|
+
try {
|
|
301
|
+
const pixels = decode(blurHash, width, height);
|
|
302
|
+
const startRow = Math.max(0, Math.floor(height * (1 - CAPTION_SAMPLE_HEIGHT_RATIO)));
|
|
303
|
+
let sum = 0;
|
|
304
|
+
let count = 0;
|
|
305
|
+
for (let y = startRow; y < height; y++) {
|
|
306
|
+
for (let x = 0; x < width; x++) {
|
|
307
|
+
const i = (y * width + x) * 4;
|
|
308
|
+
sum += 0.299 * pixels[i] + 0.587 * pixels[i + 1] + 0.114 * pixels[i + 2];
|
|
324
309
|
count++;
|
|
325
310
|
}
|
|
326
|
-
return count ? sum / count : null;
|
|
327
|
-
} catch {
|
|
328
|
-
return null;
|
|
329
311
|
}
|
|
330
|
-
|
|
312
|
+
return count ? sum / count : null;
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
331
316
|
}
|
|
332
317
|
async function createGalleryLightbox(options = {}) {
|
|
333
318
|
const photoswipeModule = await import('photoswipe');
|
|
334
319
|
const PhotoSwipe = photoswipeModule.default;
|
|
335
320
|
const lightboxModule = await import('photoswipe/lightbox');
|
|
336
321
|
const PhotoSwipeLightboxModule = lightboxModule.default;
|
|
337
|
-
const sampleBrightness = createCaptionBrightnessSampler();
|
|
338
322
|
const lightbox = new PhotoSwipeLightboxModule({
|
|
339
323
|
gallery: options.gallerySelector ?? ".gallery-grid",
|
|
340
324
|
children: options.childrenSelector ?? "a",
|
|
@@ -367,21 +351,13 @@ async function createGalleryLightbox(options = {}) {
|
|
|
367
351
|
if (currSlideElement) {
|
|
368
352
|
const caption = currSlideElement.dataset.pswpCaption;
|
|
369
353
|
el.innerHTML = caption || currSlideElement.querySelector("img")?.alt || "";
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (brightness === null) {
|
|
376
|
-
el.style.removeProperty("--pswp-caption-bg");
|
|
377
|
-
} else {
|
|
378
|
-
el.style.setProperty("--pswp-caption-bg", interpolateCaptionBg(brightness));
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
if (img && (!img.complete || img.naturalWidth === 0)) {
|
|
382
|
-
img.addEventListener("load", apply, { once: true });
|
|
354
|
+
const blurHash = currSlideElement.querySelector("canvas[data-blur-hash]")?.dataset.blurHash;
|
|
355
|
+
if (blurHash) {
|
|
356
|
+
const brightness = sampleBlurHashBrightness(blurHash);
|
|
357
|
+
if (brightness === null) {
|
|
358
|
+
el.style.removeProperty("--pswp-caption-bg");
|
|
383
359
|
} else {
|
|
384
|
-
|
|
360
|
+
el.style.setProperty("--pswp-caption-bg", captionBgForBrightness(brightness));
|
|
385
361
|
}
|
|
386
362
|
}
|
|
387
363
|
}
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/blurhash.ts","../src/client/hero-fallback.ts","../src/client/photoswipe/video-plugin.ts","../src/client/photoswipe/lightbox.ts","../src/client/photoswipe/deep-linking.ts","../src/client/css-utils.ts"],"names":[],"mappings":"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","file":"client.js","sourcesContent":["import { decode } from 'blurhash';\n\n/**\n * Decode a single blurhash and draw it to a canvas element.\n *\n * @param canvas - The canvas element with a data-blur-hash attribute\n * @param width - The width to decode at (default: 32)\n * @param height - The height to decode at (default: 32)\n */\nexport function decodeBlurhashToCanvas(canvas: HTMLCanvasElement, width: number = 32, height: number = 32): void {\n const blurHashValue = canvas.dataset.blurHash;\n if (!blurHashValue) return;\n\n const pixels = decode(blurHashValue, width, height);\n const ctx = canvas.getContext('2d');\n if (pixels && ctx) {\n const imageData = new ImageData(new Uint8ClampedArray(pixels), width, height);\n ctx.putImageData(imageData, 0, 0);\n }\n}\n\n/**\n * Decode and render all blurhash canvases on the page.\n * Finds all canvas elements with data-blur-hash attribute and draws the decoded image.\n *\n * @param selector - CSS selector for canvas elements (default: 'canvas[data-blur-hash]')\n * @param width - The width to decode at (default: 32)\n * @param height - The height to decode at (default: 32)\n */\nexport function decodeAllBlurhashes(\n selector: string = 'canvas[data-blur-hash]',\n width: number = 32,\n height: number = 32,\n): void {\n const canvases = document.querySelectorAll<HTMLCanvasElement>(selector);\n for (const canvas of canvases) {\n decodeBlurhashToCanvas(canvas, width, height);\n }\n}\n","/** Options for the hero image fallback behavior */\nexport interface HeroImageFallbackOptions {\n /** CSS selector for the picture element (default: '#hero-bg-picture') */\n pictureSelector?: string;\n /** CSS selector for the img element within picture (default: 'img.hero__bg-img') */\n imgSelector?: string;\n /** CSS selector for the blurhash canvas element (default: 'canvas[data-blur-hash]') */\n canvasSelector?: string;\n}\n\n/**\n * Initialize hero image fallback behavior.\n * Handles:\n * - Hiding blurhash canvas when image loads successfully\n * - Removing source elements and retrying with fallback src on error\n * - Keeping blurhash visible if final fallback also fails\n *\n * @param options - Configuration options for selectors\n *\n * @example\n * ```typescript\n * import { initHeroImageFallback } from '@simple-photo-gallery/common/client';\n *\n * // Use default selectors\n * initHeroImageFallback();\n *\n * // Or with custom selectors\n * initHeroImageFallback({\n * pictureSelector: '#my-hero-picture',\n * imgSelector: 'img.my-hero-img',\n * canvasSelector: 'canvas.my-blurhash',\n * });\n * ```\n */\nexport function initHeroImageFallback(options: HeroImageFallbackOptions = {}): void {\n const {\n pictureSelector = '#hero-bg-picture',\n imgSelector = 'img.hero__bg-img',\n canvasSelector = 'canvas[data-blur-hash]',\n } = options;\n\n const picture = document.querySelector<HTMLPictureElement>(pictureSelector);\n const img = picture?.querySelector<HTMLImageElement>(imgSelector);\n const canvas = document.querySelector<HTMLCanvasElement>(canvasSelector);\n\n if (!img) return;\n\n const fallbackSrc = img.getAttribute('src') || '';\n let didFallback = false;\n\n const hideBlurhash = () => {\n if (canvas) {\n canvas.style.display = 'none';\n }\n };\n\n const doFallback = () => {\n if (didFallback) return;\n didFallback = true;\n\n if (picture) {\n // Remove all <source> elements so the browser does not retry them\n for (const sourceEl of picture.querySelectorAll('source')) {\n sourceEl.remove();\n }\n }\n\n // Force reload using the <img> src as the final fallback\n const current = img.getAttribute('src') || '';\n img.setAttribute('src', '');\n img.setAttribute('src', fallbackSrc || current);\n\n // If fallback also fails, keep blurhash visible\n img.addEventListener(\n 'error',\n () => {\n // Final fallback failed, blurhash stays visible\n },\n { once: true },\n );\n\n // If fallback succeeds, hide blurhash\n img.addEventListener('load', hideBlurhash, { once: true });\n };\n\n // Check if image already loaded or failed before script runs\n if (img.complete) {\n if (img.naturalWidth === 0) {\n doFallback();\n } else {\n hideBlurhash();\n }\n } else {\n img.addEventListener('load', hideBlurhash, { once: true });\n }\n\n img.addEventListener('error', doFallback, { once: true });\n}\n","import type { Content, EventData, Slide, VideoPluginOptions } from './types';\nimport type PhotoSwipe from 'photoswipe';\nimport type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\nconst defaultOptions: VideoPluginOptions = {\n videoAttributes: { controls: '', playsinline: '', preload: 'auto' },\n autoplay: true,\n preventDragOffset: 40,\n};\n\n/**\n * Check if slide has video content\n */\nfunction isVideoContent(content: Content | Slide): boolean {\n return content && 'data' in content && content.data && content.data.type === 'video';\n}\n\nclass VideoContentSetup {\n private options: VideoPluginOptions;\n\n constructor(lightbox: PhotoSwipeLightbox, options: VideoPluginOptions) {\n this.options = options;\n\n this.initLightboxEvents(lightbox);\n lightbox.on('init', () => {\n if (lightbox.pswp) {\n this.initPswpEvents(lightbox.pswp);\n }\n });\n }\n\n private initLightboxEvents(lightbox: PhotoSwipeLightbox): void {\n lightbox.on('contentLoad', (data: unknown) => this.onContentLoad(data as EventData));\n lightbox.on('contentDestroy', (data: unknown) => this.onContentDestroy(data as { content: Content }));\n lightbox.on('contentActivate', (data: unknown) => this.onContentActivate(data as { content: Content }));\n lightbox.on('contentDeactivate', (data: unknown) => this.onContentDeactivate(data as { content: Content }));\n lightbox.on('contentAppend', (data: unknown) => this.onContentAppend(data as EventData));\n lightbox.on('contentResize', (data: unknown) => this.onContentResize(data as EventData));\n\n lightbox.addFilter('isKeepingPlaceholder', (value: unknown, ...args: unknown[]) =>\n this.isKeepingPlaceholder(value as boolean, args[0] as Content),\n );\n lightbox.addFilter('isContentZoomable', (value: unknown, ...args: unknown[]) =>\n this.isContentZoomable(value as boolean, args[0] as Content),\n );\n lightbox.addFilter('useContentPlaceholder', (value: unknown, ...args: unknown[]) =>\n this.useContentPlaceholder(value as boolean, args[0] as Content),\n );\n\n lightbox.addFilter('domItemData', (value: unknown, ...args: unknown[]) => {\n const itemData = value as Record<string, unknown>;\n const linkEl = args[1] as HTMLAnchorElement;\n\n if (itemData.type === 'video' && linkEl) {\n if (linkEl.dataset.pswpVideoSources) {\n itemData.videoSources = JSON.parse(linkEl.dataset.pswpVideoSources);\n } else if (linkEl.dataset.pswpVideoSrc) {\n itemData.videoSrc = linkEl.dataset.pswpVideoSrc;\n } else {\n itemData.videoSrc = linkEl.href;\n }\n }\n return itemData;\n });\n }\n\n private initPswpEvents(pswp: PhotoSwipe): void {\n // Prevent dragging when pointer is in bottom part of the video\n pswp.on('pointerDown', (data: unknown) => {\n const e = data as EventData;\n const slide = pswp.currSlide as Slide | undefined;\n if (slide && isVideoContent(slide) && this.options.preventDragOffset) {\n const origEvent = e.originalEvent;\n if (origEvent && origEvent.type === 'pointerdown') {\n const videoHeight = Math.ceil(slide.height * slide.currZoomLevel);\n const verticalEnding = videoHeight + slide.bounds.center.y;\n const pointerYPos = origEvent.pageY - pswp.offset.y;\n if (pointerYPos > verticalEnding - this.options.preventDragOffset! && pointerYPos < verticalEnding) {\n e.preventDefault?.();\n }\n }\n }\n });\n\n // do not append video on nearby slides\n pswp.on('appendHeavy', (data: unknown) => {\n const e = data as EventData;\n if (e.slide && isVideoContent(e.slide) && !e.slide.isActive) {\n e.preventDefault?.();\n }\n });\n\n pswp.on('close', () => {\n const slide = pswp.currSlide as Slide | undefined;\n if (slide && isVideoContent(slide.content)) {\n // Switch from zoom to fade closing transition,\n // as zoom transition is choppy for videos\n if (!pswp.options.showHideAnimationType || pswp.options.showHideAnimationType === 'zoom') {\n pswp.options.showHideAnimationType = 'fade';\n }\n\n // pause video when closing\n this.pauseVideo(slide.content);\n }\n });\n }\n\n private onContentDestroy({ content }: { content: Content }): void {\n if (isVideoContent(content) && content._videoPosterImg) {\n const handleLoad = () => {\n if (content._videoPosterImg) {\n content._videoPosterImg.removeEventListener('error', handleError);\n }\n };\n const handleError = () => {\n // Error handler\n };\n\n content._videoPosterImg.addEventListener('load', handleLoad);\n content._videoPosterImg.addEventListener('error', handleError);\n content._videoPosterImg = undefined;\n }\n }\n\n private onContentResize(e: EventData): void {\n if (e.content && isVideoContent(e.content)) {\n e.preventDefault?.();\n\n const width = e.width!;\n const height = e.height!;\n const content = e.content;\n\n if (content.element) {\n content.element.style.width = width + 'px';\n content.element.style.height = height + 'px';\n }\n\n if (content.slide && content.slide.placeholder) {\n // override placeholder size, so it more accurately matches the video\n const placeholderElStyle = content.slide.placeholder.element.style;\n placeholderElStyle.transform = 'none';\n placeholderElStyle.width = width + 'px';\n placeholderElStyle.height = height + 'px';\n }\n }\n }\n\n private isKeepingPlaceholder(isZoomable: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return false;\n }\n return isZoomable;\n }\n\n private isContentZoomable(isZoomable: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return false;\n }\n return isZoomable;\n }\n\n private onContentActivate({ content }: { content: Content }): void {\n if (isVideoContent(content) && this.options.autoplay) {\n this.playVideo(content);\n }\n }\n\n private onContentDeactivate({ content }: { content: Content }): void {\n if (isVideoContent(content)) {\n this.pauseVideo(content);\n }\n }\n\n private onContentAppend(e: EventData): void {\n if (e.content && isVideoContent(e.content)) {\n e.preventDefault?.();\n e.content.isAttached = true;\n e.content.appendImage?.();\n }\n }\n\n private onContentLoad(e: EventData): void {\n const content = e.content!;\n\n if (!isVideoContent(content)) {\n return;\n }\n\n // stop default content load\n e.preventDefault?.();\n\n if (content.element) {\n return;\n }\n\n content.state = 'loading';\n content.type = 'video';\n\n content.element = document.createElement('video');\n\n if (this.options.videoAttributes) {\n for (const key in this.options.videoAttributes) {\n content.element.setAttribute(key, this.options.videoAttributes[key] || '');\n }\n }\n\n content.element.setAttribute('poster', content.data.msrc || '');\n\n this.preloadVideoPoster(content, content.data.msrc);\n\n content.element.style.position = 'absolute';\n content.element.style.left = '0';\n content.element.style.top = '0';\n\n if (content.data.videoSources) {\n for (const source of content.data.videoSources) {\n const sourceEl = document.createElement('source');\n sourceEl.src = source.src;\n sourceEl.type = source.type;\n content.element.append(sourceEl);\n }\n } else if (content.data.videoSrc) {\n content.element.src = content.data.videoSrc;\n }\n }\n\n private preloadVideoPoster(content: Content, src?: string): void {\n if (!content._videoPosterImg && src) {\n content._videoPosterImg = new Image();\n content._videoPosterImg.src = src;\n if (content._videoPosterImg.complete) {\n content.onLoaded?.();\n } else {\n content._videoPosterImg.addEventListener('load', () => {\n content.onLoaded?.();\n });\n content._videoPosterImg.addEventListener('error', () => {\n content.onLoaded?.();\n });\n }\n }\n }\n\n private playVideo(content: Content): void {\n if (content.element) {\n (content.element as HTMLVideoElement).play();\n }\n }\n\n private pauseVideo(content: Content): void {\n if (content.element) {\n (content.element as HTMLVideoElement).pause();\n }\n }\n\n private useContentPlaceholder(usePlaceholder: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return true;\n }\n return usePlaceholder;\n }\n}\n\n/**\n * PhotoSwipe plugin that adds video support to the lightbox.\n * Videos are automatically detected by the `data-pswp-type=\"video\"` attribute\n * on gallery links.\n *\n * @example\n * ```typescript\n * import PhotoSwipe from 'photoswipe';\n * import PhotoSwipeLightbox from 'photoswipe/lightbox';\n * import { PhotoSwipeVideoPlugin } from '@simple-photo-gallery/common/client';\n *\n * const lightbox = new PhotoSwipeLightbox({\n * gallery: '.gallery',\n * children: 'a',\n * pswpModule: PhotoSwipe,\n * });\n *\n * new PhotoSwipeVideoPlugin(lightbox);\n * lightbox.init();\n * ```\n */\nexport class PhotoSwipeVideoPlugin {\n constructor(lightbox: PhotoSwipeLightbox, options: VideoPluginOptions = {}) {\n new VideoContentSetup(lightbox, {\n ...defaultOptions,\n ...options,\n });\n }\n}\n","import { PhotoSwipeVideoPlugin } from './video-plugin';\n\nimport type { VideoPluginOptions } from './types';\nimport type PhotoSwipe from 'photoswipe';\nimport type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\n/** Options for creating a gallery lightbox */\nexport interface GalleryLightboxOptions {\n /** CSS selector for gallery container (default: '.gallery-grid') */\n gallerySelector?: string;\n /** CSS selector for gallery items within container (default: 'a') */\n childrenSelector?: string;\n /** Animation duration when opening in ms (default: 300) */\n showAnimationDuration?: number;\n /** Animation duration when closing in ms (default: 300) */\n hideAnimationDuration?: number;\n /** Enable mouse wheel zoom (default: true) */\n wheelToZoom?: boolean;\n /** Loop back to first image after last (default: false) */\n loop?: boolean;\n /** Background opacity 0-1 (default: 1) */\n bgOpacity?: number;\n /** Options for the video plugin */\n videoPluginOptions?: VideoPluginOptions;\n /** Enable slide-in animations when changing slides (default: true) */\n slideAnimations?: boolean;\n /** Enable custom caption UI from data-pswp-caption attribute (default: true) */\n enableCaptions?: boolean;\n}\n\nconst CAPTION_SAMPLE_SIZE = 50;\nconst CAPTION_SAMPLE_HEIGHT_RATIO = 0.15;\n\n/* Caption background endpoints for brightness interpolation (0=dark image, 1=light image) */\nconst CAPTION_BG_DARK = { r: 255, g: 255, b: 255, a: 0.5 }; // white frost on dark images\nconst CAPTION_BG_LIGHT = { r: 0, g: 0, b: 0, a: 0.8 }; // dark frost on light images\n\nfunction interpolateCaptionBg(brightness: number): string {\n const t = Math.min(1, Math.max(0, brightness / 255));\n const r = Math.round(CAPTION_BG_DARK.r + (CAPTION_BG_LIGHT.r - CAPTION_BG_DARK.r) * t);\n const g = Math.round(CAPTION_BG_DARK.g + (CAPTION_BG_LIGHT.g - CAPTION_BG_DARK.g) * t);\n const b = Math.round(CAPTION_BG_DARK.b + (CAPTION_BG_LIGHT.b - CAPTION_BG_DARK.b) * t);\n const a = +(CAPTION_BG_DARK.a + (CAPTION_BG_LIGHT.a - CAPTION_BG_DARK.a) * t).toFixed(3);\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\n/**\n * Creates a reusable function that samples the average brightness\n * of the bottom strip of an image using an offscreen canvas.\n */\nfunction createCaptionBrightnessSampler() {\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) return null;\n\n canvas.width = CAPTION_SAMPLE_SIZE;\n canvas.height = CAPTION_SAMPLE_SIZE;\n\n return (img: HTMLImageElement): number | null => {\n if (!img.complete || img.naturalWidth === 0 || img.naturalHeight === 0) return null;\n\n const srcWidth = img.naturalWidth;\n const srcHeight = img.naturalHeight;\n const sampleHeight = Math.max(1, Math.round(srcHeight * CAPTION_SAMPLE_HEIGHT_RATIO));\n const srcY = Math.max(0, srcHeight - sampleHeight);\n\n try {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(img, 0, srcY, srcWidth, sampleHeight, 0, 0, canvas.width, canvas.height);\n\n const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height);\n let sum = 0;\n let count = 0;\n for (let i = 0; i < data.length; i += 4) {\n sum += 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];\n count++;\n }\n\n return count ? sum / count : null;\n } catch {\n return null;\n }\n };\n}\n\n/**\n * Create a PhotoSwipe lightbox with sensible defaults and video support.\n * Returns the lightbox instance for further customization before calling init().\n *\n * IMPORTANT: You must import the PhotoSwipe CSS files for the lightbox to work properly:\n * - `import 'photoswipe/style.css'` - Base PhotoSwipe styles\n * - `import '@simple-photo-gallery/common/styles/photoswipe'` - SPG enhancement styles\n *\n * @example\n * ```typescript\n * import { createGalleryLightbox } from '@simple-photo-gallery/common/client';\n * import 'photoswipe/style.css';\n * import '@simple-photo-gallery/common/styles/photoswipe';\n *\n * // Basic usage\n * const lightbox = await createGalleryLightbox();\n * lightbox.init();\n *\n * // With custom options\n * const lightbox = await createGalleryLightbox({\n * gallerySelector: '.my-gallery',\n * loop: true,\n * });\n *\n * // Add custom event handlers before init\n * lightbox.on('change', () => console.log('slide changed'));\n * lightbox.init();\n * ```\n */\nexport async function createGalleryLightbox(options: GalleryLightboxOptions = {}): Promise<PhotoSwipeLightbox> {\n const photoswipeModule = await import('photoswipe');\n const PhotoSwipe = photoswipeModule.default;\n const lightboxModule = await import('photoswipe/lightbox');\n const PhotoSwipeLightboxModule = lightboxModule.default;\n const sampleBrightness = createCaptionBrightnessSampler();\n\n const lightbox = new PhotoSwipeLightboxModule({\n gallery: options.gallerySelector ?? '.gallery-grid',\n children: options.childrenSelector ?? 'a',\n pswpModule: PhotoSwipe,\n showAnimationDuration: options.showAnimationDuration ?? 300,\n hideAnimationDuration: options.hideAnimationDuration ?? 300,\n wheelToZoom: options.wheelToZoom ?? true,\n loop: options.loop ?? false,\n bgOpacity: options.bgOpacity ?? 1,\n });\n\n new PhotoSwipeVideoPlugin(lightbox, options.videoPluginOptions);\n\n // Slide animations (enabled by default)\n if (options.slideAnimations !== false) {\n lightbox.on('contentDeactivate', ({ content }: { content: { element?: HTMLElement } }) => {\n content.element?.classList.remove('pswp__img--in-viewport');\n });\n lightbox.on('contentActivate', ({ content }: { content: { element?: HTMLElement } }) => {\n content.element?.classList.add('pswp__img--in-viewport');\n });\n }\n\n // Custom caption UI (enabled by default)\n if (options.enableCaptions !== false) {\n lightbox.on('uiRegister', () => {\n (lightbox.pswp as PhotoSwipe | undefined)?.ui?.registerElement({\n name: 'custom-caption',\n isButton: false,\n className: 'pswp__caption',\n appendTo: 'wrapper',\n onInit: (el: HTMLElement) => {\n (lightbox.pswp as PhotoSwipe | undefined)?.on('change', () => {\n const currSlideElement = (lightbox.pswp as PhotoSwipe | undefined)?.currSlide?.data.element as\n | HTMLElement\n | undefined;\n if (currSlideElement) {\n const caption = (currSlideElement as HTMLElement).dataset.pswpCaption;\n el.innerHTML = caption || currSlideElement.querySelector('img')?.alt || '';\n\n // Adapt caption background based on image brightness\n if (sampleBrightness) {\n const img = currSlideElement.querySelector('img');\n const apply = () => {\n if (!img) return;\n const brightness = sampleBrightness(img);\n if (brightness === null) {\n el.style.removeProperty('--pswp-caption-bg');\n } else {\n el.style.setProperty('--pswp-caption-bg', interpolateCaptionBg(brightness));\n }\n };\n\n if (img && (!img.complete || img.naturalWidth === 0)) {\n img.addEventListener('load', apply, { once: true });\n } else {\n apply();\n }\n }\n }\n });\n },\n });\n });\n }\n\n return lightbox;\n}\n","import type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\n/** Options for URL deep-linking functionality */\nexport interface DeepLinkingOptions {\n /** CSS selector for galleries (default: '.gallery-grid') */\n gallerySelector?: string;\n /** CSS selector for gallery items (default: 'a') */\n itemSelector?: string;\n /** URL parameter name for image ID (default: 'image') */\n parameterName?: string;\n /** Delay in ms before opening image on page load (default: 100) */\n openDelay?: number;\n}\n\n/**\n * Updates browser URL with current image ID.\n * Uses History API to replace state without page reload.\n */\nexport function updateGalleryURL(imageId: string | null, paramName: string = 'image'): void {\n const url = new URL(globalThis.location.href);\n if (imageId) {\n url.searchParams.set(paramName, imageId);\n } else {\n url.searchParams.delete(paramName);\n }\n globalThis.history.replaceState({}, '', url.toString());\n}\n\n/**\n * Extracts image ID from URL parameters.\n */\nexport function getImageIdFromURL(paramName: string = 'image'): string | null {\n const params = new URLSearchParams(globalThis.location.search);\n return params.get(paramName);\n}\n\n/**\n * Opens a specific image by ID within a gallery.\n * Searches through all gallery items and opens matching image in lightbox.\n *\n * @returns true if image was found and opened, false otherwise\n */\nexport function openImageById(lightbox: PhotoSwipeLightbox, imageId: string, options: DeepLinkingOptions = {}): boolean {\n const gallerySelector = options.gallerySelector ?? '.gallery-grid';\n const itemSelector = options.itemSelector ?? 'a';\n\n const galleries = document.querySelectorAll(gallerySelector);\n const allItems: HTMLElement[] = [];\n\n for (const gallery of galleries) {\n const items = gallery.querySelectorAll<HTMLElement>(itemSelector);\n allItems.push(...items);\n }\n\n for (const [i, item] of allItems.entries()) {\n const itemId = item.dataset.imageId || '';\n if (itemId === imageId) {\n lightbox.loadAndOpen(i);\n return true;\n }\n }\n return false;\n}\n\n/**\n * Sets up automatic URL updates when lightbox changes.\n * Call after creating lightbox but before init().\n *\n * @example\n * ```typescript\n * const lightbox = await createGalleryLightbox();\n * setupDeepLinking(lightbox);\n * lightbox.init();\n * restoreFromURL(lightbox);\n * ```\n */\nexport function setupDeepLinking(lightbox: PhotoSwipeLightbox, options: DeepLinkingOptions = {}): void {\n const paramName = options.parameterName ?? 'image';\n\n // Update URL when slide changes\n lightbox.on('change', () => {\n if (lightbox.pswp) {\n const currSlideElement = lightbox.pswp.currSlide?.data.element as HTMLElement | undefined;\n const imageId = currSlideElement?.dataset?.imageId ?? null;\n updateGalleryURL(imageId, paramName);\n }\n });\n\n // Clear URL parameter when lightbox closes\n lightbox.on('close', () => {\n updateGalleryURL(null, paramName);\n });\n}\n\n/**\n * Restores gallery state from URL on page load.\n * Automatically opens image if specified in URL parameters.\n * Call after lightbox.init().\n *\n * @example\n * ```typescript\n * const lightbox = await createGalleryLightbox();\n * setupDeepLinking(lightbox);\n * lightbox.init();\n * restoreFromURL(lightbox);\n * ```\n */\nexport function restoreFromURL(lightbox: PhotoSwipeLightbox, options: DeepLinkingOptions = {}): void {\n const paramName = options.parameterName ?? 'image';\n const openDelay = options.openDelay ?? 100;\n const imageIdFromURL = getImageIdFromURL(paramName);\n\n if (imageIdFromURL) {\n const open = () => {\n setTimeout(() => openImageById(lightbox, imageIdFromURL, options), openDelay);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', open);\n } else {\n open();\n }\n }\n}\n","/**\n * CSS utility functions for client-side theming and color manipulation.\n * These utilities are browser-only and require DOM access.\n */\n\n/**\n * Normalizes hex color values to 6-digit format (e.g., #abc -> #aabbcc).\n * Returns null if the hex value is invalid.\n *\n * @param hex - The hex color value to normalize (with or without #)\n * @returns The normalized 6-digit hex color with # prefix, or null if invalid\n */\nexport function normalizeHex(hex: string): string | null {\n hex = hex.replace('#', '');\n if (hex.length === 3) hex = [...hex].map((c) => c + c).join('');\n return hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex) ? `#${hex}` : null;\n}\n\n/**\n * Parses and validates a color value.\n * Supports CSS color names, hex values, rgb/rgba, and 'transparent'.\n * Returns null if the color is invalid.\n *\n * @param colorParam - The color string to parse\n * @returns The validated color string, or null if invalid\n */\nexport function parseColor(colorParam: string | null): string | null {\n if (!colorParam) return null;\n const normalized = colorParam.toLowerCase().trim();\n if (normalized === 'transparent') return 'transparent';\n\n const testEl = document.createElement('div');\n testEl.style.color = normalized;\n if (testEl.style.color) return normalized;\n\n return normalizeHex(colorParam);\n}\n\n/**\n * Sets or removes a CSS custom property (variable) on an element.\n * Removes the property if value is null.\n *\n * @param element - The HTML element to modify\n * @param name - The CSS variable name (e.g., '--my-color')\n * @param value - The value to set, or null to remove\n */\nexport function setCSSVar(element: HTMLElement, name: string, value: string | null): void {\n if (value) {\n element.style.setProperty(name, value);\n } else {\n element.style.removeProperty(name);\n }\n}\n\n/**\n * Derives a color with adjusted opacity from an existing color.\n * Converts rgb to rgba if needed, or adjusts existing rgba opacity.\n *\n * @param color - The source color (rgb, rgba, or other CSS color)\n * @param opacity - The target opacity (0-1)\n * @returns The color with adjusted opacity, or original if not rgb/rgba\n */\nexport function deriveOpacityColor(color: string, opacity: number): string {\n if (color.startsWith('rgba')) {\n return color.replace(/,\\s*[\\d.]+\\)$/, `, ${opacity})`);\n }\n if (color.startsWith('rgb')) {\n return color.replace('rgb', 'rgba').replace(')', `, ${opacity})`);\n }\n return color;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/blurhash.ts","../src/client/hero-fallback.ts","../src/client/photoswipe/video-plugin.ts","../src/client/photoswipe/lightbox.ts","../src/client/photoswipe/deep-linking.ts","../src/client/css-utils.ts"],"names":["decode"],"mappings":"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","file":"client.js","sourcesContent":["import { decode } from 'blurhash';\n\n/**\n * Decode a single blurhash and draw it to a canvas element.\n *\n * @param canvas - The canvas element with a data-blur-hash attribute\n * @param width - The width to decode at (default: 32)\n * @param height - The height to decode at (default: 32)\n */\nexport function decodeBlurhashToCanvas(canvas: HTMLCanvasElement, width: number = 32, height: number = 32): void {\n const blurHashValue = canvas.dataset.blurHash;\n if (!blurHashValue) return;\n\n const pixels = decode(blurHashValue, width, height);\n const ctx = canvas.getContext('2d');\n if (pixels && ctx) {\n const imageData = new ImageData(new Uint8ClampedArray(pixels), width, height);\n ctx.putImageData(imageData, 0, 0);\n }\n}\n\n/**\n * Decode and render all blurhash canvases on the page.\n * Finds all canvas elements with data-blur-hash attribute and draws the decoded image.\n *\n * @param selector - CSS selector for canvas elements (default: 'canvas[data-blur-hash]')\n * @param width - The width to decode at (default: 32)\n * @param height - The height to decode at (default: 32)\n */\nexport function decodeAllBlurhashes(\n selector: string = 'canvas[data-blur-hash]',\n width: number = 32,\n height: number = 32,\n): void {\n const canvases = document.querySelectorAll<HTMLCanvasElement>(selector);\n for (const canvas of canvases) {\n decodeBlurhashToCanvas(canvas, width, height);\n }\n}\n","/** Options for the hero image fallback behavior */\nexport interface HeroImageFallbackOptions {\n /** CSS selector for the picture element (default: '#hero-bg-picture') */\n pictureSelector?: string;\n /** CSS selector for the img element within picture (default: 'img.hero__bg-img') */\n imgSelector?: string;\n /** CSS selector for the blurhash canvas element (default: 'canvas[data-blur-hash]') */\n canvasSelector?: string;\n}\n\n/**\n * Initialize hero image fallback behavior.\n * Handles:\n * - Hiding blurhash canvas when image loads successfully\n * - Removing source elements and retrying with fallback src on error\n * - Keeping blurhash visible if final fallback also fails\n *\n * @param options - Configuration options for selectors\n *\n * @example\n * ```typescript\n * import { initHeroImageFallback } from '@simple-photo-gallery/common/client';\n *\n * // Use default selectors\n * initHeroImageFallback();\n *\n * // Or with custom selectors\n * initHeroImageFallback({\n * pictureSelector: '#my-hero-picture',\n * imgSelector: 'img.my-hero-img',\n * canvasSelector: 'canvas.my-blurhash',\n * });\n * ```\n */\nexport function initHeroImageFallback(options: HeroImageFallbackOptions = {}): void {\n const {\n pictureSelector = '#hero-bg-picture',\n imgSelector = 'img.hero__bg-img',\n canvasSelector = 'canvas[data-blur-hash]',\n } = options;\n\n const picture = document.querySelector<HTMLPictureElement>(pictureSelector);\n const img = picture?.querySelector<HTMLImageElement>(imgSelector);\n const canvas = document.querySelector<HTMLCanvasElement>(canvasSelector);\n\n if (!img) return;\n\n const fallbackSrc = img.getAttribute('src') || '';\n let didFallback = false;\n\n const hideBlurhash = () => {\n if (canvas) {\n canvas.style.display = 'none';\n }\n };\n\n const doFallback = () => {\n if (didFallback) return;\n didFallback = true;\n\n if (picture) {\n // Remove all <source> elements so the browser does not retry them\n for (const sourceEl of picture.querySelectorAll('source')) {\n sourceEl.remove();\n }\n }\n\n // Force reload using the <img> src as the final fallback\n const current = img.getAttribute('src') || '';\n img.setAttribute('src', '');\n img.setAttribute('src', fallbackSrc || current);\n\n // If fallback also fails, keep blurhash visible\n img.addEventListener(\n 'error',\n () => {\n // Final fallback failed, blurhash stays visible\n },\n { once: true },\n );\n\n // If fallback succeeds, hide blurhash\n img.addEventListener('load', hideBlurhash, { once: true });\n };\n\n // Check if image already loaded or failed before script runs\n if (img.complete) {\n if (img.naturalWidth === 0) {\n doFallback();\n } else {\n hideBlurhash();\n }\n } else {\n img.addEventListener('load', hideBlurhash, { once: true });\n }\n\n img.addEventListener('error', doFallback, { once: true });\n}\n","import type { Content, EventData, Slide, VideoPluginOptions } from './types';\nimport type PhotoSwipe from 'photoswipe';\nimport type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\nconst defaultOptions: VideoPluginOptions = {\n videoAttributes: { controls: '', playsinline: '', preload: 'auto' },\n autoplay: true,\n preventDragOffset: 40,\n};\n\n/**\n * Check if slide has video content\n */\nfunction isVideoContent(content: Content | Slide): boolean {\n return content && 'data' in content && content.data && content.data.type === 'video';\n}\n\nclass VideoContentSetup {\n private options: VideoPluginOptions;\n\n constructor(lightbox: PhotoSwipeLightbox, options: VideoPluginOptions) {\n this.options = options;\n\n this.initLightboxEvents(lightbox);\n lightbox.on('init', () => {\n if (lightbox.pswp) {\n this.initPswpEvents(lightbox.pswp);\n }\n });\n }\n\n private initLightboxEvents(lightbox: PhotoSwipeLightbox): void {\n lightbox.on('contentLoad', (data: unknown) => this.onContentLoad(data as EventData));\n lightbox.on('contentDestroy', (data: unknown) => this.onContentDestroy(data as { content: Content }));\n lightbox.on('contentActivate', (data: unknown) => this.onContentActivate(data as { content: Content }));\n lightbox.on('contentDeactivate', (data: unknown) => this.onContentDeactivate(data as { content: Content }));\n lightbox.on('contentAppend', (data: unknown) => this.onContentAppend(data as EventData));\n lightbox.on('contentResize', (data: unknown) => this.onContentResize(data as EventData));\n\n lightbox.addFilter('isKeepingPlaceholder', (value: unknown, ...args: unknown[]) =>\n this.isKeepingPlaceholder(value as boolean, args[0] as Content),\n );\n lightbox.addFilter('isContentZoomable', (value: unknown, ...args: unknown[]) =>\n this.isContentZoomable(value as boolean, args[0] as Content),\n );\n lightbox.addFilter('useContentPlaceholder', (value: unknown, ...args: unknown[]) =>\n this.useContentPlaceholder(value as boolean, args[0] as Content),\n );\n\n lightbox.addFilter('domItemData', (value: unknown, ...args: unknown[]) => {\n const itemData = value as Record<string, unknown>;\n const linkEl = args[1] as HTMLAnchorElement;\n\n if (itemData.type === 'video' && linkEl) {\n if (linkEl.dataset.pswpVideoSources) {\n itemData.videoSources = JSON.parse(linkEl.dataset.pswpVideoSources);\n } else if (linkEl.dataset.pswpVideoSrc) {\n itemData.videoSrc = linkEl.dataset.pswpVideoSrc;\n } else {\n itemData.videoSrc = linkEl.href;\n }\n }\n return itemData;\n });\n }\n\n private initPswpEvents(pswp: PhotoSwipe): void {\n // Prevent dragging when pointer is in bottom part of the video\n pswp.on('pointerDown', (data: unknown) => {\n const e = data as EventData;\n const slide = pswp.currSlide as Slide | undefined;\n if (slide && isVideoContent(slide) && this.options.preventDragOffset) {\n const origEvent = e.originalEvent;\n if (origEvent && origEvent.type === 'pointerdown') {\n const videoHeight = Math.ceil(slide.height * slide.currZoomLevel);\n const verticalEnding = videoHeight + slide.bounds.center.y;\n const pointerYPos = origEvent.pageY - pswp.offset.y;\n if (pointerYPos > verticalEnding - this.options.preventDragOffset! && pointerYPos < verticalEnding) {\n e.preventDefault?.();\n }\n }\n }\n });\n\n // do not append video on nearby slides\n pswp.on('appendHeavy', (data: unknown) => {\n const e = data as EventData;\n if (e.slide && isVideoContent(e.slide) && !e.slide.isActive) {\n e.preventDefault?.();\n }\n });\n\n pswp.on('close', () => {\n const slide = pswp.currSlide as Slide | undefined;\n if (slide && isVideoContent(slide.content)) {\n // Switch from zoom to fade closing transition,\n // as zoom transition is choppy for videos\n if (!pswp.options.showHideAnimationType || pswp.options.showHideAnimationType === 'zoom') {\n pswp.options.showHideAnimationType = 'fade';\n }\n\n // pause video when closing\n this.pauseVideo(slide.content);\n }\n });\n }\n\n private onContentDestroy({ content }: { content: Content }): void {\n if (isVideoContent(content) && content._videoPosterImg) {\n const handleLoad = () => {\n if (content._videoPosterImg) {\n content._videoPosterImg.removeEventListener('error', handleError);\n }\n };\n const handleError = () => {\n // Error handler\n };\n\n content._videoPosterImg.addEventListener('load', handleLoad);\n content._videoPosterImg.addEventListener('error', handleError);\n content._videoPosterImg = undefined;\n }\n }\n\n private onContentResize(e: EventData): void {\n if (e.content && isVideoContent(e.content)) {\n e.preventDefault?.();\n\n const width = e.width!;\n const height = e.height!;\n const content = e.content;\n\n if (content.element) {\n content.element.style.width = width + 'px';\n content.element.style.height = height + 'px';\n }\n\n if (content.slide && content.slide.placeholder) {\n // override placeholder size, so it more accurately matches the video\n const placeholderElStyle = content.slide.placeholder.element.style;\n placeholderElStyle.transform = 'none';\n placeholderElStyle.width = width + 'px';\n placeholderElStyle.height = height + 'px';\n }\n }\n }\n\n private isKeepingPlaceholder(isZoomable: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return false;\n }\n return isZoomable;\n }\n\n private isContentZoomable(isZoomable: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return false;\n }\n return isZoomable;\n }\n\n private onContentActivate({ content }: { content: Content }): void {\n if (isVideoContent(content) && this.options.autoplay) {\n this.playVideo(content);\n }\n }\n\n private onContentDeactivate({ content }: { content: Content }): void {\n if (isVideoContent(content)) {\n this.pauseVideo(content);\n }\n }\n\n private onContentAppend(e: EventData): void {\n if (e.content && isVideoContent(e.content)) {\n e.preventDefault?.();\n e.content.isAttached = true;\n e.content.appendImage?.();\n }\n }\n\n private onContentLoad(e: EventData): void {\n const content = e.content!;\n\n if (!isVideoContent(content)) {\n return;\n }\n\n // stop default content load\n e.preventDefault?.();\n\n if (content.element) {\n return;\n }\n\n content.state = 'loading';\n content.type = 'video';\n\n content.element = document.createElement('video');\n\n if (this.options.videoAttributes) {\n for (const key in this.options.videoAttributes) {\n content.element.setAttribute(key, this.options.videoAttributes[key] || '');\n }\n }\n\n content.element.setAttribute('poster', content.data.msrc || '');\n\n this.preloadVideoPoster(content, content.data.msrc);\n\n content.element.style.position = 'absolute';\n content.element.style.left = '0';\n content.element.style.top = '0';\n\n if (content.data.videoSources) {\n for (const source of content.data.videoSources) {\n const sourceEl = document.createElement('source');\n sourceEl.src = source.src;\n sourceEl.type = source.type;\n content.element.append(sourceEl);\n }\n } else if (content.data.videoSrc) {\n content.element.src = content.data.videoSrc;\n }\n }\n\n private preloadVideoPoster(content: Content, src?: string): void {\n if (!content._videoPosterImg && src) {\n content._videoPosterImg = new Image();\n content._videoPosterImg.src = src;\n if (content._videoPosterImg.complete) {\n content.onLoaded?.();\n } else {\n content._videoPosterImg.addEventListener('load', () => {\n content.onLoaded?.();\n });\n content._videoPosterImg.addEventListener('error', () => {\n content.onLoaded?.();\n });\n }\n }\n }\n\n private playVideo(content: Content): void {\n if (content.element) {\n (content.element as HTMLVideoElement).play();\n }\n }\n\n private pauseVideo(content: Content): void {\n if (content.element) {\n (content.element as HTMLVideoElement).pause();\n }\n }\n\n private useContentPlaceholder(usePlaceholder: boolean, content: Content): boolean {\n if (isVideoContent(content)) {\n return true;\n }\n return usePlaceholder;\n }\n}\n\n/**\n * PhotoSwipe plugin that adds video support to the lightbox.\n * Videos are automatically detected by the `data-pswp-type=\"video\"` attribute\n * on gallery links.\n *\n * @example\n * ```typescript\n * import PhotoSwipe from 'photoswipe';\n * import PhotoSwipeLightbox from 'photoswipe/lightbox';\n * import { PhotoSwipeVideoPlugin } from '@simple-photo-gallery/common/client';\n *\n * const lightbox = new PhotoSwipeLightbox({\n * gallery: '.gallery',\n * children: 'a',\n * pswpModule: PhotoSwipe,\n * });\n *\n * new PhotoSwipeVideoPlugin(lightbox);\n * lightbox.init();\n * ```\n */\nexport class PhotoSwipeVideoPlugin {\n constructor(lightbox: PhotoSwipeLightbox, options: VideoPluginOptions = {}) {\n new VideoContentSetup(lightbox, {\n ...defaultOptions,\n ...options,\n });\n }\n}\n","import { decode } from 'blurhash';\n\nimport { PhotoSwipeVideoPlugin } from './video-plugin';\n\nimport type { VideoPluginOptions } from './types';\nimport type PhotoSwipe from 'photoswipe';\nimport type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\n/** Options for creating a gallery lightbox */\nexport interface GalleryLightboxOptions {\n /** CSS selector for gallery container (default: '.gallery-grid') */\n gallerySelector?: string;\n /** CSS selector for gallery items within container (default: 'a') */\n childrenSelector?: string;\n /** Animation duration when opening in ms (default: 300) */\n showAnimationDuration?: number;\n /** Animation duration when closing in ms (default: 300) */\n hideAnimationDuration?: number;\n /** Enable mouse wheel zoom (default: true) */\n wheelToZoom?: boolean;\n /** Loop back to first image after last (default: false) */\n loop?: boolean;\n /** Background opacity 0-1 (default: 1) */\n bgOpacity?: number;\n /** Options for the video plugin */\n videoPluginOptions?: VideoPluginOptions;\n /** Enable slide-in animations when changing slides (default: true) */\n slideAnimations?: boolean;\n /** Enable custom caption UI from data-pswp-caption attribute (default: true) */\n enableCaptions?: boolean;\n}\n\nconst CAPTION_SAMPLE_HEIGHT_RATIO = 0.15;\n\n/* Caption backgrounds chosen based on image brightness */\nconst CAPTION_BG_DARK = 'rgba(255, 255, 255, 0.5)'; // white frost on dark images\nconst CAPTION_BG_LIGHT = 'rgba(0, 0, 0, 0.8)'; // dark frost on light images\nconst BRIGHTNESS_THRESHOLD = 80;\n\nfunction captionBgForBrightness(brightness: number): string {\n return brightness >= BRIGHTNESS_THRESHOLD ? CAPTION_BG_LIGHT : CAPTION_BG_DARK;\n}\n\n/**\n * Samples the average brightness of the bottom strip of an image\n * by decoding its BlurHash. This avoids CORS issues with cross-origin images.\n */\nfunction sampleBlurHashBrightness(blurHash: string, width = 32, height = 32): number | null {\n try {\n const pixels = decode(blurHash, width, height);\n const startRow = Math.max(0, Math.floor(height * (1 - CAPTION_SAMPLE_HEIGHT_RATIO)));\n let sum = 0;\n let count = 0;\n for (let y = startRow; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const i = (y * width + x) * 4;\n sum += 0.299 * pixels[i] + 0.587 * pixels[i + 1] + 0.114 * pixels[i + 2];\n count++;\n }\n }\n return count ? sum / count : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Create a PhotoSwipe lightbox with sensible defaults and video support.\n * Returns the lightbox instance for further customization before calling init().\n *\n * IMPORTANT: You must import the PhotoSwipe CSS files for the lightbox to work properly:\n * - `import 'photoswipe/style.css'` - Base PhotoSwipe styles\n * - `import '@simple-photo-gallery/common/styles/photoswipe'` - SPG enhancement styles\n *\n * @example\n * ```typescript\n * import { createGalleryLightbox } from '@simple-photo-gallery/common/client';\n * import 'photoswipe/style.css';\n * import '@simple-photo-gallery/common/styles/photoswipe';\n *\n * // Basic usage\n * const lightbox = await createGalleryLightbox();\n * lightbox.init();\n *\n * // With custom options\n * const lightbox = await createGalleryLightbox({\n * gallerySelector: '.my-gallery',\n * loop: true,\n * });\n *\n * // Add custom event handlers before init\n * lightbox.on('change', () => console.log('slide changed'));\n * lightbox.init();\n * ```\n */\nexport async function createGalleryLightbox(options: GalleryLightboxOptions = {}): Promise<PhotoSwipeLightbox> {\n const photoswipeModule = await import('photoswipe');\n const PhotoSwipe = photoswipeModule.default;\n const lightboxModule = await import('photoswipe/lightbox');\n const PhotoSwipeLightboxModule = lightboxModule.default;\n const lightbox = new PhotoSwipeLightboxModule({\n gallery: options.gallerySelector ?? '.gallery-grid',\n children: options.childrenSelector ?? 'a',\n pswpModule: PhotoSwipe,\n showAnimationDuration: options.showAnimationDuration ?? 300,\n hideAnimationDuration: options.hideAnimationDuration ?? 300,\n wheelToZoom: options.wheelToZoom ?? true,\n loop: options.loop ?? false,\n bgOpacity: options.bgOpacity ?? 1,\n });\n\n new PhotoSwipeVideoPlugin(lightbox, options.videoPluginOptions);\n\n // Slide animations (enabled by default)\n if (options.slideAnimations !== false) {\n lightbox.on('contentDeactivate', ({ content }: { content: { element?: HTMLElement } }) => {\n content.element?.classList.remove('pswp__img--in-viewport');\n });\n lightbox.on('contentActivate', ({ content }: { content: { element?: HTMLElement } }) => {\n content.element?.classList.add('pswp__img--in-viewport');\n });\n }\n\n // Custom caption UI (enabled by default)\n if (options.enableCaptions !== false) {\n lightbox.on('uiRegister', () => {\n (lightbox.pswp as PhotoSwipe | undefined)?.ui?.registerElement({\n name: 'custom-caption',\n isButton: false,\n className: 'pswp__caption',\n appendTo: 'wrapper',\n onInit: (el: HTMLElement) => {\n (lightbox.pswp as PhotoSwipe | undefined)?.on('change', () => {\n const currSlideElement = (lightbox.pswp as PhotoSwipe | undefined)?.currSlide?.data.element as\n | HTMLElement\n | undefined;\n if (currSlideElement) {\n const caption = (currSlideElement as HTMLElement).dataset.pswpCaption;\n el.innerHTML = caption || currSlideElement.querySelector('img')?.alt || '';\n\n // Adapt caption background based on image brightness (via BlurHash)\n const blurHash = currSlideElement.querySelector<HTMLCanvasElement>('canvas[data-blur-hash]')?.dataset.blurHash;\n if (blurHash) {\n const brightness = sampleBlurHashBrightness(blurHash);\n if (brightness === null) {\n el.style.removeProperty('--pswp-caption-bg');\n } else {\n el.style.setProperty('--pswp-caption-bg', captionBgForBrightness(brightness));\n }\n }\n }\n });\n },\n });\n });\n }\n\n return lightbox;\n}\n","import type PhotoSwipeLightbox from 'photoswipe/lightbox';\n\n/** Options for URL deep-linking functionality */\nexport interface DeepLinkingOptions {\n /** CSS selector for galleries (default: '.gallery-grid') */\n gallerySelector?: string;\n /** CSS selector for gallery items (default: 'a') */\n itemSelector?: string;\n /** URL parameter name for image ID (default: 'image') */\n parameterName?: string;\n /** Delay in ms before opening image on page load (default: 100) */\n openDelay?: number;\n}\n\n/**\n * Updates browser URL with current image ID.\n * Uses History API to replace state without page reload.\n */\nexport function updateGalleryURL(imageId: string | null, paramName: string = 'image'): void {\n const url = new URL(globalThis.location.href);\n if (imageId) {\n url.searchParams.set(paramName, imageId);\n } else {\n url.searchParams.delete(paramName);\n }\n globalThis.history.replaceState({}, '', url.toString());\n}\n\n/**\n * Extracts image ID from URL parameters.\n */\nexport function getImageIdFromURL(paramName: string = 'image'): string | null {\n const params = new URLSearchParams(globalThis.location.search);\n return params.get(paramName);\n}\n\n/**\n * Opens a specific image by ID within a gallery.\n * Searches through all gallery items and opens matching image in lightbox.\n *\n * @returns true if image was found and opened, false otherwise\n */\nexport function openImageById(lightbox: PhotoSwipeLightbox, imageId: string, options: DeepLinkingOptions = {}): boolean {\n const gallerySelector = options.gallerySelector ?? '.gallery-grid';\n const itemSelector = options.itemSelector ?? 'a';\n\n const galleries = document.querySelectorAll(gallerySelector);\n const allItems: HTMLElement[] = [];\n\n for (const gallery of galleries) {\n const items = gallery.querySelectorAll<HTMLElement>(itemSelector);\n allItems.push(...items);\n }\n\n for (const [i, item] of allItems.entries()) {\n const itemId = item.dataset.imageId || '';\n if (itemId === imageId) {\n lightbox.loadAndOpen(i);\n return true;\n }\n }\n return false;\n}\n\n/**\n * Sets up automatic URL updates when lightbox changes.\n * Call after creating lightbox but before init().\n *\n * @example\n * ```typescript\n * const lightbox = await createGalleryLightbox();\n * setupDeepLinking(lightbox);\n * lightbox.init();\n * restoreFromURL(lightbox);\n * ```\n */\nexport function setupDeepLinking(lightbox: PhotoSwipeLightbox, options: DeepLinkingOptions = {}): void {\n const paramName = options.parameterName ?? 'image';\n\n // Update URL when slide changes\n lightbox.on('change', () => {\n if (lightbox.pswp) {\n const currSlideElement = lightbox.pswp.currSlide?.data.element as HTMLElement | undefined;\n const imageId = currSlideElement?.dataset?.imageId ?? null;\n updateGalleryURL(imageId, paramName);\n }\n });\n\n // Clear URL parameter when lightbox closes\n lightbox.on('close', () => {\n updateGalleryURL(null, paramName);\n });\n}\n\n/**\n * Restores gallery state from URL on page load.\n * Automatically opens image if specified in URL parameters.\n * Call after lightbox.init().\n *\n * @example\n * ```typescript\n * const lightbox = await createGalleryLightbox();\n * setupDeepLinking(lightbox);\n * lightbox.init();\n * restoreFromURL(lightbox);\n * ```\n */\nexport function restoreFromURL(lightbox: PhotoSwipeLightbox, options: DeepLinkingOptions = {}): void {\n const paramName = options.parameterName ?? 'image';\n const openDelay = options.openDelay ?? 100;\n const imageIdFromURL = getImageIdFromURL(paramName);\n\n if (imageIdFromURL) {\n const open = () => {\n setTimeout(() => openImageById(lightbox, imageIdFromURL, options), openDelay);\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', open);\n } else {\n open();\n }\n }\n}\n","/**\n * CSS utility functions for client-side theming and color manipulation.\n * These utilities are browser-only and require DOM access.\n */\n\n/**\n * Normalizes hex color values to 6-digit format (e.g., #abc -> #aabbcc).\n * Returns null if the hex value is invalid.\n *\n * @param hex - The hex color value to normalize (with or without #)\n * @returns The normalized 6-digit hex color with # prefix, or null if invalid\n */\nexport function normalizeHex(hex: string): string | null {\n hex = hex.replace('#', '');\n if (hex.length === 3) hex = [...hex].map((c) => c + c).join('');\n return hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex) ? `#${hex}` : null;\n}\n\n/**\n * Parses and validates a color value.\n * Supports CSS color names, hex values, rgb/rgba, and 'transparent'.\n * Returns null if the color is invalid.\n *\n * @param colorParam - The color string to parse\n * @returns The validated color string, or null if invalid\n */\nexport function parseColor(colorParam: string | null): string | null {\n if (!colorParam) return null;\n const normalized = colorParam.toLowerCase().trim();\n if (normalized === 'transparent') return 'transparent';\n\n const testEl = document.createElement('div');\n testEl.style.color = normalized;\n if (testEl.style.color) return normalized;\n\n return normalizeHex(colorParam);\n}\n\n/**\n * Sets or removes a CSS custom property (variable) on an element.\n * Removes the property if value is null.\n *\n * @param element - The HTML element to modify\n * @param name - The CSS variable name (e.g., '--my-color')\n * @param value - The value to set, or null to remove\n */\nexport function setCSSVar(element: HTMLElement, name: string, value: string | null): void {\n if (value) {\n element.style.setProperty(name, value);\n } else {\n element.style.removeProperty(name);\n }\n}\n\n/**\n * Derives a color with adjusted opacity from an existing color.\n * Converts rgb to rgba if needed, or adjusts existing rgba opacity.\n *\n * @param color - The source color (rgb, rgba, or other CSS color)\n * @param opacity - The target opacity (0-1)\n * @returns The color with adjusted opacity, or original if not rgb/rgba\n */\nexport function deriveOpacityColor(color: string, opacity: number): string {\n if (color.startsWith('rgba')) {\n return color.replace(/,\\s*[\\d.]+\\)$/, `, ${opacity})`);\n }\n if (color.startsWith('rgb')) {\n return color.replace('rgb', 'rgba').replace(')', `, ${opacity})`);\n }\n return color;\n}\n"]}
|
|
@@ -120,6 +120,32 @@
|
|
|
120
120
|
font-style: italic;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/* Override built-in preloader: centered, larger, subtler */
|
|
124
|
+
.pswp__preloader {
|
|
125
|
+
position: fixed;
|
|
126
|
+
top: 50%;
|
|
127
|
+
left: 50%;
|
|
128
|
+
width: 64px;
|
|
129
|
+
height: 64px;
|
|
130
|
+
margin: -32px 0 0 -32px;
|
|
131
|
+
overflow: visible;
|
|
132
|
+
--pswp-icon-stroke-width: 1px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.pswp__preloader .pswp__icn {
|
|
136
|
+
width: 64px;
|
|
137
|
+
height: 64px;
|
|
138
|
+
top: 0;
|
|
139
|
+
left: 0;
|
|
140
|
+
opacity: 0;
|
|
141
|
+
transition: opacity 0.3s ease;
|
|
142
|
+
animation: pswp-clockwise 1.1s linear infinite;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.pswp__preloader--active .pswp__icn {
|
|
146
|
+
opacity: 0.3;
|
|
147
|
+
}
|
|
148
|
+
|
|
123
149
|
/* Slide-in animation */
|
|
124
150
|
.pswp__img {
|
|
125
151
|
opacity: 0;
|