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 { useEvent } from 'rc-util'; import useState from "rc-util/es/hooks/useState"; import useSyncState from "rc-util/es/hooks/useSyncState"; 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 _useSyncState = useSyncState(STATUS_NONE), _useSyncState2 = _slicedToArray(_useSyncState, 2), getStatus = _useSyncState2[0], setStatus = _useSyncState2[1]; var _useState3 = useState(null), _useState4 = _slicedToArray(_useState3, 2), style = _useState4[0], setStyle = _useState4[1]; var currentStatus = getStatus(); 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); setStyle(null, true); } var onInternalMotionEnd = useEvent(function (event) { var status = getStatus(); // Do nothing since not in any transition status. // This may happen when `motionDeadline` trigger. if (status === STATUS_NONE) { return; } 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 (currentActive && canEnd !== false) { updateMotionEndStatus(); } }); var _useDomMotionEvents = useDomMotionEvents(onInternalMotionEnd), _useDomMotionEvents2 = _slicedToArray(_useDomMotionEvents, 1), patchMotionEvents = _useDomMotionEvents2[0]; // ============================= Step ============================= var getEventHandlers = function getEventHandlers(targetStatus) { switch (targetStatus) { case STATUS_APPEAR: return _defineProperty(_defineProperty(_defineProperty({}, STEP_PREPARE, onAppearPrepare), STEP_START, onAppearStart), STEP_ACTIVE, onAppearActive); case STATUS_ENTER: return _defineProperty(_defineProperty(_defineProperty({}, STEP_PREPARE, onEnterPrepare), STEP_START, onEnterStart), STEP_ACTIVE, onEnterActive); case STATUS_LEAVE: return _defineProperty(_defineProperty(_defineProperty({}, STEP_PREPARE, onLeavePrepare), STEP_START, onLeaveStart), STEP_ACTIVE, onLeaveActive); default: return {}; } }; var eventHandlers = React.useMemo(function () { return getEventHandlers(currentStatus); }, [currentStatus]); var _useStepQueue = useStepQueue(currentStatus, !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 && currentStatus !== STATUS_NONE) { // 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 currentStatus === STATUS_APPEAR && !motionAppear || // Cancel enter currentStatus === STATUS_ENTER && !motionEnter || // Cancel leave currentStatus === 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 && currentStatus === STATUS_NONE) { // Skip first render is invisible since it's nothing changed if (firstMountChangeRef.current || asyncVisible) { onVisibleChanged === null || onVisibleChanged === void 0 || onVisibleChanged(asyncVisible); } firstMountChangeRef.current = true; } }, [asyncVisible, currentStatus]); // ============================ Styles ============================ var mergedStyle = style; if (eventHandlers[STEP_PREPARE] && step === STEP_START) { mergedStyle = _objectSpread({ transition: 'none' }, mergedStyle); } return [currentStatus, step, mergedStyle, asyncVisible !== null && asyncVisible !== void 0 ? asyncVisible : visible]; }