Compare commits
7 commits
0710aaf4f3
...
63fe4acc98
Author | SHA1 | Date | |
---|---|---|---|
|
63fe4acc98 | ||
|
32dffaaa3d | ||
|
5be56bb80e | ||
|
e3d9d0c4ba | ||
|
2da7bf134e | ||
|
ab37a280e7 | ||
|
9dfcfa3868 |
6 changed files with 66 additions and 36 deletions
|
@ -46,10 +46,10 @@ const Routing: Component = () => {
|
||||||
return (
|
return (
|
||||||
<StackedRouter>
|
<StackedRouter>
|
||||||
<Route path="/" component={TimelineHome} />
|
<Route path="/" component={TimelineHome} />
|
||||||
<Route path="/settings" component={Settings} />
|
|
||||||
<Route path="/settings/language" component={LanguageSettings} />
|
<Route path="/settings/language" component={LanguageSettings} />
|
||||||
<Route path="/settings/region" component={RegionSettings} />
|
<Route path="/settings/region" component={RegionSettings} />
|
||||||
<Route path="/motions" component={MotionSettings} />
|
<Route path="/settings/motions" component={MotionSettings} />
|
||||||
|
<Route path="/settings" component={Settings} />
|
||||||
<Route path="/:acct/toot/:id" component={TootBottomSheet} />
|
<Route path="/:acct/toot/:id" component={TootBottomSheet} />
|
||||||
<Route path="/:acct/profile/:id" component={Profile} />
|
<Route path="/:acct/profile/:id" component={Profile} />
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { type JSX } from "solid-js";
|
import { splitProps, type JSX } from "solid-js";
|
||||||
import { useNavigator } from "./StackedRouter";
|
import { useNavigator } from "./StackedRouter";
|
||||||
|
import { useResolvedPath } from "@solidjs/router";
|
||||||
|
|
||||||
function handleClick(
|
function handleClick(
|
||||||
push: (name: string, state: unknown) => void,
|
push: (name: string, state: unknown) => void,
|
||||||
|
@ -7,13 +8,14 @@ function handleClick(
|
||||||
) {
|
) {
|
||||||
const target = event.currentTarget;
|
const target = event.currentTarget;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
|
||||||
push(target.href, { state: target.getAttribute("state") || undefined });
|
push(target.href, { state: target.getAttribute("state") || undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
const A = (oprops: JSX.HTMLElementTags["a"]) => {
|
const A = (oprops: Omit<JSX.HTMLElementTags["a"], "onClick" | "onclick">) => {
|
||||||
|
const [props, rest] = splitProps(oprops, ["href"]);
|
||||||
|
const resolvedPath = useResolvedPath(() => props.href || "#");
|
||||||
const { push } = useNavigator();
|
const { push } = useNavigator();
|
||||||
return <a onClick={[handleClick, push]} {...oprops}></a>;
|
return <a onClick={[handleClick, push]} href={resolvedPath()} {...rest}></a>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default A;
|
export default A;
|
||||||
|
|
|
@ -21,6 +21,7 @@ dialog.StackedPage {
|
||||||
contain-intrinsic-size: auto 560px auto 100dvh;
|
contain-intrinsic-size: auto 560px auto 100dvh;
|
||||||
content-visibility: auto;
|
content-visibility: auto;
|
||||||
|
|
||||||
|
background: var(--tutu-color-surface);
|
||||||
box-shadow: var(--tutu-shadow-e16);
|
box-shadow: var(--tutu-shadow-e16);
|
||||||
|
|
||||||
@media (min-width: 560px) {
|
@media (min-width: 560px) {
|
||||||
|
|
|
@ -6,17 +6,18 @@ import {
|
||||||
createRenderEffect,
|
createRenderEffect,
|
||||||
createUniqueId,
|
createUniqueId,
|
||||||
Index,
|
Index,
|
||||||
onMount,
|
|
||||||
Show,
|
Show,
|
||||||
untrack,
|
untrack,
|
||||||
useContext,
|
useContext,
|
||||||
type Accessor,
|
type Accessor,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { createStore, unwrap } from "solid-js/store";
|
import { createStore, unwrap } from "solid-js/store";
|
||||||
import { insert, render } from "solid-js/web";
|
|
||||||
import "./StackedRouter.css";
|
import "./StackedRouter.css";
|
||||||
import { animateSlideInFromRight } 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";
|
||||||
|
|
||||||
export type StackedRouterProps = Omit<RouterProps, "url">;
|
export type StackedRouterProps = Omit<RouterProps, "url">;
|
||||||
|
|
||||||
|
@ -24,7 +25,6 @@ export type StackFrame = {
|
||||||
path: string;
|
path: string;
|
||||||
rootId: string;
|
rootId: string;
|
||||||
state: unknown;
|
state: unknown;
|
||||||
beforeShow?: (element: HTMLElement) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NewFrameOptions<T> = (T extends undefined
|
export type NewFrameOptions<T> = (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.
|
* The router that stacks the pages.
|
||||||
*/
|
*/
|
||||||
|
@ -115,10 +141,21 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
|
||||||
state: opts?.state,
|
state: opts?.state,
|
||||||
rootId: createUniqueId(),
|
rootId: createUniqueId(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mutStack(opts?.replace ? stack.length - 1 : stack.length, frame);
|
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;
|
return frame;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onlyPopFrame = (depth: number) => {
|
||||||
|
mutStack((o) => o.toSpliced(o.length - depth, depth));
|
||||||
|
window.history.go(-depth);
|
||||||
|
};
|
||||||
|
|
||||||
const popFrame = (depth: number = 1) =>
|
const popFrame = (depth: number = 1) =>
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
|
@ -130,20 +167,11 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
|
||||||
const lastFrame = stack[stack.length - 1];
|
const lastFrame = stack[stack.length - 1];
|
||||||
const element = document.getElementById(lastFrame.rootId)!;
|
const element = document.getElementById(lastFrame.rootId)!;
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const animation = element.animate(
|
const animation = animateClose(element);
|
||||||
{
|
animation.addEventListener("finish", () => onlyPopFrame(depth));
|
||||||
opacity: [0.5, 0],
|
|
||||||
},
|
|
||||||
{ easing: ANIM_CURVE_STD, duration: 220 },
|
|
||||||
);
|
|
||||||
animation.addEventListener("finish", () =>
|
|
||||||
mutStack((o) => o.toSpliced(o.length - depth, depth)),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
mutStack((o) => {
|
onlyPopFrame(depth);
|
||||||
return o.toSpliced(o.length - depth, depth);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -154,24 +182,23 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
|
||||||
|
|
||||||
createRenderEffect(() => {
|
createRenderEffect(() => {
|
||||||
if (stack.length === 0) {
|
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) => {
|
const onBeforeDialogMount = (element: HTMLDialogElement) => {
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
element.showModal();
|
element.showModal();
|
||||||
if (window.innerWidth <= 560) {
|
animateOpen(element);
|
||||||
animateSlideInFromRight(element, { easing: ANIM_CURVE_DECELERATION });
|
|
||||||
} else {
|
|
||||||
element.animate(
|
|
||||||
{
|
|
||||||
opacity: [0.5, 1],
|
|
||||||
},
|
|
||||||
{ easing: ANIM_CURVE_STD, duration: 220 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -201,7 +201,7 @@ const Settings: ParentComponent = (props) => {
|
||||||
variant="dense"
|
variant="dense"
|
||||||
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
|
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
|
||||||
>
|
>
|
||||||
<IconButton color="inherit" onClick={[pop, 11]} disableRipple>
|
<IconButton color="inherit" onClick={[pop, 1]} disableRipple>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Title>{t("Settings")}</Title>
|
<Title>{t("Settings")}</Title>
|
||||||
|
|
|
@ -51,7 +51,7 @@ const ProfileMenuButton: ParentComponent<{
|
||||||
props.onClick?.();
|
props.onClick?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
const inf = () => props.profile?.account.inf
|
const inf = () => props.profile?.account.inf;
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
props.onClick?.();
|
props.onClick?.();
|
||||||
|
@ -130,7 +130,7 @@ const ProfileMenuButton: ParentComponent<{
|
||||||
{props.children}
|
{props.children}
|
||||||
<Divider />
|
<Divider />
|
||||||
</Show>
|
</Show>
|
||||||
<MenuItem component={A} href="/settings" onClick={onClose}>
|
<MenuItem component={A} href="/settings">
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
|
|
Loading…
Reference in a new issue