reka-ui
Version:
Vue port for Radix UI Primitives.
132 lines (128 loc) • 4.63 kB
JavaScript
;
const vue = require('vue');
const shared = require('@vueuse/shared');
const core = require('@vueuse/core');
const shared_useStateMachine = require('../shared/useStateMachine.cjs');
function usePresence(present, node) {
const stylesRef = vue.ref({});
const prevAnimationNameRef = vue.ref("none");
const prevPresentRef = vue.ref(present);
const initialState = present.value ? "mounted" : "unmounted";
let timeoutId;
const ownerWindow = node.value?.ownerDocument.defaultView ?? core.defaultWindow;
const { state, dispatch } = shared_useStateMachine.useStateMachine(initialState, {
mounted: {
UNMOUNT: "unmounted",
ANIMATION_OUT: "unmountSuspended"
},
unmountSuspended: {
MOUNT: "mounted",
ANIMATION_END: "unmounted"
},
unmounted: {
MOUNT: "mounted"
}
});
const dispatchCustomEvent = (name) => {
if (shared.isClient) {
const customEvent = new CustomEvent(name, { bubbles: false, cancelable: false });
node.value?.dispatchEvent(customEvent);
}
};
vue.watch(
present,
async (currentPresent, prevPresent) => {
const hasPresentChanged = prevPresent !== currentPresent;
await vue.nextTick();
if (hasPresentChanged) {
const prevAnimationName = prevAnimationNameRef.value;
const currentAnimationName = getAnimationName(node.value);
if (currentPresent) {
dispatch("MOUNT");
dispatchCustomEvent("enter");
if (currentAnimationName === "none")
dispatchCustomEvent("after-enter");
} else if (currentAnimationName === "none" || currentAnimationName === "undefined" || stylesRef.value?.display === "none") {
dispatch("UNMOUNT");
dispatchCustomEvent("leave");
dispatchCustomEvent("after-leave");
} else {
const isAnimating = prevAnimationName !== currentAnimationName;
if (prevPresent && isAnimating) {
dispatch("ANIMATION_OUT");
dispatchCustomEvent("leave");
} else {
dispatch("UNMOUNT");
dispatchCustomEvent("after-leave");
}
}
}
},
{ immediate: true }
);
const handleAnimationEnd = (event) => {
const currentAnimationName = getAnimationName(node.value);
const isCurrentAnimation = currentAnimationName.includes(
event.animationName
);
const directionName = state.value === "mounted" ? "enter" : "leave";
if (event.target === node.value && isCurrentAnimation) {
dispatchCustomEvent(`after-${directionName}`);
dispatch("ANIMATION_END");
if (!prevPresentRef.value) {
const currentFillMode = node.value.style.animationFillMode;
node.value.style.animationFillMode = "forwards";
timeoutId = ownerWindow?.setTimeout(() => {
if (node.value?.style.animationFillMode === "forwards") {
node.value.style.animationFillMode = currentFillMode;
}
});
}
}
if (event.target === node.value && currentAnimationName === "none")
dispatch("ANIMATION_END");
};
const handleAnimationStart = (event) => {
if (event.target === node.value) {
prevAnimationNameRef.value = getAnimationName(node.value);
}
};
const watcher = vue.watch(
node,
(newNode, oldNode) => {
if (newNode) {
stylesRef.value = getComputedStyle(newNode);
newNode.addEventListener("animationstart", handleAnimationStart);
newNode.addEventListener("animationcancel", handleAnimationEnd);
newNode.addEventListener("animationend", handleAnimationEnd);
} else {
dispatch("ANIMATION_END");
if (timeoutId !== void 0)
ownerWindow?.clearTimeout(timeoutId);
oldNode?.removeEventListener("animationstart", handleAnimationStart);
oldNode?.removeEventListener("animationcancel", handleAnimationEnd);
oldNode?.removeEventListener("animationend", handleAnimationEnd);
}
},
{ immediate: true }
);
const stateWatcher = vue.watch(state, () => {
const currentAnimationName = getAnimationName(node.value);
prevAnimationNameRef.value = state.value === "mounted" ? currentAnimationName : "none";
});
vue.onUnmounted(() => {
watcher();
stateWatcher();
});
const isPresent = vue.computed(
() => ["mounted", "unmountSuspended"].includes(state.value)
);
return {
isPresent
};
}
function getAnimationName(node) {
return node ? getComputedStyle(node).animationName || "none" : "none";
}
exports.usePresence = usePresence;
//# sourceMappingURL=usePresence.cjs.map