diff --git a/src/App.tsx b/src/App.tsx
index 69249b3..cd6fe4f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -26,6 +26,8 @@ const TimelineHome = lazy(() => import("./timelines/Home.js"));
const Settings = lazy(() => import("./settings/Settings.js"));
const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js"));
const MotionSettings = lazy(() => import("./settings/Motions.js"));
+const LanguageSettings = lazy(() => import("./settings/Language.js"));
+const RegionSettings = lazy(() => import("./settings/Region.jsx"));
const Routing: Component = () => {
return (
@@ -34,6 +36,8 @@ const Routing: Component = () => {
+
+
diff --git a/src/material/BottomSheet.module.css b/src/material/BottomSheet.module.css
index 8f716e9..410858b 100644
--- a/src/material/BottomSheet.module.css
+++ b/src/material/BottomSheet.module.css
@@ -43,7 +43,7 @@
&.animated {
position: absolute;
- transform: none;
+ transform: translateY(-50%);
overflow: hidden;
will-change: width, height, top, left;
@@ -54,6 +54,12 @@
& * {
overflow: hidden;
}
+
+ @media (max-width: 560px) {
+ & {
+ transform: none;
+ }
+ }
}
&.bottom {
diff --git a/src/material/BottomSheet.tsx b/src/material/BottomSheet.tsx
index 92eabc9..e753e20 100644
--- a/src/material/BottomSheet.tsx
+++ b/src/material/BottomSheet.tsx
@@ -36,7 +36,7 @@ function composeAnimationFrame(
};
}
-const MOVE_SPEED = 1400; // 1400px/s, bottom sheet is big and a bit heavier than small papers
+const MOVE_SPEED = 1200;
const BottomSheet: ParentComponent = (props) => {
let element: HTMLDialogElement;
@@ -62,13 +62,17 @@ const BottomSheet: ParentComponent = (props) => {
});
const onClose = () => {
- hero()!.style.visibility = 'unset'
+ const srcElement = hero();
+ if (srcElement) {
+ srcElement.style.visibility = "unset";
+ }
+
element.close();
setHero();
};
const animatedClose = () => {
- const srcElement = hero()
+ const srcElement = hero();
const endRect = srcElement?.getBoundingClientRect();
if (endRect) {
const startRect = element.getBoundingClientRect();
@@ -76,19 +80,94 @@ const BottomSheet: ParentComponent = (props) => {
animation.addEventListener("finish", onClose);
animation.addEventListener("cancel", onClose);
} else {
- element.close();
- setHero();
+ if (window.innerWidth > 560 && !props.bottomUp) {
+ onClose();
+ return;
+ }
+ const animation = props.bottomUp
+ ? animateSlideInFromBottom(element, true)
+ : animateSlideInFromRight(element, true);
+ animation.addEventListener("finish", onClose);
+ animation.addEventListener("cancel", onClose);
}
};
const animatedOpen = () => {
element.showModal();
- const srcElement = hero()
+ const srcElement = hero();
const startRect = srcElement?.getBoundingClientRect();
- if (!startRect) return;
- srcElement!.style.visibility = 'hidden'
- const endRect = element.getBoundingClientRect();
- animateHero(startRect, endRect, element);
+ if (startRect) {
+ srcElement!.style.visibility = "hidden";
+ const endRect = element.getBoundingClientRect();
+ animateHero(startRect, endRect, element);
+ } else if (props.bottomUp) {
+ animateSlideInFromBottom(element);
+ } else if (window.innerWidth <= 560) {
+ animateSlideInFromRight(element);
+ }
+ };
+
+ const animateSlideInFromRight = (element: HTMLElement, reserve?: boolean) => {
+ const rect = element.getBoundingClientRect();
+ const easing = "cubic-bezier(0.4, 0, 0.2, 1)";
+ element.classList.add(styles.animated);
+ const oldOverflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ const distance = Math.abs(rect.left - window.innerWidth);
+ const duration = (distance / MOVE_SPEED) * 1000;
+
+ animation = element.animate(
+ {
+ top: [rect.top, rect.top],
+ left: reserve
+ ? [`${rect.left}px`, `${window.innerWidth}px`]
+ : [`${window.innerWidth}px`, `${rect.left}px`],
+ width: [rect.width, rect.width],
+ height: [rect.height, rect.height],
+ },
+ { easing, duration },
+ );
+ const onAnimationEnd = () => {
+ element.classList.remove(styles.animated);
+ document.body.style.overflow = oldOverflow;
+ animation = undefined;
+ };
+ animation.addEventListener("cancel", onAnimationEnd);
+ animation.addEventListener("finish", onAnimationEnd);
+ return animation;
+ };
+
+ const animateSlideInFromBottom = (
+ element: HTMLElement,
+ reserve?: boolean,
+ ) => {
+ const rect = element.getBoundingClientRect();
+ const easing = "cubic-bezier(0.4, 0, 0.2, 1)";
+ element.classList.add(styles.animated);
+ const oldOverflow = document.body.style.overflow;
+ document.body.style.overflow = "hidden";
+ const distance = Math.abs(rect.top - window.innerHeight);
+ const duration = (distance / MOVE_SPEED) * 1000;
+
+ animation = element.animate(
+ {
+ left: [rect.left, rect.left],
+ top: reserve
+ ? [`${rect.top}px`, `${window.innerHeight}px`]
+ : [`${window.innerHeight}px`, `${rect.top}px`],
+ width: [rect.width, rect.width],
+ height: [rect.height, rect.height],
+ },
+ { easing, duration },
+ );
+ const onAnimationEnd = () => {
+ element.classList.remove(styles.animated);
+ document.body.style.overflow = oldOverflow;
+ animation = undefined;
+ };
+ animation.addEventListener("cancel", onAnimationEnd);
+ animation.addEventListener("finish", onAnimationEnd);
+ return animation;
};
const animateHero = (
diff --git a/src/settings/ChooseLang.tsx b/src/settings/Language.tsx
similarity index 73%
rename from src/settings/ChooseLang.tsx
rename to src/settings/Language.tsx
index ac03c7b..f04d4b1 100644
--- a/src/settings/ChooseLang.tsx
+++ b/src/settings/Language.tsx
@@ -13,7 +13,7 @@ import {
Switch,
Toolbar,
} from "@suid/material";
-import { Close as CloseIcon } from "@suid/icons-material";
+import { ArrowBack } from "@suid/icons-material";
import iso639_1 from "iso-639-1";
import {
autoMatchLangTag,
@@ -22,14 +22,12 @@ import {
} from "../platform/i18n";
import { Title } from "../material/typography";
import type { Template } from "@solid-primitives/i18n";
+import { useStore } from "@nanostores/solid";
+import { $settings } from "./stores";
+import { useNavigate } from "@solidjs/router";
-type ChooseLangProps = {
- code?: string;
- onCodeChange: (ncode?: string) => void;
- onClose?: JSX.EventHandlerUnion;
-};
-
-const ChooseLang: Component = (props) => {
+const ChooseLang: Component = () => {
+ const navigate = useNavigate()
const [t] = createTranslator(
() => import("./i18n/lang-names.json"),
(code) =>
@@ -39,6 +37,9 @@ const ChooseLang: Component = (props) => {
};
}>,
);
+ const settings = useStore($settings)
+
+ const code = () => settings().language
const unsupportedLangCodes = createMemo(() => {
return iso639_1.getAllCodes().filter((x) => !["zh", "en"].includes(x));
@@ -46,6 +47,10 @@ const ChooseLang: Component = (props) => {
const matchedLangCode = createMemo(() => autoMatchLangTag());
+ const onCodeChange = (code?: string) => {
+ $settings.setKey("language", code)
+ }
+
return (
= (props) => {
variant="dense"
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
>
-
-
+
+
{t("Choose Language")}
@@ -69,7 +74,7 @@ const ChooseLang: Component = (props) => {
>
{
- props.onCodeChange(props.code ? undefined : matchedLangCode());
+ onCodeChange(code() ? undefined : matchedLangCode());
}}
>
@@ -78,20 +83,20 @@ const ChooseLang: Component = (props) => {
})}
-
+
{t("Supported")}}>
- {(code) => (
+ {(c) => (
- {t(`lang.${code}`)}
+ {t(`lang.${c}`)}
diff --git a/src/settings/Motions.tsx b/src/settings/Motions.tsx
index 2f111fd..a90bd44 100644
--- a/src/settings/Motions.tsx
+++ b/src/settings/Motions.tsx
@@ -14,7 +14,7 @@ import {
} from "@suid/material";
import { Title } from "../material/typography";
import { useNavigate } from "@solidjs/router";
-import { Close as CloseIcon } from "@suid/icons-material";
+import { ArrowBack } from "@suid/icons-material";
import { createTranslator } from "../platform/i18n";
import { useStore } from "@nanostores/solid";
import { $settings } from "./stores";
@@ -37,7 +37,7 @@ const Motions: Component = () => {
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
>
-
+
{t("motions")}
diff --git a/src/settings/ChooseRegion.tsx b/src/settings/Region.tsx
similarity index 66%
rename from src/settings/ChooseRegion.tsx
rename to src/settings/Region.tsx
index 77a969e..1ae1a62 100644
--- a/src/settings/ChooseRegion.tsx
+++ b/src/settings/Region.tsx
@@ -12,8 +12,7 @@ import {
Switch,
Toolbar,
} from "@suid/material";
-import { Close as CloseIcon } from "@suid/icons-material";
-import iso639_1 from "iso-639-1";
+import { ArrowBack } from "@suid/icons-material";
import {
autoMatchRegion,
createTranslator,
@@ -21,14 +20,12 @@ import {
} from "../platform/i18n";
import { Title } from "../material/typography";
import type { Template } from "@solid-primitives/i18n";
+import { useNavigate } from "@solidjs/router";
+import { $settings } from "./stores";
+import { useStore } from "@nanostores/solid";
-type ChooseRegionProps = {
- code?: string;
- onCodeChange: (ncode?: string) => void;
- onClose?: JSX.EventHandlerUnion;
-};
-
-const ChooseRegion: Component = (props) => {
+const ChooseRegion: Component = () => {
+ const navigate = useNavigate();
const [t] = createTranslator(
() => import("./i18n/lang-names.json"),
(code) =>
@@ -39,12 +36,16 @@ const ChooseRegion: Component = (props) => {
}>,
);
- const unsupportedLangCodes = createMemo(() => {
- return iso639_1.getAllCodes().filter((x) => !["zh", "en"].includes(x));
- });
+ const settings = useStore($settings);
+
+ const region = () => settings().region;
const matchedRegionCode = createMemo(() => autoMatchRegion());
+ const onCodeChange = (code?: string) => {
+ $settings.setKey("region", code);
+ };
+
return (
= (props) => {
variant="dense"
sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
>
-
-
+
+
{t("Choose Language")}
@@ -68,16 +69,17 @@ const ChooseRegion: Component = (props) => {
>
{
- props.onCodeChange(props.code ? undefined : matchedRegionCode());
+ onCodeChange(region() ? undefined : matchedRegionCode());
}}
>
{t("region.auto", {
- detected: t(`region.${matchedRegionCode()}`) ?? matchedRegionCode(),
+ detected:
+ t(`region.${matchedRegionCode()}`) ?? matchedRegionCode(),
})}
-
+
@@ -85,13 +87,16 @@ const ChooseRegion: Component = (props) => {
{(code) => (
{t(`region.${code}`)}
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index e27443a..826fea9 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -41,15 +41,10 @@ import {
autoMatchLangTag,
autoMatchRegion,
createTranslator,
- SUPPORTED_LANGS,
- SUPPORTED_REGIONS,
useDateFnLocale,
} from "../platform/i18n.jsx";
import { type Template } from "@solid-primitives/i18n";
import BottomSheet from "../material/BottomSheet.jsx";
-import ChooseLang from "./ChooseLang.jsx";
-import ChooseRegion from "./ChooseRegion.jsx";
-import { Portal } from "solid-js/web";
type Strings = {
["lang.auto"]: Template<{ detected: string }>;
@@ -69,8 +64,6 @@ const Settings: ParentComponent = (props) => {
needRefresh: [needRefresh],
} = useRegisterSW();
const dateFnLocale = useDateFnLocale();
- const [langPickerOpen, setLangPickerOpen] = createSignal(false);
- const [regionPickerOpen, setRegionPickerOpen] = createSignal(false);
const [profiles] = useSignedInProfiles();
@@ -175,7 +168,7 @@ const Settings: ParentComponent = (props) => {
{t("This Application")}
-
+
@@ -192,15 +185,8 @@ const Settings: ParentComponent = (props) => {
{t("Language")}
-
- $settings.setKey("language", nval)}
- onClose={[setLangPickerOpen, false]}
- />
-
-
+
@@ -217,13 +203,6 @@ const Settings: ParentComponent = (props) => {
{t("Region")}
-
- $settings.setKey("region", nval)}
- onClose={[setRegionPickerOpen, false]}
- />
-