StackedRouter: fixing browser backwards
* Supports hot reload
This commit is contained in:
		
							parent
							
								
									62a80ddce2
								
							
						
					
					
						commit
						fe39675d0c
					
				
					 1 changed files with 101 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -10,6 +10,7 @@ import {
 | 
			
		|||
  Show,
 | 
			
		||||
  untrack,
 | 
			
		||||
  useContext,
 | 
			
		||||
  onCleanup,
 | 
			
		||||
  type Accessor,
 | 
			
		||||
} from "solid-js";
 | 
			
		||||
import { createStore, unwrap } from "solid-js/store";
 | 
			
		||||
| 
						 | 
				
			
			@ -374,6 +375,30 @@ function createManagedSwipeToBack(
 | 
			
		|||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function animateUntil(
 | 
			
		||||
  stepfn: (onCreated: (animation: Animation) => void) => void,
 | 
			
		||||
) {
 | 
			
		||||
  const execStep = () => {
 | 
			
		||||
    requestAnimationFrame(() => {
 | 
			
		||||
      stepfn((step) => {
 | 
			
		||||
        step.addEventListener("finish", () => {
 | 
			
		||||
          execStep();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  execStep();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The cache key of saved stack for hot reload.
 | 
			
		||||
 *
 | 
			
		||||
 * We could not use symbols because every time the hot reload the `Symbol()`
 | 
			
		||||
 * call creates a new symbol.
 | 
			
		||||
 */
 | 
			
		||||
const $StackedRouterSavedStack = "$StackedRouterSavedStack";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The router that stacks the pages.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -417,6 +442,29 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
			
		|||
  const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" });
 | 
			
		||||
  const windowSize = useWindowSize();
 | 
			
		||||
 | 
			
		||||
  if (import.meta.hot) {
 | 
			
		||||
    const saveStack = () => {
 | 
			
		||||
      import.meta.hot!.data[$StackedRouterSavedStack] = unwrap(stack);
 | 
			
		||||
      console.debug("stack saved");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    import.meta.hot.on("vite:beforeUpdate", saveStack);
 | 
			
		||||
    onCleanup(() => import.meta.hot!.off("vite:beforeUpdate", saveStack));
 | 
			
		||||
 | 
			
		||||
    const loadStack = () => {
 | 
			
		||||
      const savedStack = import.meta.hot!.data[$StackedRouterSavedStack];
 | 
			
		||||
      if (savedStack) {
 | 
			
		||||
        mutStack(savedStack);
 | 
			
		||||
        console.debug("stack loaded");
 | 
			
		||||
      }
 | 
			
		||||
      delete import.meta.hot!.data[$StackedRouterSavedStack];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    createRenderEffect(() => {
 | 
			
		||||
      loadStack()
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const pushFrame = (path: string, opts?: Readonly<NewFrameOptions<any>>) =>
 | 
			
		||||
    untrack(() => {
 | 
			
		||||
      const frame = {
 | 
			
		||||
| 
						 | 
				
			
			@ -444,11 +492,35 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
			
		|||
      return frame;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  const onlyPopFrame = (depth: number) => {
 | 
			
		||||
  const onlyPopFrameOnStack = (depth: number) => {
 | 
			
		||||
    mutStack((o) => o.toSpliced(o.length - depth, depth));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onlyPopFrame = (depth: number) => {
 | 
			
		||||
    onlyPopFrameOnStack(depth);
 | 
			
		||||
    window.history.go(-depth);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const animatePopOneFrame = (onCreated: (animation: Animation) => void) => {
 | 
			
		||||
    const lastFrame = stack[stack.length - 1];
 | 
			
		||||
    const element = document.getElementById(
 | 
			
		||||
      lastFrame.rootId,
 | 
			
		||||
    )! as HTMLDialogElement;
 | 
			
		||||
    const createAnimation = lastFrame.animateClose ?? animateClose;
 | 
			
		||||
    element.classList.add("animating");
 | 
			
		||||
 | 
			
		||||
    const onNavAnimEnd = () => {
 | 
			
		||||
      element.classList.remove("animating");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    requestAnimationFrame(() => {
 | 
			
		||||
      const animation = createAnimation(element);
 | 
			
		||||
      animation.addEventListener("finish", onNavAnimEnd);
 | 
			
		||||
      animation.addEventListener("cancel", onNavAnimEnd);
 | 
			
		||||
      onCreated(animation);
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const popFrame = (depth: number = 1) =>
 | 
			
		||||
    untrack(() => {
 | 
			
		||||
      if (import.meta.env.DEV) {
 | 
			
		||||
| 
						 | 
				
			
			@ -456,30 +528,27 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
			
		|||
          console.warn("the depth to pop should not < 0, now is", depth);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (stack.length > 1) {
 | 
			
		||||
        const lastFrame = stack[stack.length - 1];
 | 
			
		||||
        const element = document.getElementById(
 | 
			
		||||
          lastFrame.rootId,
 | 
			
		||||
        )! as HTMLDialogElement;
 | 
			
		||||
        const createAnimation = lastFrame.animateClose ?? animateClose;
 | 
			
		||||
        requestAnimationFrame(() => {
 | 
			
		||||
          element.classList.add("animating");
 | 
			
		||||
          const animation = createAnimation(element);
 | 
			
		||||
          animation.addEventListener("finish", () => {
 | 
			
		||||
            element.classList.remove("animating");
 | 
			
		||||
            onlyPopFrame(depth);
 | 
			
		||||
          });
 | 
			
		||||
        let count = depth;
 | 
			
		||||
        animateUntil((created) => {
 | 
			
		||||
          if (count > 0) {
 | 
			
		||||
            animatePopOneFrame((a) => {
 | 
			
		||||
              a.addEventListener("finish", () => onlyPopFrame(1));
 | 
			
		||||
              created(a);
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          count--;
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        onlyPopFrame(depth);
 | 
			
		||||
        onlyPopFrame(1);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  createRenderEffect(() => {
 | 
			
		||||
    if (stack.length === 0) {
 | 
			
		||||
      mutStack(0, {
 | 
			
		||||
        path: window.location.pathname,
 | 
			
		||||
        rootId: createUniqueId(),
 | 
			
		||||
      pushFrame(window.location.pathname, {
 | 
			
		||||
        replace: "all",
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -488,10 +557,23 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
			
		|||
    makeEventListener(window, "popstate", (event) => {
 | 
			
		||||
      if (!event.state) return;
 | 
			
		||||
 | 
			
		||||
      // TODO: verify the stack in state and handling forwards
 | 
			
		||||
 | 
			
		||||
      if (stack.length === 0) {
 | 
			
		||||
        mutStack(event.state);
 | 
			
		||||
        mutStack(event.state || []);
 | 
			
		||||
      } else if (stack.length > event.state.length) {
 | 
			
		||||
        popFrame(stack.length - event.state.length);
 | 
			
		||||
        let count = stack.length - event.state.length;
 | 
			
		||||
        animateUntil((created) => {
 | 
			
		||||
          if (count > 0) {
 | 
			
		||||
            animatePopOneFrame((a) => {
 | 
			
		||||
              a.addEventListener("finish", () => {
 | 
			
		||||
                onlyPopFrameOnStack(1);
 | 
			
		||||
                created(a);
 | 
			
		||||
              });
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
          count--;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue