diff --git a/src/platform/StackedRouter.tsx b/src/platform/StackedRouter.tsx index 887a8a5..eab70ea 100644 --- a/src/platform/StackedRouter.tsx +++ b/src/platform/StackedRouter.tsx @@ -2,6 +2,7 @@ import { StaticRouter, type RouterProps } from "@solidjs/router"; import { Component, createContext, + createMemo, createRenderEffect, createUniqueId, Index, @@ -16,6 +17,7 @@ import "./StackedRouter.css"; import { animateSlideInFromRight, animateSlideOutToRight } from "./anim"; import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme"; import { makeEventListener } from "@solid-primitives/event-listener"; +import { useWindowSize } from "@solid-primitives/resize-observer"; export type StackedRouterProps = Omit; @@ -203,9 +205,46 @@ function serializableStack(stack: readonly StackFrame[]) { /** * The router that stacks the pages. + * + * **Routes** The router accepts the {@link RouteProps} excluding the "url" field. + * You can seamlessly use the `` from `@solidjs/router`. + * + * Be advised that this component is not a drop-in replacement of that router. + * These primitives from `@solidjs/router` won't work correctly: + * + * - `` component - use ~platform/A instead + * - `useLocation()` - see {@link useCurrentFrame} + * - `useNavigate()` - see {@link useNavigator} + * + * The other primitives may work, as long as they don't rely on the global location. + * This component uses `@solidjs/router` {@link StaticRouter} to route. + * + * **Injecting Safe Area Insets** The router calculate correct + * `--safe-area-inset-left` and `--safe-area-inset-right` from the window + * width and `--safe-area-inset-*` from the :root element. That means + * the injected insets do not reflects the overrides that are not on the :root. + * + * The recalculation is only performed when the window size changed. + * + * **Navigation Animation** The router provides default animation for + * navigation. + * + * If the default animation does not met your requirement, + * this component is also intergated with Web Animation API. + * You can provide {@link NewFrameOptions.animateOpen} and + * {@link NewFrameOptions.animateClose} to define custom animation. + * + * **Swipe to back** For the subpages (the pages stacked on the entry), + * swipe to back gesture is provided for user experience. + * + * Navigation animations (even the custom ones) will be played during + * swipe to back, please keep in mind when designing animations. + * + * The iOS default gesture is blocked on those pages. */ const StackedRouter: Component = (oprops) => { const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" }); + const windowSize = useWindowSize(); const pushFrame = (path: string, opts?: Readonly>) => untrack(() => { @@ -293,6 +332,39 @@ const StackedRouter: Component = (oprops) => { }); }; + const subInsets = createMemo(() => { + const SUBPAGE_MAX_WIDTH = 560; + const { width } = windowSize; + if (width <= SUBPAGE_MAX_WIDTH) { + // page width = 100vw, use the inset directly + return {}; + } + const computedStyle = window.getComputedStyle( + document.querySelector(":root")!, + ); + const oinsetLeft = computedStyle + .getPropertyValue("--safe-area-inset-left") + .split("px", 1)[0]; + const oinsetRight = computedStyle + .getPropertyValue("--safe-area-inset-right") + .split("px", 1)[0]; + console.debug("insets-inline", oinsetLeft, oinsetRight); + const left = Number(oinsetLeft), + right = Number(oinsetRight.slice(0, oinsetRight.length - 2)); + const totalWidth = SUBPAGE_MAX_WIDTH + left + right; + if (width >= totalWidth) { + return { + "--safe-area-inset-left": "0px", + "--safe-area-inset-right": "0px", + }; + } + const ofs = (totalWidth - width) / 2; + return { + "--safe-area-inset-left": `${Math.max(left - ofs, 0)}px`, + "--safe-area-inset-right": `${Math.max(right - ofs, 0)}px`, + }; + }); + let reenterableAnimation: Animation | undefined; let origWidth = 0, origFigX = 0, @@ -435,6 +507,7 @@ const StackedRouter: Component = (oprops) => { on:touchend={onDialogTouchEnd} on:touchcancel={onDialogTouchCancel} id={frame().rootId} + style={subInsets()} >