react-aria-components
Version:
A library of styleable components built using React Aria
130 lines (122 loc) • 6.4 kB
JavaScript
import {useRenderProps as $64fa3d84918910a7$export$4d86445c2cf5e3} from "./utils.module.js";
import {flushSync as $I3GNx$flushSync} from "react-dom";
import $I3GNx$react, {createContext as $I3GNx$createContext, useRef as $I3GNx$useRef, forwardRef as $I3GNx$forwardRef, useState as $I3GNx$useState, useContext as $I3GNx$useContext} from "react";
import {useLayoutEffect as $I3GNx$useLayoutEffect} from "@react-aria/utils";
import {useObjectRef as $I3GNx$useObjectRef} from "react-aria";
/*
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
const $c8a5a149f625efcf$var$SharedElementContext = /*#__PURE__*/ (0, $I3GNx$createContext)(null);
function $c8a5a149f625efcf$export$758399f318e6385a(props) {
let ref = (0, $I3GNx$useRef)({});
return /*#__PURE__*/ (0, $I3GNx$react).createElement($c8a5a149f625efcf$var$SharedElementContext.Provider, {
value: ref
}, props.children);
}
const $c8a5a149f625efcf$export$c34620ff8881d89f = /*#__PURE__*/ (0, $I3GNx$forwardRef)(function SharedElement(props, ref) {
let { name: name, isVisible: isVisible = true, children: children, className: className, style: style, ...divProps } = props;
let [state, setState] = (0, $I3GNx$useState)(isVisible ? 'visible' : 'hidden');
let scopeRef = (0, $I3GNx$useContext)($c8a5a149f625efcf$var$SharedElementContext);
if (!scopeRef) throw new Error('<SharedElement> must be rendered inside a <SharedElementTransition>');
if (isVisible && state === 'hidden') setState('visible');
ref = (0, $I3GNx$useObjectRef)(ref);
(0, $I3GNx$useLayoutEffect)(()=>{
let element = ref.current;
let scope = scopeRef.current;
let prevSnapshot = scope[name];
let frame = null;
if (element && isVisible && prevSnapshot) {
// Element is transitioning from a previous instance.
setState('visible');
let animations = element.getAnimations();
// Set properties to animate from.
let values = prevSnapshot.style.map(([property, prevValue])=>{
let value = element.style[property];
if (property === 'translate') {
let prevRect = prevSnapshot.rect;
let currentItem = element.getBoundingClientRect();
let deltaX = prevRect.left - (currentItem === null || currentItem === void 0 ? void 0 : currentItem.left);
let deltaY = prevRect.top - (currentItem === null || currentItem === void 0 ? void 0 : currentItem.top);
element.style.translate = `${deltaX}px ${deltaY}px`;
} else element.style[property] = prevValue;
return [
property,
value
];
});
// Cancel any new animations triggered by these properties.
for (let a of element.getAnimations())if (!animations.includes(a)) a.cancel();
// Remove overrides after one frame to animate to the current values.
frame = requestAnimationFrame(()=>{
frame = null;
for (let [property, value] of values)element.style[property] = value;
});
delete scope[name];
} else if (element && isVisible && !prevSnapshot) {
// No previous instance exists, apply the entering state.
queueMicrotask(()=>(0, $I3GNx$flushSync)(()=>setState('entering')));
frame = requestAnimationFrame(()=>{
frame = null;
setState('visible');
});
} else if (element && !isVisible) // Wait until layout effects finish, and check if a snapshot still exists.
// If so, no new SharedElement consumed it, so enter the exiting state.
queueMicrotask(()=>{
if (scope[name]) {
delete scope[name];
(0, $I3GNx$flushSync)(()=>setState('exiting'));
Promise.all(element.getAnimations().map((a)=>a.finished)).then(()=>setState('hidden')).catch(()=>{});
} else // Snapshot was consumed by another instance, unmount.
setState('hidden');
});
return ()=>{
if (frame != null) cancelAnimationFrame(frame);
if (element && element.isConnected && !element.hasAttribute('data-exiting')) {
// On unmount, store a snapshot of the rectangle and computed style for transitioning properties.
let style = window.getComputedStyle(element);
if (style.transitionProperty !== 'none') {
let transitionProperty = style.transitionProperty.split(/\s*,\s*/);
scope[name] = {
rect: element.getBoundingClientRect(),
style: transitionProperty.map((p)=>[
p,
style[p]
])
};
}
}
};
}, [
ref,
scopeRef,
name,
isVisible
]);
let renderProps = (0, $64fa3d84918910a7$export$4d86445c2cf5e3)({
children: children,
className: className,
style: style,
values: {
isEntering: state === 'entering',
isExiting: state === 'exiting'
}
});
if (state === 'hidden') return null;
return /*#__PURE__*/ (0, $I3GNx$react).createElement("div", {
...divProps,
...renderProps,
ref: ref,
"data-entering": state === 'entering' || undefined,
"data-exiting": state === 'exiting' || undefined
});
});
export {$c8a5a149f625efcf$export$758399f318e6385a as SharedElementTransition, $c8a5a149f625efcf$export$c34620ff8881d89f as SharedElement};
//# sourceMappingURL=SharedElementTransition.module.js.map