diff --git a/src/App.tsx b/src/App.tsx index 5fdb0c8..8f7ba44 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -46,10 +46,10 @@ const Routing: Component = () => { return ( - - + + diff --git a/src/platform/A.tsx b/src/platform/A.tsx index b655dc9..3d0b33d 100644 --- a/src/platform/A.tsx +++ b/src/platform/A.tsx @@ -1,5 +1,6 @@ -import { type JSX } from "solid-js"; +import { splitProps, type JSX } from "solid-js"; import { useNavigator } from "./StackedRouter"; +import { useResolvedPath } from "@solidjs/router"; function handleClick( push: (name: string, state: unknown) => void, @@ -7,13 +8,14 @@ function handleClick( ) { const target = event.currentTarget; event.preventDefault(); - event.stopPropagation(); push(target.href, { state: target.getAttribute("state") || undefined }); } -const A = (oprops: JSX.HTMLElementTags["a"]) => { +const A = (oprops: Omit) => { + const [props, rest] = splitProps(oprops, ["href"]); + const resolvedPath = useResolvedPath(() => props.href || "#"); const { push } = useNavigator(); - return ; + return ; }; export default A; diff --git a/src/platform/StackedRouter.css b/src/platform/StackedRouter.css index 714893c..5dae005 100644 --- a/src/platform/StackedRouter.css +++ b/src/platform/StackedRouter.css @@ -21,6 +21,7 @@ dialog.StackedPage { contain-intrinsic-size: auto 560px auto 100dvh; content-visibility: auto; + background: var(--tutu-color-surface); box-shadow: var(--tutu-shadow-e16); @media (min-width: 560px) { diff --git a/src/platform/StackedRouter.tsx b/src/platform/StackedRouter.tsx index 8d489ae..7325d7e 100644 --- a/src/platform/StackedRouter.tsx +++ b/src/platform/StackedRouter.tsx @@ -6,17 +6,18 @@ import { createRenderEffect, createUniqueId, Index, - onMount, Show, untrack, useContext, type Accessor, } from "solid-js"; import { createStore, unwrap } from "solid-js/store"; -import { insert, render } from "solid-js/web"; import "./StackedRouter.css"; -import { animateSlideInFromRight } from "./anim"; +import { animateSlideInFromRight, animateSlideOutToRight } from "./anim"; import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme"; +import { + makeEventListener, +} from "@solid-primitives/event-listener"; export type StackedRouterProps = Omit; @@ -24,7 +25,6 @@ export type StackFrame = { path: string; rootId: string; state: unknown; - beforeShow?: (element: HTMLElement) => void; }; export type NewFrameOptions = (T extends undefined @@ -102,6 +102,32 @@ function onDialogClick( } } +function animateClose(element: HTMLElement) { + if (window.innerWidth <= 560) { + return animateSlideOutToRight(element, { easing: ANIM_CURVE_DECELERATION }); + } else { + return element.animate( + { + opacity: [0.5, 0], + }, + { easing: ANIM_CURVE_STD, duration: 220 }, + ); + } +} + +function animateOpen(element: HTMLElement) { + if (window.innerWidth <= 560) { + animateSlideInFromRight(element, { easing: ANIM_CURVE_DECELERATION }); + } else { + element.animate( + { + opacity: [0.5, 1], + }, + { easing: ANIM_CURVE_STD, duration: 220 }, + ); + } +} + /** * The router that stacks the pages. */ @@ -115,10 +141,21 @@ const StackedRouter: Component = (oprops) => { state: opts?.state, rootId: createUniqueId(), }; + mutStack(opts?.replace ? stack.length - 1 : stack.length, frame); + if (opts?.replace) { + window.history.replaceState(unwrap(stack), "", path); + } else { + window.history.pushState(unwrap(stack), "", path); + } return frame; }); + const onlyPopFrame = (depth: number) => { + mutStack((o) => o.toSpliced(o.length - depth, depth)); + window.history.go(-depth); + }; + const popFrame = (depth: number = 1) => untrack(() => { if (import.meta.env.DEV) { @@ -130,20 +167,11 @@ const StackedRouter: Component = (oprops) => { const lastFrame = stack[stack.length - 1]; const element = document.getElementById(lastFrame.rootId)!; requestAnimationFrame(() => { - const animation = element.animate( - { - opacity: [0.5, 0], - }, - { easing: ANIM_CURVE_STD, duration: 220 }, - ); - animation.addEventListener("finish", () => - mutStack((o) => o.toSpliced(o.length - depth, depth)), - ); + const animation = animateClose(element); + animation.addEventListener("finish", () => onlyPopFrame(depth)); }); } else { - mutStack((o) => { - return o.toSpliced(o.length - depth, depth); - }); + onlyPopFrame(depth); } }); @@ -154,24 +182,23 @@ const StackedRouter: Component = (oprops) => { createRenderEffect(() => { if (stack.length === 0) { - pushFrame("/", undefined); + pushFrame(window.location.pathname); } }); + createRenderEffect(() => { + makeEventListener(window, "popstate", (event) => { + if (event.state && stack.length !== event.state.length) { + mutStack(event.state); + } + }); + }); + const onBeforeDialogMount = (element: HTMLDialogElement) => { createEffect(() => { requestAnimationFrame(() => { element.showModal(); - if (window.innerWidth <= 560) { - animateSlideInFromRight(element, { easing: ANIM_CURVE_DECELERATION }); - } else { - element.animate( - { - opacity: [0.5, 1], - }, - { easing: ANIM_CURVE_STD, duration: 220 }, - ); - } + animateOpen(element); }); }); }; diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 0aec49c..2351f8d 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -201,7 +201,7 @@ const Settings: ParentComponent = (props) => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - + {t("Settings")} diff --git a/src/timelines/ProfileMenuButton.tsx b/src/timelines/ProfileMenuButton.tsx index c33ba8e..b74386c 100644 --- a/src/timelines/ProfileMenuButton.tsx +++ b/src/timelines/ProfileMenuButton.tsx @@ -51,7 +51,7 @@ const ProfileMenuButton: ParentComponent<{ props.onClick?.(); }; - const inf = () => props.profile?.account.inf + const inf = () => props.profile?.account.inf; const onClose = () => { props.onClick?.(); @@ -130,7 +130,7 @@ const ProfileMenuButton: ParentComponent<{ {props.children} - +