UNPKG

vite-plugin-entry-shaking-debugger

Version:
132 lines (112 loc) 4.31 kB
import type { ComponentPublicInstance, Ref } from 'vue'; import { inject, onBeforeUnmount, onMounted } from 'vue'; import { getElement } from '#utils'; type AugmentedViewTransition = ViewTransition & { captured: Promise<void> }; type TransitionableElement = Ref<HTMLElement | null> | Ref<ComponentPublicInstance>; type ViewTransitionName = string; type ViewTransitionNameItem = Map<number, TransitionableElement>; export interface UseViewTransitionOptions { /** List of `view transition-name`s, and the HTMLElement they refer to. */ names?: Record<ViewTransitionName, TransitionableElement>; /** * List of lazy `view-transition-names`, those will be added to relevant * element onlys when `doTransition is called` */ lazy?: ViewTransitionName[]; } const lazyTransitions = new Set<ViewTransitionName>(); const allTransitions = new Map<ViewTransitionName, ViewTransitionNameItem>(); export function useViewTransition(options: UseViewTransitionOptions) { const localTransitions = new Map<ViewTransitionName, ViewTransitionNameItem>(); const depth = inject<number>('depth')!; const setTransitionName = (name: ViewTransitionName, element: TransitionableElement) => { const el = getElement(element); if (!el) return; if (!allTransitions.has(name)) allTransitions.set(name, new Map()); if (!localTransitions.has(name)) localTransitions.set(name, new Map()); allTransitions.get(name)?.set(depth, element); localTransitions.get(name)?.set(depth, element); if (options.lazy?.includes(name)) { lazyTransitions.add(name); } else { el.style.viewTransitionName = name; } }; onMounted(() => { if (options.names) { Object.entries(options.names).forEach(([name, element]) => { setTransitionName(name, element); }); } }); onBeforeUnmount(() => { [...localTransitions.entries()].forEach(([name]) => { allTransitions.get(name)?.delete(depth); localTransitions.get(name)?.delete(depth); }); }); function setAllOtherTransitions(disable: boolean) { [...allTransitions.entries()].forEach(([name, depthMap]) => { [...depthMap.entries()].forEach(([dth, element]) => { if (dth !== depth) { const el = getElement(element); if (el) el.style.viewTransitionName = disable ? 'none' : name; } }); }); } async function transition() { setAllOtherTransitions(true); await doTransition(undefined, () => { setAllOtherTransitions(false); }); } return { startViewTransition, transition, }; } function startViewTransition(callback?: () => Promise<void>): AugmentedViewTransition { const viewTransition = {} as AugmentedViewTransition; if (!document.startViewTransition) { console.error('[useViewTransition] Browser does not support View Transitions Api'); const callbackPromise = callback ? Promise.resolve(callback()) : Promise.resolve(); return { captured: Promise.resolve(), updateCallbackDone: callbackPromise, ready: callbackPromise, finished: callbackPromise, }; } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (document.startViewTransition) { const capturedPromise = new Promise<void>((resolve) => { const cb = async () => { resolve(); await callback?.(); }; const nativeViewTransition = document.startViewTransition!(cb); viewTransition.updateCallbackDone = nativeViewTransition.updateCallbackDone; viewTransition.ready = nativeViewTransition.ready; viewTransition.finished = nativeViewTransition.finished; }); viewTransition.captured = capturedPromise; } return viewTransition; } export function setAllLazyTransitions(disable: boolean) { [...allTransitions.entries()].forEach(([name, depthMap]) => { if (lazyTransitions.has(name)) { const item = depthMap.get(0); if (item) { const el = getElement(item); if (el) el.style.viewTransitionName = disable ? 'none' : name; } } }); } export async function doTransition(callback?: () => Promise<void>, onEnd?: () => void) { const viewTransition = startViewTransition(callback); if (onEnd) viewTransition.finished.then(onEnd); await viewTransition.captured; }