StackedRouter: hero animation support
This commit is contained in:
		
							parent
							
								
									63fe4acc98
								
							
						
					
					
						commit
						1641f3e75b
					
				
					 2 changed files with 54 additions and 18 deletions
				
			
		| 
						 | 
					@ -24,12 +24,8 @@ dialog.StackedPage {
 | 
				
			||||||
  background: var(--tutu-color-surface);
 | 
					  background: var(--tutu-color-surface);
 | 
				
			||||||
  box-shadow: var(--tutu-shadow-e16);
 | 
					  box-shadow: var(--tutu-shadow-e16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @media (min-width: 560px) {
 | 
					  margin-left: auto;
 | 
				
			||||||
    & {
 | 
					  margin-right: auto;
 | 
				
			||||||
      left: 50%;
 | 
					 | 
				
			||||||
      transform: translateX(-50%);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @media (max-width: 560px) {
 | 
					  @media (max-width: 560px) {
 | 
				
			||||||
    & {
 | 
					    & {
 | 
				
			||||||
| 
						 | 
					@ -50,4 +46,12 @@ dialog.StackedPage {
 | 
				
			||||||
  &::backdrop {
 | 
					  &::backdrop {
 | 
				
			||||||
    background: none;
 | 
					    background: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.animating {
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * {
 | 
				
			||||||
 | 
					      overflow: hidden;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import {
 | 
				
			||||||
  createRenderEffect,
 | 
					  createRenderEffect,
 | 
				
			||||||
  createUniqueId,
 | 
					  createUniqueId,
 | 
				
			||||||
  Index,
 | 
					  Index,
 | 
				
			||||||
 | 
					  onMount,
 | 
				
			||||||
  Show,
 | 
					  Show,
 | 
				
			||||||
  untrack,
 | 
					  untrack,
 | 
				
			||||||
  useContext,
 | 
					  useContext,
 | 
				
			||||||
| 
						 | 
					@ -15,9 +16,7 @@ import { createStore, unwrap } from "solid-js/store";
 | 
				
			||||||
import "./StackedRouter.css";
 | 
					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 {
 | 
					import { makeEventListener } from "@solid-primitives/event-listener";
 | 
				
			||||||
  makeEventListener,
 | 
					 | 
				
			||||||
} from "@solid-primitives/event-listener";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type StackedRouterProps = Omit<RouterProps, "url">;
 | 
					export type StackedRouterProps = Omit<RouterProps, "url">;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +24,9 @@ export type StackFrame = {
 | 
				
			||||||
  path: string;
 | 
					  path: string;
 | 
				
			||||||
  rootId: string;
 | 
					  rootId: string;
 | 
				
			||||||
  state: unknown;
 | 
					  state: unknown;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  animateOpen?: (element: HTMLElement) => Animation;
 | 
				
			||||||
 | 
					  animateClose?: (element: HTMLElement) => Animation;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type NewFrameOptions<T> = (T extends undefined
 | 
					export type NewFrameOptions<T> = (T extends undefined
 | 
				
			||||||
| 
						 | 
					@ -33,6 +35,8 @@ export type NewFrameOptions<T> = (T extends undefined
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  : { state: T }) & {
 | 
					  : { state: T }) & {
 | 
				
			||||||
  replace?: boolean;
 | 
					  replace?: boolean;
 | 
				
			||||||
 | 
					  animateOpen?: StackFrame["animateOpen"];
 | 
				
			||||||
 | 
					  animateClose?: StackFrame["animateClose"];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type FramePusher<T, K extends keyof T = keyof T> = T[K] extends
 | 
					export type FramePusher<T, K extends keyof T = keyof T> = T[K] extends
 | 
				
			||||||
| 
						 | 
					@ -117,9 +121,11 @@ function animateClose(element: HTMLElement) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function animateOpen(element: HTMLElement) {
 | 
					function animateOpen(element: HTMLElement) {
 | 
				
			||||||
  if (window.innerWidth <= 560) {
 | 
					  if (window.innerWidth <= 560) {
 | 
				
			||||||
    animateSlideInFromRight(element, { easing: ANIM_CURVE_DECELERATION });
 | 
					    return animateSlideInFromRight(element, {
 | 
				
			||||||
 | 
					      easing: ANIM_CURVE_DECELERATION,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    element.animate(
 | 
					    return element.animate(
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        opacity: [0.5, 1],
 | 
					        opacity: [0.5, 1],
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
| 
						 | 
					@ -128,6 +134,17 @@ function animateOpen(element: HTMLElement) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function serializableStack(stack: readonly StackFrame[]) {
 | 
				
			||||||
 | 
					  const frames = unwrap(stack);
 | 
				
			||||||
 | 
					  return frames.map((fr) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      path: fr.path,
 | 
				
			||||||
 | 
					      rootId: fr.rootId,
 | 
				
			||||||
 | 
					      state: fr.state,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The router that stacks the pages.
 | 
					 * The router that stacks the pages.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -140,13 +157,15 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
				
			||||||
        path,
 | 
					        path,
 | 
				
			||||||
        state: opts?.state,
 | 
					        state: opts?.state,
 | 
				
			||||||
        rootId: createUniqueId(),
 | 
					        rootId: createUniqueId(),
 | 
				
			||||||
 | 
					        animateOpen: opts?.animateOpen,
 | 
				
			||||||
 | 
					        animateClose: opts?.animateClose,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      mutStack(opts?.replace ? stack.length - 1 : stack.length, frame);
 | 
					      mutStack(opts?.replace ? stack.length - 1 : stack.length, frame);
 | 
				
			||||||
      if (opts?.replace) {
 | 
					      if (opts?.replace) {
 | 
				
			||||||
        window.history.replaceState(unwrap(stack), "", path);
 | 
					        window.history.replaceState(serializableStack(stack), "", path);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        window.history.pushState(unwrap(stack), "", path);
 | 
					        window.history.pushState(serializableStack(stack), "", path);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return frame;
 | 
					      return frame;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -165,10 +184,17 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (stack.length > 1) {
 | 
					      if (stack.length > 1) {
 | 
				
			||||||
        const lastFrame = stack[stack.length - 1];
 | 
					        const lastFrame = stack[stack.length - 1];
 | 
				
			||||||
        const element = document.getElementById(lastFrame.rootId)!;
 | 
					        const element = document.getElementById(
 | 
				
			||||||
 | 
					          lastFrame.rootId,
 | 
				
			||||||
 | 
					        )! as HTMLDialogElement;
 | 
				
			||||||
 | 
					        const createAnimation = lastFrame.animateClose ?? animateClose;
 | 
				
			||||||
        requestAnimationFrame(() => {
 | 
					        requestAnimationFrame(() => {
 | 
				
			||||||
          const animation = animateClose(element);
 | 
					          element.classList.add("animating")
 | 
				
			||||||
          animation.addEventListener("finish", () => onlyPopFrame(depth));
 | 
					          const animation = createAnimation(element);
 | 
				
			||||||
 | 
					          animation.addEventListener("finish", () => {
 | 
				
			||||||
 | 
					            element.classList.remove("animating")
 | 
				
			||||||
 | 
					            onlyPopFrame(depth);
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        onlyPopFrame(depth);
 | 
					        onlyPopFrame(depth);
 | 
				
			||||||
| 
						 | 
					@ -195,10 +221,16 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onBeforeDialogMount = (element: HTMLDialogElement) => {
 | 
					  const onBeforeDialogMount = (element: HTMLDialogElement) => {
 | 
				
			||||||
    createEffect(() => {
 | 
					    onMount(() => {
 | 
				
			||||||
 | 
					      const lastFr = untrack(() => stack[stack.length - 1]);
 | 
				
			||||||
 | 
					      const createAnimation = lastFr.animateOpen ?? animateOpen;
 | 
				
			||||||
      requestAnimationFrame(() => {
 | 
					      requestAnimationFrame(() => {
 | 
				
			||||||
        element.showModal();
 | 
					        element.showModal();
 | 
				
			||||||
        animateOpen(element);
 | 
					        element.classList.add("animating");
 | 
				
			||||||
 | 
					        const animation = createAnimation(element);
 | 
				
			||||||
 | 
					        animation.addEventListener("finish", () =>
 | 
				
			||||||
 | 
					          element.classList.remove("animating"),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue