'use strict';

var React = require('react');
var framerMotion = require('framer-motion');
var ReactDOM = require('react-dom');
var jobberHooks = require('@jobber/hooks');
var classnames = require('classnames');
var noop = require('./noop-cjs.js');
var ButtonDismiss = require('./ButtonDismiss-cjs.js');
var Text = require('./Text-cjs.js');
var Button = require('./Button-cjs.js');
var Heading = require('./Heading-cjs.js');
var Tooltip = require('./Tooltip-cjs.js');
var AtlantisThemeContext = require('./AtlantisThemeContext-cjs.js');

var styles = {"backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","lightboxWrapper":"_5p2iAj4JfoE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","leftAction":"_3DY1o57MHxg-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailBar":"_3TfQLQEE3GQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};

// A little bit more than the transition's duration
// We're doing this to prevent a bug from framer-motion
// https://github.com/framer/motion/issues/1769
const BUTTON_DEBOUNCE_DELAY = 250;
const MOVEMENT_DEBOUNCE_DELAY = 1000;
const swipeConfidenceThreshold = 10000;
const swipePower = (offset, velocity) => {
    return Math.abs(offset) * velocity;
};
const slideVariants = {
    enter: (directionRef) => ({
        x: directionRef.current > 0 ? "150%" : "-150%",
    }),
    center: {
        x: 0,
    },
    exit: (directionRef) => ({
        x: directionRef.current < 0 ? "150%" : "-150%",
    }),
};
const imageTransition = {
    x: { duration: 0.65, ease: [0.42, 0, 0, 1.03] },
};

/* eslint-disable max-statements */
const LightBoxContext = React.createContext({
    open: false,
    images: [],
    currentImageIndex: 0,
    mouseIsStationary: true,
    boxSizing: "content-box",
    directionRef: { current: 0 },
    selectedThumbnailRef: { current: null },
    lightboxRef: { current: null },
    mounted: { current: false },
    handleMouseMove: noop.noop,
    handleRequestClose: noop.noop,
    handleOnDragEnd: noop.noop,
    handleThumbnailClick: noop.noop,
    debouncedHandleNext: noop.noop,
    debouncedHandlePrevious: noop.noop,
});
function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose = noop.noop, onImageChange = noop.noop, boxSizing = "content-box", children, }) {
    const [currentImageIndex, setCurrentImageIndex] = React.useState(imageIndex);
    const directionRef = React.useRef(0);
    const [mouseIsStationary, setMouseIsStationary] = React.useState(true);
    const lightboxRef = jobberHooks.useFocusTrap(open);
    const selectedThumbnailRef = React.useRef(null);
    const debouncedHandleNext = jobberHooks.useDebounce(handleMoveNext, BUTTON_DEBOUNCE_DELAY);
    const debouncedHandlePrevious = jobberHooks.useDebounce(handleMovePrevious, BUTTON_DEBOUNCE_DELAY);
    const mounted = jobberHooks.useIsMounted();
    const prevOpen = React.useRef(open);
    jobberHooks.useRefocusOnActivator(open);
    const handleMouseMovement = jobberHooks.useDebounce(() => {
        setMouseIsStationary(true);
    }, MOVEMENT_DEBOUNCE_DELAY);
    jobberHooks.useOnKeyDown(handleRequestClose, "Escape");
    jobberHooks.useOnKeyDown(debouncedHandlePrevious, {
        key: "ArrowLeft",
    });
    jobberHooks.useOnKeyDown(debouncedHandleNext, {
        key: "ArrowRight",
    });
    React.useEffect(() => {
        setCurrentImageIndex(imageIndex);
        onImageChange(imageIndex);
    }, [imageIndex, open]);
    if (prevOpen.current !== open) {
        prevOpen.current = open;
        togglePrintStyles(open);
    }
    React.useEffect(() => {
        var _a;
        (_a = selectedThumbnailRef === null || selectedThumbnailRef === void 0 ? void 0 : selectedThumbnailRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({
            behavior: "smooth",
            block: "nearest",
            inline: "center",
        });
    }, [currentImageIndex]);
    function handleMouseMove() {
        if (mouseIsStationary) {
            setMouseIsStationary(false);
        }
        handleMouseMovement();
    }
    function handleMovePrevious() {
        directionRef.current = -1;
        const newIndex = (currentImageIndex + images.length - 1) % images.length;
        setCurrentImageIndex(newIndex);
        onImageChange(newIndex);
    }
    function handleMoveNext() {
        directionRef.current = 1;
        const newIndex = (currentImageIndex + 1) % images.length;
        setCurrentImageIndex(newIndex);
        onImageChange(newIndex);
    }
    function handleRequestClose() {
        onRequestClose({ lastPosition: currentImageIndex });
    }
    function handleOnDragEnd(_event, { offset, velocity }) {
        const swipe = swipePower(offset.x, velocity.x);
        if (swipe < -swipeConfidenceThreshold) {
            handleMoveNext();
        }
        else if (swipe > swipeConfidenceThreshold) {
            handleMovePrevious();
        }
    }
    function handleThumbnailClick(index) {
        if (index < currentImageIndex) {
            directionRef.current = -1;
        }
        else {
            directionRef.current = 1;
        }
        setCurrentImageIndex(index);
        onImageChange(index);
    }
    return (React.createElement(LightBoxContext.Provider, { value: {
            open,
            images,
            currentImageIndex,
            mouseIsStationary,
            boxSizing,
            directionRef,
            selectedThumbnailRef,
            lightboxRef,
            mounted,
            handleMouseMove,
            handleRequestClose,
            handleOnDragEnd,
            handleThumbnailClick,
            debouncedHandleNext,
            debouncedHandlePrevious,
        } }, children));
}
function useLightBoxContext() {
    return React.useContext(LightBoxContext);
}
function togglePrintStyles(open) {
    try {
        if (open) {
            document.documentElement.classList.add("atlantisLightBoxActive");
        }
        else {
            document.documentElement.classList.remove("atlantisLightBoxActive");
        }
    }
    catch (error) {
        console.error(error);
    }
}

function LightBoxContent() {
    const { open, lightboxRef, handleMouseMove } = useLightBoxContext();
    const mounted = jobberHooks.useIsMounted();
    const template = (React.createElement(React.Fragment, null, open && (React.createElement("div", { className: styles.lightboxWrapper, tabIndex: 0, "aria-label": "Lightbox", key: "Lightbox", ref: lightboxRef, onMouseMove: handleMouseMove },
        React.createElement(LightBoxBackground, null),
        React.createElement(LightBoxOverlay, null),
        React.createElement(LightBoxToolbar, null),
        React.createElement(LightBoxSlides, null),
        React.createElement(LightBoxNavigation, null),
        React.createElement(LightBoxCaption, null),
        React.createElement(LightBoxThumbnails, null)))));
    return mounted.current
        ? ReactDOM.createPortal(template, document.body)
        : template;
}
function PreviousButton({ onClick, hideButton, className }) {
    const { mediumAndUp } = jobberHooks.useBreakpoints();
    return (React.createElement("div", { className: `${className} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
        React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowLeft", ariaLabel: "Previous image", onClick: onClick })));
}
function NextButton({ onClick, hideButton, className }) {
    const { mediumAndUp } = jobberHooks.useBreakpoints();
    return (React.createElement("div", { className: `${className} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
        React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowRight", ariaLabel: "Next image", onClick: onClick })));
}
/**
 * Blurred, desaturated copy of the current image rendered as a full-bleed
 * background behind the lightbox. Pass `className` to apply additional styles.
 */
function LightBoxBackground({ className }) {
    const { images, currentImageIndex } = useLightBoxContext();
    return (React.createElement("div", { className: classnames(styles.backgroundImage, className), style: {
            backgroundImage: `url("${images[currentImageIndex].url}")`,
        } }));
}
/**
 * Semi-transparent blur backdrop. Clicking it calls `onRequestClose`.
 * Pass `className` to apply additional styles.
 */
function LightBoxOverlay({ className }) {
    const { handleRequestClose } = useLightBoxContext();
    return (React.createElement("div", { className: classnames(styles.blurOverlay, className), onClick: handleRequestClose }));
}
/**
 * Top bar showing an open-in-new-tab action, the current image counter (`1/3`),
 * and a close button. Styled for dark backgrounds.
 */
function LightBoxToolbar() {
    const { images, currentImageIndex, handleRequestClose } = useLightBoxContext();
    return (React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
        React.createElement("div", { className: styles.toolbar },
            React.createElement(LightBoxOpenInNewTabAction, null),
            React.createElement("div", { className: styles.slideNumber },
                React.createElement(Text.Text, null, `${currentImageIndex + 1}/${images.length}`)),
            React.createElement("div", { className: styles.closeButton },
                React.createElement(ButtonDismiss.ButtonDismiss, { ariaLabel: "Close", onClick: handleRequestClose })))));
}
/**
 * Opens the current image in a new browser tab. Included in the default
 * `LightBox.Toolbar`. Returns `null` if the current image has no `url`.
 */
function LightBoxOpenInNewTabAction({ ariaLabel = "Open image in new tab", }) {
    const { images, currentImageIndex } = useLightBoxContext();
    const currentImage = images[currentImageIndex];
    if (!(currentImage === null || currentImage === void 0 ? void 0 : currentImage.url))
        return null;
    return (React.createElement("div", { className: styles.leftAction },
        React.createElement(Tooltip.Tooltip, { message: ariaLabel },
            React.createElement(Button.Button, { size: "small", variation: "subtle", type: "secondary", icon: "export", ariaLabel: ariaLabel, url: currentImage.url, external: true }))));
}
/**
 * The animated hero image with swipe-to-navigate and slide animation.
 *
 * Pass `className` to add styles to the image wrapper. Supports
 * swipe-to-navigate (drag). Keyboard arrow navigation is handled by
 * `LightBox.Provider`.
 *
 * @example
 * ```tsx
 * <LightBox.Slides className={styles.imageArea} />
 * <LightBox.Navigation
 *   prevButtonClassName={styles.prev}
 *   nextButtonClassName={styles.next}
 * />
 * ```
 */
function LightBoxSlides({ className }) {
    const { images, currentImageIndex, directionRef, handleOnDragEnd } = useLightBoxContext();
    return (React.createElement("div", { className: classnames(styles.imageArea, className) },
        React.createElement(framerMotion.AnimatePresence, { initial: false },
            React.createElement(framerMotion.motion.img, { key: currentImageIndex, variants: slideVariants, src: images[currentImageIndex].url, custom: directionRef, className: styles.image, initial: "enter", alt: images[currentImageIndex].alt ||
                    images[currentImageIndex].title ||
                    "", animate: "center", exit: "exit", transition: imageTransition, drag: "x", dragConstraints: { left: 0, right: 0 }, dragElastic: 1, onDragEnd: handleOnDragEnd }))));
}
/**
 * Previous and next navigation buttons. Returns `null` when the image set
 * has only one image.
 *
 * Use `prevButtonClassName` and `nextButtonClassName` to override the styles
 * on each button's wrapper for custom layouts.
 *
 * @example
 * ```tsx
 * <LightBox.Navigation
 *   prevButtonClassName={styles.prev}
 *   nextButtonClassName={styles.next}
 * />
 * ```
 */
function LightBoxNavigation({ prevButtonClassName, nextButtonClassName, }) {
    const { images, mouseIsStationary, debouncedHandleNext, debouncedHandlePrevious, } = useLightBoxContext();
    if (images.length <= 1)
        return null;
    return (React.createElement(React.Fragment, null,
        React.createElement(PreviousButton, { onClick: debouncedHandlePrevious, hideButton: mouseIsStationary, className: classnames(styles.prev, prevButtonClassName) }),
        React.createElement(NextButton, { onClick: debouncedHandleNext, hideButton: mouseIsStationary, className: classnames(styles.next, nextButtonClassName) })));
}
/**
 * Title and caption text for the current image. Only renders when the current
 * image has a `title` or `caption`. Styled for dark backgrounds.
 */
function LightBoxCaption() {
    const { images, currentImageIndex } = useLightBoxContext();
    const { title, caption } = images[currentImageIndex];
    if (!title && !caption)
        return null;
    return (React.createElement("div", { className: styles.captionWrapper },
        React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
            title && (React.createElement("div", { className: styles.title },
                React.createElement(Heading.Heading, { level: 4 }, title))),
            caption && React.createElement(Text.Text, { size: "large" }, caption))));
}
/**
 * Scrollable thumbnail strip. Only renders when there are two or more images.
 */
function LightBoxThumbnails() {
    const { images } = useLightBoxContext();
    return (React.createElement(LightBoxThumbnailBar, null, images.map((image, index) => (React.createElement(LightBoxThumbnail, { key: index, index: index },
        React.createElement(LightBoxThumbnailImage, { src: image.url, alt: image.alt || image.title || "" }))))));
}
/**
 * Scrollable container for the thumbnail strip. Owns the scroll chrome and
 * box-sizing, and only renders when there are two or more images.
 *
 * Pass one `LightBox.Thumbnail` per image as `children` to own the thumbnail
 * loop.
 */
function LightBoxThumbnailBar({ children, className, }) {
    const { images, boxSizing } = useLightBoxContext();
    if (images.length <= 1)
        return null;
    return (React.createElement("div", { className: classnames(styles.thumbnailBar, className), style: { "--lightbox--box-sizing": boxSizing }, "data-testid": "ATL-Thumbnail-Bar" }, children));
}
/**
 * A selectable thumbnail cell identified by its `index`. Owns the selected
 * styling, click-to-navigate behaviour, and renders consumer-provided
 * `children` (typically a `LightBox.ThumbnailImage`).
 */
function LightBoxThumbnail({ index, children, className, }) {
    const { currentImageIndex, selectedThumbnailRef, handleThumbnailClick } = useLightBoxContext();
    const selected = index === currentImageIndex;
    return (React.createElement("div", { className: classnames(styles.thumbnail, className, {
            [styles.selected]: selected,
        }), onClick: () => handleThumbnailClick(index), ref: selected ? selectedThumbnailRef : null }, children));
}
/**
 * A thumbnail using our default styling.
 */
function LightBoxThumbnailImage({ src, alt, className, }) {
    return (React.createElement("img", { src: src, alt: alt, className: classnames(styles.thumbnailImage, className) }));
}
/**
 * LightBox displays images in a fullscreen overlay.
 *
 * **Self-contained (legacy) usage:**
 * ```tsx
 * <LightBox
 *   open={isOpen}
 *   images={images}
 *   imageIndex={imageIndex}
 *   onRequestClose={({ lastPosition }) => { setIsOpen(false); }}
 * />
 * ```
 *
 * **Full composable (fullscreen) usage:**
 * ```tsx
 * <LightBox.Provider open={isOpen} images={images} onRequestClose={onClose}>
 *   <LightBox.Content />
 * </LightBox.Provider>
 * ```
 *
 * **Inline gallery usage (no overlay, no close):**
 * ```tsx
 * <LightBox.Provider
 *   open={true}
 *   images={images}
 *   imageIndex={activeIndex}
 *   onImageChange={onImageChange}
 * >
 *   <div className={styles.lightboxWrapper} onMouseMove={handleMouseMove}>
 *     <LightBox.Background className={styles.backgroundImage} />
 *     <LightBox.Overlay className={styles.blurOverlay} />
 *     <LightBox.Slides className={styles.imageArea} />
 *     <LightBox.Navigation
 *       prevButtonClassName={styles.prev}
 *       nextButtonClassName={styles.next}
 *     />
 *   </div>
 * </LightBox.Provider>
 * ```
 */
function LightBox(props) {
    return (React.createElement(LightBoxProvider, Object.assign({}, props),
        React.createElement(LightBoxContent, null)));
}
LightBox.Provider = LightBoxProvider;
LightBox.Content = LightBoxContent;
LightBox.Background = LightBoxBackground;
LightBox.Overlay = LightBoxOverlay;
LightBox.Toolbar = LightBoxToolbar;
LightBox.OpenInNewTabAction = LightBoxOpenInNewTabAction;
LightBox.Slides = LightBoxSlides;
LightBox.Navigation = LightBoxNavigation;
LightBox.Caption = LightBoxCaption;
LightBox.Thumbnails = LightBoxThumbnails;
LightBox.ThumbnailBar = LightBoxThumbnailBar;
LightBox.Thumbnail = LightBoxThumbnail;
LightBox.ThumbnailImage = LightBoxThumbnailImage;

exports.LightBox = LightBox;
exports.useLightBoxContext = useLightBoxContext;
