From 169aa91e73c9fe6fb7d23070cb96582fa44a914d Mon Sep 17 00:00:00 2001 From: thislight Date: Sun, 17 Nov 2024 20:57:56 +0800 Subject: [PATCH] StackedRouter: add useMaybeIsFrameSuspended() --- src/platform/StackedRouter.css | 2 ++ src/platform/StackedRouter.tsx | 64 +++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/platform/StackedRouter.css b/src/platform/StackedRouter.css index 6b912c1..eac26ce 100644 --- a/src/platform/StackedRouter.css +++ b/src/platform/StackedRouter.css @@ -3,6 +3,8 @@ display: contents; max-width: 100vw; max-width: 100dvw; + + contain: layout; } dialog.StackedPage { diff --git a/src/platform/StackedRouter.tsx b/src/platform/StackedRouter.tsx index 792ec97..5bc6d8c 100644 --- a/src/platform/StackedRouter.tsx +++ b/src/platform/StackedRouter.tsx @@ -2,7 +2,6 @@ import { StaticRouter, type RouterProps } from "@solidjs/router"; import { Component, createContext, - createEffect, createRenderEffect, createUniqueId, Index, @@ -34,8 +33,33 @@ export type NewFrameOptions = (T extends undefined state?: T; } : { state: T }) & { + /** + * The new frame should replace the current frame. + */ replace?: boolean; + /** + * The animatedOpen phase of the life cycle. + * + * You can use this hook to animate the opening + * of the frame. In this phase, the frame content is created + * and is mounted to the document. + * + * You must return an {@link Animation}. This function must be + * without side effects. This phase is ended after the {@link Animation} + * finished. + */ animateOpen?: StackFrame["animateOpen"]; + /** + * The animatedClose phase of the life cycle. + * + * You can use this hook to animate the closing of the frame. + * In this phase, the frame content is still mounted in the + * document and will be unmounted after this phase. + * + * You must return an {@link Animation}. This function must be + * without side effects. This phase is ended after the + * {@link Animation} finished. + */ animateClose?: StackFrame["animateClose"]; }; @@ -53,6 +77,10 @@ export type Navigator> = { const NavigatorContext = /* @__PURE__ */ createContext(); +export function useMaybeNavigator() { + return useContext(NavigatorContext); +} + /** * Get the navigator of the {@link StackedRouter}. * @@ -63,7 +91,7 @@ const NavigatorContext = /* @__PURE__ */ createContext(); * navigator to the type you need. */ export function useNavigator() { - const navigator = useContext(NavigatorContext); + const navigator = useMaybeNavigator(); if (!navigator) { throw new TypeError("not in available scope of StackedRouter"); @@ -80,8 +108,12 @@ export type CurrentFrame = { const CurrentFrameContext = /* @__PURE__ */ createContext>>(); +export function useMaybeCurrentFrame() { + return useContext(CurrentFrameContext); +} + export function useCurrentFrame() { - const frame = useContext(CurrentFrameContext); + const frame = useMaybeCurrentFrame(); if (!frame) { throw new TypeError("not in available scope of StackedRouter"); @@ -90,6 +122,28 @@ export function useCurrentFrame() { return frame; } +/** + * Return an accessor of is current frame is suspended. + * + * A suspended frame is the one not on the top. "Suspended" + * is the description of a certain situtation, not in the life cycle + * of a frame. + */ +export function useMaybeIsFrameSuspended() { + const { frames } = useMaybeNavigator() || {}; + + if (typeof frames === "undefined") { + return () => false; + } + + const thisFrame = useCurrentFrame(); + + return () => { + const idx = thisFrame().index; + return frames.length - 1 > idx; + }; +} + function onDialogClick( onClose: () => void, event: MouseEvent & { currentTarget: HTMLDialogElement }, @@ -189,10 +243,10 @@ const StackedRouter: Component = (oprops) => { )! as HTMLDialogElement; const createAnimation = lastFrame.animateClose ?? animateClose; requestAnimationFrame(() => { - element.classList.add("animating") + element.classList.add("animating"); const animation = createAnimation(element); animation.addEventListener("finish", () => { - element.classList.remove("animating") + element.classList.remove("animating"); onlyPopFrame(depth); }); });