StackedRouter: inject inline insets, fix #48
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				/ depoly (push) Successful in 1m19s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	/ depoly (push) Successful in 1m19s
				
			This commit is contained in:
		
							parent
							
								
									5ecba144f0
								
							
						
					
					
						commit
						6726ffe664
					
				
					 1 changed files with 73 additions and 0 deletions
				
			
		| 
						 | 
					@ -2,6 +2,7 @@ import { StaticRouter, type RouterProps } from "@solidjs/router";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Component,
 | 
					  Component,
 | 
				
			||||||
  createContext,
 | 
					  createContext,
 | 
				
			||||||
 | 
					  createMemo,
 | 
				
			||||||
  createRenderEffect,
 | 
					  createRenderEffect,
 | 
				
			||||||
  createUniqueId,
 | 
					  createUniqueId,
 | 
				
			||||||
  Index,
 | 
					  Index,
 | 
				
			||||||
| 
						 | 
					@ -16,6 +17,7 @@ import "./StackedRouter.css";
 | 
				
			||||||
import { animateSlideInFromRight, animateSlideOutToRight } from "./anim";
 | 
					import { animateSlideInFromRight, animateSlideOutToRight } from "./anim";
 | 
				
			||||||
import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme";
 | 
					import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme";
 | 
				
			||||||
import { makeEventListener } from "@solid-primitives/event-listener";
 | 
					import { makeEventListener } from "@solid-primitives/event-listener";
 | 
				
			||||||
 | 
					import { useWindowSize } from "@solid-primitives/resize-observer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type StackedRouterProps = Omit<RouterProps, "url">;
 | 
					export type StackedRouterProps = Omit<RouterProps, "url">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -203,9 +205,46 @@ function serializableStack(stack: readonly StackFrame[]) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The router that stacks the pages.
 | 
					 * The router that stacks the pages.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * **Routes** The router accepts the {@link RouteProps} excluding the "url" field.
 | 
				
			||||||
 | 
					 * You can seamlessly use the `<Route />` 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:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * - `<A />` 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<StackedRouterProps> = (oprops) => {
 | 
					const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
				
			||||||
  const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" });
 | 
					  const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" });
 | 
				
			||||||
 | 
					  const windowSize = useWindowSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const pushFrame = (path: string, opts?: Readonly<NewFrameOptions<any>>) =>
 | 
					  const pushFrame = (path: string, opts?: Readonly<NewFrameOptions<any>>) =>
 | 
				
			||||||
    untrack(() => {
 | 
					    untrack(() => {
 | 
				
			||||||
| 
						 | 
					@ -293,6 +332,39 @@ const StackedRouter: Component<StackedRouterProps> = (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 reenterableAnimation: Animation | undefined;
 | 
				
			||||||
  let origWidth = 0,
 | 
					  let origWidth = 0,
 | 
				
			||||||
    origFigX = 0,
 | 
					    origFigX = 0,
 | 
				
			||||||
| 
						 | 
					@ -435,6 +507,7 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
				
			||||||
                  on:touchend={onDialogTouchEnd}
 | 
					                  on:touchend={onDialogTouchEnd}
 | 
				
			||||||
                  on:touchcancel={onDialogTouchCancel}
 | 
					                  on:touchcancel={onDialogTouchCancel}
 | 
				
			||||||
                  id={frame().rootId}
 | 
					                  id={frame().rootId}
 | 
				
			||||||
 | 
					                  style={subInsets()}
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                  <StaticRouter url={frame().path} {...oprops} />
 | 
					                  <StaticRouter url={frame().path} {...oprops} />
 | 
				
			||||||
                </dialog>
 | 
					                </dialog>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue