@allxsmith/bestax-bulma 5.64.0 → 5.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Carousel.js +12 -66
- package/dist/esm/components/Carousel.js.map +1 -1
- package/dist/esm/components/Lightbox.js +756 -0
- package/dist/esm/components/Lightbox.js.map +1 -0
- package/dist/esm/components/Reveal.js +1 -24
- package/dist/esm/components/Reveal.js.map +1 -1
- package/dist/esm/helpers/chevronIcons.js +24 -0
- package/dist/esm/helpers/chevronIcons.js.map +1 -0
- package/dist/esm/helpers/usePrefersReducedMotion.js +34 -0
- package/dist/esm/helpers/usePrefersReducedMotion.js.map +1 -0
- package/dist/esm/helpers/useSwipe.js +82 -0
- package/dist/esm/helpers/useSwipe.js.map +1 -0
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +924 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +1 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Lightbox.d.ts +244 -0
- package/dist/types/helpers/chevronIcons.d.ts +14 -0
- package/dist/types/helpers/usePrefersReducedMotion.d.ts +10 -0
- package/dist/types/helpers/useSwipe.d.ts +66 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types-cjs/components/Lightbox.d.ts +244 -0
- package/dist/types-cjs/helpers/chevronIcons.d.ts +14 -0
- package/dist/types-cjs/helpers/usePrefersReducedMotion.d.ts +10 -0
- package/dist/types-cjs/helpers/useSwipe.d.ts +66 -0
- package/dist/types-cjs/index.d.ts +1 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/components/_lightbox.scss +392 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Lightbox.js","sources":["../../../src/components/Lightbox.tsx"],"sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { buttonType } from '../helpers/buttonType';\nimport { type PolymorphicRef } from '../helpers/polymorphic';\nimport { isDev, warnOnce } from '../helpers/devWarnings';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { useIsHydrated } from '../helpers/useIsHydrated';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useNumberFormat } from '../helpers/localeFormat';\nimport { visuallyHidden } from '../helpers/statusRegion';\nimport { usePrefersReducedMotion } from '../helpers/usePrefersReducedMotion';\nimport { useSwipe } from '../helpers/useSwipe';\nimport { ChevronNextIcon, ChevronPrevIcon } from '../helpers/chevronIcons';\nimport { Modal } from './Modal';\nimport { Button } from '../elements/Button';\nimport { Buttons } from '../elements/Buttons';\nimport { Figure } from '../elements/Figure';\nimport { Loader } from '../elements/Loader';\n\n/** One image a Lightbox shows. */\nexport interface LightboxItem {\n /** The full-size image's URL. */\n src: string;\n /**\n * The image's text alternative, which the status line reads after each\n * move. In a viewer the image is the content, so `''` is almost never\n * right; a development build warns once for an item without one.\n */\n alt: string;\n /** Shown under the image as its `<figcaption>`. */\n caption?: React.ReactNode;\n /** The `<img>`'s `srcSet`, so the browser picks a size for the screen. Preloading uses it too. */\n srcSet?: string;\n /** The `<img>`'s `sizes`, read with `srcSet`. */\n sizes?: string;\n}\n\n/**\n * The words a Lightbox writes. Each is English unless you give your own,\n * and the numbers in the default progress line are written in the\n * `LocaleProvider`'s locale.\n */\nexport interface LightboxLabels {\n /**\n * The close button's name.\n * @defaultValue 'Close'\n */\n close?: string;\n /**\n * The previous button's name.\n * @defaultValue 'Previous image'\n */\n previous?: string;\n /**\n * The next button's name.\n * @defaultValue 'Next image'\n */\n next?: string;\n /**\n * The zoom-in button's name.\n * @defaultValue 'Zoom in'\n */\n zoomIn?: string;\n /**\n * The zoom-out button's name.\n * @defaultValue 'Zoom out'\n */\n zoomOut?: string;\n /**\n * The button that fits the image again.\n * @defaultValue 'Reset zoom'\n */\n resetZoom?: string;\n /**\n * The `Loader`'s name while an image loads.\n * @defaultValue 'Loading image'\n */\n loading?: string;\n /**\n * Shown, above the alt text, when an image fails to load. The status line\n * for a failed image starts with it, then a space and the line a loaded\n * image gets, so write it as a whole sentence, punctuated as your language\n * does.\n * @defaultValue \"This image didn't load.\"\n */\n loadError?: string;\n /**\n * The button that tries a failed image again.\n * @defaultValue 'Retry'\n */\n retry?: string;\n /**\n * The start of the status line read after each move, which goes on with\n * a colon and the image's alt: \"Image 3 of 10: Harbour at dusk\". For a\n * failed image, `loadError` and a space come first. It gets the image's\n * place counting from 1, and the number of images.\n * @defaultValue (current, total) => 'Image ' + current + ' of ' + total\n */\n progress?: (current: number, total: number) => string;\n}\n\n/**\n * Props for the Lightbox component. Its children render where it stands,\n * with no wrapper. The helper props, the other attributes and the `ref`\n * land on the viewer's `.modal` element while it's open.\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the viewer's `.modal` element, `null` while it's closed.\n */\nexport interface LightboxProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'defaultValue' | 'color'\n >,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** **Required.** The images, in order. */\n items: LightboxItem[];\n /** Rendered in place: usually `Lightbox.Trigger`s, in whatever layout you like. */\n children?: React.ReactNode;\n /** Whether the viewer is open (controlled). With no `items` it shows nothing. */\n open?: boolean;\n /**\n * Whether the viewer starts open (uncontrolled).\n * @defaultValue false\n */\n defaultOpen?: boolean;\n /**\n * Called when a trigger, the close button, Escape, a click on the\n * background or a swipe down opens or closes the viewer, and with `false`\n * when `items` goes from some to none while it's open.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * The index of the image shown (controlled). A value past either end is\n * shown as the last or the first image, or wrapped round with `repeat`,\n * without calling `onChange`.\n */\n value?: number;\n /**\n * The image an uncontrolled viewer shows when something other than a\n * trigger opens it. A trigger opens at its own `index`.\n * @defaultValue 0\n */\n defaultValue?: number;\n /**\n * Called with the new index when an arrow, a key or a swipe changes the\n * image, and when a trigger opens the viewer, with the trigger's `index`\n * even when that's the image already shown, before `onOpenChange(true)`.\n * A parent that opens the viewer from `value` hears the trigger that way.\n */\n onChange?: (value: number) => void;\n /**\n * Go from the last image to the first and back, as Carousel does. Off,\n * the previous and next buttons are `aria-disabled` at the ends.\n * @defaultValue true\n */\n repeat?: boolean;\n /**\n * Zoom with the buttons, the wheel, a double click or tap, pinch and the\n * `+`, `-` and `0` keys, and pan a zoomed image by dragging or with the\n * arrow keys. Zoom waits for the image to load. `false` drops the zoom\n * buttons and all of it.\n * @defaultValue true\n */\n zoomable?: boolean;\n /**\n * How far an image zooms, as a multiple of its fitted size. The smallest\n * zoom is always the fitted size.\n * @defaultValue 3\n */\n maxZoom?: number;\n /**\n * Close on Escape. Only the topmost open overlay answers, as with Modal.\n * @defaultValue true\n */\n closeOnEscape?: boolean;\n /**\n * Close on a click on the dark background around the image. The space\n * around the image, beside the toolbar's buttons and under the caption is\n * all background.\n * @defaultValue true\n */\n closeOnClickOutside?: boolean;\n /**\n * Renders the viewer into a portal: `true` for the `ConfigProvider`'s\n * `portalContainer` (`document.body` by default), a selector string, or an\n * element. `false` renders it in place. It's on by default, unlike Modal's,\n * Dialog's and Command.Dialog's, because its triggers sit in gallery\n * cells, and a cell with a transform or a filter would trap a viewer\n * rendered in place.\n * @defaultValue true\n */\n portal?: boolean | string | HTMLElement;\n /**\n * The dialog's accessible name. It stays the same from image to image;\n * the status line says which image is shown.\n * @defaultValue 'Image viewer'\n */\n ariaLabel?: string;\n /** The words it writes, for another language. */\n labels?: LightboxLabels;\n}\n\n/**\n * The Lightbox.Trigger component's own props, on top of the attributes of\n * whatever element `as` renders.\n */\nexport interface LightboxTriggerOwnProps extends Omit<\n BulmaClassesProps,\n 'color' | 'backgroundColor'\n> {\n /** **Required.** The index in `items` the viewer opens at. */\n index: number;\n /**\n * What the button shows: a thumbnail, or text such as \"View all photos\".\n * A ratio `Image` here takes `as=\"div\"`, since its `<figure>` would keep\n * the image's alt from naming the button.\n */\n children?: React.ReactNode;\n /** Additional CSS classes. */\n className?: string;\n}\n\n/**\n * Props for Lightbox.Trigger, a `<button type=\"button\"\n * aria-haspopup=\"dialog\">` that opens the viewer. Its content names it, so\n * give the thumbnail inside a real `alt`. A ratio `Image` renders a\n * `<figure>`, and an image inside a figure doesn't name the button around\n * it, so give that `Image` `as=\"div\"`. A development build warns for a\n * trigger outside a `Lightbox` or with no name.\n * @extraProp {PolymorphicRef<React.ElementType>} [ref] - Ref forwarded to the element `as` renders: the `<button>`, or what `Button` renders.\n */\nexport type LightboxTriggerProps<T extends React.ElementType = 'button'> =\n LightboxTriggerOwnProps &\n Omit<\n React.ComponentPropsWithoutRef<T>,\n keyof LightboxTriggerOwnProps | 'as' | ElementColor<T>\n > & {\n /**\n * Renders the trigger through another component, which gets its class,\n * `aria-haspopup`, `type=\"button\"`, click handler and ref along with\n * its own props. `as={Button} color=\"link\"` draws it as Bulma's link\n * button. `color` is taken only with a component: on an element it\n * would be HTML's old presentational attribute.\n */\n as?: T;\n };\n\n/**\n * `color`, for an element `as`: a `<button>` or another tag takes no\n * `color`, as Link and Navbar.Item take none, while a component such as\n * Button keeps its own.\n */\ntype ElementColor<T extends React.ElementType> = T extends string\n ? 'color'\n : never;\n\n/**\n * Lightbox.Trigger's call signatures, those of `PolymorphicComponent` over\n * `LightboxTriggerProps`, so `color` follows `as` as described there.\n */\ninterface LightboxTriggerComponent {\n <T extends React.ElementType = 'button'>(\n props: LightboxTriggerProps<T> & { ref?: PolymorphicRef<T> }\n ): React.ReactElement | null;\n // The derivation overload `PolymorphicComponent` documents.\n (\n props: LightboxTriggerProps & { ref?: PolymorphicRef<'button'> }\n ): React.ReactElement | null;\n displayName?: string;\n}\n\n/**\n * The shape the implementation destructures. The public contract is the\n * generic `LightboxTriggerProps<T>` above.\n */\ntype LightboxTriggerImplProps = LightboxTriggerOwnProps & {\n as?: React.ElementType;\n color?: string;\n type?: React.ButtonHTMLAttributes<HTMLButtonElement>['type'];\n onClick?: React.MouseEventHandler<HTMLElement>;\n};\n\n/** What a trigger asks of the Lightbox around it. */\ninterface LightboxContextValue {\n openAt: (index: number) => void;\n}\n\nconst LightboxContext = createContext<LightboxContextValue | null>(null);\n\n/** Each zoom button or key multiplies or divides the zoom by this. */\nconst ZOOM_STEP = 1.5;\n/** Where a double click or a double tap zooms to. */\nconst DOUBLE_ZOOM = 2;\n/** How far an arrow key pans a zoomed image, in CSS pixels. */\nconst PAN_STEP = 50;\n/** How long an image can take before the Loader shows, in milliseconds. */\nconst LOADER_DELAY = 300;\n/** How long the status line waits after a change, so a quick run of moves is read once. */\nconst STATUS_DELAY = 500;\n/** How far a finger can move and still tap, in CSS pixels. */\nconst TAP_SLOP = 10;\n/** The longest gap between the two taps of a double tap, in milliseconds. */\nconst DOUBLE_TAP_TIME = 300;\n/** How far apart the two taps of a double tap can land, in CSS pixels. */\nconst DOUBLE_TAP_SLOP = 30;\n/** A pinch that ends this close to the fitted size snaps back to it. */\nconst SNAP_ZOOM = 1.01;\n\n/**\n * The zoom and pan of the image shown: `zoom` as a multiple of its fitted\n * size, and `x` and `y` how far its centre has moved from where it rests.\n * `animate` is off while a gesture drives it, so the image follows the\n * pointer rather than easing after it.\n */\ninterface View {\n zoom: number;\n x: number;\n y: number;\n animate: boolean;\n}\n\nconst FITTED: View = { zoom: 1, x: 0, y: 0, animate: true };\n\n/**\n * Where the image rests and how much room the viewer has, in the viewer's\n * own coordinates. Read from the layout, which a transform doesn't change,\n * so it holds while the image is zoomed or easing.\n */\ninterface Geometry {\n /** The image's centre at rest. */\n cx: number;\n cy: number;\n /** Its fitted size. */\n width: number;\n height: number;\n /** The viewer's size. */\n viewWidth: number;\n viewHeight: number;\n /** Where the viewer sits on screen, to turn a pointer into its coordinates. */\n left: number;\n top: number;\n}\n\n/**\n * Reads the image's resting box and the viewer's size.\n *\n * @function\n * @param {HTMLElement} image - The image shown.\n * @param {HTMLElement} root - The viewer, its `.modal` element.\n * @returns {Geometry} The geometry the zoom and pan are kept within.\n */\nfunction measure(image: HTMLElement, root: HTMLElement): Geometry {\n let x = 0;\n let y = 0;\n for (\n let node: HTMLElement | null = image;\n node && node !== root;\n node = node.offsetParent as HTMLElement | null\n ) {\n x += node.offsetLeft;\n y += node.offsetTop;\n }\n const box = root.getBoundingClientRect();\n return {\n cx: x + image.offsetWidth / 2,\n cy: y + image.offsetHeight / 2,\n width: image.offsetWidth,\n height: image.offsetHeight,\n viewWidth: root.clientWidth,\n viewHeight: root.clientHeight,\n left: box.left,\n top: box.top,\n };\n}\n\nconst clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max);\n\n/**\n * An offset along one axis, kept to the image's edges: a zoomed image wider\n * than the viewer can't leave a gap at either side, and one that still fits\n * stays where it rests, moved only as far as it takes to keep it in view.\n */\nfunction fitAxis(\n offset: number,\n centre: number,\n size: number,\n room: number,\n zoom: number\n): number {\n const half = (size * zoom) / 2;\n // The offsets that put the image's start edge on the viewer's, and its end edge.\n const start = half - centre;\n const end = room - centre - half;\n return half * 2 > room ? clamp(offset, end, start) : clamp(0, start, end);\n}\n\n/** The view, with its offsets kept to the image's edges. */\nfunction fitView(view: View, geometry: Geometry): View {\n return {\n ...view,\n x: fitAxis(\n view.x,\n geometry.cx,\n geometry.width,\n geometry.viewWidth,\n view.zoom\n ),\n y: fitAxis(\n view.y,\n geometry.cy,\n geometry.height,\n geometry.viewHeight,\n view.zoom\n ),\n };\n}\n\n/**\n * The view at another zoom, with the point under `(px, py)`, in the\n * viewer's coordinates, still there.\n */\nfunction zoomView(\n view: View,\n zoom: number,\n px: number,\n py: number,\n geometry: Geometry,\n animate: boolean\n): View {\n if (zoom <= 1) return { ...FITTED, animate };\n const ratio = zoom / view.zoom;\n const dx = px - geometry.cx;\n const dy = py - geometry.cy;\n return fitView(\n {\n zoom,\n x: dx - (dx - view.x) * ratio,\n y: dy - (dy - view.y) * ratio,\n animate,\n },\n geometry\n );\n}\n\n/**\n * Whether a button has an accessible name from what it holds: its own\n * label, text, an image's alt or a label on an element inside, leaving out\n * what's hidden from assistive technology and what's inside a `<figure>`,\n * whose content doesn't name the button around it. A development check, so\n * it reads the common sources rather than the whole name computation.\n */\nfunction hasName(element: Element): boolean {\n if (\n (element.getAttribute('aria-label') || '').trim() ||\n (element.getAttribute('aria-labelledby') || '').trim() ||\n (element.getAttribute('title') || '').trim()\n ) {\n return true;\n }\n return Array.from(element.childNodes).some(node =>\n node instanceof Element\n ? node.getAttribute('aria-hidden') !== 'true' &&\n node.tagName !== 'FIGURE' &&\n ((node instanceof HTMLImageElement && !!node.alt.trim()) ||\n hasName(node))\n : !!(node.textContent as string).trim()\n );\n}\n\n/** Text a key typed into a field is that field's, not the viewer's. */\nconst isTextEntry = (target: EventTarget) =>\n (target as Element).matches(\n 'input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])'\n );\n\nconst ZoomInIcon = () => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"M21 21l-5-5M11 8v6M8 11h6\" />\n </svg>\n);\n\nconst ZoomOutIcon = () => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"M21 21l-5-5M8 11h6\" />\n </svg>\n);\n\nconst ResetZoomIcon = () => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5\" />\n </svg>\n);\n\n/**\n * A button around a thumbnail, or a line of text, that opens the viewer at its `index`.\n *\n * @function\n * @param {LightboxTriggerProps} props - Props for the trigger.\n * @param {React.Ref} ref - Forwarded ref to the element `as` renders.\n * @returns {JSX.Element} The rendered button.\n */\nexport const LightboxTrigger = forwardRef(function LightboxTrigger(\n triggerProps: LightboxTriggerProps,\n ref: React.Ref<HTMLElement>\n) {\n const {\n as: Component = 'button',\n index,\n className,\n children,\n onClick,\n // Kept from the helper classes, which read `color` as a text color: it\n // belongs to a component `as`, as Button's `color=\"link\"` does.\n color,\n ...props\n } = triggerProps as LightboxTriggerImplProps;\n const lightbox = useContext(LightboxContext);\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const triggerClass = usePrefixedClassNames('lightbox-trigger');\n const buttonRef = useRef<HTMLElement | null>(null);\n\n const setRefs = useCallback(\n (node: HTMLElement | null) => {\n buttonRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) {\n (ref as React.MutableRefObject<HTMLElement | null>).current = node;\n }\n return undefined;\n },\n [ref]\n );\n\n if (!lightbox) {\n warnOnce(\n 'Lightbox.Trigger:outside',\n '[bestax-bulma] <Lightbox.Trigger> is outside a <Lightbox>, so it ' +\n 'opens nothing. Put it inside the Lightbox whose items it opens.'\n );\n }\n\n // Its content is its name, and a thumbnail with `alt=\"\"` leaves it\n // nameless. Checked after each commit, since the content can change.\n useEffect(() => {\n // An `as` component that keeps its ref, or hands back a handle of its\n // own, leaves no element to check.\n const button = buttonRef.current;\n if (isDev() && button instanceof Element && !hasName(button)) {\n warnOnce(\n 'Lightbox.Trigger:no-name',\n '[bestax-bulma] <Lightbox.Trigger> has no accessible name. Give ' +\n 'the image inside it an alt, or the button some text or an ' +\n 'aria-label. An image inside a <figure>, which a ratio Image ' +\n 'renders, doesn\\'t name a button, so give that Image as=\"div\".'\n );\n }\n });\n\n return (\n <Component\n ref={setRefs}\n className={classNames(triggerClass, bulmaHelperClasses, className)}\n aria-haspopup=\"dialog\"\n {...rest}\n // Never on an element, where it would be HTML's presentational\n // attribute, even from a caller the types don't check.\n {...(typeof Component === 'string' ? {} : { color })}\n // A component such as Button renders a <button> too, and needs the\n // type as much, so a trigger in a form doesn't submit it.\n {...(Component === 'button' || typeof Component !== 'string'\n ? { type: buttonType(rest.type) }\n : {})}\n onClick={(event: React.MouseEvent<HTMLElement>) => {\n onClick?.(event);\n if (event.defaultPrevented || !lightbox) return;\n // Safari doesn't focus a button that's clicked, and the viewer hands\n // focus back to whatever had it when it opened.\n event.currentTarget.focus({ preventScroll: true });\n lightbox.openAt(index);\n }}\n >\n {children}\n </Component>\n );\n}) as LightboxTriggerComponent;\n\nLightboxTrigger.displayName = 'Lightbox.Trigger';\n\n/** What the viewer takes from the Lightbox around it. */\ninterface LightboxViewerProps extends Omit<\n LightboxProps,\n | 'items'\n | 'children'\n | 'open'\n | 'defaultOpen'\n | 'onOpenChange'\n | 'value'\n | 'defaultValue'\n | 'onChange'\n> {\n items: LightboxItem[];\n index: number;\n onIndexChange: (index: number) => void;\n onClose: () => void;\n}\n\n/**\n * The open viewer: a modal dialog showing one image, mounted only while the\n * Lightbox is open, so each opening starts fitted.\n *\n * @function\n * @param {LightboxViewerProps} props - The Lightbox's props and its state.\n * @returns {JSX.Element} The rendered dialog.\n */\nconst LightboxViewer = forwardRef<HTMLDivElement, LightboxViewerProps>(\n function LightboxViewer(\n {\n items,\n index,\n onIndexChange,\n onClose,\n repeat,\n zoomable = true,\n maxZoom = 3,\n closeOnEscape = true,\n closeOnClickOutside = true,\n portal = true,\n ariaLabel = 'Image viewer',\n labels,\n className,\n onKeyDown,\n onPointerDown,\n onPointerMove,\n onPointerUp,\n onPointerCancel,\n ...props\n },\n ref\n ) {\n const count = items.length;\n const item = items[index];\n const limit = Math.max(1, Number.isFinite(maxZoom) ? maxZoom : 3);\n const formatNumber = useNumberFormat();\n const number = (value: number) =>\n formatNumber ? formatNumber(value) : String(value);\n const words = {\n close: labels?.close || 'Close',\n previous: labels?.previous || 'Previous image',\n next: labels?.next || 'Next image',\n zoomIn: labels?.zoomIn || 'Zoom in',\n zoomOut: labels?.zoomOut || 'Zoom out',\n resetZoom: labels?.resetZoom || 'Reset zoom',\n loading: labels?.loading || 'Loading image',\n loadError: labels?.loadError || \"This image didn't load.\",\n retry: labels?.retry || 'Retry',\n progress:\n labels?.progress ??\n ((current: number, total: number) =>\n `Image ${number(current)} of ${number(total)}`),\n };\n\n const reducedMotion = usePrefersReducedMotion();\n const hydrated = useIsHydrated();\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const setRoot = useCallback(\n (node: HTMLDivElement | null) => {\n rootRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n return undefined;\n },\n [ref]\n );\n const [image, setImage] = useState<HTMLImageElement | null>(null);\n\n // The image shown and how it's doing. `leaving` is the image before it,\n // kept on screen while the new one loads and then faded out over it. The\n // fades are CSS animations, which start when the stylesheet's selector\n // first matches, even for an image that was already loaded, where a\n // transition wouldn't run.\n const [shown, setShown] = useState({ index, item });\n const [status, setStatus] = useState<\n 'loading' | 'slow' | 'loaded' | 'error'\n >('loading');\n const [attempt, setAttempt] = useState(0);\n const [leaving, setLeaving] = useState<LightboxItem>();\n const [view, setView] = useState<View>(FITTED);\n const zoomed = view.zoom > 1;\n // The view as last set, for a gesture to go on from: pointer events can\n // arrive faster than React renders.\n const viewRef = useRef(view);\n useClientLayoutEffect(() => {\n viewRef.current = view;\n });\n const updateView = (next: (current: View) => View) => {\n viewRef.current = next(viewRef.current);\n setView(viewRef.current);\n };\n\n // A move starts the new image fitted and loading, and so does another\n // image at the same index after `items` changes. Adjusted while rendering,\n // React's pattern for state that follows a prop, so the old zoom or load\n // state never paints on the new image.\n if (\n shown.index !== index ||\n shown.item.src !== item.src ||\n shown.item.srcSet !== item.srcSet\n ) {\n setShown({ index, item });\n setLeaving(\n reducedMotion ? undefined : status === 'loaded' ? shown.item : leaving\n );\n setStatus('loading');\n setAttempt(0);\n setView(FITTED);\n }\n\n const fail = () => {\n setStatus('error');\n setLeaving(undefined);\n };\n\n // An image the server rendered loads or fails before hydration, so its\n // event comes before React listens for it. A broken image is complete\n // and has no size, and so is a loaded SVG with none of its own, so\n // `decode()` tells those two apart.\n useClientLayoutEffect(() => {\n if (!image?.complete) return undefined;\n if (image.naturalWidth > 0) {\n setStatus('loaded');\n return undefined;\n }\n let live = true;\n image.decode().then(\n () => live && setStatus('loaded'),\n () => {\n if (!live) return;\n setStatus('error');\n setLeaving(undefined);\n }\n );\n return () => {\n live = false;\n };\n }, [image]);\n\n // The Loader shows only for an image that takes a while.\n useEffect(() => {\n if (status !== 'loading') return undefined;\n // Cleared by any change of status, so it only fires on a load still\n // under way.\n const timer = setTimeout(() => {\n setStatus('slow');\n setLeaving(undefined);\n }, LOADER_DELAY);\n return () => clearTimeout(timer);\n }, [status, shown, attempt]);\n\n // The images either side, fetched ahead so a move shows them at once.\n // With `repeat` and two images, both sides are the one other image, so\n // the indexes are deduplicated and it's fetched once.\n const neighbours = Array.from(\n new Set(\n [index - 1, index + 1].map(i => (repeat ? (i + count) % count : i))\n )\n )\n .filter(i => i >= 0 && i < count && i !== index)\n .map(i => items[i]);\n // None, one or two of them, in no particular order.\n const [firstNeighbour, secondNeighbour] = neighbours;\n const firstSrc = firstNeighbour?.src;\n const firstSrcSet = firstNeighbour?.srcSet;\n const firstSizes = firstNeighbour?.sizes;\n const secondSrc = secondNeighbour?.src;\n const secondSrcSet = secondNeighbour?.srcSet;\n const secondSizes = secondNeighbour?.sizes;\n useEffect(() => {\n const fetchAhead = (src?: string, srcSet?: string, sizes?: string) => {\n if (!src) return;\n const img = document.createElement('img');\n if (sizes) img.sizes = sizes;\n if (srcSet) img.srcset = srcSet;\n img.src = src;\n };\n fetchAhead(firstSrc, firstSrcSet, firstSizes);\n fetchAhead(secondSrc, secondSrcSet, secondSizes);\n }, [\n firstSrc,\n firstSrcSet,\n firstSizes,\n secondSrc,\n secondSrcSet,\n secondSizes,\n ]);\n\n // The status line, written a moment after it changes, so a quick run of\n // moves is read once. It's written on opening too, which says which image\n // the viewer opened at.\n const progress = words.progress(index + 1, count);\n const position = item.alt ? `${progress}: ${item.alt}` : progress;\n // A failed image's line says which image failed too, so a move from one\n // failed image to another still changes it.\n const message =\n status === 'error' ? `${words.loadError} ${position}` : position;\n const [said, setSaid] = useState('');\n useEffect(() => {\n const timer = setTimeout(() => setSaid(message), STATUS_DELAY);\n return () => clearTimeout(timer);\n }, [message]);\n\n // ---- Moving -------------------------------------------------------------\n\n const canPrev = repeat ? count > 1 : index > 0;\n const canNext = repeat ? count > 1 : index < count - 1;\n const go = (next: number) => {\n if (next !== index) onIndexChange(next);\n };\n const step = (delta: 1 | -1) => {\n if (delta < 0 ? !canPrev : !canNext) return;\n go((index + delta + count) % count);\n };\n const isRtl = (element: Element) =>\n getComputedStyle(element).direction === 'rtl';\n\n const swipe = useSwipe<HTMLElement>({\n enabled: !zoomed,\n vertical: true,\n onSwipe: direction => {\n if (direction === 'down') {\n onClose();\n } else if (direction !== 'up') {\n // A swipe left brings the next image in from the right, and right\n // to left the other way round.\n const forward = isRtl(rootRef.current as HTMLDivElement)\n ? 'right'\n : 'left';\n step(direction === forward ? 1 : -1);\n }\n },\n });\n\n // ---- Zooming and panning --------------------------------------------------\n\n // Zoom waits for the image: a loading one has no size to zoom, and a\n // failed one isn't there.\n const canZoom = zoomable && status === 'loaded';\n\n // Only called with an image on screen: every way to zoom or pan checks\n // `canZoom` first, and a pan needs a zoom.\n const geometry = () =>\n measure(image as HTMLImageElement, rootRef.current as HTMLDivElement);\n\n /** Zooms to `zoom`, kept within the limits, about the viewer's centre. */\n const zoomTo = (next: (zoom: number) => number) => {\n if (!canZoom) return;\n const g = geometry();\n updateView(v =>\n zoomView(\n v,\n clamp(next(v.zoom), 1, limit),\n g.viewWidth / 2,\n g.viewHeight / 2,\n g,\n true\n )\n );\n };\n const zoomIn = () => zoomTo(zoom => zoom * ZOOM_STEP);\n const zoomOut = () => zoomTo(zoom => zoom / ZOOM_STEP);\n const resetZoom = () => updateView(() => FITTED);\n\n /** A double click or tap: fitted to 2x at the pointer, and back. */\n const toggleZoomAt = (clientX: number, clientY: number) => {\n const g = geometry();\n updateView(v =>\n v.zoom > 1\n ? FITTED\n : zoomView(\n v,\n Math.min(DOUBLE_ZOOM, limit),\n clientX - g.left,\n clientY - g.top,\n g,\n true\n )\n );\n };\n\n const pan = (dx: number, dy: number) => {\n const g = geometry();\n updateView(v =>\n fitView({ ...v, x: v.x + dx, y: v.y + dy, animate: true }, g)\n );\n };\n\n // The wheel zooms at the pointer. A listener of its own, since React's\n // wheel handlers are passive and can't stop the page from zooming too.\n const handleWheel = useLatestRef((event: WheelEvent) => {\n const g = geometry();\n const lines = event.deltaMode === 1 ? 16 : 1;\n const delta = clamp(event.deltaY * lines, -100, 100);\n updateView(v =>\n zoomView(\n v,\n clamp(v.zoom * Math.exp(-delta / 300), 1, limit),\n event.clientX - g.left,\n event.clientY - g.top,\n g,\n false\n )\n );\n });\n useEffect(() => {\n if (!canZoom) return undefined;\n const target = image as HTMLImageElement;\n const onWheel = (event: WheelEvent) => {\n event.preventDefault();\n handleWheel.current(event);\n };\n target.addEventListener('wheel', onWheel, { passive: false });\n return () => target.removeEventListener('wheel', onWheel);\n }, [image, canZoom, handleWheel]);\n\n // Pointers on the image: one pans a zoomed image, two pinch. The first\n // has to land on the image; the second can land anywhere in the viewer.\n const pointersRef = useRef(new Map<number, { x: number; y: number }>());\n const gestureRef = useRef<\n | {\n kind: 'pan';\n x: number;\n y: number;\n from: View;\n geometry: Geometry;\n }\n | {\n kind: 'pinch';\n distance: number;\n x: number;\n y: number;\n from: View;\n geometry: Geometry;\n }\n | null\n >(null);\n const tapRef = useRef<{ x: number; y: number } | null>(null);\n const lastTapRef = useRef<{ x: number; y: number; time: number } | null>(\n null\n );\n const lastPointerTypeRef = useRef('');\n\n const startPan = (x: number, y: number) => {\n gestureRef.current = {\n kind: 'pan',\n x,\n y,\n from: viewRef.current,\n geometry: geometry(),\n };\n };\n\n const handlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n lastPointerTypeRef.current = event.pointerType;\n // A second finger is a pinch, never a swipe, whether or not anything\n // zooms, and wherever in the viewer it lands.\n if (!event.isPrimary) swipe.cancel();\n if (!canZoom || event.defaultPrevented) return;\n const onImage = event.target === image;\n if (!onImage && pointersRef.current.size === 0) return;\n pointersRef.current.set(event.pointerId, {\n x: event.clientX,\n y: event.clientY,\n });\n\n // Two or more is a pinch, between the first two.\n if (pointersRef.current.size > 1) {\n const g = geometry();\n const [a, b] = Array.from(pointersRef.current.values());\n tapRef.current = null;\n swipe.cancel();\n gestureRef.current = {\n kind: 'pinch',\n distance: Math.hypot(a.x - b.x, a.y - b.y) || 1,\n x: (a.x + b.x) / 2 - g.left,\n y: (a.y + b.y) / 2 - g.top,\n from: viewRef.current,\n geometry: g,\n };\n } else {\n tapRef.current = { x: event.clientX, y: event.clientY };\n if (zoomed) {\n // So the pan follows a mouse that leaves the image.\n (event.target as Element).setPointerCapture(event.pointerId);\n startPan(event.clientX, event.clientY);\n }\n }\n };\n\n const handlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n onPointerMove?.(event);\n const point = pointersRef.current.get(event.pointerId);\n if (!point) return;\n point.x = event.clientX;\n point.y = event.clientY;\n if (\n tapRef.current &&\n Math.hypot(\n event.clientX - tapRef.current.x,\n event.clientY - tapRef.current.y\n ) > TAP_SLOP\n ) {\n tapRef.current = null;\n }\n\n const current = gestureRef.current;\n if (current?.kind === 'pinch') {\n const [a, b] = Array.from(pointersRef.current.values());\n const g = current.geometry;\n const zoom = clamp(\n (current.from.zoom * Math.hypot(a.x - b.x, a.y - b.y)) /\n current.distance,\n 1,\n limit\n );\n // The point of the image under the pinch's start stays between the\n // fingers, so they can pan as they pinch.\n const midX = (a.x + b.x) / 2 - g.left;\n const midY = (a.y + b.y) / 2 - g.top;\n const imageX = (current.x - g.cx - current.from.x) / current.from.zoom;\n const imageY = (current.y - g.cy - current.from.y) / current.from.zoom;\n updateView(() =>\n fitView(\n {\n zoom,\n x: midX - g.cx - imageX * zoom,\n y: midY - g.cy - imageY * zoom,\n animate: false,\n },\n g\n )\n );\n } else if (current) {\n updateView(() =>\n fitView(\n {\n ...current.from,\n x: current.from.x + event.clientX - current.x,\n y: current.from.y + event.clientY - current.y,\n animate: false,\n },\n current.geometry\n )\n );\n }\n };\n\n const handlePointerEnd = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!pointersRef.current.delete(event.pointerId)) return;\n const wasPinch = gestureRef.current?.kind === 'pinch';\n gestureRef.current = null;\n if (wasPinch) {\n if (viewRef.current.zoom < SNAP_ZOOM) {\n updateView(() => FITTED);\n } else {\n // A finger left down after a pinch carries on as a pan.\n const [point] = Array.from(pointersRef.current.values());\n startPan(point.x, point.y);\n }\n }\n\n // Two quick taps in the same place zoom, the touch screen's double click.\n if (\n event.type === 'pointerup' &&\n event.pointerType === 'touch' &&\n tapRef.current\n ) {\n const previous = lastTapRef.current;\n if (\n previous &&\n event.timeStamp - previous.time < DOUBLE_TAP_TIME &&\n Math.hypot(event.clientX - previous.x, event.clientY - previous.y) <\n DOUBLE_TAP_SLOP\n ) {\n lastTapRef.current = null;\n toggleZoomAt(event.clientX, event.clientY);\n } else {\n lastTapRef.current = {\n x: event.clientX,\n y: event.clientY,\n time: event.timeStamp,\n };\n }\n }\n tapRef.current = null;\n };\n\n // ---- Keys -----------------------------------------------------------------\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n if (\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n // A key from content React portals elsewhere, such as a popover's,\n // is that content's.\n !event.currentTarget.contains(event.target as Node) ||\n isTextEntry(event.target)\n ) {\n return;\n }\n const forward = isRtl(event.currentTarget) ? -1 : 1;\n switch (event.key) {\n case 'ArrowLeft':\n if (zoomed) pan(PAN_STEP, 0);\n else step(forward === 1 ? -1 : 1);\n break;\n case 'ArrowRight':\n if (zoomed) pan(-PAN_STEP, 0);\n else step(forward === 1 ? 1 : -1);\n break;\n case 'ArrowUp':\n case 'ArrowDown':\n if (!zoomed) return;\n pan(0, event.key === 'ArrowUp' ? PAN_STEP : -PAN_STEP);\n break;\n case 'Home':\n go(0);\n break;\n case 'End':\n go(count - 1);\n break;\n case '+':\n case '=':\n case '-':\n case '0':\n if (!canZoom) return;\n if (event.key === '0') resetZoom();\n else if (event.key === '-') zoomOut();\n else zoomIn();\n break;\n default:\n return;\n }\n event.preventDefault();\n };\n\n // ---- Loading ----------------------------------------------------------------\n\n const retry = () => {\n setAttempt(n => n + 1);\n setStatus('loading');\n // Cleared, so a Retry that fails again is announced again.\n setSaid('');\n // The Retry button goes with the error, so focus waits on the dialog.\n (rootRef.current as HTMLDivElement).focus();\n };\n\n // ---- Rendering --------------------------------------------------------------\n\n const rootClass = usePrefixedClassNames('lightbox', {\n 'is-zoomed': zoomed,\n });\n const toolbarClass = usePrefixedClassNames('lightbox-toolbar');\n const progressClass = usePrefixedClassNames('lightbox-progress');\n const figureClass = usePrefixedClassNames('lightbox-figure');\n const imageClass = usePrefixedClassNames('lightbox-image');\n const loadingClass = usePrefixedClassNames('is-loading');\n const leavingClass = usePrefixedClassNames('lightbox-image', 'is-leaving');\n const captionClass = usePrefixedClassNames('lightbox-caption');\n const startsSwipe = (target: EventTarget) =>\n !(target as Element).closest(\n `button, a, input, select, textarea, .${captionClass}`\n );\n const errorClass = usePrefixedClassNames('lightbox-error');\n const previousClass = usePrefixedClassNames('lightbox-arrow', 'is-prev');\n const nextClass = usePrefixedClassNames('lightbox-arrow', 'is-next');\n\n const imageStyle: React.CSSProperties | undefined = zoomed\n ? {\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.zoom})`,\n transition: view.animate ? undefined : 'none',\n }\n : view.animate\n ? undefined\n : { transition: 'none' };\n const figureStyle: React.CSSProperties | undefined = swipe.isSwiping\n ? {\n // A swipe up doesn't go anywhere, so the image doesn't follow it.\n transform: `translate(${swipe.offsetX}px, ${Math.max(0, swipe.offsetY)}px)`,\n transition: 'none',\n }\n : undefined;\n\n return (\n <Modal\n ref={setRoot}\n isActive\n onClose={onClose}\n closeOnEscape={closeOnEscape}\n portal={portal}\n className={classNames(rootClass, className)}\n aria-label={ariaLabel}\n {...props}\n onKeyDown={handleKeyDown}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={event => {\n onPointerUp?.(event);\n handlePointerEnd(event);\n }}\n onPointerCancel={event => {\n onPointerCancel?.(event);\n handlePointerEnd(event);\n }}\n >\n <Modal.Background onClick={closeOnClickOutside ? onClose : undefined} />\n <Modal.Close variant=\"floating\" size=\"large\" aria-label={words.close} />\n <div className={toolbarClass}>\n {count > 1 && (\n <bdi className={progressClass} aria-hidden=\"true\">\n {`${number(index + 1)} / ${number(count)}`}\n </bdi>\n )}\n {zoomable && (\n <Buttons>\n <Button\n type=\"button\"\n aria-label={words.zoomOut}\n aria-disabled={!zoomed || undefined}\n onClick={zoomOut}\n >\n <ZoomOutIcon />\n </Button>\n <Button\n type=\"button\"\n aria-label={words.zoomIn}\n aria-disabled={!canZoom || view.zoom >= limit || undefined}\n onClick={zoomIn}\n >\n <ZoomInIcon />\n </Button>\n <Button\n type=\"button\"\n aria-label={words.resetZoom}\n aria-disabled={!zoomed || undefined}\n onClick={resetZoom}\n >\n <ResetZoomIcon />\n </Button>\n </Buttons>\n )}\n </div>\n <Modal.Content>\n <Figure\n className={figureClass}\n style={figureStyle}\n // A swipe starts on the image or a failed image's message, so\n // the caption keeps its text selection and Retry and links\n // their focus.\n {...swipe.handlers}\n onMouseDown={event => {\n if (startsSwipe(event.target)) swipe.handlers.onMouseDown(event);\n }}\n onTouchStart={event => {\n // A second finger is a pinch, not a swipe.\n if (event.touches.length > 1) swipe.cancel();\n else if (startsSwipe(event.target)) {\n swipe.handlers.onTouchStart(event);\n }\n }}\n >\n {status !== 'error' && (\n <img\n // The index as well as the file: a move between two items\n // with the same file still makes a new element, whose load\n // event clears the Loader.\n key={`${index}-${item.src}-${item.srcSet}-${attempt}`}\n ref={setImage}\n className={classNames(\n imageClass,\n hydrated && status !== 'loaded' && loadingClass\n )}\n src={item.src}\n srcSet={item.srcSet}\n sizes={item.sizes}\n alt={item.alt}\n draggable={false}\n style={imageStyle}\n onLoad={() => setStatus('loaded')}\n onError={fail}\n onDoubleClick={event => {\n // A touch screen's double tap is handled as it lands.\n if (canZoom && lastPointerTypeRef.current !== 'touch') {\n toggleZoomAt(event.clientX, event.clientY);\n }\n }}\n />\n )}\n {/* Over the new image, so it fades out as that one fades in. The\n stylesheet starts both once the new image has loaded. */}\n {leaving && (\n <img\n className={leavingClass}\n src={leaving.src}\n srcSet={leaving.srcSet}\n sizes={leaving.sizes}\n alt=\"\"\n aria-hidden=\"true\"\n draggable={false}\n onAnimationEnd={() => setLeaving(undefined)}\n />\n )}\n {status === 'slow' && <Loader ariaLabel={words.loading} />}\n {status === 'error' && (\n <div className={errorClass}>\n <p>{words.loadError}</p>\n {item.alt && <p>{item.alt}</p>}\n <Button type=\"button\" onClick={retry}>\n {words.retry}\n </Button>\n </div>\n )}\n {item.caption ? (\n <Figure.Caption className={captionClass}>\n {item.caption}\n </Figure.Caption>\n ) : null}\n </Figure>\n </Modal.Content>\n {count > 1 && (\n <>\n <Button\n type=\"button\"\n className={previousClass}\n aria-label={words.previous}\n aria-disabled={!canPrev || undefined}\n onClick={() => step(-1)}\n >\n <ChevronPrevIcon />\n </Button>\n <Button\n type=\"button\"\n className={nextClass}\n aria-label={words.next}\n aria-disabled={!canNext || undefined}\n onClick={() => step(1)}\n >\n <ChevronNextIcon />\n </Button>\n </>\n )}\n <div role=\"status\" aria-live=\"polite\" style={visuallyHidden}>\n {said}\n </div>\n </Modal>\n );\n }\n);\n\n/**\n * The `Lightbox` component shows a set of images one at a time in a modal viewer, with previous and next, a counter, captions and zoom, opened from the `Lightbox.Trigger` buttons you place.\n *\n * It renders its children where it stands, with no wrapper, and the\n * viewer only while it's open, into a portal by default. The viewer is a\n * `Modal`: focus moves to its close button, Tab stays inside, Escape and a\n * click on the background close it, and focus goes back to the trigger.\n *\n * @function\n * @param {LightboxProps} props - Props for the Lightbox component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the viewer's `.modal` element, `null` while it's closed.\n * @returns {JSX.Element} The children, and the viewer while it's open.\n *\n * @example\n * <Lightbox items={photos}>\n * {photos.map((photo, i) => (\n * <Lightbox.Trigger key={photo.src} index={i}>\n * <Image as=\"div\" src={photo.src} alt={photo.alt} size=\"4by3\" />\n * </Lightbox.Trigger>\n * ))}\n * </Lightbox>\n */\nconst LightboxRoot = forwardRef<HTMLDivElement, LightboxProps>(\n (\n {\n items,\n children,\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n value: controlledValue,\n defaultValue = 0,\n onChange,\n repeat = true,\n ...props\n },\n ref\n ) => {\n const [ownOpen, setOwnOpen] = useState(defaultOpen);\n const open = controlledOpen ?? ownOpen;\n const [ownValue, setOwnValue] = useState(defaultValue);\n const count = items.length;\n\n if (items.some(item => typeof item.alt !== 'string')) {\n warnOnce(\n 'Lightbox:no-alt',\n '[bestax-bulma] <Lightbox> has an item with no alt. In a viewer the ' +\n 'image is the content, so give each one a text alternative.'\n );\n }\n\n /** An index as one of the images: rounded, then held to the ends or wrapped. */\n const fit = (value: number) => {\n const whole = Number.isFinite(value) ? Math.trunc(value) : 0;\n return repeat\n ? ((whole % count) + count) % count\n : clamp(whole, 0, count - 1);\n };\n const index = fit(controlledValue ?? ownValue);\n\n const setOpen = (next: boolean) => {\n if (controlledOpen === undefined) setOwnOpen(next);\n onOpenChange?.(next);\n };\n const setValue = (next: number) => {\n if (controlledValue === undefined) setOwnValue(next);\n onChange?.(next);\n };\n\n // Read when a trigger is pressed, so the context stays the same object\n // and the triggers don't re-render with every change here.\n const openAt = useLatestRef((at: number) => {\n if (!count) return;\n setValue(fit(at));\n if (!open) setOpen(true);\n });\n\n // When the images run out while it's open, it closes the way a user's\n // close does, so an uncontrolled viewer doesn't open again by itself when\n // they return. Only on the drop to none, so a viewer that opens before its\n // images arrive (a deep link, or `defaultOpen` over data still loading)\n // stays open for them.\n const closeEmpty = useLatestRef(() => setOpen(false));\n const countRef = useRef(count);\n useEffect(() => {\n const had = countRef.current;\n countRef.current = count;\n if (open && had > 0 && count === 0) closeEmpty.current();\n }, [open, count, closeEmpty]);\n const context = useMemo<LightboxContextValue>(\n () => ({ openAt: at => openAt.current(at) }),\n [openAt]\n );\n\n return (\n <LightboxContext.Provider value={context}>\n {children}\n {open && count > 0 && (\n <LightboxViewer\n {...props}\n ref={ref}\n items={items}\n repeat={repeat}\n index={index}\n onIndexChange={setValue}\n onClose={() => setOpen(false)}\n />\n )}\n </LightboxContext.Provider>\n );\n }\n);\n\nLightboxRoot.displayName = 'Lightbox';\n\nexport const Lightbox = withSubComponents(\n LightboxRoot,\n { Trigger: LightboxTrigger },\n 'Lightbox'\n);\n\nexport default Lightbox;\n"],"names":["clamp","_jsxs","_jsx","_Fragment"],"mappings":"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"}
|
|
@@ -5,31 +5,8 @@ import React, { useState, useEffect, Children, isValidElement, cloneElement } fr
|
|
|
5
5
|
import { prefixedClassNames, classNames, usePrefixedClassNames } from '../helpers/classNames.js';
|
|
6
6
|
import { useClassPrefix } from '../helpers/Config.js';
|
|
7
7
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
8
|
+
import { usePrefersReducedMotion } from '../helpers/usePrefersReducedMotion.js';
|
|
8
9
|
|
|
9
|
-
/**
|
|
10
|
-
* Detects the user's `prefers-reduced-motion` preference. Always `false` on
|
|
11
|
-
* the server and on the very first client render, so SSR output and the
|
|
12
|
-
* initial hydration pass always agree.
|
|
13
|
-
*
|
|
14
|
-
* @function
|
|
15
|
-
* @returns {boolean} Whether the user has requested reduced motion.
|
|
16
|
-
*/
|
|
17
|
-
function usePrefersReducedMotion() {
|
|
18
|
-
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (typeof window === 'undefined' || !window.matchMedia)
|
|
21
|
-
return undefined;
|
|
22
|
-
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
23
|
-
// eslint-disable-next-line react-hooks/set-state-in-effect -- reads the live media query on mount
|
|
24
|
-
setPrefersReducedMotion(mediaQuery.matches);
|
|
25
|
-
const handleChange = (event) => {
|
|
26
|
-
setPrefersReducedMotion(event.matches);
|
|
27
|
-
};
|
|
28
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
29
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
30
|
-
}, []);
|
|
31
|
-
return prefersReducedMotion;
|
|
32
|
-
}
|
|
33
10
|
/**
|
|
34
11
|
* The `Reveal` component animates its content into view as it scrolls into the viewport, backed by `IntersectionObserver`.
|
|
35
12
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Reveal.js","sources":["../../../src/components/Reveal.tsx"],"sourcesContent":["import React, {\n Children,\n cloneElement,\n isValidElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n classNames,\n prefixedClassNames,\n usePrefixedClassNames,\n} from '../helpers/classNames';\nimport { useClassPrefix } from '../helpers/Config';\nimport type { PolymorphicComponentWithoutRef } from '../helpers/polymorphic';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\n\n/**\n * Animation styles available for the Reveal component.\n */\nexport type RevealAnimation =\n | 'fade'\n | 'fade-up'\n | 'fade-down'\n | 'slide-left'\n | 'slide-right'\n | 'zoom'\n | 'flip';\n\n/**\n * The Reveal component's own props — everything it adds on top of the\n * attributes of whatever element `as` renders.\n */\nexport interface RevealOwnProps extends BulmaClassesProps {\n /** Animation style applied when the element enters the viewport. */\n animation?: RevealAnimation;\n /** Delay in milliseconds before the animation starts. Default: 0. */\n delay?: number;\n /** Animation duration in milliseconds. Default: 600. */\n duration?: number;\n /** Fraction (0-1) of the element that must be visible to trigger the reveal. Clamped to the 0-1 range; non-finite values fall back to `0.15`. */\n threshold?: number;\n /** Animate only the first time the element enters the viewport. If `false`, it re-animates on every entry/exit. */\n once?: boolean;\n /** Stagger direct children with an incrementing delay instead of animating this element as a single block. */\n cascade?: boolean;\n /** Milliseconds added to each successive child's delay when `cascade` is set. Default: 80. */\n cascadeInterval?: number;\n /** Content to reveal. */\n children?: React.ReactNode;\n /** Additional CSS classes. Applied to the observed element (the wrapper `div` when `as` is a component). */\n className?: string;\n /** Inline styles, merged after the animation's own. Applied to the observed element. */\n style?: React.CSSProperties;\n}\n\n/**\n * Props for the Reveal component. The DOM attributes follow `as`.\n *\n * Reveal forwards no `ref`: it owns the node it observes for scroll\n * intersection, and when `as` is a component that node is a wrapper `div`\n * rather than the element `as` names — so a ref derived from `as` would point\n * at the wrong thing.\n */\nexport type RevealProps<T extends React.ElementType = 'div'> = RevealOwnProps &\n Omit<React.ComponentPropsWithoutRef<T>, keyof RevealOwnProps | 'as'> & {\n /** Element or component to render as. Default: 'div'. When `as` is a plain intrinsic tag (e.g. `'section'`), your `className`, `style`, and Bulma helper classes plus everything in `...rest` all land on that single element. When `as` is a component (e.g. `Section`, `Card`), scroll detection needs a real DOM node with a ref, so `Reveal` wraps it in an observed `div`: `className`/`style`/helper classes go on that wrapper `div`, while `...rest` (`id`, `aria-*`, `data-*`, event handlers) is forwarded to the inner component. */\n as?: T;\n };\n\n/**\n * The shape the implementation destructures. The public contract is the generic\n * `RevealProps<T>` above — the body cannot see through `T`.\n */\ntype RevealImplProps = RevealOwnProps & { as?: React.ElementType };\n\n/**\n * Detects the user's `prefers-reduced-motion` preference. Always `false` on\n * the server and on the very first client render, so SSR output and the\n * initial hydration pass always agree.\n *\n * @function\n * @returns {boolean} Whether the user has requested reduced motion.\n */\nfunction usePrefersReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return undefined;\n\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n // eslint-disable-next-line react-hooks/set-state-in-effect -- reads the live media query on mount\n setPrefersReducedMotion(mediaQuery.matches);\n\n const handleChange = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, []);\n\n return prefersReducedMotion;\n}\n\n/**\n * The `Reveal` component animates its content into view as it scrolls into the viewport, backed by `IntersectionObserver`.\n *\n * @function\n * @param {RevealProps} props - Props for the Reveal component.\n * @returns {JSX.Element} The rendered reveal wrapper.\n *\n * @example\n * // Fade a section up into view\n * <Reveal animation=\"fade-up\" as={Section}>\n * <Title>Why Grass Doctor</Title>\n * </Reveal>\n *\n * @example\n * // Stagger a set of cards as they enter the viewport\n * <Reveal animation=\"fade-up\" cascade cascadeInterval={80}>\n * {services.map(service => (\n * <Card key={service.name}>{service.name}</Card>\n * ))}\n * </Reveal>\n */\nfunction RevealImpl(revealProps: RevealProps) {\n const {\n animation = 'fade-up',\n delay = 0,\n duration = 600,\n threshold = 0.15,\n once = true,\n as: Component = 'div',\n cascade = false,\n cascadeInterval = 80,\n className,\n style,\n children,\n ...props\n } = revealProps as RevealImplProps;\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const classPrefix = useClassPrefix();\n const prefersReducedMotion = usePrefersReducedMotion();\n const [node, setNode] = useState<HTMLElement | null>(null);\n const [isMounted, setIsMounted] = useState(false);\n const [isRevealed, setIsRevealed] = useState(false);\n\n // IntersectionObserver throws a RangeError for NaN or thresholds outside\n // 0-1, so clamp valid numbers and fall back to the default for anything else.\n const observerThreshold = Number.isFinite(threshold)\n ? Math.min(Math.max(threshold, 0), 1)\n : 0.15;\n\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- flips to true once mounted on the client so SSR/first-paint markup matches the server\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n if (prefersReducedMotion) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- reduced motion skips straight to the revealed state\n setIsRevealed(true);\n return undefined;\n }\n\n if (typeof IntersectionObserver === 'undefined') {\n setIsRevealed(true);\n return undefined;\n }\n\n // The ref callback attaches after this effect's first pass; it re-runs\n // once `node` becomes available since it's a dependency.\n if (!node) return undefined;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsRevealed(true);\n if (once) observer.unobserve(node);\n } else if (!once) {\n setIsRevealed(false);\n }\n },\n { threshold: observerThreshold }\n );\n\n observer.observe(node);\n return () => observer.disconnect();\n }, [node, prefersReducedMotion, once, observerThreshold]);\n\n // Only apply the hidden/animating state once mounted on the client with\n // motion allowed; otherwise render the final, visible state (SSR-safe).\n const isAnimated = isMounted && !prefersReducedMotion;\n const revealed = !isAnimated || isRevealed;\n\n const itemAnimationClasses = prefixedClassNames(classPrefix, {\n [`reveal-${animation}`]: isAnimated,\n 'is-revealed': revealed,\n });\n\n const combinedClasses = classNames(\n usePrefixedClassNames('reveal', { 'is-cascade': cascade }),\n !cascade && itemAnimationClasses,\n bulmaHelperClasses,\n className\n );\n\n const wrapperStyle: React.CSSProperties = cascade\n ? { ...style }\n : {\n ...style,\n transitionDelay: `${delay}ms`,\n transitionDuration: `${duration}ms`,\n };\n\n const content = cascade\n ? Children.toArray(children).map((child, index) => {\n const itemStyle: React.CSSProperties = {\n transitionDelay: `${delay + index * cascadeInterval}ms`,\n transitionDuration: `${duration}ms`,\n };\n\n if (\n isValidElement<{ className?: string; style?: React.CSSProperties }>(\n child\n )\n ) {\n return cloneElement(child, {\n // Children.toArray always assigns a key, generating one from\n // the position when the element doesn't already have one.\n key: child.key,\n className: classNames(itemAnimationClasses, child.props.className),\n style: { ...itemStyle, ...child.props.style },\n });\n }\n\n return (\n <span key={index} className={itemAnimationClasses} style={itemStyle}>\n {child}\n </span>\n );\n })\n : children;\n\n // Scroll observation needs a DOM node this component OWNS — the observer and\n // the animation classes both key off it. An intrinsic tag is that node, so\n // the ref goes straight on it. A component passed via `as` is not: whatever\n // it renders is its own business, and it may render several elements or\n // none. So it gets wrapped in an observed `div`.\n //\n // Not a statement about ref forwarding. Several components in this library\n // forward refs (#641 added four more), and that changes nothing here — a\n // forwarded ref would still point at the target's node rather than at one\n // this component controls.\n if (typeof Component === 'string') {\n // A plain createElement call sidesteps the combinatorial JSX prop types\n // for `ref` across every possible intrinsic tag `as` could be.\n return React.createElement(\n Component,\n {\n ...rest,\n ref: setNode,\n className: combinedClasses,\n style: wrapperStyle,\n },\n content\n );\n }\n\n return (\n <div ref={setNode} className={combinedClasses} style={wrapperStyle}>\n <Component {...rest}>{content}</Component>\n </div>\n );\n}\n\nexport const Reveal = RevealImpl as PolymorphicComponentWithoutRef<\n RevealOwnProps,\n 'div'\n>;\n\nReveal.displayName = 'Reveal';\n\nexport default Reveal;\n"],"names":["_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Reveal.js","sources":["../../../src/components/Reveal.tsx"],"sourcesContent":["import React, {\n Children,\n cloneElement,\n isValidElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n classNames,\n prefixedClassNames,\n usePrefixedClassNames,\n} from '../helpers/classNames';\nimport { useClassPrefix } from '../helpers/Config';\nimport type { PolymorphicComponentWithoutRef } from '../helpers/polymorphic';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { usePrefersReducedMotion } from '../helpers/usePrefersReducedMotion';\n\n/**\n * Animation styles available for the Reveal component.\n */\nexport type RevealAnimation =\n | 'fade'\n | 'fade-up'\n | 'fade-down'\n | 'slide-left'\n | 'slide-right'\n | 'zoom'\n | 'flip';\n\n/**\n * The Reveal component's own props — everything it adds on top of the\n * attributes of whatever element `as` renders.\n */\nexport interface RevealOwnProps extends BulmaClassesProps {\n /** Animation style applied when the element enters the viewport. */\n animation?: RevealAnimation;\n /** Delay in milliseconds before the animation starts. Default: 0. */\n delay?: number;\n /** Animation duration in milliseconds. Default: 600. */\n duration?: number;\n /** Fraction (0-1) of the element that must be visible to trigger the reveal. Clamped to the 0-1 range; non-finite values fall back to `0.15`. */\n threshold?: number;\n /** Animate only the first time the element enters the viewport. If `false`, it re-animates on every entry/exit. */\n once?: boolean;\n /** Stagger direct children with an incrementing delay instead of animating this element as a single block. */\n cascade?: boolean;\n /** Milliseconds added to each successive child's delay when `cascade` is set. Default: 80. */\n cascadeInterval?: number;\n /** Content to reveal. */\n children?: React.ReactNode;\n /** Additional CSS classes. Applied to the observed element (the wrapper `div` when `as` is a component). */\n className?: string;\n /** Inline styles, merged after the animation's own. Applied to the observed element. */\n style?: React.CSSProperties;\n}\n\n/**\n * Props for the Reveal component. The DOM attributes follow `as`.\n *\n * Reveal forwards no `ref`: it owns the node it observes for scroll\n * intersection, and when `as` is a component that node is a wrapper `div`\n * rather than the element `as` names — so a ref derived from `as` would point\n * at the wrong thing.\n */\nexport type RevealProps<T extends React.ElementType = 'div'> = RevealOwnProps &\n Omit<React.ComponentPropsWithoutRef<T>, keyof RevealOwnProps | 'as'> & {\n /** Element or component to render as. Default: 'div'. When `as` is a plain intrinsic tag (e.g. `'section'`), your `className`, `style`, and Bulma helper classes plus everything in `...rest` all land on that single element. When `as` is a component (e.g. `Section`, `Card`), scroll detection needs a real DOM node with a ref, so `Reveal` wraps it in an observed `div`: `className`/`style`/helper classes go on that wrapper `div`, while `...rest` (`id`, `aria-*`, `data-*`, event handlers) is forwarded to the inner component. */\n as?: T;\n };\n\n/**\n * The shape the implementation destructures. The public contract is the generic\n * `RevealProps<T>` above — the body cannot see through `T`.\n */\ntype RevealImplProps = RevealOwnProps & { as?: React.ElementType };\n\n/**\n * The `Reveal` component animates its content into view as it scrolls into the viewport, backed by `IntersectionObserver`.\n *\n * @function\n * @param {RevealProps} props - Props for the Reveal component.\n * @returns {JSX.Element} The rendered reveal wrapper.\n *\n * @example\n * // Fade a section up into view\n * <Reveal animation=\"fade-up\" as={Section}>\n * <Title>Why Grass Doctor</Title>\n * </Reveal>\n *\n * @example\n * // Stagger a set of cards as they enter the viewport\n * <Reveal animation=\"fade-up\" cascade cascadeInterval={80}>\n * {services.map(service => (\n * <Card key={service.name}>{service.name}</Card>\n * ))}\n * </Reveal>\n */\nfunction RevealImpl(revealProps: RevealProps) {\n const {\n animation = 'fade-up',\n delay = 0,\n duration = 600,\n threshold = 0.15,\n once = true,\n as: Component = 'div',\n cascade = false,\n cascadeInterval = 80,\n className,\n style,\n children,\n ...props\n } = revealProps as RevealImplProps;\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const classPrefix = useClassPrefix();\n const prefersReducedMotion = usePrefersReducedMotion();\n const [node, setNode] = useState<HTMLElement | null>(null);\n const [isMounted, setIsMounted] = useState(false);\n const [isRevealed, setIsRevealed] = useState(false);\n\n // IntersectionObserver throws a RangeError for NaN or thresholds outside\n // 0-1, so clamp valid numbers and fall back to the default for anything else.\n const observerThreshold = Number.isFinite(threshold)\n ? Math.min(Math.max(threshold, 0), 1)\n : 0.15;\n\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- flips to true once mounted on the client so SSR/first-paint markup matches the server\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n if (prefersReducedMotion) {\n // eslint-disable-next-line react-hooks/set-state-in-effect -- reduced motion skips straight to the revealed state\n setIsRevealed(true);\n return undefined;\n }\n\n if (typeof IntersectionObserver === 'undefined') {\n setIsRevealed(true);\n return undefined;\n }\n\n // The ref callback attaches after this effect's first pass; it re-runs\n // once `node` becomes available since it's a dependency.\n if (!node) return undefined;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setIsRevealed(true);\n if (once) observer.unobserve(node);\n } else if (!once) {\n setIsRevealed(false);\n }\n },\n { threshold: observerThreshold }\n );\n\n observer.observe(node);\n return () => observer.disconnect();\n }, [node, prefersReducedMotion, once, observerThreshold]);\n\n // Only apply the hidden/animating state once mounted on the client with\n // motion allowed; otherwise render the final, visible state (SSR-safe).\n const isAnimated = isMounted && !prefersReducedMotion;\n const revealed = !isAnimated || isRevealed;\n\n const itemAnimationClasses = prefixedClassNames(classPrefix, {\n [`reveal-${animation}`]: isAnimated,\n 'is-revealed': revealed,\n });\n\n const combinedClasses = classNames(\n usePrefixedClassNames('reveal', { 'is-cascade': cascade }),\n !cascade && itemAnimationClasses,\n bulmaHelperClasses,\n className\n );\n\n const wrapperStyle: React.CSSProperties = cascade\n ? { ...style }\n : {\n ...style,\n transitionDelay: `${delay}ms`,\n transitionDuration: `${duration}ms`,\n };\n\n const content = cascade\n ? Children.toArray(children).map((child, index) => {\n const itemStyle: React.CSSProperties = {\n transitionDelay: `${delay + index * cascadeInterval}ms`,\n transitionDuration: `${duration}ms`,\n };\n\n if (\n isValidElement<{ className?: string; style?: React.CSSProperties }>(\n child\n )\n ) {\n return cloneElement(child, {\n // Children.toArray always assigns a key, generating one from\n // the position when the element doesn't already have one.\n key: child.key,\n className: classNames(itemAnimationClasses, child.props.className),\n style: { ...itemStyle, ...child.props.style },\n });\n }\n\n return (\n <span key={index} className={itemAnimationClasses} style={itemStyle}>\n {child}\n </span>\n );\n })\n : children;\n\n // Scroll observation needs a DOM node this component OWNS — the observer and\n // the animation classes both key off it. An intrinsic tag is that node, so\n // the ref goes straight on it. A component passed via `as` is not: whatever\n // it renders is its own business, and it may render several elements or\n // none. So it gets wrapped in an observed `div`.\n //\n // Not a statement about ref forwarding. Several components in this library\n // forward refs (#641 added four more), and that changes nothing here — a\n // forwarded ref would still point at the target's node rather than at one\n // this component controls.\n if (typeof Component === 'string') {\n // A plain createElement call sidesteps the combinatorial JSX prop types\n // for `ref` across every possible intrinsic tag `as` could be.\n return React.createElement(\n Component,\n {\n ...rest,\n ref: setNode,\n className: combinedClasses,\n style: wrapperStyle,\n },\n content\n );\n }\n\n return (\n <div ref={setNode} className={combinedClasses} style={wrapperStyle}>\n <Component {...rest}>{content}</Component>\n </div>\n );\n}\n\nexport const Reveal = RevealImpl as PolymorphicComponentWithoutRef<\n RevealOwnProps,\n 'div'\n>;\n\nReveal.displayName = 'Reveal';\n\nexport default Reveal;\n"],"names":["_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// INTERNAL: deliberately not exported from src/index.ts. The chevrons the
|
|
6
|
+
// Carousel's and the Lightbox's previous and next buttons draw, in one place
|
|
7
|
+
// so the two look the same.
|
|
8
|
+
/**
|
|
9
|
+
* Chevron pointing left, for a previous button.
|
|
10
|
+
*
|
|
11
|
+
* @function
|
|
12
|
+
* @returns {JSX.Element} The rendered SVG chevron-left icon.
|
|
13
|
+
*/
|
|
14
|
+
const ChevronPrevIcon = () => (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("polyline", { points: "15 18 9 12 15 6" }) }));
|
|
15
|
+
/**
|
|
16
|
+
* Chevron pointing right, for a next button.
|
|
17
|
+
*
|
|
18
|
+
* @function
|
|
19
|
+
* @returns {JSX.Element} The rendered SVG chevron-right icon.
|
|
20
|
+
*/
|
|
21
|
+
const ChevronNextIcon = () => (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("polyline", { points: "9 18 15 12 9 6" }) }));
|
|
22
|
+
|
|
23
|
+
export { ChevronNextIcon, ChevronPrevIcon };
|
|
24
|
+
//# sourceMappingURL=chevronIcons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chevronIcons.js","sources":["../../../src/helpers/chevronIcons.tsx"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts. The chevrons the\n// Carousel's and the Lightbox's previous and next buttons draw, in one place\n// so the two look the same.\n\n/**\n * Chevron pointing left, for a previous button.\n *\n * @function\n * @returns {JSX.Element} The rendered SVG chevron-left icon.\n */\nexport const ChevronPrevIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <polyline points=\"15 18 9 12 15 6\" />\n </svg>\n);\n\n/**\n * Chevron pointing right, for a next button.\n *\n * @function\n * @returns {JSX.Element} The rendered SVG chevron-right icon.\n */\nexport const ChevronNextIcon = () => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <polyline points=\"9 18 15 12 9 6\" />\n </svg>\n);\n"],"names":["_jsx"],"mappings":";;;;AAAA;AACA;AACA;AAEA;;;;;AAKG;AACI,MAAM,eAAe,GAAG,OAC7BA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EAAA,QAAA,EAEfA,GAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,iBAAiB,EAAA,CAAG,EAAA,CACjC;AAGR;;;;;AAKG;AACI,MAAM,eAAe,GAAG,OAC7BA,GAAA,CAAA,KAAA,EAAA,EACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EAAA,QAAA,EAEfA,GAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,gBAAgB,EAAA,CAAG,EAAA,CAChC;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
6
|
+
/**
|
|
7
|
+
* Detects the user's `prefers-reduced-motion` preference, for motion that
|
|
8
|
+
* runs in script, where a stylesheet's media query can't reach it. Always
|
|
9
|
+
* `false` on the server and on the very first client render, so SSR output
|
|
10
|
+
* and the initial hydration pass always agree.
|
|
11
|
+
*
|
|
12
|
+
* @function
|
|
13
|
+
* @returns {boolean} Whether the user has requested reduced motion.
|
|
14
|
+
*/
|
|
15
|
+
function usePrefersReducedMotion() {
|
|
16
|
+
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
|
|
17
|
+
// An effect, so it only ever runs in the browser.
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!window.matchMedia)
|
|
20
|
+
return undefined;
|
|
21
|
+
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
22
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- reads the live media query on mount
|
|
23
|
+
setPrefersReducedMotion(mediaQuery.matches);
|
|
24
|
+
const handleChange = (event) => {
|
|
25
|
+
setPrefersReducedMotion(event.matches);
|
|
26
|
+
};
|
|
27
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
28
|
+
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
29
|
+
}, []);
|
|
30
|
+
return prefersReducedMotion;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { usePrefersReducedMotion };
|
|
34
|
+
//# sourceMappingURL=usePrefersReducedMotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePrefersReducedMotion.js","sources":["../../../src/helpers/usePrefersReducedMotion.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useEffect, useState } from 'react';\n\n/**\n * Detects the user's `prefers-reduced-motion` preference, for motion that\n * runs in script, where a stylesheet's media query can't reach it. Always\n * `false` on the server and on the very first client render, so SSR output\n * and the initial hydration pass always agree.\n *\n * @function\n * @returns {boolean} Whether the user has requested reduced motion.\n */\nexport function usePrefersReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n // An effect, so it only ever runs in the browser.\n useEffect(() => {\n if (!window.matchMedia) return undefined;\n\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n // eslint-disable-next-line react-hooks/set-state-in-effect -- reads the live media query on mount\n setPrefersReducedMotion(mediaQuery.matches);\n\n const handleChange = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, []);\n\n return prefersReducedMotion;\n}\n"],"names":[],"mappings":";;;;AAAA;AAGA;;;;;;;;AAQG;SACa,uBAAuB,GAAA;IACrC,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;;IAGvE,SAAS,CAAC,MAAK;QACb,IAAI,CAAC,MAAM,CAAC,UAAU;AAAE,YAAA,OAAO,SAAS;QAExC,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC;;AAExE,QAAA,uBAAuB,CAAC,UAAU,CAAC,OAAO,CAAC;AAE3C,QAAA,MAAM,YAAY,GAAG,CAAC,KAA0B,KAAI;AAClD,YAAA,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC;AACxC,QAAA,CAAC;AAED,QAAA,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC;QACnD,OAAO,MAAM,UAAU,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC;IACrE,CAAC,EAAE,EAAE,CAAC;AAEN,IAAA,OAAO,oBAAoB;AAC7B;;"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { useState, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
6
|
+
/** How far, in CSS pixels, a drag has to travel along an axis to count. */
|
|
7
|
+
const SWIPE_THRESHOLD = 50;
|
|
8
|
+
const AT_REST = { x: 0, y: 0 };
|
|
9
|
+
/**
|
|
10
|
+
* Follows a mouse or touch drag on a surface and reports a swipe when it
|
|
11
|
+
* ends far enough from where it started. Carousel's drag, shared: it hands
|
|
12
|
+
* back the handlers to put on the surface and the live offset to draw the
|
|
13
|
+
* drag with, and leaves what a swipe does to the component.
|
|
14
|
+
*
|
|
15
|
+
* @function
|
|
16
|
+
* @param {UseSwipeOptions} options - Whether it's on, the axes, and the callback.
|
|
17
|
+
* @returns {UseSwipeResult} The drag's state, its handlers, and a way to drop it.
|
|
18
|
+
*/
|
|
19
|
+
function useSwipe({ enabled = true, vertical = false, onSwipe, }) {
|
|
20
|
+
const [isSwiping, setIsSwiping] = useState(false);
|
|
21
|
+
const [start, setStart] = useState(AT_REST);
|
|
22
|
+
const [offset, setOffset] = useState(AT_REST);
|
|
23
|
+
const begin = useCallback((x, y) => {
|
|
24
|
+
if (!enabled)
|
|
25
|
+
return;
|
|
26
|
+
setIsSwiping(true);
|
|
27
|
+
setStart({ x, y: vertical ? y : 0 });
|
|
28
|
+
setOffset(AT_REST);
|
|
29
|
+
}, [enabled, vertical]);
|
|
30
|
+
const follow = useCallback((x, y) => {
|
|
31
|
+
if (!isSwiping)
|
|
32
|
+
return;
|
|
33
|
+
setOffset({ x: x - start.x, y: vertical ? y - start.y : 0 });
|
|
34
|
+
}, [isSwiping, start, vertical]);
|
|
35
|
+
const end = useCallback(() => {
|
|
36
|
+
if (!isSwiping)
|
|
37
|
+
return;
|
|
38
|
+
setIsSwiping(false);
|
|
39
|
+
if (vertical && Math.abs(offset.y) > Math.abs(offset.x)) {
|
|
40
|
+
if (offset.y > SWIPE_THRESHOLD)
|
|
41
|
+
onSwipe('down');
|
|
42
|
+
else if (offset.y < -SWIPE_THRESHOLD)
|
|
43
|
+
onSwipe('up');
|
|
44
|
+
}
|
|
45
|
+
else if (offset.x < -SWIPE_THRESHOLD) {
|
|
46
|
+
onSwipe('left');
|
|
47
|
+
}
|
|
48
|
+
else if (offset.x > SWIPE_THRESHOLD) {
|
|
49
|
+
onSwipe('right');
|
|
50
|
+
}
|
|
51
|
+
setOffset(AT_REST);
|
|
52
|
+
}, [isSwiping, vertical, offset, onSwipe]);
|
|
53
|
+
const cancel = useCallback(() => {
|
|
54
|
+
setIsSwiping(false);
|
|
55
|
+
setOffset(AT_REST);
|
|
56
|
+
}, []);
|
|
57
|
+
return {
|
|
58
|
+
isSwiping,
|
|
59
|
+
offsetX: offset.x,
|
|
60
|
+
offsetY: offset.y,
|
|
61
|
+
handlers: {
|
|
62
|
+
onMouseDown: event => {
|
|
63
|
+
// Disabled, the press is left as it is: a click still focuses and
|
|
64
|
+
// text can still be selected.
|
|
65
|
+
if (!enabled)
|
|
66
|
+
return;
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
begin(event.clientX, event.clientY);
|
|
69
|
+
},
|
|
70
|
+
onMouseMove: event => follow(event.clientX, event.clientY),
|
|
71
|
+
onMouseUp: end,
|
|
72
|
+
onMouseLeave: end,
|
|
73
|
+
onTouchStart: event => begin(event.touches[0].clientX, event.touches[0].clientY),
|
|
74
|
+
onTouchMove: event => follow(event.touches[0].clientX, event.touches[0].clientY),
|
|
75
|
+
onTouchEnd: end,
|
|
76
|
+
},
|
|
77
|
+
cancel,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { useSwipe };
|
|
82
|
+
//# sourceMappingURL=useSwipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSwipe.js","sources":["../../../src/helpers/useSwipe.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\nimport { useCallback, useState } from 'react';\nimport type React from 'react';\n\n/** Which way a finished swipe went, on screen: where the pointer travelled. */\nexport type SwipeDirection = 'left' | 'right' | 'up' | 'down';\n\n/** How far, in CSS pixels, a drag has to travel along an axis to count. */\nconst SWIPE_THRESHOLD = 50;\n\n/** Options for `useSwipe`. */\nexport interface UseSwipeOptions {\n /**\n * Whether a press starts a drag. Off, the handlers leave a press alone,\n * `preventDefault()` included. A drag already under way still finishes.\n * @defaultValue true\n */\n enabled?: boolean;\n /**\n * Follow the vertical axis too, so a swipe up or down is reported. Off,\n * only the horizontal distance counts and `offsetY` stays `0`.\n * @defaultValue false\n */\n vertical?: boolean;\n /**\n * Called when a drag ends more than 50px from where it started along an\n * axis. With `vertical`, the axis it travelled further along decides.\n * The direction is physical, so a caller that follows the reading\n * direction maps it itself.\n */\n onSwipe: (direction: SwipeDirection) => void;\n}\n\n/** The mouse and touch handlers `useSwipe` hands back, to spread on the surface. */\nexport interface SwipeHandlers<T extends Element = Element> {\n /** Starts a drag. Calls `preventDefault()` while enabled, so the press neither selects text nor drags an image. */\n onMouseDown: (event: React.MouseEvent<T>) => void;\n /** Follows a drag. */\n onMouseMove: (event: React.MouseEvent<T>) => void;\n /** Ends a drag. */\n onMouseUp: () => void;\n /** Ends a drag when the pointer leaves the surface, as a release would. */\n onMouseLeave: () => void;\n /** Starts a drag with the first finger. */\n onTouchStart: (event: React.TouchEvent<T>) => void;\n /** Follows the first finger. */\n onTouchMove: (event: React.TouchEvent<T>) => void;\n /** Ends a drag. */\n onTouchEnd: () => void;\n}\n\n/** What `useSwipe` returns. */\nexport interface UseSwipeResult<T extends Element = Element> {\n /** Whether a drag is under way. */\n isSwiping: boolean;\n /** How far the drag has gone across, in CSS pixels, `0` between drags. */\n offsetX: number;\n /** How far the drag has gone down (up is negative), `0` without `vertical`. */\n offsetY: number;\n /** The handlers to put on the surface that takes the drag. */\n handlers: SwipeHandlers<T>;\n /** Drops the drag under way without reporting a swipe, as when a second finger lands. */\n cancel: () => void;\n}\n\nconst AT_REST = { x: 0, y: 0 };\n\n/**\n * Follows a mouse or touch drag on a surface and reports a swipe when it\n * ends far enough from where it started. Carousel's drag, shared: it hands\n * back the handlers to put on the surface and the live offset to draw the\n * drag with, and leaves what a swipe does to the component.\n *\n * @function\n * @param {UseSwipeOptions} options - Whether it's on, the axes, and the callback.\n * @returns {UseSwipeResult} The drag's state, its handlers, and a way to drop it.\n */\nexport function useSwipe<T extends Element = Element>({\n enabled = true,\n vertical = false,\n onSwipe,\n}: UseSwipeOptions): UseSwipeResult<T> {\n const [isSwiping, setIsSwiping] = useState(false);\n const [start, setStart] = useState(AT_REST);\n const [offset, setOffset] = useState(AT_REST);\n\n const begin = useCallback(\n (x: number, y: number) => {\n if (!enabled) return;\n setIsSwiping(true);\n setStart({ x, y: vertical ? y : 0 });\n setOffset(AT_REST);\n },\n [enabled, vertical]\n );\n\n const follow = useCallback(\n (x: number, y: number) => {\n if (!isSwiping) return;\n setOffset({ x: x - start.x, y: vertical ? y - start.y : 0 });\n },\n [isSwiping, start, vertical]\n );\n\n const end = useCallback(() => {\n if (!isSwiping) return;\n setIsSwiping(false);\n\n if (vertical && Math.abs(offset.y) > Math.abs(offset.x)) {\n if (offset.y > SWIPE_THRESHOLD) onSwipe('down');\n else if (offset.y < -SWIPE_THRESHOLD) onSwipe('up');\n } else if (offset.x < -SWIPE_THRESHOLD) {\n onSwipe('left');\n } else if (offset.x > SWIPE_THRESHOLD) {\n onSwipe('right');\n }\n\n setOffset(AT_REST);\n }, [isSwiping, vertical, offset, onSwipe]);\n\n const cancel = useCallback(() => {\n setIsSwiping(false);\n setOffset(AT_REST);\n }, []);\n\n return {\n isSwiping,\n offsetX: offset.x,\n offsetY: offset.y,\n handlers: {\n onMouseDown: event => {\n // Disabled, the press is left as it is: a click still focuses and\n // text can still be selected.\n if (!enabled) return;\n event.preventDefault();\n begin(event.clientX, event.clientY);\n },\n onMouseMove: event => follow(event.clientX, event.clientY),\n onMouseUp: end,\n onMouseLeave: end,\n onTouchStart: event =>\n begin(event.touches[0].clientX, event.touches[0].clientY),\n onTouchMove: event =>\n follow(event.touches[0].clientX, event.touches[0].clientY),\n onTouchEnd: end,\n },\n cancel,\n };\n}\n"],"names":[],"mappings":"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"}
|