From a9dcb21c20212e341f5a430d4ddbedb74eb02d85 Mon Sep 17 00:00:00 2001 From: Landon & Emma <80786423+LandonAndEmma@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:59:44 -0400 Subject: [PATCH 1/2] Feat: Add Better Sliders Fix #19 --- src/components/mii/instructions.tsx | 22 ++-- src/components/mii/voice-viewer.tsx | 53 ++++---- .../mii-editor/enhanced-slider.tsx | 123 ++++++++++++++++++ .../submit-form/mii-editor/tabs/misc.tsx | 71 ++++------ 4 files changed, 183 insertions(+), 86 deletions(-) create mode 100644 src/components/submit-form/mii-editor/enhanced-slider.tsx diff --git a/src/components/mii/instructions.tsx b/src/components/mii/instructions.tsx index 2e13647..a1b9bda 100644 --- a/src/components/mii/instructions.tsx +++ b/src/components/mii/instructions.tsx @@ -200,24 +200,18 @@ export default function MiiInstructions({ instructions }: Props) { {height && (
- -
- -
-
+ Height + + {height === 64 ? "0" : height > 64 ? `+${height - 64}` : `${height - 64}`} +
)} {weight && (
- -
- -
-
+ Weight + + {weight === 64 ? "0" : weight > 64 ? `+${weight - 64}` : `${weight - 64}`} +
)} {birthday && ( diff --git a/src/components/mii/voice-viewer.tsx b/src/components/mii/voice-viewer.tsx index d809588..d51cb99 100644 --- a/src/components/mii/voice-viewer.tsx +++ b/src/components/mii/voice-viewer.tsx @@ -1,48 +1,45 @@ "use client"; - import { SwitchMiiInstructions } from "@/types"; -import { ChangeEvent } from "react"; +import EnhancedSlider from "@/components/submit-form/mii-editor/enhanced-slider"; interface Props { data: SwitchMiiInstructions["voice"]; - onChange?: (e: ChangeEvent, label: string) => void; + onChange?: (value: number, label: string) => void; onClickTone?: (i: number) => void; } -const VOICE_SETTINGS: string[] = ["Speed", "Pitch", "Depth", "Delivery"]; +const VOICE_SETTINGS = ["Speed", "Pitch", "Depth", "Delivery"]; export default function VoiceViewer({ data, onChange, onClickTone }: Props) { return ( -
- {VOICE_SETTINGS.map((label) => ( -
- -
- { - if (onChange) onChange(e, label.toLowerCase()); - }} - /> -
+
+ {VOICE_SETTINGS.map((label) => { + const value = data[label.toLowerCase() as keyof typeof data] ?? 25; + return onChange ? ( + onChange?.(v, label.toLowerCase())} + min={0} + max={50} + mid={25} + /> + ) : ( +
+ {label} + + {value === 25 ? "0" : value > 25 ? `+${value - 25}` : `${value - 25}`} +
-
- ))} + ); + })}
-
+
{Array.from({ length: 6 }).map((_, i) => ( + +
+ {/* Tick mark at center */} +
+ + handleChange(Number(e.target.value))} + className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer slider-thumb" + style={{ + background: `linear-gradient(to right, #fb923c 0%, #fb923c ${((internalValue - min) / (max - min)) * 100}%, #fed7aa ${((internalValue - min) / (max - min)) * 100}%, #fed7aa 100%)` + }} + /> +
+ + +
+ + +
+ ); +} diff --git a/src/components/submit-form/mii-editor/tabs/misc.tsx b/src/components/submit-form/mii-editor/tabs/misc.tsx index 2cfaf1d..31aece1 100644 --- a/src/components/submit-form/mii-editor/tabs/misc.tsx +++ b/src/components/submit-form/mii-editor/tabs/misc.tsx @@ -1,17 +1,16 @@ import { useState } from "react"; import { MiiGender } from "@prisma/client"; - import DatingPreferencesViewer from "@/components/mii/dating-preferences"; import VoiceViewer from "@/components/mii/voice-viewer"; import PersonalityViewer from "@/components/mii/personality-viewer"; - +import EnhancedSlider from "@/components/submit-form/mii-editor/enhanced-slider"; import { SwitchMiiInstructions } from "@/types"; interface Props { instructions: React.RefObject; } -export default function HeadTab({ instructions }: Props) { +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 ?? []); @@ -50,47 +49,31 @@ export default function HeadTab({ instructions }: Props) {
- -
- { - setHeight(e.target.valueAsNumber); - instructions.current.height = e.target.valueAsNumber; - }} - /> -
-
+ { + setHeight(v); + instructions.current.height = v; + }} + min={0} + max={128} + mid={64} + />
- -
- { - setWeight(e.target.valueAsNumber); - instructions.current.weight = e.target.valueAsNumber; - }} - /> -
-
+ { + setWeight(v); + instructions.current.weight = v; + }} + min={0} + max={128} + mid={64} + />
@@ -122,9 +105,9 @@ export default function HeadTab({ instructions }: Props) { { - setVoice((p) => ({ ...p, [label]: e.target.valueAsNumber })); - instructions.current.voice[label as keyof typeof voice] = e.target.valueAsNumber; + onChange={(v, label) => { + setVoice((p) => ({ ...p, [label]: v })); + instructions.current.voice[label as keyof typeof voice] = v; }} onClickTone={(i) => { setVoice((p) => ({ ...p, tone: i })); From cbbe7887b8f3469c6f5859e666761b700647e33d Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Wed, 8 Apr 2026 23:54:05 +0100 Subject: [PATCH 2/2] feat: improve instructions formatting --- src/app/globals.css | 7 +- src/components/mii/instructions.tsx | 53 ++++++------- src/components/mii/voice-viewer.tsx | 31 ++------ .../mii-editor/enhanced-slider.tsx | 79 ++++--------------- 4 files changed, 50 insertions(+), 120 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index 359d3cf..dc01217 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -119,12 +119,9 @@ input[type="range"]::-moz-range-track { } /* Thumb */ -input[type="range"]::-webkit-slider-thumb { - @apply appearance-none size-4 bg-orange-300 border-2 border-orange-400 rounded-full shadow-md transition -mt-1.5; -} - +input[type="range"]::-webkit-slider-thumb, input[type="range"]::-moz-range-thumb { - @apply size-3.5 bg-orange-300 border-2 border-orange-400 rounded-full shadow-md transition; + @apply appearance-none size-4.5 bg-orange-400 border-2 border-orange-600 rounded-full shadow-md transition; } /* Hover */ diff --git a/src/components/mii/instructions.tsx b/src/components/mii/instructions.tsx index a1b9bda..7a7b18b 100644 --- a/src/components/mii/instructions.tsx +++ b/src/components/mii/instructions.tsx @@ -5,7 +5,6 @@ import VoiceViewer from "./voice-viewer"; import PersonalityViewer from "./personality-viewer"; import { SwitchMiiInstructions } from "@/types"; -import { Icon } from "@iconify/react"; import { COLORS } from "@/lib/switch"; interface Props { @@ -198,31 +197,35 @@ export default function MiiInstructions({ instructions }: Props) {

Misc

- {height && ( -
- Height - - {height === 64 ? "0" : height > 64 ? `+${height - 64}` : `${height - 64}`} - -
- )} - {weight && ( -
- Weight - - {weight === 64 ? "0" : weight > 64 ? `+${weight - 64}` : `${weight - 64}`} - -
- )} + + + {not(height) && {height === 64 ? "0" : height! > 64 ? `+${height! - 64}` : `${height! - 64}`}} + {not(weight) && {weight === 64 ? "0" : weight! > 64 ? `+${weight! - 64}` : `${weight! - 64}`}} + +
{birthday && (

Birthday

- {birthday.day && {birthday.day}} - {birthday.month && {birthday.month}} - {birthday.age && {birthday.age}} - {birthday.dontAge && {birthday.dontAge ? "Yes" : "No"}} + {not(birthday.day) && {birthday.day}} + {not(birthday.month) && {birthday.month}} + {not(birthday.age) && {birthday.age}} + {not(birthday.dontAge) && {birthday.dontAge ? "Yes" : "No"}} + +
+
+ )} + {voice && ( +
+

Voice

+ + + {not(voice.speed) && {voice.speed}} + {not(voice.pitch) && {voice.pitch}} + {not(voice.depth) && {voice.depth}} + {not(voice.delivery) && {voice.delivery}} + {not(voice.tone) && {voice.tone}}
@@ -235,14 +238,6 @@ export default function MiiInstructions({ instructions }: Props) {
)} - {voice && ( -
-

Voice

-
- -
-
- )} {personality && (

Personality

diff --git a/src/components/mii/voice-viewer.tsx b/src/components/mii/voice-viewer.tsx index d51cb99..7a1d965 100644 --- a/src/components/mii/voice-viewer.tsx +++ b/src/components/mii/voice-viewer.tsx @@ -4,42 +4,25 @@ import EnhancedSlider from "@/components/submit-form/mii-editor/enhanced-slider" interface Props { data: SwitchMiiInstructions["voice"]; - onChange?: (value: number, label: string) => void; - onClickTone?: (i: number) => void; + onChange: (value: number, label: string) => void; + onClickTone: (i: number) => void; } const VOICE_SETTINGS = ["Speed", "Pitch", "Depth", "Delivery"]; export default function VoiceViewer({ data, onChange, onClickTone }: Props) { return ( -
+
{VOICE_SETTINGS.map((label) => { const value = data[label.toLowerCase() as keyof typeof data] ?? 25; - return onChange ? ( - onChange?.(v, label.toLowerCase())} - min={0} - max={50} - mid={25} - /> - ) : ( -
- {label} - - {value === 25 ? "0" : value > 25 ? `+${value - 25}` : `${value - 25}`} - -
- ); + return onChange?.(v, label.toLowerCase())} min={0} max={50} mid={25} />; })} -
+
-
+
{Array.from({ length: 6 }).map((_, i) => ( diff --git a/src/components/submit-form/mii-editor/enhanced-slider.tsx b/src/components/submit-form/mii-editor/enhanced-slider.tsx index 695e399..3b1b86d 100644 --- a/src/components/submit-form/mii-editor/enhanced-slider.tsx +++ b/src/components/submit-form/mii-editor/enhanced-slider.tsx @@ -1,5 +1,4 @@ import { Icon } from "@iconify/react"; -import React, { useState, useEffect } from "react"; interface SliderProps { label: string; @@ -12,42 +11,26 @@ interface SliderProps { className?: string; } -export default function EnhancedSlider({ - label, - value, - onChange, - min = 0, - max = 128, - mid = 64, - step = 1, - className = "", -}: SliderProps) { - const [internalValue, setInternalValue] = useState(value); - - // Sync with external value - React.useEffect(() => { - setInternalValue(value); - }, [value]); - +export default function EnhancedSlider({ label, value, onChange, min = 0, max = 128, mid = 64, step = 1, className = "" }: SliderProps) { const handleChange = (newValue: number) => { const clampedValue = Math.min(max, Math.max(min, newValue)); - setInternalValue(clampedValue); onChange(clampedValue); }; const nudge = (direction: number) => { - const newValue = internalValue + (direction * step); + const newValue = value + direction * step; handleChange(newValue); }; - const displayValue = internalValue - mid; + const displayValue = value - mid; const displayText = displayValue > 0 ? `+${displayValue}` : displayValue.toString(); + const percentage = ((value - min) / (max - min)) * 100; return (
-
+

{label}

- + {displayText}
@@ -55,69 +38,41 @@ export default function EnhancedSlider({ - +
{/* Tick mark at center */}
- + handleChange(Number(e.target.value))} - className="w-full h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer slider-thumb" + value={value} + onChange={(e) => handleChange(e.target.valueAsNumber)} + className="w-full px-0.5 h-2 bg-orange-200 rounded-lg appearance-none cursor-pointer focus:outline-0" style={{ - background: `linear-gradient(to right, #fb923c 0%, #fb923c ${((internalValue - min) / (max - min)) * 100}%, #fed7aa ${((internalValue - min) / (max - min)) * 100}%, #fed7aa 100%)` + background: `linear-gradient(to right, #fb923c 0%, #fb923c ${percentage}%, #fed7aa ${percentage}%, #fed7aa 100%)`, }} />
- +
- -
); }