vite-plugin-entry-shaking-debugger
Version:
Debugger for vite-plugin-entry-shaking
132 lines (112 loc) • 4.31 kB
text/typescript
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;
}