@lumx/react 3.9.2 → 3.9.4-alpha.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/index.d.ts +11 -9
- package/index.js +464 -436
- package/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/date-picker/DatePickerControlled.test.tsx +3 -2
- package/src/components/date-picker/DatePickerControlled.tsx +3 -2
- package/src/components/image-lightbox/ImageLightbox.tsx +2 -0
- package/src/components/image-lightbox/internal/ImageSlideshow.tsx +2 -0
- package/src/components/image-lightbox/useImageLightbox.tsx +1 -1
- package/src/components/slideshow/Slides.tsx +7 -5
- package/src/components/slideshow/Slideshow.stories.tsx +12 -1
- package/src/components/slideshow/Slideshow.tsx +2 -16
- package/src/components/slideshow/SlideshowControls.stories.tsx +2 -3
- package/src/components/slideshow/SlideshowControls.tsx +63 -73
- package/src/components/slideshow/SlideshowItem.tsx +1 -10
- package/src/components/slideshow/SlideshowItemGroup.tsx +16 -33
- package/src/components/slideshow/useSlideFocusManagement.tsx +51 -36
- package/src/{hooks → components/slideshow}/useSlideshowControls.ts +57 -60
- package/src/components/tooltip/Tooltip.test.tsx +4 -3
- package/src/components/tooltip/Tooltip.tsx +3 -2
- package/src/constants.ts +5 -0
- package/src/utils/DOM/startViewTransition.ts +23 -11
|
@@ -3,7 +3,7 @@ import { getFocusableElements } from '@lumx/react/utils/focus/getFocusableElemen
|
|
|
3
3
|
|
|
4
4
|
export interface UseSlideFocusManagementProps {
|
|
5
5
|
isSlideDisplayed?: boolean;
|
|
6
|
-
|
|
6
|
+
slidesRef?: React.RefObject<HTMLDivElement>;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -17,42 +17,45 @@ const BLOCKED_FOCUS_CLASSNAME = 'focus-blocked';
|
|
|
17
17
|
* Manage how slides must behave when visible or not.
|
|
18
18
|
* When not visible, they should be hidden from screen readers and not focusable.
|
|
19
19
|
*/
|
|
20
|
-
export const useSlideFocusManagement = ({ isSlideDisplayed,
|
|
20
|
+
export const useSlideFocusManagement = ({ isSlideDisplayed, slidesRef }: UseSlideFocusManagementProps) => {
|
|
21
|
+
const [slide, setSlide] = React.useState<HTMLDivElement | null>(null);
|
|
21
22
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!element) {
|
|
23
|
+
if (!slide) {
|
|
25
24
|
return undefined;
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
const isUserActivated = slidesRef?.current?.dataset.lumxUserActivated === 'true';
|
|
28
|
+
let focusableElements: Array<HTMLElement> = [];
|
|
29
|
+
|
|
28
30
|
/**
|
|
29
31
|
* Display given slide to screen readers and, if focus was blocked, restore focus on elements.
|
|
30
32
|
*/
|
|
31
33
|
const enableSlide = () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
slide.removeAttribute('inert');
|
|
35
|
+
slide.setAttribute('aria-hidden', 'false');
|
|
34
36
|
// Find elements we have blocked focus on
|
|
35
|
-
|
|
37
|
+
// (won't be necessary once "inert" gets sufficient browser support)
|
|
38
|
+
focusableElements = Array.from(slide.querySelectorAll(`.${BLOCKED_FOCUS_CLASSNAME}`));
|
|
39
|
+
for (const focusableElement of focusableElements) {
|
|
36
40
|
focusableElement.removeAttribute('tabindex');
|
|
37
41
|
focusableElement.classList.remove(BLOCKED_FOCUS_CLASSNAME);
|
|
38
|
-
}
|
|
42
|
+
}
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
/**
|
|
42
46
|
* Hide given slide from screen readers and block focus on all focusable elements within.
|
|
43
47
|
*/
|
|
44
48
|
const blockSlide = () => {
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
slide.setAttribute('inert', '');
|
|
50
|
+
slide.setAttribute('aria-hidden', 'true');
|
|
51
|
+
focusableElements = getFocusableElements(slide);
|
|
52
|
+
for (const focusableElement of focusableElements) {
|
|
47
53
|
focusableElement.setAttribute('tabindex', '-1');
|
|
48
54
|
focusableElement.classList.add(BLOCKED_FOCUS_CLASSNAME);
|
|
49
|
-
}
|
|
55
|
+
}
|
|
50
56
|
};
|
|
51
57
|
|
|
52
58
|
const handleDisplay = () => {
|
|
53
|
-
if (!element) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
59
|
if (isSlideDisplayed) {
|
|
57
60
|
enableSlide();
|
|
58
61
|
} else {
|
|
@@ -60,33 +63,45 @@ export const useSlideFocusManagement = ({ isSlideDisplayed, slideRef }: UseSlide
|
|
|
60
63
|
}
|
|
61
64
|
};
|
|
62
65
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
66
|
+
// Create an observer instance linked to the callback function
|
|
67
|
+
// (won't be necessary once "inert" gets sufficient browser support)
|
|
68
|
+
const observer = new MutationObserver((mutationsList) => {
|
|
69
|
+
for (const mutation of mutationsList) {
|
|
70
|
+
if (mutation.type === 'childList') {
|
|
71
|
+
handleDisplay();
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
|
-
};
|
|
74
|
+
});
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
const observer = new MutationObserver(callback);
|
|
76
|
+
handleDisplay();
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
// Change focus on slide displayed
|
|
79
|
+
if (isSlideDisplayed && isUserActivated) {
|
|
80
|
+
let elementToFocus: HTMLElement | undefined = slide;
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
82
|
+
// We have exactly one focusable element => focus it
|
|
83
|
+
if (focusableElements.length === 1) {
|
|
84
|
+
// eslint-disable-next-line prefer-destructuring
|
|
85
|
+
elementToFocus = focusableElements[0];
|
|
83
86
|
}
|
|
84
|
-
}
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
88
|
+
// We have not focusable element => focus the pagination item
|
|
89
|
+
if (focusableElements.length === 0) {
|
|
90
|
+
elementToFocus = document.querySelector(`[aria-controls="${slide?.id}"]`) as HTMLElement;
|
|
89
91
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
|
|
93
|
+
elementToFocus?.focus({ preventScroll: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** If slide is hidden, start observing for elements to block focus */
|
|
97
|
+
if (!isSlideDisplayed) {
|
|
98
|
+
observer.observe(slide, { attributes: true, childList: true, subtree: true });
|
|
99
|
+
|
|
100
|
+
return () => observer.disconnect();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return undefined;
|
|
104
|
+
}, [isSlideDisplayed, slide, slidesRef]);
|
|
105
|
+
|
|
106
|
+
return setSlide;
|
|
92
107
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { SetStateAction, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { clamp } from '@lumx/react';
|
|
3
3
|
import { useInterval } from '@lumx/react/hooks/useInterval';
|
|
4
|
-
import { AUTOPLAY_DEFAULT_INTERVAL } from '@lumx/react/components/slideshow/constants';
|
|
5
4
|
import { useId } from '@lumx/react/hooks/useId';
|
|
6
5
|
|
|
6
|
+
import { AUTOPLAY_DEFAULT_INTERVAL } from './constants';
|
|
7
|
+
|
|
7
8
|
export interface UseSlideshowControlsOptions {
|
|
8
9
|
/** default active index to be displayed */
|
|
9
10
|
defaultActiveIndex?: number;
|
|
@@ -43,9 +44,9 @@ export interface UseSlideshowControls {
|
|
|
43
44
|
/** id to be used for the wrapper that contains the slides */
|
|
44
45
|
slideshowSlidesId: string;
|
|
45
46
|
/** callback that triggers the previous slide while using the slideshow controls */
|
|
46
|
-
onPreviousClick: (
|
|
47
|
+
onPreviousClick: (loopBack: boolean) => void;
|
|
47
48
|
/** callback that triggers the next slide while using the slideshow controls */
|
|
48
|
-
onNextClick: (
|
|
49
|
+
onNextClick: (loopBack: boolean) => void;
|
|
49
50
|
/** callback that triggers a specific page while using the slideshow controls */
|
|
50
51
|
onPaginationClick: (index: number) => void;
|
|
51
52
|
/** whether the slideshow is autoplaying or not */
|
|
@@ -54,16 +55,18 @@ export interface UseSlideshowControls {
|
|
|
54
55
|
isForcePaused: boolean;
|
|
55
56
|
/** callback to change whether the slideshow is autoplaying or not */
|
|
56
57
|
toggleAutoPlay: () => void;
|
|
57
|
-
/**
|
|
58
|
+
/** callback to change whether the slideshow should be force paused or not */
|
|
58
59
|
toggleForcePause: () => void;
|
|
59
60
|
/** current active slide index */
|
|
60
61
|
activeIndex: number;
|
|
61
62
|
/** set the current index as the active one */
|
|
62
63
|
setActiveIndex: (index: number) => void;
|
|
63
|
-
/** callback that stops the
|
|
64
|
+
/** callback that stops the autoplay */
|
|
64
65
|
stopAutoPlay: () => void;
|
|
65
|
-
/** callback that starts the
|
|
66
|
+
/** callback that starts the autoplay */
|
|
66
67
|
startAutoPlay: () => void;
|
|
68
|
+
/** True if the last slide change is user activated */
|
|
69
|
+
isUserActivated?: boolean;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
export const DEFAULT_OPTIONS: Partial<UseSlideshowControlsOptions> = {
|
|
@@ -90,40 +93,34 @@ export const useSlideshowControls = ({
|
|
|
90
93
|
// Number of slides when using groupBy prop.
|
|
91
94
|
const slidesCount = Math.ceil(itemsCount / Math.min(groupBy as number, itemsCount));
|
|
92
95
|
|
|
93
|
-
//
|
|
94
|
-
const
|
|
95
|
-
(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
return index;
|
|
106
|
-
});
|
|
96
|
+
// Set current active index (& if is user activated)
|
|
97
|
+
const setActiveIndex = useCallback(
|
|
98
|
+
(setStateAction: SetStateAction<number>, isUser?: boolean) => {
|
|
99
|
+
// Store on element a boolean value when the slide change was not from a user action.
|
|
100
|
+
const elementDataset = element?.dataset as any;
|
|
101
|
+
if (elementDataset) {
|
|
102
|
+
if (isUser) elementDataset.lumxUserActivated = true;
|
|
103
|
+
else delete elementDataset.lumxUserActivated;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setCurrentIndex(setStateAction);
|
|
107
107
|
},
|
|
108
|
-
[
|
|
108
|
+
[element],
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
-
// Change
|
|
112
|
-
const
|
|
113
|
-
(
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
return slidesCount
|
|
111
|
+
// Change slide given delta (-1/+1) with or without loop back.
|
|
112
|
+
const goTo = useCallback(
|
|
113
|
+
(delta: -1 | 1 = 1, loopBack = true, isUser?: boolean) => {
|
|
114
|
+
setActiveIndex((index) => {
|
|
115
|
+
if (loopBack) {
|
|
116
|
+
const newIndex = (index + delta) % slidesCount;
|
|
117
|
+
if (newIndex < 0) return slidesCount + newIndex;
|
|
118
|
+
return newIndex;
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return index - 1;
|
|
122
|
-
}
|
|
123
|
-
return index;
|
|
124
|
-
});
|
|
120
|
+
return clamp(index + delta, 0, slidesCount - 1);
|
|
121
|
+
}, isUser);
|
|
125
122
|
},
|
|
126
|
-
[slidesCount,
|
|
123
|
+
[slidesCount, setActiveIndex],
|
|
127
124
|
);
|
|
128
125
|
|
|
129
126
|
// Auto play
|
|
@@ -132,22 +129,22 @@ export const useSlideshowControls = ({
|
|
|
132
129
|
|
|
133
130
|
const isSlideshowAutoPlaying = isForcePaused ? false : isAutoPlaying;
|
|
134
131
|
// Start
|
|
135
|
-
useInterval(
|
|
132
|
+
useInterval(goTo, isSlideshowAutoPlaying && slidesCount > 1 ? (interval as number) : null);
|
|
136
133
|
|
|
137
|
-
// Reset current index if it
|
|
134
|
+
// Reset current index if it becomes invalid.
|
|
138
135
|
useEffect(() => {
|
|
139
136
|
if (currentIndex > slidesCount - 1) {
|
|
140
|
-
|
|
137
|
+
setActiveIndex(defaultActiveIndex as number);
|
|
141
138
|
}
|
|
142
|
-
}, [currentIndex, slidesCount, defaultActiveIndex]);
|
|
139
|
+
}, [currentIndex, slidesCount, defaultActiveIndex, setActiveIndex]);
|
|
143
140
|
|
|
144
|
-
const startAutoPlay = () => {
|
|
141
|
+
const startAutoPlay = useCallback(() => {
|
|
145
142
|
setIsAutoPlaying(Boolean(autoPlay));
|
|
146
|
-
};
|
|
143
|
+
}, [autoPlay]);
|
|
147
144
|
|
|
148
|
-
const stopAutoPlay = () => {
|
|
145
|
+
const stopAutoPlay = useCallback(() => {
|
|
149
146
|
setIsAutoPlaying(false);
|
|
150
|
-
};
|
|
147
|
+
}, []);
|
|
151
148
|
|
|
152
149
|
// Handle click on a bullet to go to a specific slide.
|
|
153
150
|
const onPaginationClick = useCallback(
|
|
@@ -156,36 +153,36 @@ export const useSlideshowControls = ({
|
|
|
156
153
|
setIsForcePaused(true);
|
|
157
154
|
|
|
158
155
|
if (index >= 0 && index < slidesCount) {
|
|
159
|
-
|
|
156
|
+
setActiveIndex(index, true);
|
|
160
157
|
}
|
|
161
158
|
},
|
|
162
|
-
[slidesCount,
|
|
159
|
+
[stopAutoPlay, slidesCount, setActiveIndex],
|
|
163
160
|
);
|
|
164
161
|
|
|
165
162
|
// Handle click or keyboard event to go to next slide.
|
|
166
163
|
const onNextClick = useCallback(
|
|
167
|
-
(
|
|
164
|
+
(loopBack = true) => {
|
|
168
165
|
stopAutoPlay();
|
|
169
166
|
setIsForcePaused(true);
|
|
170
|
-
|
|
167
|
+
goTo(1, loopBack, true);
|
|
171
168
|
},
|
|
172
|
-
[
|
|
169
|
+
[goTo, stopAutoPlay],
|
|
173
170
|
);
|
|
174
171
|
|
|
175
172
|
// Handle click or keyboard event to go to previous slide.
|
|
176
173
|
const onPreviousClick = useCallback(
|
|
177
|
-
(
|
|
174
|
+
(loopBack = true) => {
|
|
178
175
|
stopAutoPlay();
|
|
179
176
|
setIsForcePaused(true);
|
|
180
|
-
|
|
177
|
+
goTo(-1, loopBack, true);
|
|
181
178
|
},
|
|
182
|
-
[
|
|
179
|
+
[goTo, stopAutoPlay],
|
|
183
180
|
);
|
|
184
181
|
|
|
185
182
|
// If the activeIndex props changes, update the current slide
|
|
186
183
|
useEffect(() => {
|
|
187
|
-
|
|
188
|
-
}, [activeIndex]);
|
|
184
|
+
setActiveIndex(activeIndex as number);
|
|
185
|
+
}, [activeIndex, setActiveIndex]);
|
|
189
186
|
|
|
190
187
|
// If the slide changes, with autoplay for example, trigger "onChange"
|
|
191
188
|
useEffect(() => {
|
|
@@ -199,15 +196,15 @@ export const useSlideshowControls = ({
|
|
|
199
196
|
const generatedSlidesId = useId();
|
|
200
197
|
const slideshowSlidesId = slidesId || generatedSlidesId;
|
|
201
198
|
|
|
202
|
-
const toggleAutoPlay = () => {
|
|
199
|
+
const toggleAutoPlay = useCallback(() => {
|
|
203
200
|
if (isSlideshowAutoPlaying) {
|
|
204
201
|
stopAutoPlay();
|
|
205
202
|
} else {
|
|
206
203
|
startAutoPlay();
|
|
207
204
|
}
|
|
208
|
-
};
|
|
205
|
+
}, [isSlideshowAutoPlaying, startAutoPlay, stopAutoPlay]);
|
|
209
206
|
|
|
210
|
-
const toggleForcePause = () => {
|
|
207
|
+
const toggleForcePause = useCallback(() => {
|
|
211
208
|
const shouldBePaused = !isForcePaused;
|
|
212
209
|
|
|
213
210
|
setIsForcePaused(shouldBePaused);
|
|
@@ -217,7 +214,7 @@ export const useSlideshowControls = ({
|
|
|
217
214
|
} else {
|
|
218
215
|
stopAutoPlay();
|
|
219
216
|
}
|
|
220
|
-
};
|
|
217
|
+
}, [isForcePaused, startAutoPlay, stopAutoPlay]);
|
|
221
218
|
|
|
222
219
|
// Start index and end index of visible slides.
|
|
223
220
|
const startIndexVisible = currentIndex * (groupBy as number);
|
|
@@ -237,7 +234,7 @@ export const useSlideshowControls = ({
|
|
|
237
234
|
toggleAutoPlay,
|
|
238
235
|
activeIndex: currentIndex,
|
|
239
236
|
slidesCount,
|
|
240
|
-
setActiveIndex
|
|
237
|
+
setActiveIndex,
|
|
241
238
|
startAutoPlay,
|
|
242
239
|
stopAutoPlay,
|
|
243
240
|
isForcePaused,
|
|
@@ -5,8 +5,9 @@ import { screen, render } from '@testing-library/react';
|
|
|
5
5
|
import { queryAllByTagName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
6
6
|
import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
|
|
7
7
|
import userEvent from '@testing-library/user-event';
|
|
8
|
-
|
|
9
8
|
import { isFocusVisible } from '@lumx/react/utils/isFocusVisible';
|
|
9
|
+
import { VISUALLY_HIDDEN } from '@lumx/react/constants';
|
|
10
|
+
|
|
10
11
|
import { Tooltip, TooltipProps } from './Tooltip';
|
|
11
12
|
|
|
12
13
|
const CLASSNAME = Tooltip.className as string;
|
|
@@ -142,11 +143,11 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
142
143
|
forceOpen: false,
|
|
143
144
|
});
|
|
144
145
|
expect(tooltip).toBeInTheDocument();
|
|
145
|
-
expect(tooltip).toHaveClass(
|
|
146
|
+
expect(tooltip).toHaveClass(VISUALLY_HIDDEN);
|
|
146
147
|
|
|
147
148
|
const anchor = screen.getByRole('button', { name: 'Anchor' });
|
|
148
149
|
await userEvent.hover(anchor);
|
|
149
|
-
expect(tooltip).not.toHaveClass(
|
|
150
|
+
expect(tooltip).not.toHaveClass(VISUALLY_HIDDEN);
|
|
150
151
|
});
|
|
151
152
|
});
|
|
152
153
|
|
|
@@ -4,7 +4,7 @@ import { createPortal } from 'react-dom';
|
|
|
4
4
|
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
|
|
7
|
-
import { DOCUMENT } from '@lumx/react/constants';
|
|
7
|
+
import { DOCUMENT, VISUALLY_HIDDEN } from '@lumx/react/constants';
|
|
8
8
|
import { Comp, GenericProps, HasCloseMode } from '@lumx/react/utils/type';
|
|
9
9
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
10
10
|
import { useMergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
@@ -106,6 +106,7 @@ export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, re
|
|
|
106
106
|
const { isOpen: isActivated, onPopperMount } = useTooltipOpen(delay, anchorElement);
|
|
107
107
|
const isOpen = (isActivated || forceOpen) && !!label;
|
|
108
108
|
const isMounted = !!label && (isOpen || closeMode === 'hide');
|
|
109
|
+
const isHidden = !isOpen && closeMode === 'hide';
|
|
109
110
|
const wrappedChildren = useInjectTooltipRef({
|
|
110
111
|
children,
|
|
111
112
|
setAnchorElement,
|
|
@@ -139,8 +140,8 @@ export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, re
|
|
|
139
140
|
handleBasicClasses({
|
|
140
141
|
prefix: CLASSNAME,
|
|
141
142
|
position,
|
|
142
|
-
hidden: !isOpen && closeMode === 'hide',
|
|
143
143
|
}),
|
|
144
|
+
isHidden && VISUALLY_HIDDEN,
|
|
144
145
|
)}
|
|
145
146
|
style={{ ...styles.popper, zIndex }}
|
|
146
147
|
{...attributes.popper}
|
package/src/constants.ts
CHANGED
|
@@ -20,3 +20,8 @@ export const DOCUMENT = typeof document !== 'undefined' ? document : undefined;
|
|
|
20
20
|
* Check if we are running in a true browser
|
|
21
21
|
*/
|
|
22
22
|
export const IS_BROWSER = typeof navigator !== 'undefined' && !navigator.userAgent.includes('jsdom');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Visually hidden a11y utility class name
|
|
26
|
+
*/
|
|
27
|
+
export const VISUALLY_HIDDEN = 'visually-hidden';
|
|
@@ -5,9 +5,21 @@ import { MaybeElementOrRef } from '@lumx/react/utils/type';
|
|
|
5
5
|
import { unref } from '../react/unref';
|
|
6
6
|
import { getPrefersReducedMotion } from '../browser/getPrefersReducedMotion';
|
|
7
7
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
function setupViewTransitionName(elementRef: MaybeElementOrRef<HTMLElement>, name: string) {
|
|
9
|
+
let originalName: string | null = null;
|
|
10
|
+
return {
|
|
11
|
+
set() {
|
|
12
|
+
const element = unref(elementRef);
|
|
13
|
+
if (!element) return;
|
|
14
|
+
originalName = element.style.viewTransitionName;
|
|
15
|
+
element.style.viewTransitionName = name;
|
|
16
|
+
},
|
|
17
|
+
unset() {
|
|
18
|
+
const element = unref(elementRef);
|
|
19
|
+
if (!element || originalName === null) return;
|
|
20
|
+
element.style.viewTransitionName = originalName;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
11
23
|
}
|
|
12
24
|
|
|
13
25
|
/**
|
|
@@ -37,20 +49,20 @@ export async function startViewTransition({
|
|
|
37
49
|
return;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
//
|
|
41
|
-
|
|
52
|
+
// Setup set/unset transition name on source & target
|
|
53
|
+
const sourceTransitionName = setupViewTransitionName(viewTransitionName.source, viewTransitionName.name);
|
|
54
|
+
const targetTransitionName = setupViewTransitionName(viewTransitionName.target, viewTransitionName.name);
|
|
55
|
+
|
|
56
|
+
sourceTransitionName.set();
|
|
42
57
|
|
|
43
58
|
// Start view transition, apply changes & flush to DOM
|
|
44
59
|
await start(() => {
|
|
45
|
-
|
|
46
|
-
setTransitionViewName(viewTransitionName.source, null);
|
|
60
|
+
sourceTransitionName.unset();
|
|
47
61
|
|
|
48
62
|
flushSync(changes);
|
|
49
63
|
|
|
50
|
-
|
|
51
|
-
setTransitionViewName(viewTransitionName.target, viewTransitionName.name);
|
|
64
|
+
targetTransitionName.set();
|
|
52
65
|
}).updateCallbackDone;
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
setTransitionViewName(viewTransitionName.target, null);
|
|
67
|
+
targetTransitionName.unset();
|
|
56
68
|
}
|