UNPKG

@agnos-ui/core

Version:

Framework-agnostic headless component library.

176 lines (175 loc) 6.38 kB
import { computed, writable, derived, batch } from "@amadeus-it-group/tansu"; import { d as typeBooleanOrNull, g as typeFunction, c as typeBoolean } from "../../writables-CgpOQ4hA.js"; import { c as promiseWithResolve } from "../../promise-XSP94FjG.js"; import { noop } from "../../utils/func.js"; import { writablesForProps, bindableDerived, stateStores } from "../../utils/stores.js"; import { h as createStoreDirective, k as mergeDirectives, d as directiveSubscribe } from "../../dom-gfxqXJpK.js"; const neverEndingPromise = new Promise(noop); const noAnimation = (element, direction) => { element.style.display = direction === "show" ? "" : "none"; }; const defaultValues = { animated: true, animatedOnInit: false, initDone: null, visible: true, transition: noAnimation, onShown: noop, onHidden: noop, onVisibleChange: noop }; const configValidator = { animated: typeBoolean, animatedOnInit: typeBoolean, visible: typeBoolean, transition: typeFunction, onShown: typeFunction, onHidden: typeFunction, onVisibleChange: typeFunction, initDone: typeBooleanOrNull }; const createTransition = (config) => { const [{ animated$, initDone$, visible$: requestedVisible$, transition$, onShown$, onHidden$, onVisibleChange$, animatedOnInit$ }, patch] = writablesForProps(defaultValues, config, configValidator); const { element$, directive: storeDirective } = createStoreDirective(); const elementPresent$ = computed(() => !!element$()); const visible$ = bindableDerived(onVisibleChange$, [requestedVisible$], ([visible]) => visible); const currentTransition$ = writable( null ); const stop = () => { let context; currentTransition$.update((currentTransition) => { currentTransition == null ? void 0 : currentTransition.abort.abort(); context = currentTransition == null ? void 0 : currentTransition.context; return null; }); return context; }; const runTransition = (visible, animated, element, transitionFn) => batch(() => { const abort = new AbortController(); const signal = abort.signal; const context = stop() ?? {}; const { promise, resolve } = promiseWithResolve(); const currentTransition = { abort, animated, visible, context, element, transitionFn, promise }; currentTransition$.set(currentTransition); resolve( (async () => { var _a; try { await transitionFn(element, visible ? "show" : "hide", animated, signal, context); } finally { if (signal.aborted) { await neverEndingPromise; } else { currentTransition$.set(null); (_a = (visible ? onShown$ : onHidden$)()) == null ? void 0 : _a(); } } })() ); return currentTransition; }); const effectiveAnimation$ = computed(() => initDone$() ? animated$() : animatedOnInit$()); const animationFromToggle$ = writable(null); let previousElement; let previousVisible = requestedVisible$(); let pendingTransition = null; const visibleAction$ = derived( [visible$, element$, effectiveAnimation$, animationFromToggle$, transition$, currentTransition$], ([visible, element, animated, animationFromToggle, transition, currentTransition]) => { const elementChanged = previousElement !== element; previousElement = element; const visibleChanged = previousVisible !== visible; previousVisible = visible; if (element) { if (initDone$() == null) { initDone$.set(true); } const interruptAnimation = animationFromToggle != null && currentTransition && currentTransition.animated != animationFromToggle; if (elementChanged || visibleChanged || interruptAnimation) { if (visibleChanged || animationFromToggle != null) { pendingTransition = null; } const animate = animationFromToggle ?? (pendingTransition == null ? void 0 : pendingTransition.animated) ?? (elementChanged && !visible ? false : animated); currentTransition = runTransition(visible, animate, element, transition); pendingTransition == null ? void 0 : pendingTransition.resolve(currentTransition.promise); pendingTransition = null; } } else { if (elementChanged) { stop(); currentTransition = null; } if (visibleChanged || visible && (pendingTransition == null ? void 0 : pendingTransition.animated) !== animationFromToggle) { pendingTransition = visible && animationFromToggle != null ? { // toggle was called to display the element, but the element is not yet in the DOM // let's keep the animation setting from toggle and provide the promise for the end of toggle animated: animationFromToggle, ...promiseWithResolve() } : null; } } return (pendingTransition == null ? void 0 : pendingTransition.promise) ?? (currentTransition == null ? void 0 : currentTransition.promise); } ); let lastToggle = {}; const toggle = async (visible = !requestedVisible$(), animated = effectiveAnimation$()) => { const currentToggle = {}; lastToggle = currentToggle; try { await batch(() => { try { animationFromToggle$.set(animated); requestedVisible$.set(visible); return visibleAction$(); } finally { animationFromToggle$.set(null); } }); } finally { if (lastToggle !== currentToggle) { await neverEndingPromise; } } }; const transitioning$ = computed(() => { if (elementPresent$()) { void visibleAction$(); } return !!currentTransition$(); }); const shown$ = computed(() => !transitioning$() && visible$() && elementPresent$()); const hidden$ = computed(() => !transitioning$() && !visible$()); const directive = mergeDirectives(storeDirective, directiveSubscribe(visibleAction$)); return { ...stateStores({ visible$, element$, elementPresent$, transitioning$, shown$, hidden$ }), patch, directives: { directive }, api: { show: toggle.bind(null, true), hide: toggle.bind(null, false), toggle } }; }; export { createTransition, noAnimation };