From af7f1380bc8e9ca4032e1f7f5d19f8b2391d1380 Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Fri, 24 Apr 2026 18:30:35 +0100 Subject: [PATCH] fix: can't scroll misc tab on mobile --- DEVELOPMENT.md | 2 +- .../submit-form/mii-editor/index.tsx | 160 +++---- .../submit-form/mii-editor/tabs/misc.tsx | 426 +++++++++--------- 3 files changed, 294 insertions(+), 294 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 21a0250..149521f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -106,7 +106,7 @@ It's a good idea to build the project locally before submitting a pull request. $ pnpm --filter backend build $ pnpm --filter frontend build -# Run the built version (Vite likes to change the port when this happens, so you probably need to change both .env files) +# Run the built version (Note: Vite likes to change the port when this happens, so you probably need to change both .env files) $ pnpm --filter backend start $ pnpm --filter frontend build ``` diff --git a/frontend/src/components/submit-form/mii-editor/index.tsx b/frontend/src/components/submit-form/mii-editor/index.tsx index 9c2068b..d3471ad 100644 --- a/frontend/src/components/submit-form/mii-editor/index.tsx +++ b/frontend/src/components/submit-form/mii-editor/index.tsx @@ -1,80 +1,80 @@ -import { type SwitchMiiInstructions } from "@tomodachi-share/shared"; -import React, { useState } from "react"; -import { Icon } from "@iconify/react"; - -import HeadTab from "./tabs/head"; -import HairTab from "./tabs/hair"; -import EyebrowsTab from "./tabs/eyebrows"; -import EyesTab from "./tabs/eyes"; -import NoseTab from "./tabs/nose"; -import LipsTab from "./tabs/lips"; -import EarsTab from "./tabs/ears"; -import GlassesTab from "./tabs/glasses"; -import OtherTab from "./tabs/other"; -import MiscTab from "./tabs/misc"; - -interface Props { - instructions: React.RefObject; -} - -type Tab = "head" | "hair" | "eyebrows" | "eyes" | "nose" | "lips" | "ears" | "glasses" | "other" | "misc"; - -export const TAB_ICONS: Record = { - head: "mingcute:head-fill", - hair: "mingcute:hair-fill", - eyebrows: "material-symbols:eyebrow", - eyes: "mdi:eye", - nose: "mingcute:nose-fill", - lips: "material-symbols-light:lips", - ears: "ion:ear", - glasses: "solar:glasses-bold", - other: "mdi:sparkles", - misc: "material-symbols:settings", -}; - -export const TAB_COMPONENTS: Record> = { - head: HeadTab, - hair: HairTab, - eyebrows: EyebrowsTab, - eyes: EyesTab, - nose: NoseTab, - lips: LipsTab, - ears: EarsTab, - glasses: GlassesTab, - other: OtherTab, - misc: MiscTab, -}; - -export default function MiiEditor({ instructions }: Props) { - const [tab, setTab] = useState("head"); - - return ( - <> -
-
- {(Object.keys(TAB_COMPONENTS) as Tab[]).map((t) => ( - - ))} -
- - {/* Keep all tabs loaded to avoid flickering */} - {(Object.keys(TAB_COMPONENTS) as Tab[]).map((t) => { - const TabComponent = TAB_COMPONENTS[t]; - return ( -
- -
- ); - })} -
- - ); -} +import { type SwitchMiiInstructions } from "@tomodachi-share/shared"; +import React, { useState } from "react"; +import { Icon } from "@iconify/react"; + +import HeadTab from "./tabs/head"; +import HairTab from "./tabs/hair"; +import EyebrowsTab from "./tabs/eyebrows"; +import EyesTab from "./tabs/eyes"; +import NoseTab from "./tabs/nose"; +import LipsTab from "./tabs/lips"; +import EarsTab from "./tabs/ears"; +import GlassesTab from "./tabs/glasses"; +import OtherTab from "./tabs/other"; +import MiscTab from "./tabs/misc"; + +interface Props { + instructions: React.RefObject; +} + +type Tab = "head" | "hair" | "eyebrows" | "eyes" | "nose" | "lips" | "ears" | "glasses" | "other" | "misc"; + +export const TAB_ICONS: Record = { + head: "mingcute:head-fill", + hair: "mingcute:hair-fill", + eyebrows: "material-symbols:eyebrow", + eyes: "mdi:eye", + nose: "mingcute:nose-fill", + lips: "material-symbols-light:lips", + ears: "ion:ear", + glasses: "solar:glasses-bold", + other: "mdi:sparkles", + misc: "material-symbols:settings", +}; + +export const TAB_COMPONENTS: Record> = { + head: HeadTab, + hair: HairTab, + eyebrows: EyebrowsTab, + eyes: EyesTab, + nose: NoseTab, + lips: LipsTab, + ears: EarsTab, + glasses: GlassesTab, + other: OtherTab, + misc: MiscTab, +}; + +export default function MiiEditor({ instructions }: Props) { + const [tab, setTab] = useState("head"); + + return ( + <> +
+
+ {(Object.keys(TAB_COMPONENTS) as Tab[]).map((t) => ( + + ))} +
+ + {/* Keep all tabs loaded to avoid flickering */} + {(Object.keys(TAB_COMPONENTS) as Tab[]).map((t) => { + const TabComponent = TAB_COMPONENTS[t]; + return ( +
+ +
+ ); + })} +
+ + ); +} diff --git a/frontend/src/components/submit-form/mii-editor/tabs/misc.tsx b/frontend/src/components/submit-form/mii-editor/tabs/misc.tsx index 6a8a988..caac609 100644 --- a/frontend/src/components/submit-form/mii-editor/tabs/misc.tsx +++ b/frontend/src/components/submit-form/mii-editor/tabs/misc.tsx @@ -1,213 +1,213 @@ -import { useState } from "react"; -import type { MiiGender, SwitchMiiInstructions } from "@tomodachi-share/shared"; -import EnhancedSlider from "../enhanced-slider"; -import DatingPreferencesViewer from "../../../mii/dating-preferences"; -import VoiceViewer from "../../../mii/voice-viewer"; -import PersonalityViewer from "../../../mii/personality-viewer"; - -interface Props { - instructions: React.RefObject; -} - -export default function MiscTab({ instructions }: Props) { - const [height, setHeight] = useState(instructions.current.height ?? 64); - const [weight, setWeight] = useState(instructions.current.weight ?? 64); - const [datingPreferences, setDatingPreferences] = useState(instructions.current.datingPreferences ?? []); - const [voice, setVoice] = useState({ - speed: instructions.current.voice.speed ?? 25, - pitch: instructions.current.voice.pitch ?? 25, - depth: instructions.current.voice.depth ?? 25, - delivery: instructions.current.voice.delivery ?? 25, - tone: instructions.current.voice.tone ?? 0, - }); - const [birthday, setBirthday] = useState({ - day: instructions.current.birthday.day ?? (null as number | null), - month: instructions.current.birthday.month ?? (null as number | null), - age: instructions.current.birthday.age ?? (null as number | null), - dontAge: instructions.current.birthday.dontAge, - }); - const [personality, setPersonality] = useState({ - movement: instructions.current.personality.movement ?? -1, - speech: instructions.current.personality.speech ?? -1, - energy: instructions.current.personality.energy ?? -1, - thinking: instructions.current.personality.thinking ?? -1, - overall: instructions.current.personality.overall ?? -1, - }); - - return ( - <> -

Misc

- -
-
-
-
-
- Body -
-
- -
- { - setHeight(v); - instructions.current.height = v; - }} - min={0} - max={128} - mid={64} - /> -
- -
- { - setWeight(v); - instructions.current.weight = v; - }} - min={0} - max={128} - mid={64} - /> -
- -
-
- Dating Preferences -
-
- -
- { - setDatingPreferences((prev) => { - const updated = e.target.checked ? (prev.includes(gender) ? prev : [...prev, gender]) : prev.filter((p) => p !== gender); - instructions.current.datingPreferences = updated; - return updated; - }); - }} - /> -
-
- -
-
-
- Voice -
-
- - { - setVoice((p) => ({ ...p, [label]: v })); - instructions.current.voice[label as keyof typeof voice] = v; - }} - onClickTone={(i) => { - setVoice((p) => ({ ...p, tone: i })); - instructions.current.voice.tone = i; - }} - /> - -
-
- Birthday -
-
- -
-
- - { - setBirthday((p) => ({ ...p, day: e.target.valueAsNumber })); - instructions.current.birthday.day = e.target.valueAsNumber; - }} - /> -
-
- - { - setBirthday((p) => ({ ...p, month: e.target.valueAsNumber })); - instructions.current.birthday.month = e.target.valueAsNumber; - }} - /> -
-
- - { - setBirthday((p) => ({ ...p, age: e.target.valueAsNumber })); - instructions.current.birthday.age = e.target.valueAsNumber; - }} - /> -
-
- { - setBirthday((p) => ({ ...p, dontAge: e.target.checked })); - instructions.current.birthday.dontAge = e.target.checked; - }} - /> - -
-
-
-
- -
-
- Personality -
-
- - { - setPersonality((p) => { - const updated = { ...p, [key]: i }; - instructions.current.personality = updated; - return updated; - }); - }} - /> -
- - ); -} +import { useState } from "react"; +import type { MiiGender, SwitchMiiInstructions } from "@tomodachi-share/shared"; +import EnhancedSlider from "../enhanced-slider"; +import DatingPreferencesViewer from "../../../mii/dating-preferences"; +import VoiceViewer from "../../../mii/voice-viewer"; +import PersonalityViewer from "../../../mii/personality-viewer"; + +interface Props { + instructions: React.RefObject; +} + +export default function MiscTab({ instructions }: Props) { + const [height, setHeight] = useState(instructions.current.height ?? 64); + const [weight, setWeight] = useState(instructions.current.weight ?? 64); + const [datingPreferences, setDatingPreferences] = useState(instructions.current.datingPreferences ?? []); + const [voice, setVoice] = useState({ + speed: instructions.current.voice.speed ?? 25, + pitch: instructions.current.voice.pitch ?? 25, + depth: instructions.current.voice.depth ?? 25, + delivery: instructions.current.voice.delivery ?? 25, + tone: instructions.current.voice.tone ?? 0, + }); + const [birthday, setBirthday] = useState({ + day: instructions.current.birthday.day ?? (null as number | null), + month: instructions.current.birthday.month ?? (null as number | null), + age: instructions.current.birthday.age ?? (null as number | null), + dontAge: instructions.current.birthday.dontAge, + }); + const [personality, setPersonality] = useState({ + movement: instructions.current.personality.movement ?? -1, + speech: instructions.current.personality.speech ?? -1, + energy: instructions.current.personality.energy ?? -1, + thinking: instructions.current.personality.thinking ?? -1, + overall: instructions.current.personality.overall ?? -1, + }); + + return ( + <> +

Misc

+ +
+
+
+
+
+ Body +
+
+ +
+ { + setHeight(v); + instructions.current.height = v; + }} + min={0} + max={128} + mid={64} + /> +
+ +
+ { + setWeight(v); + instructions.current.weight = v; + }} + min={0} + max={128} + mid={64} + /> +
+ +
+
+ Dating Preferences +
+
+ +
+ { + setDatingPreferences((prev) => { + const updated = e.target.checked ? (prev.includes(gender) ? prev : [...prev, gender]) : prev.filter((p) => p !== gender); + instructions.current.datingPreferences = updated; + return updated; + }); + }} + /> +
+
+ +
+
+
+ Voice +
+
+ + { + setVoice((p) => ({ ...p, [label]: v })); + instructions.current.voice[label as keyof typeof voice] = v; + }} + onClickTone={(i) => { + setVoice((p) => ({ ...p, tone: i })); + instructions.current.voice.tone = i; + }} + /> + +
+
+ Birthday +
+
+ +
+
+ + { + setBirthday((p) => ({ ...p, day: e.target.valueAsNumber })); + instructions.current.birthday.day = e.target.valueAsNumber; + }} + /> +
+
+ + { + setBirthday((p) => ({ ...p, month: e.target.valueAsNumber })); + instructions.current.birthday.month = e.target.valueAsNumber; + }} + /> +
+
+ + { + setBirthday((p) => ({ ...p, age: e.target.valueAsNumber })); + instructions.current.birthday.age = e.target.valueAsNumber; + }} + /> +
+
+ { + setBirthday((p) => ({ ...p, dontAge: e.target.checked })); + instructions.current.birthday.dontAge = e.target.checked; + }} + /> + +
+
+
+
+ +
+
+ Personality +
+
+ + { + setPersonality((p) => { + const updated = { ...p, [key]: i }; + instructions.current.personality = updated; + return updated; + }); + }} + /> +
+ + ); +}