import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; import _defineProperty from "@babel/runtime/helpers/esm/defineProperty"; import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; import useState from "rc-util/es/hooks/useState"; import * as React from 'react'; import { useEffect, useRef } from 'react'; import { STATUS_APPEAR, STATUS_ENTER, STATUS_LEAVE, STATUS_NONE, STEP_ACTIVE, STEP_PREPARE, STEP_PREPARED, STEP_START } from "../interface"; import useDomMotionEvents from "./useDomMotionEvents"; import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect"; import useStepQueue, { DoStep, isActive, SkipStep } from "./useStepQueue"; export default function useStatus(supportMotion, visible, getElement, _ref) { var _ref$motionEnter = _ref.motionEnter, motionEnter = _ref$motionEnter === void 0 ? true : _ref$motionEnter, _ref$motionAppear = _ref.motionAppear, motionAppear = _ref$motionAppear === void 0 ? true : _ref$motionAppear, _ref$motionLeave = _ref.motionLeave, motionLeave = _ref$motionLeave === void 0 ? true : _ref$motionLeave, motionDeadline = _ref.motionDeadline, motionLeaveImmediately = _ref.motionLeaveImmediately, onAppearPrepare = _ref.onAppearPrepare, onEnterPrepare = _ref.onEnterPrepare, onLeavePrepare = _ref.onLeavePrepare, onAppearStart = _ref.onAppearStart, onEnterStart = _ref.onEnterStart, onLeaveStart = _ref.onLeaveStart, onAppearActive = _ref.onAppearActive, onEnterActive = _ref.onEnterActive, onLeaveActive = _ref.onLeaveActive, onAppearEnd = _ref.onAppearEnd, onEnterEnd = _ref.onEnterEnd, onLeaveEnd = _ref.onLeaveEnd, onVisibleChanged = _ref.onVisibleChanged; // Used for outer render usage to avoid `visible: false & status: none` to render nothing var _useState = useState(), _useState2 = _slicedToArray(_useState, 2), asyncVisible = _useState2[0], setAsyncVisible = _useState2[1]; var _useState3 = useState(STATUS_NONE), _useState4 = _slicedToArray(_useState3, 2), status = _useState4[0], setStatus = _useState4[1]; var _useState5 = useState(null), _useState6 = _slicedToArray(_useState5, 2), style = _useState6[0], setStyle = _useState6[1]; var mountedRef = useRef(false); var deadlineRef = useRef(null); // =========================== Dom Node =========================== function getDomElement() { return getElement(); } // ========================== Motion End ========================== var activeRef = useRef(false); /** * Clean up status & style */ function updateMotionEndStatus() { setStatus(STATUS_NONE, true); setStyle(null, true); } function onInternalMotionEnd(event) { var element = getDomElement(); if (event && !event.deadline && event.target !== element) { // event exists // not initiated by deadline // transitionEnd not fired by inner elements return; } var currentActive = activeRef.current; var canEnd; if (status === STATUS_APPEAR && currentActive) { canEnd = onAppearEnd === null || onAppearEnd === void 0 ? void 0 : onAppearEnd(element, event); } else if (status === STATUS_ENTER && currentActive) { canEnd = onEnterEnd === null || onEnterEnd === void 0 ? void 0 : onEnterEnd(element, event); } else if (status === STATUS_LEAVE && currentActive) { canEnd = onLeaveEnd === null || onLeaveEnd === void 0 ? void 0 : onLeaveEnd(element, event); } // Only update status when `canEnd` and not destroyed if (status !== STATUS_NONE && currentActive && canEnd !== false) { updateMotionEndStatus(); } } var _useDomMotionEvents = useDomMotionEvents(onInternalMotionEnd), _useDomMotionEvents2 = _slicedToArray(_useDomMotionEvents, 1), patchMotionEvents = _useDomMotionEvents2[0]; // ============================= Step ============================= var getEventHandlers = function getEventHandlers(targetStatus) { var _ref2, _ref3, _ref4; switch (targetStatus) { case STATUS_APPEAR: return _ref2 = {}, _defineProperty(_ref2, STEP_PREPARE, onAppearPrepare), _defineProperty(_ref2, STEP_START, onAppearStart), _defineProperty(_ref2, STEP_ACTIVE, onAppearActive), _ref2; case STATUS_ENTER: return _ref3 = {}, _defineProperty(_ref3, STEP_PREPARE, onEnterPrepare), _defineProperty(_ref3, STEP_START, onEnterStart), _defineProperty(_ref3, STEP_ACTIVE, onEnterActive), _ref3; case STATUS_LEAVE: return _ref4 = {}, _defineProperty(_ref4, STEP_PREPARE, onLeavePrepare), _defineProperty(_ref4, STEP_START, onLeaveStart), _defineProperty(_ref4, STEP_ACTIVE, onLeaveActive), _ref4; default: return {}; } }; var eventHandlers = React.useMemo(function () { return getEventHandlers(status); }, [status]); var _useStepQueue = useStepQueue(status, !supportMotion, function (newStep) { // Only prepare step can be skip if (newStep === STEP_PREPARE) { var onPrepare = eventHandlers[STEP_PREPARE]; if (!onPrepare) { return SkipStep; } return onPrepare(getDomElement()); } // Rest step is sync update if (step in eventHandlers) { var _eventHandlers$step; setStyle(((_eventHandlers$step = eventHandlers[step]) === null || _eventHandlers$step === void 0 ? void 0 : _eventHandlers$step.call(eventHandlers, getDomElement(), null)) || null); } if (step === STEP_ACTIVE) { // Patch events when motion needed patchMotionEvents(getDomElement()); if (motionDeadline > 0) { clearTimeout(deadlineRef.current); deadlineRef.current = setTimeout(function () { onInternalMotionEnd({ deadline: true }); }, motionDeadline); } } if (step === STEP_PREPARED) { updateMotionEndStatus(); } return DoStep; }), _useStepQueue2 = _slicedToArray(_useStepQueue, 2), startStep = _useStepQueue2[0], step = _useStepQueue2[1]; var active = isActive(step); activeRef.current = active; // ============================ Status ============================ // Update with new status useIsomorphicLayoutEffect(function () { setAsyncVisible(visible); var isMounted = mountedRef.current; mountedRef.current = true; // if (!supportMotion) { // return; // } var nextStatus; // Appear if (!isMounted && visible && motionAppear) { nextStatus = STATUS_APPEAR; } // Enter if (isMounted && visible && motionEnter) { nextStatus = STATUS_ENTER; } // Leave if (isMounted && !visible && motionLeave || !isMounted && motionLeaveImmediately && !visible && motionLeave) { nextStatus = STATUS_LEAVE; } var nextEventHandlers = getEventHandlers(nextStatus); // Update to next status if (nextStatus && (supportMotion || nextEventHandlers[STEP_PREPARE])) { setStatus(nextStatus); startStep(); } else { // Set back in case no motion but prev status has prepare step setStatus(STATUS_NONE); } }, [visible]); // ============================ Effect ============================ // Reset when motion changed useEffect(function () { if ( // Cancel appear status === STATUS_APPEAR && !motionAppear || // Cancel enter status === STATUS_ENTER && !motionEnter || // Cancel leave status === STATUS_LEAVE && !motionLeave) { setStatus(STATUS_NONE); } }, [motionAppear, motionEnter, motionLeave]); useEffect(function () { return function () { mountedRef.current = false; clearTimeout(deadlineRef.current); }; }, []); // Trigger `onVisibleChanged` var firstMountChangeRef = React.useRef(false); useEffect(function () { // [visible & motion not end] => [!visible & motion end] still need trigger onVisibleChanged if (asyncVisible) { firstMountChangeRef.current = true; } if (asyncVisible !== undefined && status === STATUS_NONE) { // Skip first render is invisible since it's nothing changed if (firstMountChangeRef.current || asyncVisible) { onVisibleChanged === null || onVisibleChanged === void 0 ? void 0 : onVisibleChanged(asyncVisible); } firstMountChangeRef.current = true; } }, [asyncVisible, status]); // ============================ Styles ============================ var mergedStyle = style; if (eventHandlers[STEP_PREPARE] && step === STEP_START) { mergedStyle = _objectSpread({ transition: 'none' }, mergedStyle); } return [status, step, mergedStyle, asyncVisible !== null && asyncVisible !== void 0 ? asyncVisible : visible]; }