@jobber/components 6.113.1 → 6.114.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DatePicker/useFocusOnSelectedDate.d.ts +2 -2
- package/dist/DatePicker-cjs.js +48 -82
- package/dist/DatePicker-es.js +50 -84
- package/dist/Gallery/index.cjs +2 -2
- package/dist/Gallery/index.mjs +2 -2
- package/dist/LightBox/LightBox.constants.d.ts +22 -0
- package/dist/LightBox/LightBox.d.ts +118 -54
- package/dist/LightBox/LightBox.types.d.ts +94 -0
- package/dist/LightBox/LightBoxContext.d.ts +5 -0
- package/dist/LightBox/index.cjs +3 -1
- package/dist/LightBox/index.d.ts +2 -0
- package/dist/LightBox/index.mjs +3 -2
- package/dist/LightBox-cjs.js +245 -57
- package/dist/LightBox-es.js +246 -59
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +1 -1
- package/dist/styles.css +3 -6
- package/dist/utils/meta/meta.json +9 -0
- package/package.json +2 -2
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode, RefObject } from "react";
|
|
2
|
+
import type { PanInfo } from "framer-motion";
|
|
3
|
+
export interface PresentedImage {
|
|
4
|
+
title?: string;
|
|
5
|
+
caption?: string;
|
|
6
|
+
alt?: string;
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
export interface RequestCloseOptions {
|
|
10
|
+
lastPosition: number;
|
|
11
|
+
}
|
|
12
|
+
export interface LightBoxContextType {
|
|
13
|
+
readonly open: boolean;
|
|
14
|
+
readonly images: PresentedImage[];
|
|
15
|
+
readonly currentImageIndex: number;
|
|
16
|
+
readonly mouseIsStationary: boolean;
|
|
17
|
+
readonly boxSizing: CSSProperties["boxSizing"];
|
|
18
|
+
readonly directionRef: RefObject<number>;
|
|
19
|
+
readonly selectedThumbnailRef: RefObject<HTMLDivElement | null>;
|
|
20
|
+
readonly lightboxRef: RefObject<HTMLDivElement | null>;
|
|
21
|
+
readonly mounted: RefObject<boolean>;
|
|
22
|
+
handleMouseMove(): void;
|
|
23
|
+
handleRequestClose(): void;
|
|
24
|
+
handleOnDragEnd(event: MouseEvent | TouchEvent | PointerEvent, info: PanInfo): void;
|
|
25
|
+
handleThumbnailClick(index: number): void;
|
|
26
|
+
debouncedHandleNext(): void;
|
|
27
|
+
debouncedHandlePrevious(): void;
|
|
28
|
+
}
|
|
29
|
+
export interface NavButtonProps {
|
|
30
|
+
readonly onClick: () => void;
|
|
31
|
+
readonly hideButton: boolean;
|
|
32
|
+
readonly className: string;
|
|
33
|
+
}
|
|
34
|
+
export interface LightBoxProps {
|
|
35
|
+
/**
|
|
36
|
+
* Specify if the Lightbox is open or closed.
|
|
37
|
+
*/
|
|
38
|
+
readonly open: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Images is an array of objects defining a LightBox image. This object consists of
|
|
41
|
+
* `title`, `caption`, `alt` and `url`. `title`, `alt` and `caption` are optional, `url` is
|
|
42
|
+
* required, for each image.
|
|
43
|
+
*/
|
|
44
|
+
readonly images: PresentedImage[];
|
|
45
|
+
/**
|
|
46
|
+
* Use this to specify which image in `images` to initialize the lightbox with.
|
|
47
|
+
* This is useful when you have a collection of thumbnails as you only need one
|
|
48
|
+
* collection of image urls, order doesn't matter.
|
|
49
|
+
*/
|
|
50
|
+
readonly imageIndex?: number;
|
|
51
|
+
/**
|
|
52
|
+
* This function must set open to false in order to close the lightbox. Note there
|
|
53
|
+
* is a 300ms easing animation on lightbox close that occurs before this function
|
|
54
|
+
* is called.
|
|
55
|
+
* This function receives an object as an argument with the key `lastPosition`
|
|
56
|
+
* that has the index of the image the user was on when LightBox was closed.
|
|
57
|
+
*/
|
|
58
|
+
onRequestClose(options: RequestCloseOptions): void;
|
|
59
|
+
/**
|
|
60
|
+
* Sets the box-sizing for the thumbnails in the lightbox. This is a solution for a problem where
|
|
61
|
+
* tailwind was setting the box-sizing to `border-box` and causing issues with the lightbox.
|
|
62
|
+
* @default "content-box"
|
|
63
|
+
*/
|
|
64
|
+
readonly boxSizing?: CSSProperties["boxSizing"];
|
|
65
|
+
}
|
|
66
|
+
export type LightBoxProviderProps = Omit<LightBoxProps, "onRequestClose"> & {
|
|
67
|
+
/**
|
|
68
|
+
* This function must set open to false in order to close the lightbox. Note there
|
|
69
|
+
* is a 300ms easing animation on lightbox close that occurs before this function
|
|
70
|
+
* is called.
|
|
71
|
+
* This function receives an object as an argument with the key `lastPosition`
|
|
72
|
+
* that has the index of the image the user was on when LightBox was closed.
|
|
73
|
+
*/
|
|
74
|
+
onRequestClose?(options: RequestCloseOptions): void;
|
|
75
|
+
/**
|
|
76
|
+
* Callback function that is invoked whenever the current image index changes.
|
|
77
|
+
* This includes when the user navigates to a different image (via arrow keys,
|
|
78
|
+
* navigation buttons, or swipe gestures) or when clicking a thumbnail.
|
|
79
|
+
*
|
|
80
|
+
* @param index - The new current image index (0-based)
|
|
81
|
+
*/
|
|
82
|
+
onImageChange?(index: number): void;
|
|
83
|
+
readonly children: ReactNode;
|
|
84
|
+
};
|
|
85
|
+
export interface LightBoxNavigationProps {
|
|
86
|
+
/**
|
|
87
|
+
* The class name to apply to the previous button wrapper.
|
|
88
|
+
*/
|
|
89
|
+
readonly prevButtonClassName?: string;
|
|
90
|
+
/**
|
|
91
|
+
* The class name to apply to the next button wrapper.
|
|
92
|
+
*/
|
|
93
|
+
readonly nextButtonClassName?: string;
|
|
94
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { LightBoxContextType, LightBoxProviderProps } from "./LightBox.types";
|
|
3
|
+
export declare const LightBoxContext: React.Context<LightBoxContextType>;
|
|
4
|
+
export declare function LightBoxProvider({ open, images, imageIndex, onRequestClose, onImageChange, boxSizing, children, }: LightBoxProviderProps): React.JSX.Element;
|
|
5
|
+
export declare function useLightBoxContext(): LightBoxContextType;
|
package/dist/LightBox/index.cjs
CHANGED
|
@@ -6,6 +6,8 @@ require('framer-motion');
|
|
|
6
6
|
require('react-dom');
|
|
7
7
|
require('@jobber/hooks');
|
|
8
8
|
require('classnames');
|
|
9
|
+
require('../noop-cjs.js');
|
|
10
|
+
require('../_commonjsHelpers-cjs.js');
|
|
9
11
|
require('../ButtonDismiss-cjs.js');
|
|
10
12
|
require('../Button-cjs.js');
|
|
11
13
|
require('react-router-dom');
|
|
@@ -16,7 +18,6 @@ require('../Typography-cjs.js');
|
|
|
16
18
|
require('../Text-cjs.js');
|
|
17
19
|
require('../Heading-cjs.js');
|
|
18
20
|
require('../AtlantisThemeContext-cjs.js');
|
|
19
|
-
require('../_commonjsHelpers-cjs.js');
|
|
20
21
|
require('../identity-cjs.js');
|
|
21
22
|
require('../isTypedArray-cjs.js');
|
|
22
23
|
require('../isObjectLike-cjs.js');
|
|
@@ -29,3 +30,4 @@ require('../_setToString-cjs.js');
|
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
exports.LightBox = LightBox.LightBox;
|
|
33
|
+
exports.useLightBoxContext = LightBox.useLightBoxContext;
|
package/dist/LightBox/index.d.ts
CHANGED
package/dist/LightBox/index.mjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { L as LightBox } from '../LightBox-es.js';
|
|
1
|
+
export { L as LightBox, u as useLightBoxContext } from '../LightBox-es.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
import 'framer-motion';
|
|
4
4
|
import 'react-dom';
|
|
5
5
|
import '@jobber/hooks';
|
|
6
6
|
import 'classnames';
|
|
7
|
+
import '../noop-es.js';
|
|
8
|
+
import '../_commonjsHelpers-es.js';
|
|
7
9
|
import '../ButtonDismiss-es.js';
|
|
8
10
|
import '../Button-es.js';
|
|
9
11
|
import 'react-router-dom';
|
|
@@ -14,7 +16,6 @@ import '../Typography-es.js';
|
|
|
14
16
|
import '../Text-es.js';
|
|
15
17
|
import '../Heading-es.js';
|
|
16
18
|
import '../AtlantisThemeContext-es.js';
|
|
17
|
-
import '../_commonjsHelpers-es.js';
|
|
18
19
|
import '../identity-es.js';
|
|
19
20
|
import '../isTypedArray-es.js';
|
|
20
21
|
import '../isObjectLike-es.js';
|
package/dist/LightBox-cjs.js
CHANGED
|
@@ -5,6 +5,7 @@ var framerMotion = require('framer-motion');
|
|
|
5
5
|
var ReactDOM = require('react-dom');
|
|
6
6
|
var jobberHooks = require('@jobber/hooks');
|
|
7
7
|
var classnames = require('classnames');
|
|
8
|
+
var noop = require('./noop-cjs.js');
|
|
8
9
|
var ButtonDismiss = require('./ButtonDismiss-cjs.js');
|
|
9
10
|
var Text = require('./Text-cjs.js');
|
|
10
11
|
var Button = require('./Button-cjs.js');
|
|
@@ -13,6 +14,11 @@ var AtlantisThemeContext = require('./AtlantisThemeContext-cjs.js');
|
|
|
13
14
|
|
|
14
15
|
var styles = {"backgroundImage":"i9Tw1T65W-k-","next":"Q8amcRaTGf0-","prev":"W9FVb24yJrk-","buttonHidden":"nsN0TPWsBXI-","buttonVisible":"dkLYp7AD2jE-","lightboxWrapper":"_5p2iAj4JfoE-","toolbar":"rMK4cKdOxFw-","closeButton":"_0m6vb11DgiA-","slideNumber":"kCc68gGuTgg-","image":"yYFVVScosfQ-","imageArea":"UskuwLHR6fg-","captionWrapper":"OGjhge-r-U4-","title":"tZU2g-NYdIs-","blurOverlay":"GKIdLTmvcvQ-","thumbnailBar":"_3TfQLQEE3GQ-","thumbnailImage":"eBMzUOlcfQ4-","thumbnail":"eapm2zruLn8-","selected":"PeLn2u-QB0k-","spinning":"_8tDoqjgfLcw-"};
|
|
15
16
|
|
|
17
|
+
// A little bit more than the transition's duration
|
|
18
|
+
// We're doing this to prevent a bug from framer-motion
|
|
19
|
+
// https://github.com/framer/motion/issues/1769
|
|
20
|
+
const BUTTON_DEBOUNCE_DELAY = 250;
|
|
21
|
+
const MOVEMENT_DEBOUNCE_DELAY = 1000;
|
|
16
22
|
const swipeConfidenceThreshold = 10000;
|
|
17
23
|
const swipePower = (offset, velocity) => {
|
|
18
24
|
return Math.abs(offset) * velocity;
|
|
@@ -31,12 +37,26 @@ const slideVariants = {
|
|
|
31
37
|
const imageTransition = {
|
|
32
38
|
x: { duration: 0.65, ease: [0.42, 0, 0, 1.03] },
|
|
33
39
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
/* eslint-disable max-statements */
|
|
42
|
+
const LightBoxContext = React.createContext({
|
|
43
|
+
open: false,
|
|
44
|
+
images: [],
|
|
45
|
+
currentImageIndex: 0,
|
|
46
|
+
mouseIsStationary: true,
|
|
47
|
+
boxSizing: "content-box",
|
|
48
|
+
directionRef: { current: 0 },
|
|
49
|
+
selectedThumbnailRef: { current: null },
|
|
50
|
+
lightboxRef: { current: null },
|
|
51
|
+
mounted: { current: false },
|
|
52
|
+
handleMouseMove: noop.noop,
|
|
53
|
+
handleRequestClose: noop.noop,
|
|
54
|
+
handleOnDragEnd: noop.noop,
|
|
55
|
+
handleThumbnailClick: noop.noop,
|
|
56
|
+
debouncedHandleNext: noop.noop,
|
|
57
|
+
debouncedHandlePrevious: noop.noop,
|
|
58
|
+
});
|
|
59
|
+
function LightBoxProvider({ open = true, images, imageIndex = 0, onRequestClose = noop.noop, onImageChange = noop.noop, boxSizing = "content-box", children, }) {
|
|
40
60
|
const [currentImageIndex, setCurrentImageIndex] = React.useState(imageIndex);
|
|
41
61
|
const directionRef = React.useRef(0);
|
|
42
62
|
const [mouseIsStationary, setMouseIsStationary] = React.useState(true);
|
|
@@ -59,6 +79,7 @@ function LightBox({ boxSizing = "content-box", open, images, imageIndex = 0, onR
|
|
|
59
79
|
});
|
|
60
80
|
React.useEffect(() => {
|
|
61
81
|
setCurrentImageIndex(imageIndex);
|
|
82
|
+
onImageChange(imageIndex);
|
|
62
83
|
}, [imageIndex, open]);
|
|
63
84
|
if (prevOpen.current !== open) {
|
|
64
85
|
prevOpen.current = open;
|
|
@@ -72,57 +93,28 @@ function LightBox({ boxSizing = "content-box", open, images, imageIndex = 0, onR
|
|
|
72
93
|
inline: "center",
|
|
73
94
|
});
|
|
74
95
|
}, [currentImageIndex]);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
React.createElement("div", { className: styles.backgroundImage, style: {
|
|
82
|
-
backgroundImage: `url("${images[currentImageIndex].url}")`,
|
|
83
|
-
} }),
|
|
84
|
-
React.createElement("div", { className: styles.blurOverlay, onClick: handleRequestClose }),
|
|
85
|
-
React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
86
|
-
React.createElement("div", { className: styles.toolbar },
|
|
87
|
-
React.createElement("div", { className: styles.slideNumber },
|
|
88
|
-
React.createElement(Text.Text, null, `${currentImageIndex + 1}/${images.length}`)),
|
|
89
|
-
React.createElement("div", { className: styles.closeButton },
|
|
90
|
-
React.createElement(ButtonDismiss.ButtonDismiss, { ariaLabel: "Close", onClick: handleRequestClose })))),
|
|
91
|
-
React.createElement("div", { className: styles.imageArea },
|
|
92
|
-
React.createElement(framerMotion.AnimatePresence, { initial: false },
|
|
93
|
-
React.createElement(framerMotion.motion.img, { key: currentImageIndex, variants: slideVariants, src: images[currentImageIndex].url, custom: directionRef, className: styles.image, initial: "enter", alt: images[currentImageIndex].alt ||
|
|
94
|
-
images[currentImageIndex].title ||
|
|
95
|
-
"", animate: "center", exit: "exit", transition: imageTransition, drag: "x", dragConstraints: { left: 0, right: 0 }, dragElastic: 1, onDragEnd: handleOnDragEnd }))),
|
|
96
|
-
images.length > 1 && (React.createElement(React.Fragment, null,
|
|
97
|
-
React.createElement(PreviousButton, { onClick: debouncedHandlePrevious, hideButton: mouseIsStationary }),
|
|
98
|
-
React.createElement(NextButton, { onClick: debouncedHandleNext, hideButton: mouseIsStationary }))),
|
|
99
|
-
(images[currentImageIndex].title ||
|
|
100
|
-
images[currentImageIndex].caption) && (React.createElement("div", { className: styles.captionWrapper },
|
|
101
|
-
React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
102
|
-
images[currentImageIndex].title && (React.createElement("div", { className: styles.title },
|
|
103
|
-
React.createElement(Heading.Heading, { level: 4 }, images[currentImageIndex].title))),
|
|
104
|
-
images[currentImageIndex].caption && (React.createElement(Text.Text, { size: "large" }, images[currentImageIndex].caption))))),
|
|
105
|
-
images.length > 1 && (React.createElement("div", { className: styles.thumbnailBar, style: {
|
|
106
|
-
"--lightbox--box-sizing": boxSizing,
|
|
107
|
-
}, "data-testid": "ATL-Thumbnail-Bar" }, images.map((image, index) => (React.createElement("div", { key: index, className: classnames(styles.thumbnail, {
|
|
108
|
-
[styles.selected]: index === currentImageIndex,
|
|
109
|
-
}), onClick: () => handleThumbnailClick(index), ref: index === currentImageIndex ? selectedThumbnailRef : null },
|
|
110
|
-
React.createElement("img", { key: index, src: image.url, alt: image.alt || image.title || "", className: styles.thumbnailImage }))))))))));
|
|
111
|
-
return mounted.current
|
|
112
|
-
? ReactDOM.createPortal(template, document.body)
|
|
113
|
-
: template;
|
|
96
|
+
function handleMouseMove() {
|
|
97
|
+
if (mouseIsStationary) {
|
|
98
|
+
setMouseIsStationary(false);
|
|
99
|
+
}
|
|
100
|
+
handleMouseMovement();
|
|
101
|
+
}
|
|
114
102
|
function handleMovePrevious() {
|
|
115
103
|
directionRef.current = -1;
|
|
116
|
-
|
|
104
|
+
const newIndex = (currentImageIndex + images.length - 1) % images.length;
|
|
105
|
+
setCurrentImageIndex(newIndex);
|
|
106
|
+
onImageChange(newIndex);
|
|
117
107
|
}
|
|
118
108
|
function handleMoveNext() {
|
|
119
109
|
directionRef.current = 1;
|
|
120
|
-
|
|
110
|
+
const newIndex = (currentImageIndex + 1) % images.length;
|
|
111
|
+
setCurrentImageIndex(newIndex);
|
|
112
|
+
onImageChange(newIndex);
|
|
121
113
|
}
|
|
122
114
|
function handleRequestClose() {
|
|
123
115
|
onRequestClose({ lastPosition: currentImageIndex });
|
|
124
116
|
}
|
|
125
|
-
function handleOnDragEnd(
|
|
117
|
+
function handleOnDragEnd(_event, { offset, velocity }) {
|
|
126
118
|
const swipe = swipePower(offset.x, velocity.x);
|
|
127
119
|
if (swipe < -swipeConfidenceThreshold) {
|
|
128
120
|
handleMoveNext();
|
|
@@ -139,17 +131,28 @@ function LightBox({ boxSizing = "content-box", open, images, imageIndex = 0, onR
|
|
|
139
131
|
directionRef.current = 1;
|
|
140
132
|
}
|
|
141
133
|
setCurrentImageIndex(index);
|
|
134
|
+
onImageChange(index);
|
|
142
135
|
}
|
|
136
|
+
return (React.createElement(LightBoxContext.Provider, { value: {
|
|
137
|
+
open,
|
|
138
|
+
images,
|
|
139
|
+
currentImageIndex,
|
|
140
|
+
mouseIsStationary,
|
|
141
|
+
boxSizing,
|
|
142
|
+
directionRef,
|
|
143
|
+
selectedThumbnailRef,
|
|
144
|
+
lightboxRef,
|
|
145
|
+
mounted,
|
|
146
|
+
handleMouseMove,
|
|
147
|
+
handleRequestClose,
|
|
148
|
+
handleOnDragEnd,
|
|
149
|
+
handleThumbnailClick,
|
|
150
|
+
debouncedHandleNext,
|
|
151
|
+
debouncedHandlePrevious,
|
|
152
|
+
} }, children));
|
|
143
153
|
}
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
return (React.createElement("div", { className: `${styles.prev} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
|
|
147
|
-
React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowLeft", ariaLabel: "Previous image", onClick: onClick })));
|
|
148
|
-
}
|
|
149
|
-
function NextButton({ onClick, hideButton }) {
|
|
150
|
-
const { mediumAndUp } = jobberHooks.useBreakpoints();
|
|
151
|
-
return (React.createElement("div", { className: `${styles.next} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
|
|
152
|
-
React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowRight", ariaLabel: "Next image", onClick: onClick })));
|
|
154
|
+
function useLightBoxContext() {
|
|
155
|
+
return React.useContext(LightBoxContext);
|
|
153
156
|
}
|
|
154
157
|
function togglePrintStyles(open) {
|
|
155
158
|
try {
|
|
@@ -165,4 +168,189 @@ function togglePrintStyles(open) {
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
function LightBoxContent() {
|
|
172
|
+
const { open, lightboxRef, handleMouseMove } = useLightBoxContext();
|
|
173
|
+
const mounted = jobberHooks.useIsMounted();
|
|
174
|
+
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 },
|
|
175
|
+
React.createElement(LightBoxBackground, null),
|
|
176
|
+
React.createElement(LightBoxOverlay, null),
|
|
177
|
+
React.createElement(LightBoxToolbar, null),
|
|
178
|
+
React.createElement(LightBoxSlides, null),
|
|
179
|
+
React.createElement(LightBoxNavigation, null),
|
|
180
|
+
React.createElement(LightBoxCaption, null),
|
|
181
|
+
React.createElement(LightBoxThumbnails, null)))));
|
|
182
|
+
return mounted.current
|
|
183
|
+
? ReactDOM.createPortal(template, document.body)
|
|
184
|
+
: template;
|
|
185
|
+
}
|
|
186
|
+
function PreviousButton({ onClick, hideButton, className }) {
|
|
187
|
+
const { mediumAndUp } = jobberHooks.useBreakpoints();
|
|
188
|
+
return (React.createElement("div", { className: `${className} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
|
|
189
|
+
React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowLeft", ariaLabel: "Previous image", onClick: onClick })));
|
|
190
|
+
}
|
|
191
|
+
function NextButton({ onClick, hideButton, className }) {
|
|
192
|
+
const { mediumAndUp } = jobberHooks.useBreakpoints();
|
|
193
|
+
return (React.createElement("div", { className: `${className} ${hideButton ? styles.buttonHidden : styles.buttonVisible}` },
|
|
194
|
+
React.createElement(Button.Button, { size: mediumAndUp ? "large" : "small", variation: "subtle", type: "secondary", icon: "arrowRight", ariaLabel: "Next image", onClick: onClick })));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Blurred, desaturated copy of the current image rendered as a full-bleed
|
|
198
|
+
* background behind the lightbox. Pass `className` to apply additional styles.
|
|
199
|
+
*/
|
|
200
|
+
function LightBoxBackground({ className }) {
|
|
201
|
+
const { images, currentImageIndex } = useLightBoxContext();
|
|
202
|
+
return (React.createElement("div", { className: classnames(styles.backgroundImage, className), style: {
|
|
203
|
+
backgroundImage: `url("${images[currentImageIndex].url}")`,
|
|
204
|
+
} }));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Semi-transparent blur backdrop. Clicking it calls `onRequestClose`.
|
|
208
|
+
* Pass `className` to apply additional styles.
|
|
209
|
+
*/
|
|
210
|
+
function LightBoxOverlay({ className }) {
|
|
211
|
+
const { handleRequestClose } = useLightBoxContext();
|
|
212
|
+
return (React.createElement("div", { className: classnames(styles.blurOverlay, className), onClick: handleRequestClose }));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Top bar showing the current image counter (`1/3`) and a close button.
|
|
216
|
+
* Styled for dark backgrounds.
|
|
217
|
+
*/
|
|
218
|
+
function LightBoxToolbar() {
|
|
219
|
+
const { images, currentImageIndex, handleRequestClose } = useLightBoxContext();
|
|
220
|
+
return (React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
221
|
+
React.createElement("div", { className: styles.toolbar },
|
|
222
|
+
React.createElement("div", { className: styles.slideNumber },
|
|
223
|
+
React.createElement(Text.Text, null, `${currentImageIndex + 1}/${images.length}`)),
|
|
224
|
+
React.createElement("div", { className: styles.closeButton },
|
|
225
|
+
React.createElement(ButtonDismiss.ButtonDismiss, { ariaLabel: "Close", onClick: handleRequestClose })))));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The animated hero image with swipe-to-navigate and slide animation.
|
|
229
|
+
*
|
|
230
|
+
* Pass `className` to add styles to the image wrapper. Supports
|
|
231
|
+
* swipe-to-navigate (drag). Keyboard arrow navigation is handled by
|
|
232
|
+
* `LightBox.Provider`.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```tsx
|
|
236
|
+
* <LightBox.Slides className={styles.imageArea} />
|
|
237
|
+
* <LightBox.Navigation
|
|
238
|
+
* prevButtonClassName={styles.prev}
|
|
239
|
+
* nextButtonClassName={styles.next}
|
|
240
|
+
* />
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
function LightBoxSlides({ className }) {
|
|
244
|
+
const { images, currentImageIndex, directionRef, handleOnDragEnd } = useLightBoxContext();
|
|
245
|
+
return (React.createElement("div", { className: classnames(styles.imageArea, className) },
|
|
246
|
+
React.createElement(framerMotion.AnimatePresence, { initial: false },
|
|
247
|
+
React.createElement(framerMotion.motion.img, { key: currentImageIndex, variants: slideVariants, src: images[currentImageIndex].url, custom: directionRef, className: styles.image, initial: "enter", alt: images[currentImageIndex].alt ||
|
|
248
|
+
images[currentImageIndex].title ||
|
|
249
|
+
"", animate: "center", exit: "exit", transition: imageTransition, drag: "x", dragConstraints: { left: 0, right: 0 }, dragElastic: 1, onDragEnd: handleOnDragEnd }))));
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Previous and next navigation buttons. Returns `null` when the image set
|
|
253
|
+
* has only one image.
|
|
254
|
+
*
|
|
255
|
+
* Use `prevButtonClassName` and `nextButtonClassName` to override the styles
|
|
256
|
+
* on each button's wrapper for custom layouts.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```tsx
|
|
260
|
+
* <LightBox.Navigation
|
|
261
|
+
* prevButtonClassName={styles.prev}
|
|
262
|
+
* nextButtonClassName={styles.next}
|
|
263
|
+
* />
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
function LightBoxNavigation({ prevButtonClassName, nextButtonClassName, }) {
|
|
267
|
+
const { images, mouseIsStationary, debouncedHandleNext, debouncedHandlePrevious, } = useLightBoxContext();
|
|
268
|
+
if (images.length <= 1)
|
|
269
|
+
return null;
|
|
270
|
+
return (React.createElement(React.Fragment, null,
|
|
271
|
+
React.createElement(PreviousButton, { onClick: debouncedHandlePrevious, hideButton: mouseIsStationary, className: classnames(styles.prev, prevButtonClassName) }),
|
|
272
|
+
React.createElement(NextButton, { onClick: debouncedHandleNext, hideButton: mouseIsStationary, className: classnames(styles.next, nextButtonClassName) })));
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Title and caption text for the current image. Only renders when the current
|
|
276
|
+
* image has a `title` or `caption`. Styled for dark backgrounds.
|
|
277
|
+
*/
|
|
278
|
+
function LightBoxCaption() {
|
|
279
|
+
const { images, currentImageIndex } = useLightBoxContext();
|
|
280
|
+
const { title, caption } = images[currentImageIndex];
|
|
281
|
+
if (!title && !caption)
|
|
282
|
+
return null;
|
|
283
|
+
return (React.createElement("div", { className: styles.captionWrapper },
|
|
284
|
+
React.createElement(AtlantisThemeContext.AtlantisThemeContextProvider, { dangerouslyOverrideTheme: "dark" },
|
|
285
|
+
title && (React.createElement("div", { className: styles.title },
|
|
286
|
+
React.createElement(Heading.Heading, { level: 4 }, title))),
|
|
287
|
+
caption && React.createElement(Text.Text, { size: "large" }, caption))));
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Scrollable thumbnail strip. Only renders when there are two or more images.
|
|
291
|
+
*/
|
|
292
|
+
function LightBoxThumbnails() {
|
|
293
|
+
const { images, currentImageIndex, boxSizing, selectedThumbnailRef, handleThumbnailClick, } = useLightBoxContext();
|
|
294
|
+
if (images.length <= 1)
|
|
295
|
+
return null;
|
|
296
|
+
return (React.createElement("div", { className: styles.thumbnailBar, style: { "--lightbox--box-sizing": boxSizing }, "data-testid": "ATL-Thumbnail-Bar" }, images.map((image, index) => (React.createElement("div", { key: index, className: classnames(styles.thumbnail, {
|
|
297
|
+
[styles.selected]: index === currentImageIndex,
|
|
298
|
+
}), onClick: () => handleThumbnailClick(index), ref: index === currentImageIndex ? selectedThumbnailRef : null },
|
|
299
|
+
React.createElement("img", { key: index, src: image.url, alt: image.alt || image.title || "", className: styles.thumbnailImage }))))));
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* LightBox displays images in a fullscreen overlay.
|
|
303
|
+
*
|
|
304
|
+
* **Self-contained (legacy) usage:**
|
|
305
|
+
* ```tsx
|
|
306
|
+
* <LightBox
|
|
307
|
+
* open={isOpen}
|
|
308
|
+
* images={images}
|
|
309
|
+
* imageIndex={imageIndex}
|
|
310
|
+
* onRequestClose={({ lastPosition }) => { setIsOpen(false); }}
|
|
311
|
+
* />
|
|
312
|
+
* ```
|
|
313
|
+
*
|
|
314
|
+
* **Full composable (fullscreen) usage:**
|
|
315
|
+
* ```tsx
|
|
316
|
+
* <LightBox.Provider open={isOpen} images={images} onRequestClose={onClose}>
|
|
317
|
+
* <LightBox.Content />
|
|
318
|
+
* </LightBox.Provider>
|
|
319
|
+
* ```
|
|
320
|
+
*
|
|
321
|
+
* **Inline gallery usage (no overlay, no close):**
|
|
322
|
+
* ```tsx
|
|
323
|
+
* <LightBox.Provider
|
|
324
|
+
* open={true}
|
|
325
|
+
* images={images}
|
|
326
|
+
* imageIndex={activeIndex}
|
|
327
|
+
* onImageChange={onImageChange}
|
|
328
|
+
* >
|
|
329
|
+
* <div className={styles.lightboxWrapper} onMouseMove={handleMouseMove}>
|
|
330
|
+
* <LightBox.Background className={styles.backgroundImage} />
|
|
331
|
+
* <LightBox.Overlay className={styles.blurOverlay} />
|
|
332
|
+
* <LightBox.Slides className={styles.imageArea} />
|
|
333
|
+
* <LightBox.Navigation
|
|
334
|
+
* prevButtonClassName={styles.prev}
|
|
335
|
+
* nextButtonClassName={styles.next}
|
|
336
|
+
* />
|
|
337
|
+
* </div>
|
|
338
|
+
* </LightBox.Provider>
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
function LightBox(props) {
|
|
342
|
+
return (React.createElement(LightBoxProvider, Object.assign({}, props),
|
|
343
|
+
React.createElement(LightBoxContent, null)));
|
|
344
|
+
}
|
|
345
|
+
LightBox.Provider = LightBoxProvider;
|
|
346
|
+
LightBox.Content = LightBoxContent;
|
|
347
|
+
LightBox.Background = LightBoxBackground;
|
|
348
|
+
LightBox.Overlay = LightBoxOverlay;
|
|
349
|
+
LightBox.Toolbar = LightBoxToolbar;
|
|
350
|
+
LightBox.Slides = LightBoxSlides;
|
|
351
|
+
LightBox.Navigation = LightBoxNavigation;
|
|
352
|
+
LightBox.Caption = LightBoxCaption;
|
|
353
|
+
LightBox.Thumbnails = LightBoxThumbnails;
|
|
354
|
+
|
|
168
355
|
exports.LightBox = LightBox;
|
|
356
|
+
exports.useLightBoxContext = useLightBoxContext;
|