@gallerist/react 0.3.0 → 0.3.1
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/README.md +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -33,6 +33,11 @@ new values.
|
|
|
33
33
|
rail options for any option you don't pass (`preset="reviewer"` gives the timeline and numbered items;
|
|
34
34
|
`thumbnails({ timeline: false })` keeps the numbers and drops the timeline).
|
|
35
35
|
|
|
36
|
+
`linear` follows the rail when you leave it out: with `thumbnails({ groupDisplay: 'flat' })` ←/→ walk
|
|
37
|
+
every item in order across groups (the counter reads `item N / total`); with another rail, or none,
|
|
38
|
+
they move between groups. `linear={false}` keeps the group and item axes under a flat rail, and
|
|
39
|
+
`linear` turns linear traversal on whatever the rail.
|
|
40
|
+
|
|
36
41
|
`plugins`, `on` and render props are functions, so `<Gallerist>` is used from a client component. The
|
|
37
42
|
build carries a `"use client"` banner, so a Next.js App Router page can import it from inside a client
|
|
38
43
|
boundary (a file that starts with `"use client"`); a Server Component can't pass it those props.
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/useGallerist.ts","../src/useCurrentItem.ts","../src/Gallerist.tsx"],"sourcesContent":["// @gallerist/react — React adapter (spec §16). Thin binding over the headless store.\nexport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type GroupAxis,\n type ItemIndicator,\n type PresetName,\n type RailPlacement,\n type ResolvedPreset,\n type Skin,\n type Strings,\n DEFAULT_STRINGS,\n resolvePreset,\n} from '@gallerist/renderer-dom'\nexport { useViewerContext, GalleristContext } from './context'\nexport { type UseGallerist, useGallerist } from './useGallerist'\nexport { type CurrentItem, useCurrentItem } from './useCurrentItem'\nexport {\n Gallerist,\n type GalleristCaptionProps,\n type GalleristOpenAt,\n type GalleristOverlayProps,\n type GalleristProps,\n type GalleristRegionProps,\n} from './Gallerist'\n","import type { ViewerApi } from '@gallerist/core'\nimport { createContext, useContext } from 'react'\n\nexport const GalleristContext = createContext<ViewerApi | null>(null)\n\nexport function useViewerContext(): ViewerApi {\n const viewer = useContext(GalleristContext)\n if (!viewer)\n throw new Error(\n 'Gallerist.Overlay / Gallerist.Region / Gallerist.Caption must be used inside <Gallerist>',\n )\n return viewer\n}\n","import type { ViewerApi, ViewerState } from '@gallerist/core'\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\n\nexport interface UseGallerist {\n state: Readonly<ViewerState>\n viewer: ViewerApi\n open: ViewerApi['open']\n close: ViewerApi['close']\n next: ViewerApi['next']\n prev: ViewerApi['prev']\n nextItem: ViewerApi['nextItem']\n prevItem: ViewerApi['prevItem']\n goTo: ViewerApi['goTo']\n goToId: ViewerApi['goToId']\n zoomTo: ViewerApi['zoomTo']\n resetZoom: ViewerApi['resetZoom']\n}\n\n/** Bind a React component to a viewer's state (useSyncExternalStore) + its actions. */\nexport function useGallerist(viewer: ViewerApi): UseGallerist {\n const subscribe = useCallback((cb: () => void) => viewer.subscribe(cb), [viewer])\n const getSnapshot = useCallback(() => viewer.getState(), [viewer])\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Stabilize the action closures so they keep a constant identity across re-renders (this hook\n // re-renders on every store commit). Otherwise each commit handed consumers fresh function\n // references, defeating React.memo / dependency-array memoization downstream (S-PERF4).\n const actions = useMemo(\n () => ({\n open: (at?: Parameters<ViewerApi['open']>[0]) => viewer.open(at),\n close: () => viewer.close(),\n next: () => viewer.next(),\n prev: () => viewer.prev(),\n nextItem: () => viewer.nextItem(),\n prevItem: () => viewer.prevItem(),\n goTo: (g: number, i?: number) => viewer.goTo(g, i),\n goToId: (g: string, i?: string) => viewer.goToId(g, i),\n zoomTo: (s: number, f?: Parameters<ViewerApi['zoomTo']>[1]) => viewer.zoomTo(s, f),\n resetZoom: () => viewer.resetZoom(),\n }),\n [viewer],\n )\n\n return { state, viewer, ...actions }\n}\n","import type { Cursor, Group, Item, ViewerApi } from '@gallerist/core'\nimport { useCallback, useContext, useRef, useSyncExternalStore } from 'react'\nimport { GalleristContext } from './context'\n\n/** The viewer's current item, its group and its position. */\nexport interface CurrentItem<GM = unknown, IM = unknown> {\n /** undefined only for an empty collection */\n item: Item<IM> | undefined\n group: Group<GM, IM> | undefined\n cursor: Readonly<Cursor>\n}\n\n/**\n * The current item, group and cursor of the enclosing `<Gallerist>` (or of `viewer`). Re-renders\n * when the cursor or the collection changes, not on zoom or pan.\n */\nexport function useCurrentItem<GM = unknown, IM = unknown>(\n viewer?: ViewerApi,\n): CurrentItem<GM, IM> {\n const fromContext = useContext(GalleristContext)\n const v = viewer ?? fromContext\n if (!v) throw new Error('useCurrentItem() must be used inside <Gallerist> or given a viewer')\n // useSyncExternalStore needs a stable snapshot: the same object until the item really changes\n const last = useRef<CurrentItem | null>(null)\n const subscribe = useCallback((cb: () => void) => v.subscribe(cb), [v])\n const getSnapshot = useCallback((): CurrentItem => {\n const { g, i } = v.getState().cursor\n const group = v.getCollection().groups[g]\n const item = group?.items[i]\n const prev = last.current\n if (\n prev &&\n prev.item === item &&\n prev.group === group &&\n prev.cursor.g === g &&\n prev.cursor.i === i\n )\n return prev\n last.current = { item, group, cursor: { g, i } }\n return last.current\n }, [v])\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as CurrentItem<GM, IM>\n}\n","import {\n type Collection,\n type Cursor,\n type EventHandlers,\n type EventMap,\n type Group,\n type Item,\n type OverlaySpec,\n type RegionName,\n type ViewerApi,\n type ViewerOptions,\n createViewer,\n} from '@gallerist/core'\nimport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type ItemIndicator,\n type MountOptions,\n type PresetName,\n type RailPlacement,\n type Skin,\n type Strings,\n applyAppearance,\n mount,\n} from '@gallerist/renderer-dom'\nimport {\n type ReactNode,\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useInsertionEffect,\n useRef,\n useState,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { GalleristContext, useViewerContext } from './context'\nimport { useCurrentItem } from './useCurrentItem'\n\n/** A position to open at: group `g`, item `i` (default: the group's remembered item). */\nexport interface GalleristOpenAt {\n g: number\n i?: number\n}\n\n/**\n * `items` / `groups` / `collection` and `on` are reactive. Changed data swaps the collection in\n * place (diffed by id, keeping the cursor); data rebuilt with the same content is ignored. Every other viewer option\n * (`loop`, `itemMemory`, `startAt`, `plugins`, `overlays`, …) and the layout props (`container`,\n * `preset`, `arrows`, `rail`, `label`, `preload`, `adapters`, …) are read once; change the\n * component's `key` to apply new ones. `skin`, `accent`, `scheme` and `open` are reactive.\n */\nexport interface GalleristProps extends ViewerOptions {\n /**\n * Controlled visibility. `true` opens at the current position; `{ g, i }` opens there, and a new\n * position while open moves the cursor (applied when the value changes, so the user can still\n * navigate away from it); `false` closes. Leave it undefined to drive the viewer through the ref.\n */\n open?: boolean | GalleristOpenAt\n onClose?: () => void\n container?: HTMLElement | null\n controls?: boolean\n /** layout preset (arrow placement, auto-hide, data-gallerist-preset, the thumbnails() defaults) — UX-E. */\n preset?: PresetName\n /** visual skin, default `gallery`; other skins need their stylesheet (reactive). */\n skin?: Skin\n /** the skin's accent by name, e.g. `mint` (Gallery) or `cyan` (Console) (reactive). */\n accent?: Accent\n /** colour scheme: `dark`, `light` or `auto`; unset, the skin's own default (reactive). */\n scheme?: ColorScheme\n /** within-group arrow placement (overrides the preset default) — UX-D. */\n arrows?: ArrowLayout\n /** thumbnail rail placement (overrides the preset default) — UX-E. */\n rail?: RailPlacement\n /** auto-hide chrome on idle (overrides the preset default) — UX-C. */\n autoHide?: boolean\n /** linear flat traversal: ←/→ walks the flattened item sequence; pairs with a flat thumbnail rail. */\n linear?: boolean\n /** UI text overrides (button labels, announcements) for translation; read once at mount. */\n strings?: Partial<Strings>\n /** item position indicator: `pills` (default), `dots`, `counter` or `placard`; read once at mount. */\n itemIndicator?: ItemIndicator\n /** zoom navigator while zoomed in (overrides the preset default); read once at mount. */\n navigator?: boolean\n /** framed media: the work hangs with room around it (overrides the preset); read once at mount. */\n frame?: boolean\n /** which arrows move between groups (overrides the rail and preset default); read once at mount. */\n groupAxis?: MountOptions['groupAxis']\n /** name the group arrows after their target group (overrides the preset); read once at mount. */\n arrowLabels?: MountOptions['arrowLabels']\n /** the overlay dialog's accessible name (default `strings.dialog`); read once at mount. */\n label?: string\n /** where focus returns when the overlay closes (default: what had focus when it opened); read\n * once at mount, so pass a getter for a target that changes. */\n returnFocus?: MountOptions['returnFocus']\n /** how many groups to keep mounted ahead of and behind the current one; read once at mount. */\n preload?: MountOptions['preload']\n /** media adapters, replacing the default image adapter; read once at mount. */\n adapters?: MountOptions['adapters']\n children?: ReactNode\n}\n\n// Every viewer event, checked against EventMap (a missing or unknown name fails to compile), so\n// the forwarders below can route each one to the latest `on` handlers.\nconst EVENT_NAMES = Object.keys({\n init: 1,\n destroy: 1,\n open: 1,\n opened: 1,\n close: 1,\n closed: 1,\n beforeSlide: 1,\n slide: 1,\n afterSlide: 1,\n beforeItem: 1,\n item: 1,\n afterItem: 1,\n zoom: 1,\n zoomEnd: 1,\n load: 1,\n loaded: 1,\n loadError: 1,\n collectionChange: 1,\n fullscreenChange: 1,\n overlayClick: 1,\n} satisfies Record<keyof EventMap, 1>) as (keyof EventMap)[]\n\n// The collection a data prop describes (flat items → singleton groups). setCollection validates it.\nconst toCollection = (items?: Item[], groups?: Group[], collection?: Collection): Collection =>\n collection ?? { groups: groups ?? (items ?? []).map((it) => ({ id: it.id, items: [it] })) }\n\n// Values equal at one level of depth: identical, or arrays/plain objects with identical entries\n// (a `source: { src }`, `naturalSize`, or a re-created `overlays` array of the same specs).\nconst shallowSame = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => Object.is(v, b[i]))\n }\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n const ka = Object.keys(a)\n const rb = b as Record<string, unknown>\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => Object.is((a as Record<string, unknown>)[k], rb[k]))\n )\n}\nconst sameRecord = (a: object, b: object, skip?: string): boolean => {\n const ra = a as Record<string, unknown>\n const rb = b as Record<string, unknown>\n const ka = Object.keys(ra).filter((k) => k !== skip)\n const kb = Object.keys(rb).filter((k) => k !== skip)\n return ka.length === kb.length && ka.every((k) => shallowSame(ra[k], rb[k]))\n}\n// Same content (groups, items and their fields), even when rebuilt as fresh objects — so an\n// inline `items={photos.map(…)}` doesn't swap the collection (and reset load status) every render.\nconst sameCollection = (a: Collection, b: Readonly<Collection>): boolean =>\n a.groups.length === b.groups.length &&\n shallowSame(a.meta, b.meta) &&\n a.groups.every((g, gi) => {\n const h = b.groups[gi]\n return (\n !!h &&\n sameRecord(g, h, 'items') &&\n g.items.length === h.items.length &&\n g.items.every((it, ii) => {\n const other = h.items[ii]\n return !!other && sameRecord(it, other)\n })\n )\n })\n\nconst GalleristBase = forwardRef<ViewerApi, GalleristProps>(function Gallerist(props, ref) {\n const {\n open,\n onClose,\n container,\n controls,\n preset,\n skin,\n accent,\n scheme,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n children,\n ...options\n } = props\n const rootRef = useRef<HTMLElement | null>(null)\n const { items, groups, collection, on } = options\n\n // The viewer outlives renders, so its handlers must not capture the first render's `on`: they\n // forward to whatever `on` the latest committed render passed (a returned `false` still cancels).\n const onRef = useRef<EventHandlers | undefined>(on)\n useInsertionEffect(() => {\n onRef.current = on\n })\n // Create the viewer once (core is DOM-free → SSR-safe); options are read at construction. Lazy\n // useState (not useMemo, which React may discard) guarantees a single stable instance. Plugins\n // are deliberately NOT registered at construction — they're managed in an effect below so their\n // teardown runs on unmount (I-X2).\n const [viewer] = useState(() => {\n const { plugins: _plugins, on: _on, ...rest } = options\n const forward: EventHandlers = {}\n for (const name of EVENT_NAMES) {\n ;(forward as Record<string, (p: unknown) => unknown>)[name] = (payload) =>\n (onRef.current?.[name] as ((p: unknown) => unknown) | undefined)?.(payload)\n }\n return createViewer({ ...rest, on: forward })\n })\n\n // Data props are reactive: new data swaps the collection via setCollection (diffed by id, so the\n // cursor and zoom survive when the current item does). The first render's data is already in the\n // viewer; a new reference with the same content is ignored; invalid data (a duplicate id) is\n // reported and the viewer keeps its current collection instead of crashing the React tree.\n const appliedData = useRef({ items, groups, collection })\n useEffect(() => {\n const prev = appliedData.current\n if (prev.items === items && prev.groups === groups && prev.collection === collection) return\n appliedData.current = { items, groups, collection }\n const next = toCollection(items, groups, collection)\n if (sameCollection(next, viewer.getCollection())) return\n try {\n viewer.setCollection(next)\n } catch (error) {\n console.error('[gallerist] ignored an invalid collection update:', error)\n }\n }, [viewer, items, groups, collection])\n useImperativeHandle(ref, () => viewer, [viewer])\n\n // Register the construction-time plugins via use() and dispose them on unmount, so their\n // init()-created store subscriptions / observers / DOM don't leak on every remount (the viewer\n // itself is intentionally never destroyed — see the mount effect). Plugins are mount-time config.\n // biome-ignore lint/correctness/useExhaustiveDependencies: register once for the viewer's life\n useEffect(() => {\n const disposers = (options.plugins ?? []).map((plugin) => viewer.use(plugin))\n return () => {\n for (const dispose of disposers) dispose()\n }\n }, [viewer])\n\n // Mount the renderer once per viewer (client-side); container/controls are mount-time only. Only\n // the renderer (the DOM holder) is torn down here. The viewer is NOT destroyed on cleanup: under\n // StrictMode the simulated unmount would run viewer.destroy() on the stable useState instance,\n // permanently poisoning it (registerPlugin/nav short-circuit once destroyed) so re-registered\n // overlays/regions vanish. The viewer is plain JS (no DOM) and is reclaimed by GC once the\n // component unmounts and the renderer/hook/overlay subscriptions have all cleaned themselves up.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-once (layout opts read once)\n useEffect(() => {\n const handle = mount(viewer, {\n container: container ?? null,\n controls,\n preset,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n skin,\n accent,\n scheme,\n loop: options.loop,\n })\n rootRef.current = handle.root\n return () => {\n handle.destroy()\n rootRef.current = null\n }\n }, [viewer])\n\n // The appearance is reactive: the skin files key off root attributes, so no remount is needed.\n useEffect(() => {\n const root = rootRef.current\n if (root) applyAppearance(root, { skin, accent, scheme })\n }, [skin, accent, scheme])\n\n // `open` is compared by value, so an inline `open={{ g: 2 }}` re-applies only when g or i change\n const openG = typeof open === 'object' && open ? open.g : undefined\n const openI = typeof open === 'object' && open ? open.i : undefined\n const shown = open === undefined ? undefined : !!open\n useEffect(() => {\n if (shown === undefined) return\n // open({ g, i }) on a viewer already showing is a navigation (core)\n if (shown) viewer.open(openG === undefined ? undefined : { g: openG, i: openI })\n else viewer.close()\n }, [shown, openG, openI, viewer])\n\n useEffect(() => {\n if (!onClose) return\n return viewer.on('closed', () => onClose())\n }, [onClose, viewer])\n\n return <GalleristContext.Provider value={viewer}>{children}</GalleristContext.Provider>\n})\n\nexport interface GalleristOverlayProps {\n anchor: OverlaySpec['anchor']\n id?: string\n layer?: number\n interactive?: boolean\n visibleAt?: [number, number]\n className?: string\n children?: ReactNode\n}\n\n/** Render a React subtree as an image-space / viewport overlay via a portal (decision P0-G). */\nfunction Overlay(props: GalleristOverlayProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n // useId is SSR-deterministic (server + client agree), unlike a module-global counter.\n const autoId = useId()\n const id = props.id ?? autoId\n\n // Depend on the VALUE of the object/array props, not their identity. The common usage passes\n // inline literals (`anchor={{ kind: 'viewport', … }}`) that are a fresh reference every render;\n // keying on identity disposed+re-registered the overlay plugin on every parent render, forcing a\n // full overlay repaint. Serializing makes re-registration happen only on a real change (I-X3).\n const anchorKey = JSON.stringify(props.anchor)\n const visibleAtKey = JSON.stringify(props.visibleAt ?? null)\n // biome-ignore lint/correctness/useExhaustiveDependencies: anchor/visibleAt tracked by value key\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const spec: OverlaySpec = {\n id,\n anchor: props.anchor,\n layer: props.layer,\n interactive: props.interactive,\n visibleAt: props.visibleAt,\n class: props.className,\n render: () => node, // stable host; React owns its children through the portal\n }\n const dispose = viewer.use(() => ({ name: `overlay:${id}`, overlays: [spec] }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, id, anchorKey, visibleAtKey, props.layer, props.interactive, props.className])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristRegionProps {\n name: RegionName\n children?: ReactNode\n}\n\n/** Render a React subtree into a named renderer region (toolbar, footer, …) via a portal. */\nfunction Region(props: GalleristRegionProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n const { name } = props\n\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const dispose = viewer.use(() => ({\n name: `region:${name}`,\n regions: { [name]: () => node },\n }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, name])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristCaptionProps<GM = unknown, IM = unknown> {\n /**\n * the caption for the current item; `null`, `undefined`, `true` or `false` shows its text\n * `caption` (in a `span.gallerist-caption-text`, the theme's caption type)\n */\n children: (current: {\n item: Item<IM>\n group: Group<GM, IM>\n cursor: Readonly<Cursor>\n }) => ReactNode\n}\n\n/**\n * Render the caption for the current item with React, into the renderer's caption region (the\n * caption bar, the editorial placard, wherever the preset puts it). Re-renders on navigation and\n * collection changes. Its text is the viewer's accessible description, like a text caption.\n */\nfunction Caption<GM = unknown, IM = unknown>(props: GalleristCaptionProps<GM, IM>): ReactNode {\n const { item, group, cursor } = useCurrentItem<GM, IM>()\n const out = item && group ? props.children({ item, group, cursor }) : null\n return (\n <Region name=\"caption\">\n {out == null || typeof out === 'boolean' ? text(item?.caption) : out}\n </Region>\n )\n}\n\n// The item's own text caption, styled like the bar's text. Untyped data (JSON, a CDN page) can still\n// carry an old object-shaped caption, which React would throw on: anything but a string is none.\nfunction text(caption: unknown): ReactNode {\n return typeof caption === 'string' && caption ? (\n <span className=\"gallerist-caption-text\">{caption}</span>\n ) : null\n}\n\nexport const Gallerist = Object.assign(GalleristBase, { Overlay, Region, Caption })\n"],"mappings":"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","names":["Gallerist","import_renderer_dom","import_react","import_react","import_react","Gallerist"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/useGallerist.ts","../src/useCurrentItem.ts","../src/Gallerist.tsx"],"sourcesContent":["// @gallerist/react — React adapter (spec §16). Thin binding over the headless store.\nexport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type GroupAxis,\n type ItemIndicator,\n type PresetName,\n type RailPlacement,\n type ResolvedPreset,\n type Skin,\n type Strings,\n DEFAULT_STRINGS,\n resolvePreset,\n} from '@gallerist/renderer-dom'\nexport { useViewerContext, GalleristContext } from './context'\nexport { type UseGallerist, useGallerist } from './useGallerist'\nexport { type CurrentItem, useCurrentItem } from './useCurrentItem'\nexport {\n Gallerist,\n type GalleristCaptionProps,\n type GalleristOpenAt,\n type GalleristOverlayProps,\n type GalleristProps,\n type GalleristRegionProps,\n} from './Gallerist'\n","import type { ViewerApi } from '@gallerist/core'\nimport { createContext, useContext } from 'react'\n\nexport const GalleristContext = createContext<ViewerApi | null>(null)\n\nexport function useViewerContext(): ViewerApi {\n const viewer = useContext(GalleristContext)\n if (!viewer)\n throw new Error(\n 'Gallerist.Overlay / Gallerist.Region / Gallerist.Caption must be used inside <Gallerist>',\n )\n return viewer\n}\n","import type { ViewerApi, ViewerState } from '@gallerist/core'\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\n\nexport interface UseGallerist {\n state: Readonly<ViewerState>\n viewer: ViewerApi\n open: ViewerApi['open']\n close: ViewerApi['close']\n next: ViewerApi['next']\n prev: ViewerApi['prev']\n nextItem: ViewerApi['nextItem']\n prevItem: ViewerApi['prevItem']\n goTo: ViewerApi['goTo']\n goToId: ViewerApi['goToId']\n zoomTo: ViewerApi['zoomTo']\n resetZoom: ViewerApi['resetZoom']\n}\n\n/** Bind a React component to a viewer's state (useSyncExternalStore) + its actions. */\nexport function useGallerist(viewer: ViewerApi): UseGallerist {\n const subscribe = useCallback((cb: () => void) => viewer.subscribe(cb), [viewer])\n const getSnapshot = useCallback(() => viewer.getState(), [viewer])\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Stabilize the action closures so they keep a constant identity across re-renders (this hook\n // re-renders on every store commit). Otherwise each commit handed consumers fresh function\n // references, defeating React.memo / dependency-array memoization downstream (S-PERF4).\n const actions = useMemo(\n () => ({\n open: (at?: Parameters<ViewerApi['open']>[0]) => viewer.open(at),\n close: () => viewer.close(),\n next: () => viewer.next(),\n prev: () => viewer.prev(),\n nextItem: () => viewer.nextItem(),\n prevItem: () => viewer.prevItem(),\n goTo: (g: number, i?: number) => viewer.goTo(g, i),\n goToId: (g: string, i?: string) => viewer.goToId(g, i),\n zoomTo: (s: number, f?: Parameters<ViewerApi['zoomTo']>[1]) => viewer.zoomTo(s, f),\n resetZoom: () => viewer.resetZoom(),\n }),\n [viewer],\n )\n\n return { state, viewer, ...actions }\n}\n","import type { Cursor, Group, Item, ViewerApi } from '@gallerist/core'\nimport { useCallback, useContext, useRef, useSyncExternalStore } from 'react'\nimport { GalleristContext } from './context'\n\n/** The viewer's current item, its group and its position. */\nexport interface CurrentItem<GM = unknown, IM = unknown> {\n /** undefined only for an empty collection */\n item: Item<IM> | undefined\n group: Group<GM, IM> | undefined\n cursor: Readonly<Cursor>\n}\n\n/**\n * The current item, group and cursor of the enclosing `<Gallerist>` (or of `viewer`). Re-renders\n * when the cursor or the collection changes, not on zoom or pan.\n */\nexport function useCurrentItem<GM = unknown, IM = unknown>(\n viewer?: ViewerApi,\n): CurrentItem<GM, IM> {\n const fromContext = useContext(GalleristContext)\n const v = viewer ?? fromContext\n if (!v) throw new Error('useCurrentItem() must be used inside <Gallerist> or given a viewer')\n // useSyncExternalStore needs a stable snapshot: the same object until the item really changes\n const last = useRef<CurrentItem | null>(null)\n const subscribe = useCallback((cb: () => void) => v.subscribe(cb), [v])\n const getSnapshot = useCallback((): CurrentItem => {\n const { g, i } = v.getState().cursor\n const group = v.getCollection().groups[g]\n const item = group?.items[i]\n const prev = last.current\n if (\n prev &&\n prev.item === item &&\n prev.group === group &&\n prev.cursor.g === g &&\n prev.cursor.i === i\n )\n return prev\n last.current = { item, group, cursor: { g, i } }\n return last.current\n }, [v])\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as CurrentItem<GM, IM>\n}\n","import {\n type Collection,\n type Cursor,\n type EventHandlers,\n type EventMap,\n type Group,\n type Item,\n type OverlaySpec,\n type RegionName,\n type ViewerApi,\n type ViewerOptions,\n createViewer,\n} from '@gallerist/core'\nimport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type ItemIndicator,\n type MountOptions,\n type PresetName,\n type RailPlacement,\n type Skin,\n type Strings,\n applyAppearance,\n mount,\n} from '@gallerist/renderer-dom'\nimport {\n type ReactNode,\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useInsertionEffect,\n useRef,\n useState,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { GalleristContext, useViewerContext } from './context'\nimport { useCurrentItem } from './useCurrentItem'\n\n/** A position to open at: group `g`, item `i` (default: the group's remembered item). */\nexport interface GalleristOpenAt {\n g: number\n i?: number\n}\n\n/**\n * `items` / `groups` / `collection` and `on` are reactive. Changed data swaps the collection in\n * place (diffed by id, keeping the cursor); data rebuilt with the same content is ignored. Every other viewer option\n * (`loop`, `itemMemory`, `startAt`, `plugins`, `overlays`, …) and the layout props (`container`,\n * `preset`, `arrows`, `rail`, `label`, `preload`, `adapters`, …) are read once; change the\n * component's `key` to apply new ones. `skin`, `accent`, `scheme` and `open` are reactive.\n */\nexport interface GalleristProps extends ViewerOptions {\n /**\n * Controlled visibility. `true` opens at the current position; `{ g, i }` opens there, and a new\n * position while open moves the cursor (applied when the value changes, so the user can still\n * navigate away from it); `false` closes. Leave it undefined to drive the viewer through the ref.\n */\n open?: boolean | GalleristOpenAt\n onClose?: () => void\n container?: HTMLElement | null\n controls?: boolean\n /** layout preset (arrow placement, auto-hide, data-gallerist-preset, the thumbnails() defaults) — UX-E. */\n preset?: PresetName\n /** visual skin, default `gallery`; other skins need their stylesheet (reactive). */\n skin?: Skin\n /** the skin's accent by name, e.g. `mint` (Gallery) or `cyan` (Console) (reactive). */\n accent?: Accent\n /** colour scheme: `dark`, `light` or `auto`; unset, the skin's own default (reactive). */\n scheme?: ColorScheme\n /** within-group arrow placement (overrides the preset default) — UX-D. */\n arrows?: ArrowLayout\n /** thumbnail rail placement (overrides the preset default) — UX-E. */\n rail?: RailPlacement\n /** auto-hide chrome on idle (overrides the preset default) — UX-C. */\n autoHide?: boolean\n /** linear traversal: ←/→ walk the flattened item sequence. Omitted, it follows the rail (on with a\n * flat thumbnails rail, off otherwise); `true` / `false` fix it. */\n linear?: boolean\n /** UI text overrides (button labels, announcements) for translation; read once at mount. */\n strings?: Partial<Strings>\n /** item position indicator: `pills` (default), `dots`, `counter` or `placard`; read once at mount. */\n itemIndicator?: ItemIndicator\n /** zoom navigator while zoomed in (overrides the preset default); read once at mount. */\n navigator?: boolean\n /** framed media: the work hangs with room around it (overrides the preset); read once at mount. */\n frame?: boolean\n /** which arrows move between groups (overrides the rail and preset default); read once at mount. */\n groupAxis?: MountOptions['groupAxis']\n /** name the group arrows after their target group (overrides the preset); read once at mount. */\n arrowLabels?: MountOptions['arrowLabels']\n /** the overlay dialog's accessible name (default `strings.dialog`); read once at mount. */\n label?: string\n /** where focus returns when the overlay closes (default: what had focus when it opened); read\n * once at mount, so pass a getter for a target that changes. */\n returnFocus?: MountOptions['returnFocus']\n /** how many groups to keep mounted ahead of and behind the current one; read once at mount. */\n preload?: MountOptions['preload']\n /** media adapters, replacing the default image adapter; read once at mount. */\n adapters?: MountOptions['adapters']\n children?: ReactNode\n}\n\n// Every viewer event, checked against EventMap (a missing or unknown name fails to compile), so\n// the forwarders below can route each one to the latest `on` handlers.\nconst EVENT_NAMES = Object.keys({\n init: 1,\n destroy: 1,\n open: 1,\n opened: 1,\n close: 1,\n closed: 1,\n beforeSlide: 1,\n slide: 1,\n afterSlide: 1,\n beforeItem: 1,\n item: 1,\n afterItem: 1,\n zoom: 1,\n zoomEnd: 1,\n load: 1,\n loaded: 1,\n loadError: 1,\n collectionChange: 1,\n fullscreenChange: 1,\n overlayClick: 1,\n} satisfies Record<keyof EventMap, 1>) as (keyof EventMap)[]\n\n// The collection a data prop describes (flat items → singleton groups). setCollection validates it.\nconst toCollection = (items?: Item[], groups?: Group[], collection?: Collection): Collection =>\n collection ?? { groups: groups ?? (items ?? []).map((it) => ({ id: it.id, items: [it] })) }\n\n// Values equal at one level of depth: identical, or arrays/plain objects with identical entries\n// (a `source: { src }`, `naturalSize`, or a re-created `overlays` array of the same specs).\nconst shallowSame = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => Object.is(v, b[i]))\n }\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n const ka = Object.keys(a)\n const rb = b as Record<string, unknown>\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => Object.is((a as Record<string, unknown>)[k], rb[k]))\n )\n}\nconst sameRecord = (a: object, b: object, skip?: string): boolean => {\n const ra = a as Record<string, unknown>\n const rb = b as Record<string, unknown>\n const ka = Object.keys(ra).filter((k) => k !== skip)\n const kb = Object.keys(rb).filter((k) => k !== skip)\n return ka.length === kb.length && ka.every((k) => shallowSame(ra[k], rb[k]))\n}\n// Same content (groups, items and their fields), even when rebuilt as fresh objects — so an\n// inline `items={photos.map(…)}` doesn't swap the collection (and reset load status) every render.\nconst sameCollection = (a: Collection, b: Readonly<Collection>): boolean =>\n a.groups.length === b.groups.length &&\n shallowSame(a.meta, b.meta) &&\n a.groups.every((g, gi) => {\n const h = b.groups[gi]\n return (\n !!h &&\n sameRecord(g, h, 'items') &&\n g.items.length === h.items.length &&\n g.items.every((it, ii) => {\n const other = h.items[ii]\n return !!other && sameRecord(it, other)\n })\n )\n })\n\nconst GalleristBase = forwardRef<ViewerApi, GalleristProps>(function Gallerist(props, ref) {\n const {\n open,\n onClose,\n container,\n controls,\n preset,\n skin,\n accent,\n scheme,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n children,\n ...options\n } = props\n const rootRef = useRef<HTMLElement | null>(null)\n const { items, groups, collection, on } = options\n\n // The viewer outlives renders, so its handlers must not capture the first render's `on`: they\n // forward to whatever `on` the latest committed render passed (a returned `false` still cancels).\n const onRef = useRef<EventHandlers | undefined>(on)\n useInsertionEffect(() => {\n onRef.current = on\n })\n // Create the viewer once (core is DOM-free → SSR-safe); options are read at construction. Lazy\n // useState (not useMemo, which React may discard) guarantees a single stable instance. Plugins\n // are deliberately NOT registered at construction — they're managed in an effect below so their\n // teardown runs on unmount (I-X2).\n const [viewer] = useState(() => {\n const { plugins: _plugins, on: _on, ...rest } = options\n const forward: EventHandlers = {}\n for (const name of EVENT_NAMES) {\n ;(forward as Record<string, (p: unknown) => unknown>)[name] = (payload) =>\n (onRef.current?.[name] as ((p: unknown) => unknown) | undefined)?.(payload)\n }\n return createViewer({ ...rest, on: forward })\n })\n\n // Data props are reactive: new data swaps the collection via setCollection (diffed by id, so the\n // cursor and zoom survive when the current item does). The first render's data is already in the\n // viewer; a new reference with the same content is ignored; invalid data (a duplicate id) is\n // reported and the viewer keeps its current collection instead of crashing the React tree.\n const appliedData = useRef({ items, groups, collection })\n useEffect(() => {\n const prev = appliedData.current\n if (prev.items === items && prev.groups === groups && prev.collection === collection) return\n appliedData.current = { items, groups, collection }\n const next = toCollection(items, groups, collection)\n if (sameCollection(next, viewer.getCollection())) return\n try {\n viewer.setCollection(next)\n } catch (error) {\n console.error('[gallerist] ignored an invalid collection update:', error)\n }\n }, [viewer, items, groups, collection])\n useImperativeHandle(ref, () => viewer, [viewer])\n\n // Register the construction-time plugins via use() and dispose them on unmount, so their\n // init()-created store subscriptions / observers / DOM don't leak on every remount (the viewer\n // itself is intentionally never destroyed — see the mount effect). Plugins are mount-time config.\n // biome-ignore lint/correctness/useExhaustiveDependencies: register once for the viewer's life\n useEffect(() => {\n const disposers = (options.plugins ?? []).map((plugin) => viewer.use(plugin))\n return () => {\n for (const dispose of disposers) dispose()\n }\n }, [viewer])\n\n // Mount the renderer once per viewer (client-side); container/controls are mount-time only. Only\n // the renderer (the DOM holder) is torn down here. The viewer is NOT destroyed on cleanup: under\n // StrictMode the simulated unmount would run viewer.destroy() on the stable useState instance,\n // permanently poisoning it (registerPlugin/nav short-circuit once destroyed) so re-registered\n // overlays/regions vanish. The viewer is plain JS (no DOM) and is reclaimed by GC once the\n // component unmounts and the renderer/hook/overlay subscriptions have all cleaned themselves up.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-once (layout opts read once)\n useEffect(() => {\n const handle = mount(viewer, {\n container: container ?? null,\n controls,\n preset,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n skin,\n accent,\n scheme,\n loop: options.loop,\n })\n rootRef.current = handle.root\n return () => {\n handle.destroy()\n rootRef.current = null\n }\n }, [viewer])\n\n // The appearance is reactive: the skin files key off root attributes, so no remount is needed.\n useEffect(() => {\n const root = rootRef.current\n if (root) applyAppearance(root, { skin, accent, scheme })\n }, [skin, accent, scheme])\n\n // `open` is compared by value, so an inline `open={{ g: 2 }}` re-applies only when g or i change\n const openG = typeof open === 'object' && open ? open.g : undefined\n const openI = typeof open === 'object' && open ? open.i : undefined\n const shown = open === undefined ? undefined : !!open\n useEffect(() => {\n if (shown === undefined) return\n // open({ g, i }) on a viewer already showing is a navigation (core)\n if (shown) viewer.open(openG === undefined ? undefined : { g: openG, i: openI })\n else viewer.close()\n }, [shown, openG, openI, viewer])\n\n useEffect(() => {\n if (!onClose) return\n return viewer.on('closed', () => onClose())\n }, [onClose, viewer])\n\n return <GalleristContext.Provider value={viewer}>{children}</GalleristContext.Provider>\n})\n\nexport interface GalleristOverlayProps {\n anchor: OverlaySpec['anchor']\n id?: string\n layer?: number\n interactive?: boolean\n visibleAt?: [number, number]\n className?: string\n children?: ReactNode\n}\n\n/** Render a React subtree as an image-space / viewport overlay via a portal (decision P0-G). */\nfunction Overlay(props: GalleristOverlayProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n // useId is SSR-deterministic (server + client agree), unlike a module-global counter.\n const autoId = useId()\n const id = props.id ?? autoId\n\n // Depend on the VALUE of the object/array props, not their identity. The common usage passes\n // inline literals (`anchor={{ kind: 'viewport', … }}`) that are a fresh reference every render;\n // keying on identity disposed+re-registered the overlay plugin on every parent render, forcing a\n // full overlay repaint. Serializing makes re-registration happen only on a real change (I-X3).\n const anchorKey = JSON.stringify(props.anchor)\n const visibleAtKey = JSON.stringify(props.visibleAt ?? null)\n // biome-ignore lint/correctness/useExhaustiveDependencies: anchor/visibleAt tracked by value key\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const spec: OverlaySpec = {\n id,\n anchor: props.anchor,\n layer: props.layer,\n interactive: props.interactive,\n visibleAt: props.visibleAt,\n class: props.className,\n render: () => node, // stable host; React owns its children through the portal\n }\n const dispose = viewer.use(() => ({ name: `overlay:${id}`, overlays: [spec] }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, id, anchorKey, visibleAtKey, props.layer, props.interactive, props.className])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristRegionProps {\n name: RegionName\n children?: ReactNode\n}\n\n/** Render a React subtree into a named renderer region (toolbar, footer, …) via a portal. */\nfunction Region(props: GalleristRegionProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n const { name } = props\n\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const dispose = viewer.use(() => ({\n name: `region:${name}`,\n regions: { [name]: () => node },\n }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, name])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristCaptionProps<GM = unknown, IM = unknown> {\n /**\n * the caption for the current item; `null`, `undefined`, `true` or `false` shows its text\n * `caption` (in a `span.gallerist-caption-text`, the theme's caption type)\n */\n children: (current: {\n item: Item<IM>\n group: Group<GM, IM>\n cursor: Readonly<Cursor>\n }) => ReactNode\n}\n\n/**\n * Render the caption for the current item with React, into the renderer's caption region (the\n * caption bar, the editorial placard, wherever the preset puts it). Re-renders on navigation and\n * collection changes. Its text is the viewer's accessible description, like a text caption.\n */\nfunction Caption<GM = unknown, IM = unknown>(props: GalleristCaptionProps<GM, IM>): ReactNode {\n const { item, group, cursor } = useCurrentItem<GM, IM>()\n const out = item && group ? props.children({ item, group, cursor }) : null\n return (\n <Region name=\"caption\">\n {out == null || typeof out === 'boolean' ? text(item?.caption) : out}\n </Region>\n )\n}\n\n// The item's own text caption, styled like the bar's text. Untyped data (JSON, a CDN page) can still\n// carry an old object-shaped caption, which React would throw on: anything but a string is none.\nfunction text(caption: unknown): ReactNode {\n return typeof caption === 'string' && caption ? (\n <span className=\"gallerist-caption-text\">{caption}</span>\n ) : null\n}\n\nexport const Gallerist = Object.assign(GalleristBase, { Overlay, Region, Caption })\n"],"mappings":"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","names":["Gallerist","import_renderer_dom","import_react","import_react","import_react","Gallerist"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -73,7 +73,8 @@ interface GalleristProps extends ViewerOptions {
|
|
|
73
73
|
rail?: RailPlacement;
|
|
74
74
|
/** auto-hide chrome on idle (overrides the preset default) — UX-C. */
|
|
75
75
|
autoHide?: boolean;
|
|
76
|
-
/** linear
|
|
76
|
+
/** linear traversal: ←/→ walk the flattened item sequence. Omitted, it follows the rail (on with a
|
|
77
|
+
* flat thumbnails rail, off otherwise); `true` / `false` fix it. */
|
|
77
78
|
linear?: boolean;
|
|
78
79
|
/** UI text overrides (button labels, announcements) for translation; read once at mount. */
|
|
79
80
|
strings?: Partial<Strings>;
|
package/dist/index.d.ts
CHANGED
|
@@ -73,7 +73,8 @@ interface GalleristProps extends ViewerOptions {
|
|
|
73
73
|
rail?: RailPlacement;
|
|
74
74
|
/** auto-hide chrome on idle (overrides the preset default) — UX-C. */
|
|
75
75
|
autoHide?: boolean;
|
|
76
|
-
/** linear
|
|
76
|
+
/** linear traversal: ←/→ walk the flattened item sequence. Omitted, it follows the rail (on with a
|
|
77
|
+
* flat thumbnails rail, off otherwise); `true` / `false` fix it. */
|
|
77
78
|
linear?: boolean;
|
|
78
79
|
/** UI text overrides (button labels, announcements) for translation; read once at mount. */
|
|
79
80
|
strings?: Partial<Strings>;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/useGallerist.ts","../src/useCurrentItem.ts","../src/Gallerist.tsx"],"sourcesContent":["// @gallerist/react — React adapter (spec §16). Thin binding over the headless store.\nexport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type GroupAxis,\n type ItemIndicator,\n type PresetName,\n type RailPlacement,\n type ResolvedPreset,\n type Skin,\n type Strings,\n DEFAULT_STRINGS,\n resolvePreset,\n} from '@gallerist/renderer-dom'\nexport { useViewerContext, GalleristContext } from './context'\nexport { type UseGallerist, useGallerist } from './useGallerist'\nexport { type CurrentItem, useCurrentItem } from './useCurrentItem'\nexport {\n Gallerist,\n type GalleristCaptionProps,\n type GalleristOpenAt,\n type GalleristOverlayProps,\n type GalleristProps,\n type GalleristRegionProps,\n} from './Gallerist'\n","import type { ViewerApi } from '@gallerist/core'\nimport { createContext, useContext } from 'react'\n\nexport const GalleristContext = createContext<ViewerApi | null>(null)\n\nexport function useViewerContext(): ViewerApi {\n const viewer = useContext(GalleristContext)\n if (!viewer)\n throw new Error(\n 'Gallerist.Overlay / Gallerist.Region / Gallerist.Caption must be used inside <Gallerist>',\n )\n return viewer\n}\n","import type { ViewerApi, ViewerState } from '@gallerist/core'\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\n\nexport interface UseGallerist {\n state: Readonly<ViewerState>\n viewer: ViewerApi\n open: ViewerApi['open']\n close: ViewerApi['close']\n next: ViewerApi['next']\n prev: ViewerApi['prev']\n nextItem: ViewerApi['nextItem']\n prevItem: ViewerApi['prevItem']\n goTo: ViewerApi['goTo']\n goToId: ViewerApi['goToId']\n zoomTo: ViewerApi['zoomTo']\n resetZoom: ViewerApi['resetZoom']\n}\n\n/** Bind a React component to a viewer's state (useSyncExternalStore) + its actions. */\nexport function useGallerist(viewer: ViewerApi): UseGallerist {\n const subscribe = useCallback((cb: () => void) => viewer.subscribe(cb), [viewer])\n const getSnapshot = useCallback(() => viewer.getState(), [viewer])\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Stabilize the action closures so they keep a constant identity across re-renders (this hook\n // re-renders on every store commit). Otherwise each commit handed consumers fresh function\n // references, defeating React.memo / dependency-array memoization downstream (S-PERF4).\n const actions = useMemo(\n () => ({\n open: (at?: Parameters<ViewerApi['open']>[0]) => viewer.open(at),\n close: () => viewer.close(),\n next: () => viewer.next(),\n prev: () => viewer.prev(),\n nextItem: () => viewer.nextItem(),\n prevItem: () => viewer.prevItem(),\n goTo: (g: number, i?: number) => viewer.goTo(g, i),\n goToId: (g: string, i?: string) => viewer.goToId(g, i),\n zoomTo: (s: number, f?: Parameters<ViewerApi['zoomTo']>[1]) => viewer.zoomTo(s, f),\n resetZoom: () => viewer.resetZoom(),\n }),\n [viewer],\n )\n\n return { state, viewer, ...actions }\n}\n","import type { Cursor, Group, Item, ViewerApi } from '@gallerist/core'\nimport { useCallback, useContext, useRef, useSyncExternalStore } from 'react'\nimport { GalleristContext } from './context'\n\n/** The viewer's current item, its group and its position. */\nexport interface CurrentItem<GM = unknown, IM = unknown> {\n /** undefined only for an empty collection */\n item: Item<IM> | undefined\n group: Group<GM, IM> | undefined\n cursor: Readonly<Cursor>\n}\n\n/**\n * The current item, group and cursor of the enclosing `<Gallerist>` (or of `viewer`). Re-renders\n * when the cursor or the collection changes, not on zoom or pan.\n */\nexport function useCurrentItem<GM = unknown, IM = unknown>(\n viewer?: ViewerApi,\n): CurrentItem<GM, IM> {\n const fromContext = useContext(GalleristContext)\n const v = viewer ?? fromContext\n if (!v) throw new Error('useCurrentItem() must be used inside <Gallerist> or given a viewer')\n // useSyncExternalStore needs a stable snapshot: the same object until the item really changes\n const last = useRef<CurrentItem | null>(null)\n const subscribe = useCallback((cb: () => void) => v.subscribe(cb), [v])\n const getSnapshot = useCallback((): CurrentItem => {\n const { g, i } = v.getState().cursor\n const group = v.getCollection().groups[g]\n const item = group?.items[i]\n const prev = last.current\n if (\n prev &&\n prev.item === item &&\n prev.group === group &&\n prev.cursor.g === g &&\n prev.cursor.i === i\n )\n return prev\n last.current = { item, group, cursor: { g, i } }\n return last.current\n }, [v])\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as CurrentItem<GM, IM>\n}\n","import {\n type Collection,\n type Cursor,\n type EventHandlers,\n type EventMap,\n type Group,\n type Item,\n type OverlaySpec,\n type RegionName,\n type ViewerApi,\n type ViewerOptions,\n createViewer,\n} from '@gallerist/core'\nimport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type ItemIndicator,\n type MountOptions,\n type PresetName,\n type RailPlacement,\n type Skin,\n type Strings,\n applyAppearance,\n mount,\n} from '@gallerist/renderer-dom'\nimport {\n type ReactNode,\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useInsertionEffect,\n useRef,\n useState,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { GalleristContext, useViewerContext } from './context'\nimport { useCurrentItem } from './useCurrentItem'\n\n/** A position to open at: group `g`, item `i` (default: the group's remembered item). */\nexport interface GalleristOpenAt {\n g: number\n i?: number\n}\n\n/**\n * `items` / `groups` / `collection` and `on` are reactive. Changed data swaps the collection in\n * place (diffed by id, keeping the cursor); data rebuilt with the same content is ignored. Every other viewer option\n * (`loop`, `itemMemory`, `startAt`, `plugins`, `overlays`, …) and the layout props (`container`,\n * `preset`, `arrows`, `rail`, `label`, `preload`, `adapters`, …) are read once; change the\n * component's `key` to apply new ones. `skin`, `accent`, `scheme` and `open` are reactive.\n */\nexport interface GalleristProps extends ViewerOptions {\n /**\n * Controlled visibility. `true` opens at the current position; `{ g, i }` opens there, and a new\n * position while open moves the cursor (applied when the value changes, so the user can still\n * navigate away from it); `false` closes. Leave it undefined to drive the viewer through the ref.\n */\n open?: boolean | GalleristOpenAt\n onClose?: () => void\n container?: HTMLElement | null\n controls?: boolean\n /** layout preset (arrow placement, auto-hide, data-gallerist-preset, the thumbnails() defaults) — UX-E. */\n preset?: PresetName\n /** visual skin, default `gallery`; other skins need their stylesheet (reactive). */\n skin?: Skin\n /** the skin's accent by name, e.g. `mint` (Gallery) or `cyan` (Console) (reactive). */\n accent?: Accent\n /** colour scheme: `dark`, `light` or `auto`; unset, the skin's own default (reactive). */\n scheme?: ColorScheme\n /** within-group arrow placement (overrides the preset default) — UX-D. */\n arrows?: ArrowLayout\n /** thumbnail rail placement (overrides the preset default) — UX-E. */\n rail?: RailPlacement\n /** auto-hide chrome on idle (overrides the preset default) — UX-C. */\n autoHide?: boolean\n /** linear flat traversal: ←/→ walks the flattened item sequence; pairs with a flat thumbnail rail. */\n linear?: boolean\n /** UI text overrides (button labels, announcements) for translation; read once at mount. */\n strings?: Partial<Strings>\n /** item position indicator: `pills` (default), `dots`, `counter` or `placard`; read once at mount. */\n itemIndicator?: ItemIndicator\n /** zoom navigator while zoomed in (overrides the preset default); read once at mount. */\n navigator?: boolean\n /** framed media: the work hangs with room around it (overrides the preset); read once at mount. */\n frame?: boolean\n /** which arrows move between groups (overrides the rail and preset default); read once at mount. */\n groupAxis?: MountOptions['groupAxis']\n /** name the group arrows after their target group (overrides the preset); read once at mount. */\n arrowLabels?: MountOptions['arrowLabels']\n /** the overlay dialog's accessible name (default `strings.dialog`); read once at mount. */\n label?: string\n /** where focus returns when the overlay closes (default: what had focus when it opened); read\n * once at mount, so pass a getter for a target that changes. */\n returnFocus?: MountOptions['returnFocus']\n /** how many groups to keep mounted ahead of and behind the current one; read once at mount. */\n preload?: MountOptions['preload']\n /** media adapters, replacing the default image adapter; read once at mount. */\n adapters?: MountOptions['adapters']\n children?: ReactNode\n}\n\n// Every viewer event, checked against EventMap (a missing or unknown name fails to compile), so\n// the forwarders below can route each one to the latest `on` handlers.\nconst EVENT_NAMES = Object.keys({\n init: 1,\n destroy: 1,\n open: 1,\n opened: 1,\n close: 1,\n closed: 1,\n beforeSlide: 1,\n slide: 1,\n afterSlide: 1,\n beforeItem: 1,\n item: 1,\n afterItem: 1,\n zoom: 1,\n zoomEnd: 1,\n load: 1,\n loaded: 1,\n loadError: 1,\n collectionChange: 1,\n fullscreenChange: 1,\n overlayClick: 1,\n} satisfies Record<keyof EventMap, 1>) as (keyof EventMap)[]\n\n// The collection a data prop describes (flat items → singleton groups). setCollection validates it.\nconst toCollection = (items?: Item[], groups?: Group[], collection?: Collection): Collection =>\n collection ?? { groups: groups ?? (items ?? []).map((it) => ({ id: it.id, items: [it] })) }\n\n// Values equal at one level of depth: identical, or arrays/plain objects with identical entries\n// (a `source: { src }`, `naturalSize`, or a re-created `overlays` array of the same specs).\nconst shallowSame = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => Object.is(v, b[i]))\n }\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n const ka = Object.keys(a)\n const rb = b as Record<string, unknown>\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => Object.is((a as Record<string, unknown>)[k], rb[k]))\n )\n}\nconst sameRecord = (a: object, b: object, skip?: string): boolean => {\n const ra = a as Record<string, unknown>\n const rb = b as Record<string, unknown>\n const ka = Object.keys(ra).filter((k) => k !== skip)\n const kb = Object.keys(rb).filter((k) => k !== skip)\n return ka.length === kb.length && ka.every((k) => shallowSame(ra[k], rb[k]))\n}\n// Same content (groups, items and their fields), even when rebuilt as fresh objects — so an\n// inline `items={photos.map(…)}` doesn't swap the collection (and reset load status) every render.\nconst sameCollection = (a: Collection, b: Readonly<Collection>): boolean =>\n a.groups.length === b.groups.length &&\n shallowSame(a.meta, b.meta) &&\n a.groups.every((g, gi) => {\n const h = b.groups[gi]\n return (\n !!h &&\n sameRecord(g, h, 'items') &&\n g.items.length === h.items.length &&\n g.items.every((it, ii) => {\n const other = h.items[ii]\n return !!other && sameRecord(it, other)\n })\n )\n })\n\nconst GalleristBase = forwardRef<ViewerApi, GalleristProps>(function Gallerist(props, ref) {\n const {\n open,\n onClose,\n container,\n controls,\n preset,\n skin,\n accent,\n scheme,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n children,\n ...options\n } = props\n const rootRef = useRef<HTMLElement | null>(null)\n const { items, groups, collection, on } = options\n\n // The viewer outlives renders, so its handlers must not capture the first render's `on`: they\n // forward to whatever `on` the latest committed render passed (a returned `false` still cancels).\n const onRef = useRef<EventHandlers | undefined>(on)\n useInsertionEffect(() => {\n onRef.current = on\n })\n // Create the viewer once (core is DOM-free → SSR-safe); options are read at construction. Lazy\n // useState (not useMemo, which React may discard) guarantees a single stable instance. Plugins\n // are deliberately NOT registered at construction — they're managed in an effect below so their\n // teardown runs on unmount (I-X2).\n const [viewer] = useState(() => {\n const { plugins: _plugins, on: _on, ...rest } = options\n const forward: EventHandlers = {}\n for (const name of EVENT_NAMES) {\n ;(forward as Record<string, (p: unknown) => unknown>)[name] = (payload) =>\n (onRef.current?.[name] as ((p: unknown) => unknown) | undefined)?.(payload)\n }\n return createViewer({ ...rest, on: forward })\n })\n\n // Data props are reactive: new data swaps the collection via setCollection (diffed by id, so the\n // cursor and zoom survive when the current item does). The first render's data is already in the\n // viewer; a new reference with the same content is ignored; invalid data (a duplicate id) is\n // reported and the viewer keeps its current collection instead of crashing the React tree.\n const appliedData = useRef({ items, groups, collection })\n useEffect(() => {\n const prev = appliedData.current\n if (prev.items === items && prev.groups === groups && prev.collection === collection) return\n appliedData.current = { items, groups, collection }\n const next = toCollection(items, groups, collection)\n if (sameCollection(next, viewer.getCollection())) return\n try {\n viewer.setCollection(next)\n } catch (error) {\n console.error('[gallerist] ignored an invalid collection update:', error)\n }\n }, [viewer, items, groups, collection])\n useImperativeHandle(ref, () => viewer, [viewer])\n\n // Register the construction-time plugins via use() and dispose them on unmount, so their\n // init()-created store subscriptions / observers / DOM don't leak on every remount (the viewer\n // itself is intentionally never destroyed — see the mount effect). Plugins are mount-time config.\n // biome-ignore lint/correctness/useExhaustiveDependencies: register once for the viewer's life\n useEffect(() => {\n const disposers = (options.plugins ?? []).map((plugin) => viewer.use(plugin))\n return () => {\n for (const dispose of disposers) dispose()\n }\n }, [viewer])\n\n // Mount the renderer once per viewer (client-side); container/controls are mount-time only. Only\n // the renderer (the DOM holder) is torn down here. The viewer is NOT destroyed on cleanup: under\n // StrictMode the simulated unmount would run viewer.destroy() on the stable useState instance,\n // permanently poisoning it (registerPlugin/nav short-circuit once destroyed) so re-registered\n // overlays/regions vanish. The viewer is plain JS (no DOM) and is reclaimed by GC once the\n // component unmounts and the renderer/hook/overlay subscriptions have all cleaned themselves up.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-once (layout opts read once)\n useEffect(() => {\n const handle = mount(viewer, {\n container: container ?? null,\n controls,\n preset,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n skin,\n accent,\n scheme,\n loop: options.loop,\n })\n rootRef.current = handle.root\n return () => {\n handle.destroy()\n rootRef.current = null\n }\n }, [viewer])\n\n // The appearance is reactive: the skin files key off root attributes, so no remount is needed.\n useEffect(() => {\n const root = rootRef.current\n if (root) applyAppearance(root, { skin, accent, scheme })\n }, [skin, accent, scheme])\n\n // `open` is compared by value, so an inline `open={{ g: 2 }}` re-applies only when g or i change\n const openG = typeof open === 'object' && open ? open.g : undefined\n const openI = typeof open === 'object' && open ? open.i : undefined\n const shown = open === undefined ? undefined : !!open\n useEffect(() => {\n if (shown === undefined) return\n // open({ g, i }) on a viewer already showing is a navigation (core)\n if (shown) viewer.open(openG === undefined ? undefined : { g: openG, i: openI })\n else viewer.close()\n }, [shown, openG, openI, viewer])\n\n useEffect(() => {\n if (!onClose) return\n return viewer.on('closed', () => onClose())\n }, [onClose, viewer])\n\n return <GalleristContext.Provider value={viewer}>{children}</GalleristContext.Provider>\n})\n\nexport interface GalleristOverlayProps {\n anchor: OverlaySpec['anchor']\n id?: string\n layer?: number\n interactive?: boolean\n visibleAt?: [number, number]\n className?: string\n children?: ReactNode\n}\n\n/** Render a React subtree as an image-space / viewport overlay via a portal (decision P0-G). */\nfunction Overlay(props: GalleristOverlayProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n // useId is SSR-deterministic (server + client agree), unlike a module-global counter.\n const autoId = useId()\n const id = props.id ?? autoId\n\n // Depend on the VALUE of the object/array props, not their identity. The common usage passes\n // inline literals (`anchor={{ kind: 'viewport', … }}`) that are a fresh reference every render;\n // keying on identity disposed+re-registered the overlay plugin on every parent render, forcing a\n // full overlay repaint. Serializing makes re-registration happen only on a real change (I-X3).\n const anchorKey = JSON.stringify(props.anchor)\n const visibleAtKey = JSON.stringify(props.visibleAt ?? null)\n // biome-ignore lint/correctness/useExhaustiveDependencies: anchor/visibleAt tracked by value key\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const spec: OverlaySpec = {\n id,\n anchor: props.anchor,\n layer: props.layer,\n interactive: props.interactive,\n visibleAt: props.visibleAt,\n class: props.className,\n render: () => node, // stable host; React owns its children through the portal\n }\n const dispose = viewer.use(() => ({ name: `overlay:${id}`, overlays: [spec] }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, id, anchorKey, visibleAtKey, props.layer, props.interactive, props.className])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristRegionProps {\n name: RegionName\n children?: ReactNode\n}\n\n/** Render a React subtree into a named renderer region (toolbar, footer, …) via a portal. */\nfunction Region(props: GalleristRegionProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n const { name } = props\n\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const dispose = viewer.use(() => ({\n name: `region:${name}`,\n regions: { [name]: () => node },\n }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, name])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristCaptionProps<GM = unknown, IM = unknown> {\n /**\n * the caption for the current item; `null`, `undefined`, `true` or `false` shows its text\n * `caption` (in a `span.gallerist-caption-text`, the theme's caption type)\n */\n children: (current: {\n item: Item<IM>\n group: Group<GM, IM>\n cursor: Readonly<Cursor>\n }) => ReactNode\n}\n\n/**\n * Render the caption for the current item with React, into the renderer's caption region (the\n * caption bar, the editorial placard, wherever the preset puts it). Re-renders on navigation and\n * collection changes. Its text is the viewer's accessible description, like a text caption.\n */\nfunction Caption<GM = unknown, IM = unknown>(props: GalleristCaptionProps<GM, IM>): ReactNode {\n const { item, group, cursor } = useCurrentItem<GM, IM>()\n const out = item && group ? props.children({ item, group, cursor }) : null\n return (\n <Region name=\"caption\">\n {out == null || typeof out === 'boolean' ? text(item?.caption) : out}\n </Region>\n )\n}\n\n// The item's own text caption, styled like the bar's text. Untyped data (JSON, a CDN page) can still\n// carry an old object-shaped caption, which React would throw on: anything but a string is none.\nfunction text(caption: unknown): ReactNode {\n return typeof caption === 'string' && caption ? (\n <span className=\"gallerist-caption-text\">{caption}</span>\n ) : null\n}\n\nexport const Gallerist = Object.assign(GalleristBase, { Overlay, Region, Caption })\n"],"mappings":";;;AACA;AAAA,EAWE;AAAA,EACA;AAAA,OACK;;;ACbP,SAAS,eAAe,kBAAkB;AAEnC,IAAM,mBAAmB,cAAgC,IAAI;AAE7D,SAAS,mBAA8B;AAC5C,QAAM,SAAS,WAAW,gBAAgB;AAC1C,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AACF,SAAO;AACT;;;ACXA,SAAS,aAAa,SAAS,4BAA4B;AAkBpD,SAAS,aAAa,QAAiC;AAC5D,QAAM,YAAY,YAAY,CAAC,OAAmB,OAAO,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC;AAChF,QAAM,cAAc,YAAY,MAAM,OAAO,SAAS,GAAG,CAAC,MAAM,CAAC;AACjE,QAAM,QAAQ,qBAAqB,WAAW,aAAa,WAAW;AAKtE,QAAM,UAAU;AAAA,IACd,OAAO;AAAA,MACL,MAAM,CAAC,OAA0C,OAAO,KAAK,EAAE;AAAA,MAC/D,OAAO,MAAM,OAAO,MAAM;AAAA,MAC1B,MAAM,MAAM,OAAO,KAAK;AAAA,MACxB,MAAM,MAAM,OAAO,KAAK;AAAA,MACxB,UAAU,MAAM,OAAO,SAAS;AAAA,MAChC,UAAU,MAAM,OAAO,SAAS;AAAA,MAChC,MAAM,CAAC,GAAW,MAAe,OAAO,KAAK,GAAG,CAAC;AAAA,MACjD,QAAQ,CAAC,GAAW,MAAe,OAAO,OAAO,GAAG,CAAC;AAAA,MACrD,QAAQ,CAAC,GAAW,MAA2C,OAAO,OAAO,GAAG,CAAC;AAAA,MACjF,WAAW,MAAM,OAAO,UAAU;AAAA,IACpC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,QAAQ,GAAG,QAAQ;AACrC;;;AC3CA,SAAS,eAAAA,cAAa,cAAAC,aAAY,QAAQ,wBAAAC,6BAA4B;AAe/D,SAAS,eACd,QACqB;AACrB,QAAM,cAAcC,YAAW,gBAAgB;AAC/C,QAAM,IAAI,UAAU;AACpB,MAAI,CAAC,EAAG,OAAM,IAAI,MAAM,oEAAoE;AAE5F,QAAM,OAAO,OAA2B,IAAI;AAC5C,QAAM,YAAYC,aAAY,CAAC,OAAmB,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;AACtE,QAAM,cAAcA,aAAY,MAAmB;AACjD,UAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE;AAC9B,UAAM,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC;AACxC,UAAM,OAAO,OAAO,MAAM,CAAC;AAC3B,UAAM,OAAO,KAAK;AAClB,QACE,QACA,KAAK,SAAS,QACd,KAAK,UAAU,SACf,KAAK,OAAO,MAAM,KAClB,KAAK,OAAO,MAAM;AAElB,aAAO;AACT,SAAK,UAAU,EAAE,MAAM,OAAO,QAAQ,EAAE,GAAG,EAAE,EAAE;AAC/C,WAAO,KAAK;AAAA,EACd,GAAG,CAAC,CAAC,CAAC;AACN,SAAOC,sBAAqB,WAAW,aAAa,WAAW;AACjE;;;AC1CA;AAAA,EAWE;AAAA,OACK;AACP;AAAA,EAUE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAoRpB;AA/MT,IAAM,cAAc,OAAO,KAAK;AAAA,EAC9B,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,WAAW;AAAA,EACX,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,cAAc;AAChB,CAAqC;AAGrC,IAAM,eAAe,CAAC,OAAgB,QAAkB,eACtD,cAAc,EAAE,QAAQ,WAAW,SAAS,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE;AAI5F,IAAM,cAAc,CAAC,GAAY,MAAwB;AACvD,MAAI,OAAO,GAAG,GAAG,CAAC,EAAG,QAAO;AAC5B,MAAI,MAAM,QAAQ,CAAC,KAAK,MAAM,QAAQ,CAAC,GAAG;AACxC,WAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;AAAA,EACtE;AACA,MAAI,CAAC,KAAK,CAAC,KAAK,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AACvE,QAAM,KAAK,OAAO,KAAK,CAAC;AACxB,QAAM,KAAK;AACX,SACE,GAAG,WAAW,OAAO,KAAK,CAAC,EAAE,UAC7B,GAAG,MAAM,CAAC,MAAM,OAAO,GAAI,EAA8B,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAEvE;AACA,IAAM,aAAa,CAAC,GAAW,GAAW,SAA2B;AACnE,QAAM,KAAK;AACX,QAAM,KAAK;AACX,QAAM,KAAK,OAAO,KAAK,EAAE,EAAE,OAAO,CAAC,MAAM,MAAM,IAAI;AACnD,QAAM,KAAK,OAAO,KAAK,EAAE,EAAE,OAAO,CAAC,MAAM,MAAM,IAAI;AACnD,SAAO,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;AAC7E;AAGA,IAAM,iBAAiB,CAAC,GAAe,MACrC,EAAE,OAAO,WAAW,EAAE,OAAO,UAC7B,YAAY,EAAE,MAAM,EAAE,IAAI,KAC1B,EAAE,OAAO,MAAM,CAAC,GAAG,OAAO;AACxB,QAAM,IAAI,EAAE,OAAO,EAAE;AACrB,SACE,CAAC,CAAC,KACF,WAAW,GAAG,GAAG,OAAO,KACxB,EAAE,MAAM,WAAW,EAAE,MAAM,UAC3B,EAAE,MAAM,MAAM,CAAC,IAAI,OAAO;AACxB,UAAM,QAAQ,EAAE,MAAM,EAAE;AACxB,WAAO,CAAC,CAAC,SAAS,WAAW,IAAI,KAAK;AAAA,EACxC,CAAC;AAEL,CAAC;AAEH,IAAM,gBAAgB,WAAsC,SAAS,UAAU,OAAO,KAAK;AACzF,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,UAAUC,QAA2B,IAAI;AAC/C,QAAM,EAAE,OAAO,QAAQ,YAAY,GAAG,IAAI;AAI1C,QAAM,QAAQA,QAAkC,EAAE;AAClD,qBAAmB,MAAM;AACvB,UAAM,UAAU;AAAA,EAClB,CAAC;AAKD,QAAM,CAAC,MAAM,IAAI,SAAS,MAAM;AAC9B,UAAM,EAAE,SAAS,UAAU,IAAI,KAAK,GAAG,KAAK,IAAI;AAChD,UAAM,UAAyB,CAAC;AAChC,eAAW,QAAQ,aAAa;AAC9B;AAAC,MAAC,QAAoD,IAAI,IAAI,CAAC,YAC5D,MAAM,UAAU,IAAI,IAA8C,OAAO;AAAA,IAC9E;AACA,WAAO,aAAa,EAAE,GAAG,MAAM,IAAI,QAAQ,CAAC;AAAA,EAC9C,CAAC;AAMD,QAAM,cAAcA,QAAO,EAAE,OAAO,QAAQ,WAAW,CAAC;AACxD,YAAU,MAAM;AACd,UAAM,OAAO,YAAY;AACzB,QAAI,KAAK,UAAU,SAAS,KAAK,WAAW,UAAU,KAAK,eAAe,WAAY;AACtF,gBAAY,UAAU,EAAE,OAAO,QAAQ,WAAW;AAClD,UAAM,OAAO,aAAa,OAAO,QAAQ,UAAU;AACnD,QAAI,eAAe,MAAM,OAAO,cAAc,CAAC,EAAG;AAClD,QAAI;AACF,aAAO,cAAc,IAAI;AAAA,IAC3B,SAAS,OAAO;AACd,cAAQ,MAAM,qDAAqD,KAAK;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,QAAQ,UAAU,CAAC;AACtC,sBAAoB,KAAK,MAAM,QAAQ,CAAC,MAAM,CAAC;AAM/C,YAAU,MAAM;AACd,UAAM,aAAa,QAAQ,WAAW,CAAC,GAAG,IAAI,CAAC,WAAW,OAAO,IAAI,MAAM,CAAC;AAC5E,WAAO,MAAM;AACX,iBAAW,WAAW,UAAW,SAAQ;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AASX,YAAU,MAAM;AACd,UAAM,SAAS,MAAM,QAAQ;AAAA,MAC3B,WAAW,aAAa;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,QAAQ;AAAA,IAChB,CAAC;AACD,YAAQ,UAAU,OAAO;AACzB,WAAO,MAAM;AACX,aAAO,QAAQ;AACf,cAAQ,UAAU;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,KAAM,iBAAgB,MAAM,EAAE,MAAM,QAAQ,OAAO,CAAC;AAAA,EAC1D,GAAG,CAAC,MAAM,QAAQ,MAAM,CAAC;AAGzB,QAAM,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI;AAC1D,QAAM,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI;AAC1D,QAAM,QAAQ,SAAS,SAAY,SAAY,CAAC,CAAC;AACjD,YAAU,MAAM;AACd,QAAI,UAAU,OAAW;AAEzB,QAAI,MAAO,QAAO,KAAK,UAAU,SAAY,SAAY,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC;AAAA,QAC1E,QAAO,MAAM;AAAA,EACpB,GAAG,CAAC,OAAO,OAAO,OAAO,MAAM,CAAC;AAEhC,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,WAAO,OAAO,GAAG,UAAU,MAAM,QAAQ,CAAC;AAAA,EAC5C,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,SAAO,oBAAC,iBAAiB,UAAjB,EAA0B,OAAO,QAAS,UAAS;AAC7D,CAAC;AAaD,SAAS,QAAQ,OAAyC;AACxD,QAAM,SAAS,iBAAiB;AAChC,QAAM,CAAC,MAAM,OAAO,IAAI,SAA6B,IAAI;AAEzD,QAAM,SAAS,MAAM;AACrB,QAAM,KAAK,MAAM,MAAM;AAMvB,QAAM,YAAY,KAAK,UAAU,MAAM,MAAM;AAC7C,QAAM,eAAe,KAAK,UAAU,MAAM,aAAa,IAAI;AAE3D,YAAU,MAAM;AACd,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,YAAQ,IAAI;AACZ,UAAM,OAAoB;AAAA,MACxB;AAAA,MACA,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,aAAa,MAAM;AAAA,MACnB,WAAW,MAAM;AAAA,MACjB,OAAO,MAAM;AAAA,MACb,QAAQ,MAAM;AAAA;AAAA,IAChB;AACA,UAAM,UAAU,OAAO,IAAI,OAAO,EAAE,MAAM,WAAW,EAAE,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE;AAC9E,WAAO,MAAM;AACX,cAAQ;AACR,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,IAAI,WAAW,cAAc,MAAM,OAAO,MAAM,aAAa,MAAM,SAAS,CAAC;AAEzF,SAAO,OAAO,aAAa,MAAM,UAAU,IAAI,IAAI;AACrD;AAQA,SAAS,OAAO,OAAwC;AACtD,QAAM,SAAS,iBAAiB;AAChC,QAAM,CAAC,MAAM,OAAO,IAAI,SAA6B,IAAI;AACzD,QAAM,EAAE,KAAK,IAAI;AAEjB,YAAU,MAAM;AACd,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,YAAQ,IAAI;AACZ,UAAM,UAAU,OAAO,IAAI,OAAO;AAAA,MAChC,MAAM,UAAU,IAAI;AAAA,MACpB,SAAS,EAAE,CAAC,IAAI,GAAG,MAAM,KAAK;AAAA,IAChC,EAAE;AACF,WAAO,MAAM;AACX,cAAQ;AACR,cAAQ,IAAI;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,SAAO,OAAO,aAAa,MAAM,UAAU,IAAI,IAAI;AACrD;AAmBA,SAAS,QAAoC,OAAiD;AAC5F,QAAM,EAAE,MAAM,OAAO,OAAO,IAAI,eAAuB;AACvD,QAAM,MAAM,QAAQ,QAAQ,MAAM,SAAS,EAAE,MAAM,OAAO,OAAO,CAAC,IAAI;AACtE,SACE,oBAAC,UAAO,MAAK,WACV,iBAAO,QAAQ,OAAO,QAAQ,YAAY,KAAK,MAAM,OAAO,IAAI,KACnE;AAEJ;AAIA,SAAS,KAAK,SAA6B;AACzC,SAAO,OAAO,YAAY,YAAY,UACpC,oBAAC,UAAK,WAAU,0BAA0B,mBAAQ,IAChD;AACN;AAEO,IAAMC,aAAY,OAAO,OAAO,eAAe,EAAE,SAAS,QAAQ,QAAQ,CAAC;","names":["useCallback","useContext","useSyncExternalStore","useContext","useCallback","useSyncExternalStore","useRef","useRef","Gallerist"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/useGallerist.ts","../src/useCurrentItem.ts","../src/Gallerist.tsx"],"sourcesContent":["// @gallerist/react — React adapter (spec §16). Thin binding over the headless store.\nexport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type GroupAxis,\n type ItemIndicator,\n type PresetName,\n type RailPlacement,\n type ResolvedPreset,\n type Skin,\n type Strings,\n DEFAULT_STRINGS,\n resolvePreset,\n} from '@gallerist/renderer-dom'\nexport { useViewerContext, GalleristContext } from './context'\nexport { type UseGallerist, useGallerist } from './useGallerist'\nexport { type CurrentItem, useCurrentItem } from './useCurrentItem'\nexport {\n Gallerist,\n type GalleristCaptionProps,\n type GalleristOpenAt,\n type GalleristOverlayProps,\n type GalleristProps,\n type GalleristRegionProps,\n} from './Gallerist'\n","import type { ViewerApi } from '@gallerist/core'\nimport { createContext, useContext } from 'react'\n\nexport const GalleristContext = createContext<ViewerApi | null>(null)\n\nexport function useViewerContext(): ViewerApi {\n const viewer = useContext(GalleristContext)\n if (!viewer)\n throw new Error(\n 'Gallerist.Overlay / Gallerist.Region / Gallerist.Caption must be used inside <Gallerist>',\n )\n return viewer\n}\n","import type { ViewerApi, ViewerState } from '@gallerist/core'\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\n\nexport interface UseGallerist {\n state: Readonly<ViewerState>\n viewer: ViewerApi\n open: ViewerApi['open']\n close: ViewerApi['close']\n next: ViewerApi['next']\n prev: ViewerApi['prev']\n nextItem: ViewerApi['nextItem']\n prevItem: ViewerApi['prevItem']\n goTo: ViewerApi['goTo']\n goToId: ViewerApi['goToId']\n zoomTo: ViewerApi['zoomTo']\n resetZoom: ViewerApi['resetZoom']\n}\n\n/** Bind a React component to a viewer's state (useSyncExternalStore) + its actions. */\nexport function useGallerist(viewer: ViewerApi): UseGallerist {\n const subscribe = useCallback((cb: () => void) => viewer.subscribe(cb), [viewer])\n const getSnapshot = useCallback(() => viewer.getState(), [viewer])\n const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n\n // Stabilize the action closures so they keep a constant identity across re-renders (this hook\n // re-renders on every store commit). Otherwise each commit handed consumers fresh function\n // references, defeating React.memo / dependency-array memoization downstream (S-PERF4).\n const actions = useMemo(\n () => ({\n open: (at?: Parameters<ViewerApi['open']>[0]) => viewer.open(at),\n close: () => viewer.close(),\n next: () => viewer.next(),\n prev: () => viewer.prev(),\n nextItem: () => viewer.nextItem(),\n prevItem: () => viewer.prevItem(),\n goTo: (g: number, i?: number) => viewer.goTo(g, i),\n goToId: (g: string, i?: string) => viewer.goToId(g, i),\n zoomTo: (s: number, f?: Parameters<ViewerApi['zoomTo']>[1]) => viewer.zoomTo(s, f),\n resetZoom: () => viewer.resetZoom(),\n }),\n [viewer],\n )\n\n return { state, viewer, ...actions }\n}\n","import type { Cursor, Group, Item, ViewerApi } from '@gallerist/core'\nimport { useCallback, useContext, useRef, useSyncExternalStore } from 'react'\nimport { GalleristContext } from './context'\n\n/** The viewer's current item, its group and its position. */\nexport interface CurrentItem<GM = unknown, IM = unknown> {\n /** undefined only for an empty collection */\n item: Item<IM> | undefined\n group: Group<GM, IM> | undefined\n cursor: Readonly<Cursor>\n}\n\n/**\n * The current item, group and cursor of the enclosing `<Gallerist>` (or of `viewer`). Re-renders\n * when the cursor or the collection changes, not on zoom or pan.\n */\nexport function useCurrentItem<GM = unknown, IM = unknown>(\n viewer?: ViewerApi,\n): CurrentItem<GM, IM> {\n const fromContext = useContext(GalleristContext)\n const v = viewer ?? fromContext\n if (!v) throw new Error('useCurrentItem() must be used inside <Gallerist> or given a viewer')\n // useSyncExternalStore needs a stable snapshot: the same object until the item really changes\n const last = useRef<CurrentItem | null>(null)\n const subscribe = useCallback((cb: () => void) => v.subscribe(cb), [v])\n const getSnapshot = useCallback((): CurrentItem => {\n const { g, i } = v.getState().cursor\n const group = v.getCollection().groups[g]\n const item = group?.items[i]\n const prev = last.current\n if (\n prev &&\n prev.item === item &&\n prev.group === group &&\n prev.cursor.g === g &&\n prev.cursor.i === i\n )\n return prev\n last.current = { item, group, cursor: { g, i } }\n return last.current\n }, [v])\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as CurrentItem<GM, IM>\n}\n","import {\n type Collection,\n type Cursor,\n type EventHandlers,\n type EventMap,\n type Group,\n type Item,\n type OverlaySpec,\n type RegionName,\n type ViewerApi,\n type ViewerOptions,\n createViewer,\n} from '@gallerist/core'\nimport {\n type Accent,\n type ArrowLayout,\n type ColorScheme,\n type ItemIndicator,\n type MountOptions,\n type PresetName,\n type RailPlacement,\n type Skin,\n type Strings,\n applyAppearance,\n mount,\n} from '@gallerist/renderer-dom'\nimport {\n type ReactNode,\n forwardRef,\n useEffect,\n useId,\n useImperativeHandle,\n useInsertionEffect,\n useRef,\n useState,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { GalleristContext, useViewerContext } from './context'\nimport { useCurrentItem } from './useCurrentItem'\n\n/** A position to open at: group `g`, item `i` (default: the group's remembered item). */\nexport interface GalleristOpenAt {\n g: number\n i?: number\n}\n\n/**\n * `items` / `groups` / `collection` and `on` are reactive. Changed data swaps the collection in\n * place (diffed by id, keeping the cursor); data rebuilt with the same content is ignored. Every other viewer option\n * (`loop`, `itemMemory`, `startAt`, `plugins`, `overlays`, …) and the layout props (`container`,\n * `preset`, `arrows`, `rail`, `label`, `preload`, `adapters`, …) are read once; change the\n * component's `key` to apply new ones. `skin`, `accent`, `scheme` and `open` are reactive.\n */\nexport interface GalleristProps extends ViewerOptions {\n /**\n * Controlled visibility. `true` opens at the current position; `{ g, i }` opens there, and a new\n * position while open moves the cursor (applied when the value changes, so the user can still\n * navigate away from it); `false` closes. Leave it undefined to drive the viewer through the ref.\n */\n open?: boolean | GalleristOpenAt\n onClose?: () => void\n container?: HTMLElement | null\n controls?: boolean\n /** layout preset (arrow placement, auto-hide, data-gallerist-preset, the thumbnails() defaults) — UX-E. */\n preset?: PresetName\n /** visual skin, default `gallery`; other skins need their stylesheet (reactive). */\n skin?: Skin\n /** the skin's accent by name, e.g. `mint` (Gallery) or `cyan` (Console) (reactive). */\n accent?: Accent\n /** colour scheme: `dark`, `light` or `auto`; unset, the skin's own default (reactive). */\n scheme?: ColorScheme\n /** within-group arrow placement (overrides the preset default) — UX-D. */\n arrows?: ArrowLayout\n /** thumbnail rail placement (overrides the preset default) — UX-E. */\n rail?: RailPlacement\n /** auto-hide chrome on idle (overrides the preset default) — UX-C. */\n autoHide?: boolean\n /** linear traversal: ←/→ walk the flattened item sequence. Omitted, it follows the rail (on with a\n * flat thumbnails rail, off otherwise); `true` / `false` fix it. */\n linear?: boolean\n /** UI text overrides (button labels, announcements) for translation; read once at mount. */\n strings?: Partial<Strings>\n /** item position indicator: `pills` (default), `dots`, `counter` or `placard`; read once at mount. */\n itemIndicator?: ItemIndicator\n /** zoom navigator while zoomed in (overrides the preset default); read once at mount. */\n navigator?: boolean\n /** framed media: the work hangs with room around it (overrides the preset); read once at mount. */\n frame?: boolean\n /** which arrows move between groups (overrides the rail and preset default); read once at mount. */\n groupAxis?: MountOptions['groupAxis']\n /** name the group arrows after their target group (overrides the preset); read once at mount. */\n arrowLabels?: MountOptions['arrowLabels']\n /** the overlay dialog's accessible name (default `strings.dialog`); read once at mount. */\n label?: string\n /** where focus returns when the overlay closes (default: what had focus when it opened); read\n * once at mount, so pass a getter for a target that changes. */\n returnFocus?: MountOptions['returnFocus']\n /** how many groups to keep mounted ahead of and behind the current one; read once at mount. */\n preload?: MountOptions['preload']\n /** media adapters, replacing the default image adapter; read once at mount. */\n adapters?: MountOptions['adapters']\n children?: ReactNode\n}\n\n// Every viewer event, checked against EventMap (a missing or unknown name fails to compile), so\n// the forwarders below can route each one to the latest `on` handlers.\nconst EVENT_NAMES = Object.keys({\n init: 1,\n destroy: 1,\n open: 1,\n opened: 1,\n close: 1,\n closed: 1,\n beforeSlide: 1,\n slide: 1,\n afterSlide: 1,\n beforeItem: 1,\n item: 1,\n afterItem: 1,\n zoom: 1,\n zoomEnd: 1,\n load: 1,\n loaded: 1,\n loadError: 1,\n collectionChange: 1,\n fullscreenChange: 1,\n overlayClick: 1,\n} satisfies Record<keyof EventMap, 1>) as (keyof EventMap)[]\n\n// The collection a data prop describes (flat items → singleton groups). setCollection validates it.\nconst toCollection = (items?: Item[], groups?: Group[], collection?: Collection): Collection =>\n collection ?? { groups: groups ?? (items ?? []).map((it) => ({ id: it.id, items: [it] })) }\n\n// Values equal at one level of depth: identical, or arrays/plain objects with identical entries\n// (a `source: { src }`, `naturalSize`, or a re-created `overlays` array of the same specs).\nconst shallowSame = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((v, i) => Object.is(v, b[i]))\n }\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false\n const ka = Object.keys(a)\n const rb = b as Record<string, unknown>\n return (\n ka.length === Object.keys(b).length &&\n ka.every((k) => Object.is((a as Record<string, unknown>)[k], rb[k]))\n )\n}\nconst sameRecord = (a: object, b: object, skip?: string): boolean => {\n const ra = a as Record<string, unknown>\n const rb = b as Record<string, unknown>\n const ka = Object.keys(ra).filter((k) => k !== skip)\n const kb = Object.keys(rb).filter((k) => k !== skip)\n return ka.length === kb.length && ka.every((k) => shallowSame(ra[k], rb[k]))\n}\n// Same content (groups, items and their fields), even when rebuilt as fresh objects — so an\n// inline `items={photos.map(…)}` doesn't swap the collection (and reset load status) every render.\nconst sameCollection = (a: Collection, b: Readonly<Collection>): boolean =>\n a.groups.length === b.groups.length &&\n shallowSame(a.meta, b.meta) &&\n a.groups.every((g, gi) => {\n const h = b.groups[gi]\n return (\n !!h &&\n sameRecord(g, h, 'items') &&\n g.items.length === h.items.length &&\n g.items.every((it, ii) => {\n const other = h.items[ii]\n return !!other && sameRecord(it, other)\n })\n )\n })\n\nconst GalleristBase = forwardRef<ViewerApi, GalleristProps>(function Gallerist(props, ref) {\n const {\n open,\n onClose,\n container,\n controls,\n preset,\n skin,\n accent,\n scheme,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n children,\n ...options\n } = props\n const rootRef = useRef<HTMLElement | null>(null)\n const { items, groups, collection, on } = options\n\n // The viewer outlives renders, so its handlers must not capture the first render's `on`: they\n // forward to whatever `on` the latest committed render passed (a returned `false` still cancels).\n const onRef = useRef<EventHandlers | undefined>(on)\n useInsertionEffect(() => {\n onRef.current = on\n })\n // Create the viewer once (core is DOM-free → SSR-safe); options are read at construction. Lazy\n // useState (not useMemo, which React may discard) guarantees a single stable instance. Plugins\n // are deliberately NOT registered at construction — they're managed in an effect below so their\n // teardown runs on unmount (I-X2).\n const [viewer] = useState(() => {\n const { plugins: _plugins, on: _on, ...rest } = options\n const forward: EventHandlers = {}\n for (const name of EVENT_NAMES) {\n ;(forward as Record<string, (p: unknown) => unknown>)[name] = (payload) =>\n (onRef.current?.[name] as ((p: unknown) => unknown) | undefined)?.(payload)\n }\n return createViewer({ ...rest, on: forward })\n })\n\n // Data props are reactive: new data swaps the collection via setCollection (diffed by id, so the\n // cursor and zoom survive when the current item does). The first render's data is already in the\n // viewer; a new reference with the same content is ignored; invalid data (a duplicate id) is\n // reported and the viewer keeps its current collection instead of crashing the React tree.\n const appliedData = useRef({ items, groups, collection })\n useEffect(() => {\n const prev = appliedData.current\n if (prev.items === items && prev.groups === groups && prev.collection === collection) return\n appliedData.current = { items, groups, collection }\n const next = toCollection(items, groups, collection)\n if (sameCollection(next, viewer.getCollection())) return\n try {\n viewer.setCollection(next)\n } catch (error) {\n console.error('[gallerist] ignored an invalid collection update:', error)\n }\n }, [viewer, items, groups, collection])\n useImperativeHandle(ref, () => viewer, [viewer])\n\n // Register the construction-time plugins via use() and dispose them on unmount, so their\n // init()-created store subscriptions / observers / DOM don't leak on every remount (the viewer\n // itself is intentionally never destroyed — see the mount effect). Plugins are mount-time config.\n // biome-ignore lint/correctness/useExhaustiveDependencies: register once for the viewer's life\n useEffect(() => {\n const disposers = (options.plugins ?? []).map((plugin) => viewer.use(plugin))\n return () => {\n for (const dispose of disposers) dispose()\n }\n }, [viewer])\n\n // Mount the renderer once per viewer (client-side); container/controls are mount-time only. Only\n // the renderer (the DOM holder) is torn down here. The viewer is NOT destroyed on cleanup: under\n // StrictMode the simulated unmount would run viewer.destroy() on the stable useState instance,\n // permanently poisoning it (registerPlugin/nav short-circuit once destroyed) so re-registered\n // overlays/regions vanish. The viewer is plain JS (no DOM) and is reclaimed by GC once the\n // component unmounts and the renderer/hook/overlay subscriptions have all cleaned themselves up.\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentional mount-once (layout opts read once)\n useEffect(() => {\n const handle = mount(viewer, {\n container: container ?? null,\n controls,\n preset,\n arrows,\n rail,\n autoHide,\n linear,\n strings,\n itemIndicator,\n navigator,\n frame,\n groupAxis,\n arrowLabels,\n label,\n returnFocus,\n preload,\n adapters,\n skin,\n accent,\n scheme,\n loop: options.loop,\n })\n rootRef.current = handle.root\n return () => {\n handle.destroy()\n rootRef.current = null\n }\n }, [viewer])\n\n // The appearance is reactive: the skin files key off root attributes, so no remount is needed.\n useEffect(() => {\n const root = rootRef.current\n if (root) applyAppearance(root, { skin, accent, scheme })\n }, [skin, accent, scheme])\n\n // `open` is compared by value, so an inline `open={{ g: 2 }}` re-applies only when g or i change\n const openG = typeof open === 'object' && open ? open.g : undefined\n const openI = typeof open === 'object' && open ? open.i : undefined\n const shown = open === undefined ? undefined : !!open\n useEffect(() => {\n if (shown === undefined) return\n // open({ g, i }) on a viewer already showing is a navigation (core)\n if (shown) viewer.open(openG === undefined ? undefined : { g: openG, i: openI })\n else viewer.close()\n }, [shown, openG, openI, viewer])\n\n useEffect(() => {\n if (!onClose) return\n return viewer.on('closed', () => onClose())\n }, [onClose, viewer])\n\n return <GalleristContext.Provider value={viewer}>{children}</GalleristContext.Provider>\n})\n\nexport interface GalleristOverlayProps {\n anchor: OverlaySpec['anchor']\n id?: string\n layer?: number\n interactive?: boolean\n visibleAt?: [number, number]\n className?: string\n children?: ReactNode\n}\n\n/** Render a React subtree as an image-space / viewport overlay via a portal (decision P0-G). */\nfunction Overlay(props: GalleristOverlayProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n // useId is SSR-deterministic (server + client agree), unlike a module-global counter.\n const autoId = useId()\n const id = props.id ?? autoId\n\n // Depend on the VALUE of the object/array props, not their identity. The common usage passes\n // inline literals (`anchor={{ kind: 'viewport', … }}`) that are a fresh reference every render;\n // keying on identity disposed+re-registered the overlay plugin on every parent render, forcing a\n // full overlay repaint. Serializing makes re-registration happen only on a real change (I-X3).\n const anchorKey = JSON.stringify(props.anchor)\n const visibleAtKey = JSON.stringify(props.visibleAt ?? null)\n // biome-ignore lint/correctness/useExhaustiveDependencies: anchor/visibleAt tracked by value key\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const spec: OverlaySpec = {\n id,\n anchor: props.anchor,\n layer: props.layer,\n interactive: props.interactive,\n visibleAt: props.visibleAt,\n class: props.className,\n render: () => node, // stable host; React owns its children through the portal\n }\n const dispose = viewer.use(() => ({ name: `overlay:${id}`, overlays: [spec] }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, id, anchorKey, visibleAtKey, props.layer, props.interactive, props.className])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristRegionProps {\n name: RegionName\n children?: ReactNode\n}\n\n/** Render a React subtree into a named renderer region (toolbar, footer, …) via a portal. */\nfunction Region(props: GalleristRegionProps): ReactNode {\n const viewer = useViewerContext()\n const [host, setHost] = useState<HTMLElement | null>(null)\n const { name } = props\n\n useEffect(() => {\n const node = document.createElement('div')\n setHost(node)\n const dispose = viewer.use(() => ({\n name: `region:${name}`,\n regions: { [name]: () => node },\n }))\n return () => {\n dispose()\n setHost(null)\n }\n }, [viewer, name])\n\n return host ? createPortal(props.children, host) : null\n}\n\nexport interface GalleristCaptionProps<GM = unknown, IM = unknown> {\n /**\n * the caption for the current item; `null`, `undefined`, `true` or `false` shows its text\n * `caption` (in a `span.gallerist-caption-text`, the theme's caption type)\n */\n children: (current: {\n item: Item<IM>\n group: Group<GM, IM>\n cursor: Readonly<Cursor>\n }) => ReactNode\n}\n\n/**\n * Render the caption for the current item with React, into the renderer's caption region (the\n * caption bar, the editorial placard, wherever the preset puts it). Re-renders on navigation and\n * collection changes. Its text is the viewer's accessible description, like a text caption.\n */\nfunction Caption<GM = unknown, IM = unknown>(props: GalleristCaptionProps<GM, IM>): ReactNode {\n const { item, group, cursor } = useCurrentItem<GM, IM>()\n const out = item && group ? props.children({ item, group, cursor }) : null\n return (\n <Region name=\"caption\">\n {out == null || typeof out === 'boolean' ? text(item?.caption) : out}\n </Region>\n )\n}\n\n// The item's own text caption, styled like the bar's text. Untyped data (JSON, a CDN page) can still\n// carry an old object-shaped caption, which React would throw on: anything but a string is none.\nfunction text(caption: unknown): ReactNode {\n return typeof caption === 'string' && caption ? (\n <span className=\"gallerist-caption-text\">{caption}</span>\n ) : null\n}\n\nexport const Gallerist = Object.assign(GalleristBase, { Overlay, Region, Caption })\n"],"mappings":"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","names":["useCallback","useContext","useSyncExternalStore","useContext","useCallback","useSyncExternalStore","useRef","useRef","Gallerist"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gallerist/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "React adapter for Gallerist — <Gallerist> component, useGallerist() hook, JSX overlay/region portals.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"react-dom": "^18.3.1",
|
|
40
40
|
"tsup": "^8.3.5",
|
|
41
41
|
"typescript": "^5.7.2",
|
|
42
|
-
"@gallerist/core": "0.3.
|
|
43
|
-
"@gallerist/renderer-dom": "0.3.
|
|
42
|
+
"@gallerist/core": "0.3.1",
|
|
43
|
+
"@gallerist/renderer-dom": "0.3.1"
|
|
44
44
|
},
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|