Files
video-gen/video-gen-admin/node_modules/@rc-component/motion/es/CSSMotion.js
T
2026-05-25 17:08:18 +08:00

161 lines
5.1 KiB
JavaScript

/* eslint-disable react/default-props-match-prop-types, react/no-multi-comp, react/prop-types */
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
import { composeRef, getNodeRef, supportNodeRef } from "@rc-component/util/es/ref";
import { clsx } from 'clsx';
import * as React from 'react';
import { useRef } from 'react';
import { Context } from "./context";
import useStatus from "./hooks/useStatus";
import { isActive } from "./hooks/useStepQueue";
import { STATUS_NONE, STEP_PREPARE, STEP_START } from "./interface";
import { getTransitionName, supportTransition } from "./util/motion";
export function isRefNotConsumed(children) {
return children?.length < 2;
}
/**
* `transitionSupport` is used for none transition test case.
* Default we use browser transition event support check.
*/
export function genCSSMotion(config) {
let transitionSupport = config;
if (typeof config === 'object') {
({
transitionSupport
} = config);
}
function isSupportTransition(props, contextMotion) {
return !!(props.motionName && transitionSupport && contextMotion !== false);
}
const CSSMotion = /*#__PURE__*/React.forwardRef((props, ref) => {
const {
// Default config
visible = true,
removeOnLeave = true,
forceRender,
children,
motionName,
leavedClassName,
eventProps
} = props;
const {
motion: contextMotion
} = React.useContext(Context);
const supportMotion = isSupportTransition(props, contextMotion);
// Ref to the react node, it may be a HTMLElement
const nodeRef = useRef();
function getDomElement() {
return getDOM(nodeRef.current);
}
const [getStatus, statusStep, statusStyle, mergedVisible, styleReady] = useStatus(supportMotion, visible, getDomElement, props);
const status = getStatus();
// Record whether content has rendered
// Will return null for un-rendered even when `removeOnLeave={false}`
const renderedRef = React.useRef(mergedVisible);
if (mergedVisible) {
renderedRef.current = true;
}
// ====================== Refs ======================
const refObj = React.useMemo(() => {
const obj = {};
Object.defineProperties(obj, {
nativeElement: {
enumerable: true,
get: getDomElement
},
inMotion: {
enumerable: true,
get: () => () => getStatus() !== STATUS_NONE
},
enableMotion: {
enumerable: true,
get: () => () => supportMotion
}
});
return obj;
}, []);
// We lock `deps` here since function return object
// will repeat trigger ref from `refConfig` -> `null` -> `refConfig`
React.useImperativeHandle(ref, () => refObj, []);
// ===================== Render =====================
// return motionChildren as React.ReactElement;
const idRef = React.useRef(0);
if (styleReady) {
idRef.current += 1;
}
// We should render children when motionStyle is sync with stepStatus
const returnNode = React.useMemo(() => {
if (styleReady === 'NONE') {
return null;
}
let motionChildren;
const mergedProps = {
...eventProps,
visible
};
if (!children) {
// No children
motionChildren = null;
} else if (status === STATUS_NONE) {
// Stable children
if (mergedVisible) {
motionChildren = children({
...mergedProps
}, nodeRef);
} else if (!removeOnLeave && renderedRef.current && leavedClassName) {
motionChildren = children({
...mergedProps,
className: leavedClassName
}, nodeRef);
} else if (forceRender || !removeOnLeave && !leavedClassName) {
motionChildren = children({
...mergedProps,
style: {
display: 'none'
}
}, nodeRef);
} else {
motionChildren = null;
}
} else {
// In motion
let statusSuffix;
if (statusStep === STEP_PREPARE) {
statusSuffix = 'prepare';
} else if (isActive(statusStep)) {
statusSuffix = 'active';
} else if (statusStep === STEP_START) {
statusSuffix = 'start';
}
const motionCls = getTransitionName(motionName, `${status}-${statusSuffix}`);
motionChildren = children({
...mergedProps,
className: clsx(getTransitionName(motionName, status), {
[motionCls]: motionCls && statusSuffix,
[motionName]: typeof motionName === 'string'
}),
style: statusStyle
}, nodeRef);
}
return motionChildren;
}, [idRef.current]);
if (isRefNotConsumed(children) && supportNodeRef(returnNode)) {
const originNodeRef = getNodeRef(returnNode);
if (originNodeRef !== nodeRef) {
return /*#__PURE__*/React.cloneElement(returnNode, {
ref: composeRef(originNodeRef, nodeRef)
});
}
}
return returnNode;
});
CSSMotion.displayName = 'CSSMotion';
return CSSMotion;
}
export default genCSSMotion(supportTransition);