@lumx/react 3.20.1-alpha.36 → 3.20.1-alpha.37
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/index.js +13990 -64
- package/index.js.map +1 -0
- package/package.json +3 -3
- package/utils/index.js +159 -3
- package/utils/index.js.map +1 -0
- package/_internal/035c132d.js +0 -314
- package/_internal/0b9c76cb.js +0 -5
- package/_internal/0ea95af1.js +0 -819
- package/_internal/230173a8.js +0 -12
- package/_internal/2a3d237c.js +0 -11
- package/_internal/2c2b6a89.js +0 -51
- package/_internal/3181f000.js +0 -13
- package/_internal/36bd7352.js +0 -218
- package/_internal/3864a6c1.js +0 -436
- package/_internal/3a1facc0.js +0 -17
- package/_internal/49bbeed3.js +0 -5
- package/_internal/4daccdd5.js +0 -24
- package/_internal/4f4c0582.js +0 -2169
- package/_internal/5cef5e8c.js +0 -74
- package/_internal/6a60381c.js +0 -117
- package/_internal/6f084a40.js +0 -111
- package/_internal/76ddefd4.js +0 -61
- package/_internal/78df9309.js +0 -19
- package/_internal/7ad474e4.js +0 -98
- package/_internal/7b293665.js +0 -19
- package/_internal/7daf0f24.js +0 -76
- package/_internal/825ac334.js +0 -129
- package/_internal/827b804a.js +0 -5
- package/_internal/8bce732e.js +0 -8
- package/_internal/9d1336a1.js +0 -18
- package/_internal/a38bdcd0.js +0 -49
- package/_internal/b326655a.js +0 -682
- package/_internal/b83e864b.js +0 -76
- package/_internal/bace1bf1.js +0 -103
- package/_internal/c57190eb.js +0 -146
- package/_internal/components/alert-dialog/index-284fed80.js +0 -163
- package/_internal/components/autocomplete/__mockData__/index-96fc7ea7.js +0 -27
- package/_internal/components/autocomplete/index-27a49d5d.js +0 -261
- package/_internal/components/avatar/index-b8bc3cae.js +0 -83
- package/_internal/components/badge/index-9880f056.js +0 -81
- package/_internal/components/button/index-d919f96d.js +0 -47
- package/_internal/components/checkbox/index-dd478de9.js +0 -143
- package/_internal/components/chip/index-7f84cdbd.js +0 -102
- package/_internal/components/comment-block/index-e942de30.js +0 -138
- package/_internal/components/date-picker/index-a258d3ce.js +0 -596
- package/_internal/components/dialog/index-c7066813.js +0 -238
- package/_internal/components/divider/index-b6934baf.js +0 -50
- package/_internal/components/drag-handle/index-80e5af6c.js +0 -52
- package/_internal/components/dropdown/index-cebfc268.js +0 -147
- package/_internal/components/expansion-panel/index-9d45a211.js +0 -167
- package/_internal/components/flag/index-33e9d909.js +0 -59
- package/_internal/components/flex-box/index-0d6b6b43.js +0 -56
- package/_internal/components/generic-block/index-836c4352.js +0 -124
- package/_internal/components/grid/index-8c16a4f1.js +0 -104
- package/_internal/components/grid-column/index-4a2f56bb.js +0 -59
- package/_internal/components/heading/index-fd558a9a.js +0 -53
- package/_internal/components/icon/index-e7b9b40e.js +0 -1
- package/_internal/components/image-block/index-7021c2f1.js +0 -110
- package/_internal/components/image-lightbox/index-27017547.js +0 -759
- package/_internal/components/inline-list/index-52efec7f.js +0 -74
- package/_internal/components/input-helper/index-5a837e7c.js +0 -71
- package/_internal/components/input-label/index-0113f382.js +0 -59
- package/_internal/components/lightbox/index-fad5a9f2.js +0 -155
- package/_internal/components/link/index-008bae4b.js +0 -72
- package/_internal/components/link-preview/index-3e9c32f2.js +0 -117
- package/_internal/components/list/index-da88c6e9.js +0 -71
- package/_internal/components/message/index-17b568f2.js +0 -98
- package/_internal/components/mosaic/index-2d40e01c.js +0 -95
- package/_internal/components/navigation/index-dc17c639.js +0 -225
- package/_internal/components/notification/index-a1101015.js +0 -146
- package/_internal/components/popover/index-dbca63f1.js +0 -2
- package/_internal/components/popover-dialog/index-654d2abc.js +0 -66
- package/_internal/components/post-block/index-4b6b4dcb.js +0 -109
- package/_internal/components/progress/index-6f578c31.js +0 -182
- package/_internal/components/progress-tracker/index-700bc4c2.js +0 -309
- package/_internal/components/radio-button/index-89b725af.js +0 -149
- package/_internal/components/select/index-2755aa32.js +0 -458
- package/_internal/components/side-navigation/index-0bbe1fdd.js +0 -165
- package/_internal/components/skeleton/index-b2f8501c.js +0 -166
- package/_internal/components/slider/index-4f9b891b.js +0 -311
- package/_internal/components/slideshow/index-d4604589.js +0 -150
- package/_internal/components/switch/index-58fe0f0c.js +0 -122
- package/_internal/components/table/index-d541aa88.js +0 -297
- package/_internal/components/tabs/index-af1538d6.js +0 -298
- package/_internal/components/text/index-5bfc6d5e.js +0 -1
- package/_internal/components/text-field/index-47c98d09.js +0 -360
- package/_internal/components/thumbnail/index-71545d95.js +0 -41
- package/_internal/components/toolbar/index-7041a5a6.js +0 -61
- package/_internal/components/tooltip/index-8bb97e60.js +0 -327
- package/_internal/components/uploader/index-8f6f55de.js +0 -153
- package/_internal/components/user-block/index-70d4ce8f.js +0 -145
- package/_internal/d0e3f49e.js +0 -23
- package/_internal/de24f857.js +0 -3
- package/_internal/e806b848.js +0 -8
- package/_internal/e913a3af.js +0 -117
- package/_internal/ea04260d.js +0 -28
- package/_internal/eaf6c45a.js +0 -3
- package/_internal/ebdcb0c7.js +0 -60
- package/_internal/ef5d1aac.js +0 -3
- package/_internal/f0d7d6ea.js +0 -29
- package/_internal/utils/ClickAwayProvider/index-1234ce76.js +0 -94
- package/_internal/utils/MaterialThemeSwitcher/index-8d22de5a.js +0 -48
- package/_internal/utils/Portal/index-6e0a7404.js +0 -44
- package/_internal/utils/disabled/index-075b99c3.js +0 -36
- package/_internal/utils/type/index-ea9fc197.js +0 -2
|
@@ -1,759 +0,0 @@
|
|
|
1
|
-
import React__default from 'react';
|
|
2
|
-
import classNames from 'classnames';
|
|
3
|
-
import { u as useMergeRefs, m as mergeRefs } from '../../f0d7d6ea.js';
|
|
4
|
-
import { f as forwardRef } from '../../8bce732e.js';
|
|
5
|
-
import memoize from 'lodash/memoize';
|
|
6
|
-
import { I as ImageCaption } from '../../5cef5e8c.js';
|
|
7
|
-
import { getRootClassName } from '@lumx/core/js/utils/className';
|
|
8
|
-
import throttle from 'lodash/throttle';
|
|
9
|
-
import { i as isReducedMotion } from '../../a38bdcd0.js';
|
|
10
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
|
-
import { S as SlideshowItem, a as SlideshowControls, b as Slides } from '../../b326655a.js';
|
|
12
|
-
import { T as Thumbnail } from '../../035c132d.js';
|
|
13
|
-
import { I as IconButton } from '../../b83e864b.js';
|
|
14
|
-
import { FlexBox } from '../flex-box/index-0d6b6b43.js';
|
|
15
|
-
import ReactDOM from 'react-dom';
|
|
16
|
-
import { Lightbox } from '../lightbox/index-fad5a9f2.js';
|
|
17
|
-
import { T as ThemeProvider } from '../../3181f000.js';
|
|
18
|
-
import { ClickAwayProvider } from '../../utils/ClickAwayProvider/index-1234ce76.js';
|
|
19
|
-
|
|
20
|
-
const mdiMagnifyMinusOutline = 'M15.5 14h-.79l-.28-.27A6.5 6.5 0 0 0 16 9.5 6.5 6.5 0 0 0 9.5 3 6.5 6.5 0 0 0 3 9.5 6.5 6.5 0 0 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 5 1.5-1.5zm-6 0C7 14 5 12 5 9.5S7 5 9.5 5 14 7 14 9.5 12 14 9.5 14M7 9h5v1H7z';
|
|
21
|
-
|
|
22
|
-
const mdiMagnifyPlusOutline = 'm15.5 14 5 5-1.5 1.5-5-5v-.79l-.27-.28A6.5 6.5 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3 6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.57 4.23l.28.27zm-6 0C12 14 14 12 14 9.5S12 5 9.5 5 5 7 5 9.5 7 14 9.5 14m2.5-4h-2v2H9v-2H7V9h2V7h1v2h2z';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Component display name.
|
|
26
|
-
*/
|
|
27
|
-
const COMPONENT_NAME = 'ImageLightbox';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Component default class name and class prefix.
|
|
31
|
-
*/
|
|
32
|
-
const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Observe element size (only works if it's size depends on the window size).
|
|
36
|
-
*
|
|
37
|
-
* (Not using ResizeObserver for better browser backward compat)
|
|
38
|
-
*
|
|
39
|
-
* @param elementRef Element to observe
|
|
40
|
-
* @return the size and a manual update callback
|
|
41
|
-
*/
|
|
42
|
-
function useSizeOnWindowResize(elementRef) {
|
|
43
|
-
const [size, setSize] = React__default.useState(null);
|
|
44
|
-
const updateSize = React__default.useMemo(() => throttle(() => {
|
|
45
|
-
const newSize = elementRef.current?.getBoundingClientRect();
|
|
46
|
-
if (newSize) setSize(newSize);
|
|
47
|
-
}, 10), [elementRef]);
|
|
48
|
-
React__default.useEffect(() => {
|
|
49
|
-
updateSize();
|
|
50
|
-
window.addEventListener('resize', updateSize);
|
|
51
|
-
return () => window.removeEventListener('resize', updateSize);
|
|
52
|
-
}, [updateSize]);
|
|
53
|
-
return [size, updateSize];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Get natural image size after load. */
|
|
57
|
-
function useImageSize(imgRef, getInitialSize) {
|
|
58
|
-
const [imageSize, setImageSize] = React__default.useState(getInitialSize || null);
|
|
59
|
-
React__default.useEffect(() => {
|
|
60
|
-
const {
|
|
61
|
-
current: img
|
|
62
|
-
} = imgRef;
|
|
63
|
-
if (!img) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
const onLoad = () => setImageSize({
|
|
67
|
-
width: img.naturalWidth,
|
|
68
|
-
height: img.naturalHeight
|
|
69
|
-
});
|
|
70
|
-
img.addEventListener('load', onLoad);
|
|
71
|
-
return () => img.removeEventListener('load', onLoad);
|
|
72
|
-
}, [imgRef]);
|
|
73
|
-
return imageSize;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Minimal recursive deep equal of JS values */
|
|
77
|
-
function isEqual(obj1, obj2) {
|
|
78
|
-
if (obj1 === obj2) return true;
|
|
79
|
-
if (typeof obj1 === 'object' && typeof obj2 === 'object') {
|
|
80
|
-
const keys1 = Object.keys(obj1);
|
|
81
|
-
const keys2 = Object.keys(obj2);
|
|
82
|
-
if (keys1.length !== keys2.length) return false;
|
|
83
|
-
return keys1.every(key1 => isEqual(obj1[key1], obj2[key1]));
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Listen to mouse wheel + ctrl and multi-pointer pinch to zoom
|
|
90
|
-
*/
|
|
91
|
-
function usePointerZoom(scrollAreaRef, onScaleChange, animateScroll) {
|
|
92
|
-
const [isPointerZooming, setPointerZooming] = React__default.useState(false);
|
|
93
|
-
React__default.useEffect(() => {
|
|
94
|
-
const scrollArea = scrollAreaRef.current;
|
|
95
|
-
if (!scrollArea || !onScaleChange) {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
let animationFrame;
|
|
99
|
-
let zoomStateTimeoutId;
|
|
100
|
-
function updateScaleOnNextFrame(newScale, mousePosition) {
|
|
101
|
-
// Cancel previously scheduled frame
|
|
102
|
-
if (animationFrame) cancelAnimationFrame(animationFrame);
|
|
103
|
-
|
|
104
|
-
// Cancel previously scheduled zoom state change
|
|
105
|
-
if (zoomStateTimeoutId) clearTimeout(zoomStateTimeoutId);
|
|
106
|
-
function nextFrame() {
|
|
107
|
-
setPointerZooming(true);
|
|
108
|
-
onScaleChange?.(newScale);
|
|
109
|
-
animationFrame = null;
|
|
110
|
-
// Wait a bit before indicating the pointer zooming is finished
|
|
111
|
-
zoomStateTimeoutId = setTimeout(() => setPointerZooming(false), 100);
|
|
112
|
-
}
|
|
113
|
-
animationFrame = requestAnimationFrame(nextFrame);
|
|
114
|
-
|
|
115
|
-
// Animate scroll in parallel (centering on the current mouse position)
|
|
116
|
-
animateScroll(mousePosition, {
|
|
117
|
-
width: scrollArea.scrollWidth,
|
|
118
|
-
height: scrollArea.scrollHeight
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
function onWheel(event) {
|
|
122
|
-
if (!event.ctrlKey) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
event.preventDefault();
|
|
126
|
-
const newScale = Math.exp(-event.deltaY / 50);
|
|
127
|
-
|
|
128
|
-
// Update scale on next frame (focused on the mouse position)
|
|
129
|
-
updateScaleOnNextFrame(newScale, {
|
|
130
|
-
x: event.clientX,
|
|
131
|
-
y: event.clientY
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
const activePointers = {};
|
|
135
|
-
let prevDistance = null;
|
|
136
|
-
let previousCenterPoint = null;
|
|
137
|
-
function onPointerDown(event) {
|
|
138
|
-
activePointers[event.pointerId] = event;
|
|
139
|
-
}
|
|
140
|
-
function onPointerMove(event) {
|
|
141
|
-
// Update pointer in cache
|
|
142
|
-
if (activePointers[event.pointerId]) {
|
|
143
|
-
activePointers[event.pointerId] = event;
|
|
144
|
-
}
|
|
145
|
-
const pointers = Object.values(activePointers);
|
|
146
|
-
|
|
147
|
-
// Make sure we run computation on one of the pointer in the group
|
|
148
|
-
if (pointers[0]?.pointerId !== event.pointerId) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Centered point between all pointers
|
|
153
|
-
const centerPoint = {
|
|
154
|
-
x: pointers.reduce((x, {
|
|
155
|
-
clientX
|
|
156
|
-
}) => x + clientX, 0) / pointers.length,
|
|
157
|
-
y: pointers.reduce((y, {
|
|
158
|
-
clientY
|
|
159
|
-
}) => y + clientY, 0) / pointers.length
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// Movement of the center point
|
|
163
|
-
const deltaCenterPoint = previousCenterPoint && {
|
|
164
|
-
left: previousCenterPoint.x - centerPoint.x,
|
|
165
|
-
top: previousCenterPoint.y - centerPoint.y
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// Pan X & Y
|
|
169
|
-
if (deltaCenterPoint) {
|
|
170
|
-
// Apply movement of the center point to the scroll
|
|
171
|
-
scrollArea.scrollBy({
|
|
172
|
-
top: deltaCenterPoint.top / 2,
|
|
173
|
-
left: deltaCenterPoint.left / 2
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Pinch to zoom
|
|
178
|
-
if (pointers.length === 2) {
|
|
179
|
-
const [pointer1, pointer2] = pointers;
|
|
180
|
-
const distance = Math.hypot(pointer2.clientX - pointer1.clientX, pointer2.clientY - pointer1.clientY);
|
|
181
|
-
if (prevDistance && deltaCenterPoint) {
|
|
182
|
-
const delta = prevDistance - distance;
|
|
183
|
-
const absDelta = Math.abs(delta);
|
|
184
|
-
|
|
185
|
-
// Zoom only if we are "pinching" more than we are moving the pointers
|
|
186
|
-
if (absDelta > Math.abs(deltaCenterPoint.left) && absDelta > Math.abs(deltaCenterPoint.top)) {
|
|
187
|
-
// Update scale on next frame (focused on the center point between the two pointers)
|
|
188
|
-
const newScale = Math.exp(-delta / 100);
|
|
189
|
-
updateScaleOnNextFrame(newScale, centerPoint);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
prevDistance = distance;
|
|
193
|
-
}
|
|
194
|
-
previousCenterPoint = centerPoint;
|
|
195
|
-
}
|
|
196
|
-
function onPointerUp(event) {
|
|
197
|
-
prevDistance = null;
|
|
198
|
-
previousCenterPoint = null;
|
|
199
|
-
delete activePointers[event.pointerId];
|
|
200
|
-
}
|
|
201
|
-
scrollArea.addEventListener('wheel', onWheel, {
|
|
202
|
-
passive: false
|
|
203
|
-
});
|
|
204
|
-
const isMultiTouch = navigator.maxTouchPoints >= 2;
|
|
205
|
-
if (isMultiTouch) {
|
|
206
|
-
scrollArea.addEventListener('pointerdown', onPointerDown);
|
|
207
|
-
scrollArea.addEventListener('pointermove', onPointerMove);
|
|
208
|
-
scrollArea.addEventListener('pointerup', onPointerUp);
|
|
209
|
-
}
|
|
210
|
-
return () => {
|
|
211
|
-
scrollArea.removeEventListener('wheel', onWheel);
|
|
212
|
-
if (isMultiTouch) {
|
|
213
|
-
scrollArea.removeEventListener('pointerdown', onPointerDown);
|
|
214
|
-
scrollArea.removeEventListener('pointermove', onPointerMove);
|
|
215
|
-
scrollArea.removeEventListener('pointerup', onPointerUp);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
}, [animateScroll, onScaleChange, scrollAreaRef]);
|
|
219
|
-
return isPointerZooming;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** Maintains the scroll position centered relative to the original scroll area's dimensions when the content scales. */
|
|
223
|
-
function useAnimateScroll(scrollAreaRef) {
|
|
224
|
-
return React__default.useMemo(() => {
|
|
225
|
-
let animationFrame = null;
|
|
226
|
-
return function animate(centerPoint, initialScrollAreaSize) {
|
|
227
|
-
const scrollArea = scrollAreaRef.current;
|
|
228
|
-
if (!scrollArea) {
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Cancel previously running animation
|
|
233
|
-
if (animationFrame) cancelAnimationFrame(animationFrame);
|
|
234
|
-
|
|
235
|
-
// Center on the given point or else on the scroll area visual center
|
|
236
|
-
const clientHeightRatio = centerPoint?.y ? centerPoint.y / scrollArea.clientHeight : 0.5;
|
|
237
|
-
const clientWidthRatio = centerPoint?.x ? centerPoint.x / scrollArea.clientWidth : 0.5;
|
|
238
|
-
const initialScrollHeight = initialScrollAreaSize?.height || scrollArea.scrollHeight;
|
|
239
|
-
const initialScrollWidth = initialScrollAreaSize?.width || scrollArea.scrollWidth;
|
|
240
|
-
const heightCenter = scrollArea.scrollTop + scrollArea.clientHeight * clientHeightRatio;
|
|
241
|
-
const heightRatio = heightCenter / initialScrollHeight;
|
|
242
|
-
const widthCenter = scrollArea.scrollLeft + scrollArea.clientWidth * clientWidthRatio;
|
|
243
|
-
const widthRatio = widthCenter / initialScrollWidth;
|
|
244
|
-
let prevScrollHeight = 0;
|
|
245
|
-
let prevScrollWidth = 0;
|
|
246
|
-
function nextFrame() {
|
|
247
|
-
const {
|
|
248
|
-
scrollHeight,
|
|
249
|
-
scrollWidth,
|
|
250
|
-
clientHeight,
|
|
251
|
-
clientWidth
|
|
252
|
-
} = scrollArea;
|
|
253
|
-
|
|
254
|
-
// Scroll area stopped expanding => stop animation
|
|
255
|
-
if (scrollHeight === prevScrollHeight && scrollWidth === prevScrollWidth) {
|
|
256
|
-
animationFrame = null;
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Compute next scroll position
|
|
261
|
-
const top = heightRatio * scrollHeight - clientHeight * clientHeightRatio;
|
|
262
|
-
const left = widthRatio * scrollWidth - clientWidth * clientWidthRatio;
|
|
263
|
-
scrollArea.scrollTo({
|
|
264
|
-
top,
|
|
265
|
-
left
|
|
266
|
-
});
|
|
267
|
-
prevScrollHeight = scrollHeight;
|
|
268
|
-
prevScrollWidth = scrollWidth;
|
|
269
|
-
animationFrame = requestAnimationFrame(nextFrame);
|
|
270
|
-
}
|
|
271
|
-
animationFrame = requestAnimationFrame(nextFrame);
|
|
272
|
-
};
|
|
273
|
-
}, [scrollAreaRef]);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/** Internal image slide component for ImageLightbox */
|
|
277
|
-
const ImageSlide = /*#__PURE__*/React__default.memo(props => {
|
|
278
|
-
const {
|
|
279
|
-
isActive,
|
|
280
|
-
scale,
|
|
281
|
-
onScaleChange,
|
|
282
|
-
image: {
|
|
283
|
-
image,
|
|
284
|
-
imgRef: propImgRef,
|
|
285
|
-
imgProps,
|
|
286
|
-
alt,
|
|
287
|
-
loadingPlaceholderImageRef
|
|
288
|
-
}
|
|
289
|
-
} = props;
|
|
290
|
-
|
|
291
|
-
// Get scroll area size
|
|
292
|
-
const scrollAreaRef = React__default.useRef(null);
|
|
293
|
-
const [scrollAreaSize, updateSize] = useSizeOnWindowResize(scrollAreaRef);
|
|
294
|
-
React__default.useEffect(() => {
|
|
295
|
-
// Update size when active
|
|
296
|
-
if (isActive) updateSize();
|
|
297
|
-
}, [isActive, updateSize]);
|
|
298
|
-
|
|
299
|
-
// Get image size
|
|
300
|
-
const imgRef = React__default.useRef(null);
|
|
301
|
-
const imageSize = useImageSize(imgRef, () => {
|
|
302
|
-
const width = Number.parseInt(imgProps?.width, 10);
|
|
303
|
-
const height = Number.parseInt(imgProps?.height, 10);
|
|
304
|
-
return width && height ? {
|
|
305
|
-
width,
|
|
306
|
-
height
|
|
307
|
-
} : null;
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Calculate new image size with scale
|
|
311
|
-
const scaledImageSize = React__default.useMemo(() => {
|
|
312
|
-
if (!scrollAreaSize || !imageSize) {
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
const horizontalScale = scrollAreaSize.width / imageSize.width;
|
|
316
|
-
const verticalScale = scrollAreaSize.height / imageSize.height;
|
|
317
|
-
const baseScale = Math.min(1, Math.min(horizontalScale, verticalScale));
|
|
318
|
-
return {
|
|
319
|
-
width: imageSize.width * baseScale * (scale ?? 1),
|
|
320
|
-
height: imageSize.height * baseScale * (scale ?? 1)
|
|
321
|
-
};
|
|
322
|
-
}, [scrollAreaSize, imageSize, scale]);
|
|
323
|
-
|
|
324
|
-
// Animate scroll to preserve the center of the current visible window in the scroll area
|
|
325
|
-
const animateScroll = useAnimateScroll(scrollAreaRef);
|
|
326
|
-
|
|
327
|
-
// Zoom via mouse wheel or multi-touch pinch zoom
|
|
328
|
-
const isPointerZooming = usePointerZoom(scrollAreaRef, onScaleChange, animateScroll);
|
|
329
|
-
|
|
330
|
-
// Animate scroll on scale change
|
|
331
|
-
React__default.useLayoutEffect(() => {
|
|
332
|
-
if (scale && !isPointerZooming) {
|
|
333
|
-
animateScroll();
|
|
334
|
-
}
|
|
335
|
-
}, [isPointerZooming, scale, animateScroll]);
|
|
336
|
-
const isScrollable = scaledImageSize && scrollAreaSize && (scaledImageSize.width > scrollAreaSize.width || scaledImageSize.height > scrollAreaSize.height);
|
|
337
|
-
return /*#__PURE__*/jsx(SlideshowItem, {
|
|
338
|
-
ref: scrollAreaRef
|
|
339
|
-
// Make it accessible to keyboard nav when the zone is scrollable
|
|
340
|
-
,
|
|
341
|
-
tabIndex: isScrollable ? 0 : undefined,
|
|
342
|
-
className: `${CLASSNAME}__image-slide`,
|
|
343
|
-
children: /*#__PURE__*/jsx(Thumbnail, {
|
|
344
|
-
imgRef: useMergeRefs(imgRef, propImgRef),
|
|
345
|
-
image: image,
|
|
346
|
-
alt: alt,
|
|
347
|
-
className: `${CLASSNAME}__thumbnail`,
|
|
348
|
-
imgProps: {
|
|
349
|
-
...imgProps,
|
|
350
|
-
style: {
|
|
351
|
-
...imgProps?.style,
|
|
352
|
-
...(scaledImageSize || {
|
|
353
|
-
maxHeight: scrollAreaSize?.height,
|
|
354
|
-
maxWidth: scrollAreaSize?.width
|
|
355
|
-
}),
|
|
356
|
-
// Only animate when scale is set, and we are not pointer zooming and the user does not prefer reduced motion
|
|
357
|
-
transition: scale && !isPointerZooming && !isReducedMotion() ? 'all 250ms' : undefined
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
loadingPlaceholderImageRef: loadingPlaceholderImageRef
|
|
361
|
-
})
|
|
362
|
-
});
|
|
363
|
-
}, isEqual);
|
|
364
|
-
|
|
365
|
-
/** Internal image slideshow component for ImageLightbox */
|
|
366
|
-
const ImageSlideshow = ({
|
|
367
|
-
activeImageIndex,
|
|
368
|
-
images,
|
|
369
|
-
slideGroupLabel,
|
|
370
|
-
zoomInButtonProps,
|
|
371
|
-
zoomOutButtonProps,
|
|
372
|
-
slideshowControlsProps,
|
|
373
|
-
currentPaginationItemRef,
|
|
374
|
-
footerRef,
|
|
375
|
-
activeImageRef
|
|
376
|
-
}) => {
|
|
377
|
-
const {
|
|
378
|
-
activeIndex,
|
|
379
|
-
slideshowId,
|
|
380
|
-
setSlideshow,
|
|
381
|
-
slideshowSlidesId,
|
|
382
|
-
slidesCount,
|
|
383
|
-
onNextClick,
|
|
384
|
-
onPaginationClick,
|
|
385
|
-
onPreviousClick,
|
|
386
|
-
toggleAutoPlay
|
|
387
|
-
} = SlideshowControls.useSlideshowControls({
|
|
388
|
-
itemsCount: images.length,
|
|
389
|
-
activeIndex: activeImageIndex
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
// Image metadata (caption)
|
|
393
|
-
const title = images[activeIndex]?.title;
|
|
394
|
-
const description = images[activeIndex]?.description;
|
|
395
|
-
const tags = images[activeIndex]?.tags;
|
|
396
|
-
const metadata = title || description || tags ? /*#__PURE__*/jsx(ImageCaption, {
|
|
397
|
-
as: "div",
|
|
398
|
-
title: title,
|
|
399
|
-
description: description,
|
|
400
|
-
tags: tags,
|
|
401
|
-
align: "center"
|
|
402
|
-
}) : null;
|
|
403
|
-
|
|
404
|
-
// Slideshow controls
|
|
405
|
-
const slideShowControls = slidesCount > 1 && slideshowControlsProps ? /*#__PURE__*/jsx(SlideshowControls, {
|
|
406
|
-
activeIndex: activeIndex,
|
|
407
|
-
slidesCount: slidesCount,
|
|
408
|
-
onNextClick: onNextClick,
|
|
409
|
-
onPreviousClick: onPreviousClick,
|
|
410
|
-
onPaginationClick: onPaginationClick,
|
|
411
|
-
...slideshowControlsProps,
|
|
412
|
-
paginationItemProps: index => {
|
|
413
|
-
const props = slideshowControlsProps?.paginationItemProps?.(index) || {};
|
|
414
|
-
return {
|
|
415
|
-
...props,
|
|
416
|
-
ref: mergeRefs(props?.ref,
|
|
417
|
-
// Focus the active pagination item once on mount
|
|
418
|
-
activeIndex === index ? currentPaginationItemRef : undefined)
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
}) : null;
|
|
422
|
-
|
|
423
|
-
// Zoom controls
|
|
424
|
-
const [scale, setScale] = React__default.useState(undefined);
|
|
425
|
-
const zoomEnabled = zoomInButtonProps && zoomOutButtonProps;
|
|
426
|
-
const onScaleChange = React__default.useMemo(() => {
|
|
427
|
-
if (!zoomEnabled) return undefined;
|
|
428
|
-
return newScale => {
|
|
429
|
-
setScale((prevScale = 1) => Math.max(1, newScale * prevScale));
|
|
430
|
-
};
|
|
431
|
-
}, [zoomEnabled]);
|
|
432
|
-
const zoomIn = React__default.useCallback(() => onScaleChange?.(1.5), [onScaleChange]);
|
|
433
|
-
const zoomOut = React__default.useCallback(() => onScaleChange?.(0.5), [onScaleChange]);
|
|
434
|
-
React__default.useEffect(() => {
|
|
435
|
-
// Reset scale on slide change
|
|
436
|
-
if (typeof activeIndex === 'number') setScale(undefined);
|
|
437
|
-
}, [activeIndex]);
|
|
438
|
-
const zoomControls = zoomEnabled && /*#__PURE__*/jsxs(Fragment, {
|
|
439
|
-
children: [/*#__PURE__*/jsx(IconButton, {
|
|
440
|
-
...zoomInButtonProps,
|
|
441
|
-
emphasis: "low",
|
|
442
|
-
icon: mdiMagnifyPlusOutline,
|
|
443
|
-
onClick: zoomIn
|
|
444
|
-
}), /*#__PURE__*/jsx(IconButton, {
|
|
445
|
-
...zoomOutButtonProps,
|
|
446
|
-
emphasis: "low",
|
|
447
|
-
isDisabled: !scale || scale <= 1,
|
|
448
|
-
icon: mdiMagnifyMinusOutline,
|
|
449
|
-
onClick: zoomOut
|
|
450
|
-
})]
|
|
451
|
-
});
|
|
452
|
-
const getImgRef = React__default.useMemo(() => memoize((index, isActive) => {
|
|
453
|
-
return mergeRefs(images?.[index].imgRef, isActive ? activeImageRef : undefined);
|
|
454
|
-
},
|
|
455
|
-
// memoize based on both arguments
|
|
456
|
-
(...args) => args.join()), [images, activeImageRef]);
|
|
457
|
-
return /*#__PURE__*/jsxs(Fragment, {
|
|
458
|
-
children: [/*#__PURE__*/jsx(Slides, {
|
|
459
|
-
activeIndex: activeIndex,
|
|
460
|
-
slideGroupLabel: slideGroupLabel,
|
|
461
|
-
fillHeight: true,
|
|
462
|
-
id: slideshowId,
|
|
463
|
-
ref: setSlideshow,
|
|
464
|
-
slidesId: slideshowSlidesId,
|
|
465
|
-
toggleAutoPlay: toggleAutoPlay,
|
|
466
|
-
children: images.map(({
|
|
467
|
-
image,
|
|
468
|
-
imgRef,
|
|
469
|
-
...imageProps
|
|
470
|
-
}, index) => {
|
|
471
|
-
const isActive = index === activeIndex;
|
|
472
|
-
return /*#__PURE__*/jsx(ImageSlide, {
|
|
473
|
-
isActive: isActive,
|
|
474
|
-
image: {
|
|
475
|
-
...imageProps,
|
|
476
|
-
image,
|
|
477
|
-
imgRef: getImgRef(index, isActive)
|
|
478
|
-
},
|
|
479
|
-
scale: isActive ? scale : undefined,
|
|
480
|
-
onScaleChange: onScaleChange
|
|
481
|
-
}, image);
|
|
482
|
-
})
|
|
483
|
-
}), (metadata || slideShowControls || zoomControls) && /*#__PURE__*/jsxs(FlexBox, {
|
|
484
|
-
ref: footerRef,
|
|
485
|
-
className: `${CLASSNAME}__footer`,
|
|
486
|
-
orientation: "vertical",
|
|
487
|
-
vAlign: "center",
|
|
488
|
-
gap: "big",
|
|
489
|
-
children: [metadata, /*#__PURE__*/jsxs(FlexBox, {
|
|
490
|
-
className: `${CLASSNAME}__footer-actions`,
|
|
491
|
-
orientation: "horizontal",
|
|
492
|
-
gap: "regular",
|
|
493
|
-
children: [slideShowControls, zoomControls]
|
|
494
|
-
})]
|
|
495
|
-
})]
|
|
496
|
-
});
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
/** Unref a react ref or element */
|
|
500
|
-
function unref(maybeElement) {
|
|
501
|
-
if (maybeElement instanceof HTMLElement) return maybeElement;
|
|
502
|
-
return maybeElement?.current;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function setupViewTransitionName(elementRef, name) {
|
|
506
|
-
let originalName = null;
|
|
507
|
-
return {
|
|
508
|
-
set() {
|
|
509
|
-
const element = unref(elementRef);
|
|
510
|
-
if (!element) return;
|
|
511
|
-
originalName = element.style.viewTransitionName;
|
|
512
|
-
element.style.viewTransitionName = name;
|
|
513
|
-
},
|
|
514
|
-
unset() {
|
|
515
|
-
const element = unref(elementRef);
|
|
516
|
-
if (!element || originalName === null) return;
|
|
517
|
-
element.style.viewTransitionName = originalName;
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Wrapper around the `document.startViewTransition` handling browser incompatibilities, react DOM flush and
|
|
524
|
-
* user preference.
|
|
525
|
-
*
|
|
526
|
-
* @param changes callback containing the changes to apply within the view transition.
|
|
527
|
-
* @param setViewTransitionName set the `viewTransitionName` style on a `source` & `target` to morph these elements.
|
|
528
|
-
*/
|
|
529
|
-
async function startViewTransition({
|
|
530
|
-
changes,
|
|
531
|
-
viewTransitionName
|
|
532
|
-
}) {
|
|
533
|
-
const start = document?.startViewTransition?.bind(document);
|
|
534
|
-
const prefersReducedMotion = isReducedMotion();
|
|
535
|
-
const {
|
|
536
|
-
flushSync
|
|
537
|
-
} = ReactDOM;
|
|
538
|
-
if (prefersReducedMotion || !start || !flushSync || !viewTransitionName?.source || !viewTransitionName?.target) {
|
|
539
|
-
// Skip, apply changes without a transition
|
|
540
|
-
changes();
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Setup set/unset transition name on source & target
|
|
545
|
-
const sourceTransitionName = setupViewTransitionName(viewTransitionName.source, viewTransitionName.name);
|
|
546
|
-
const targetTransitionName = setupViewTransitionName(viewTransitionName.target, viewTransitionName.name);
|
|
547
|
-
sourceTransitionName.set();
|
|
548
|
-
|
|
549
|
-
// Start view transition, apply changes & flush to DOM
|
|
550
|
-
await start(() => {
|
|
551
|
-
sourceTransitionName.unset();
|
|
552
|
-
flushSync(changes);
|
|
553
|
-
targetTransitionName.set();
|
|
554
|
-
}).updateCallbackDone;
|
|
555
|
-
targetTransitionName.unset();
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/** Find image in element including the element */
|
|
559
|
-
const findImage = element => element?.matches('img') ? element : element?.querySelector('img') || null;
|
|
560
|
-
|
|
561
|
-
/** Subset of the ImageLightboxProps managed by the useImageLightbox hook */
|
|
562
|
-
|
|
563
|
-
const EMPTY_PROPS = {
|
|
564
|
-
isOpen: false,
|
|
565
|
-
images: [],
|
|
566
|
-
parentElement: /*#__PURE__*/React__default.createRef()
|
|
567
|
-
};
|
|
568
|
-
/**
|
|
569
|
-
* Set up an ImageLightbox with images and triggers.
|
|
570
|
-
*
|
|
571
|
-
* - Associate a trigger with the lightbox to properly focus the trigger on close
|
|
572
|
-
* - Associate a trigger with an image to display on open
|
|
573
|
-
* - Automatically provide a view transition between an image trigger and the displayed image on open & close
|
|
574
|
-
*
|
|
575
|
-
* @param props Images to display in the image lightbox
|
|
576
|
-
*/
|
|
577
|
-
function useImageLightbox(props) {
|
|
578
|
-
const propsRef = React__default.useRef(props);
|
|
579
|
-
React__default.useEffect(() => {
|
|
580
|
-
propsRef.current = props;
|
|
581
|
-
}, [props]);
|
|
582
|
-
|
|
583
|
-
// Keep reference for each image elements
|
|
584
|
-
const imageRefsRef = React__default.useRef([]);
|
|
585
|
-
const currentImageRef = React__default.useRef(null);
|
|
586
|
-
const [imageLightboxProps, setImageLightboxProps] = React__default.useState(() => ({
|
|
587
|
-
...EMPTY_PROPS,
|
|
588
|
-
...props
|
|
589
|
-
}));
|
|
590
|
-
const getTriggerProps = React__default.useMemo(() => {
|
|
591
|
-
const triggerImageRefs = {};
|
|
592
|
-
async function close() {
|
|
593
|
-
const currentImage = currentImageRef.current;
|
|
594
|
-
if (!currentImage) {
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
const currentIndex = imageRefsRef.current.findIndex(imageRef => imageRef.current === currentImage);
|
|
598
|
-
await startViewTransition({
|
|
599
|
-
changes() {
|
|
600
|
-
// Close lightbox
|
|
601
|
-
setImageLightboxProps(prevProps => ({
|
|
602
|
-
...prevProps,
|
|
603
|
-
isOpen: false
|
|
604
|
-
}));
|
|
605
|
-
},
|
|
606
|
-
// Morph from the image in lightbox to the image in trigger
|
|
607
|
-
viewTransitionName: {
|
|
608
|
-
source: currentImageRef,
|
|
609
|
-
target: triggerImageRefs[currentIndex],
|
|
610
|
-
name: CLASSNAME
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
async function open(triggerElement, {
|
|
615
|
-
activeImageIndex
|
|
616
|
-
} = {}) {
|
|
617
|
-
// If we find an image inside the trigger, animate it in transition with the opening image
|
|
618
|
-
const triggerImage = triggerImageRefs[activeImageIndex]?.current || findImage(triggerElement);
|
|
619
|
-
|
|
620
|
-
// Inject refs to improve transition and loading style
|
|
621
|
-
const images = propsRef.current?.images?.map((image, idx) => {
|
|
622
|
-
// Get or create image reference
|
|
623
|
-
let imgRef = imageRefsRef.current[idx];
|
|
624
|
-
if (!imgRef) {
|
|
625
|
-
imgRef = /*#__PURE__*/React__default.createRef();
|
|
626
|
-
imageRefsRef.current[idx] = imgRef;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
// Try to use the trigger image as the loading placeholder
|
|
630
|
-
const loadingPlaceholderImageRef = triggerImage && idx === activeImageIndex ? {
|
|
631
|
-
current: triggerImage
|
|
632
|
-
} : undefined;
|
|
633
|
-
return {
|
|
634
|
-
loadingPlaceholderImageRef,
|
|
635
|
-
...image,
|
|
636
|
-
imgRef
|
|
637
|
-
};
|
|
638
|
-
});
|
|
639
|
-
await startViewTransition({
|
|
640
|
-
changes: () => {
|
|
641
|
-
// Open lightbox with setup props
|
|
642
|
-
setImageLightboxProps(prevProps => ({
|
|
643
|
-
...prevProps,
|
|
644
|
-
...propsRef.current,
|
|
645
|
-
activeImageRef: currentImageRef,
|
|
646
|
-
parentElement: {
|
|
647
|
-
current: triggerElement
|
|
648
|
-
},
|
|
649
|
-
isOpen: true,
|
|
650
|
-
onClose: () => {
|
|
651
|
-
close();
|
|
652
|
-
propsRef.current?.onClose?.();
|
|
653
|
-
},
|
|
654
|
-
images,
|
|
655
|
-
activeImageIndex: activeImageIndex || 0
|
|
656
|
-
}));
|
|
657
|
-
},
|
|
658
|
-
// Morph from the image in trigger to the image in lightbox
|
|
659
|
-
viewTransitionName: {
|
|
660
|
-
source: triggerImage,
|
|
661
|
-
target: currentImageRef,
|
|
662
|
-
name: CLASSNAME
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
return memoize(options => ({
|
|
667
|
-
ref(element) {
|
|
668
|
-
// Track trigger image ref if any
|
|
669
|
-
if (options?.activeImageIndex !== undefined && element) {
|
|
670
|
-
triggerImageRefs[options.activeImageIndex] = {
|
|
671
|
-
current: findImage(element)
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
},
|
|
675
|
-
onClick(e) {
|
|
676
|
-
open(e.target, options);
|
|
677
|
-
}
|
|
678
|
-
}));
|
|
679
|
-
}, []);
|
|
680
|
-
return {
|
|
681
|
-
getTriggerProps,
|
|
682
|
-
imageLightboxProps
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const Inner = forwardRef((props, ref) => {
|
|
687
|
-
const {
|
|
688
|
-
className,
|
|
689
|
-
isOpen,
|
|
690
|
-
closeButtonProps,
|
|
691
|
-
onClose,
|
|
692
|
-
parentElement,
|
|
693
|
-
activeImageIndex,
|
|
694
|
-
slideshowControlsProps,
|
|
695
|
-
slideGroupLabel,
|
|
696
|
-
images,
|
|
697
|
-
zoomOutButtonProps,
|
|
698
|
-
zoomInButtonProps,
|
|
699
|
-
activeImageRef: propImageRef,
|
|
700
|
-
...forwardedProps
|
|
701
|
-
} = props;
|
|
702
|
-
const currentPaginationItemRef = React__default.useRef(null);
|
|
703
|
-
const footerRef = React__default.useRef(null);
|
|
704
|
-
const imageRef = React__default.useRef(null);
|
|
705
|
-
const clickAwayChildrenRefs = React__default.useRef([imageRef, footerRef]);
|
|
706
|
-
const onClickAway = React__default.useCallback(evt => {
|
|
707
|
-
const targetElement = evt.target;
|
|
708
|
-
if (!(targetElement instanceof HTMLElement) || !(evt instanceof MouseEvent)) return;
|
|
709
|
-
|
|
710
|
-
// Skip click away if clicking on the scrollbar
|
|
711
|
-
if (targetElement.clientWidth < evt.clientX || targetElement.clientHeight < evt.clientY) return;
|
|
712
|
-
onClose?.();
|
|
713
|
-
}, [onClose]);
|
|
714
|
-
return /*#__PURE__*/jsx(Lightbox, {
|
|
715
|
-
ref: ref,
|
|
716
|
-
className: classNames(className, CLASSNAME),
|
|
717
|
-
parentElement: parentElement,
|
|
718
|
-
isOpen: isOpen,
|
|
719
|
-
onClose: onClose,
|
|
720
|
-
closeButtonProps: closeButtonProps,
|
|
721
|
-
focusElement: currentPaginationItemRef,
|
|
722
|
-
...forwardedProps,
|
|
723
|
-
// Disable the close on click away as we want a custom one here
|
|
724
|
-
preventAutoClose: true,
|
|
725
|
-
children: /*#__PURE__*/jsx(ClickAwayProvider, {
|
|
726
|
-
childrenRefs: clickAwayChildrenRefs,
|
|
727
|
-
callback: onClickAway,
|
|
728
|
-
children: /*#__PURE__*/jsx(ThemeProvider, {
|
|
729
|
-
value: "dark",
|
|
730
|
-
children: /*#__PURE__*/jsx(ImageSlideshow, {
|
|
731
|
-
activeImageIndex: activeImageIndex,
|
|
732
|
-
slideGroupLabel: slideGroupLabel,
|
|
733
|
-
slideshowControlsProps: slideshowControlsProps,
|
|
734
|
-
images: images,
|
|
735
|
-
zoomInButtonProps: zoomInButtonProps,
|
|
736
|
-
zoomOutButtonProps: zoomOutButtonProps,
|
|
737
|
-
footerRef: footerRef,
|
|
738
|
-
activeImageRef: useMergeRefs(propImageRef, imageRef),
|
|
739
|
-
currentPaginationItemRef: currentPaginationItemRef
|
|
740
|
-
})
|
|
741
|
-
})
|
|
742
|
-
})
|
|
743
|
-
});
|
|
744
|
-
});
|
|
745
|
-
Inner.displayName = COMPONENT_NAME;
|
|
746
|
-
Inner.className = CLASSNAME;
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* ImageLightbox component.
|
|
750
|
-
*
|
|
751
|
-
* @param props Component props.
|
|
752
|
-
* @param ref Component ref.
|
|
753
|
-
* @return React element.
|
|
754
|
-
*/
|
|
755
|
-
const ImageLightbox = Object.assign(Inner, {
|
|
756
|
-
useImageLightbox
|
|
757
|
-
});
|
|
758
|
-
|
|
759
|
-
export { ImageLightbox };
|