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 2e13647..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,37 +197,35 @@ export default function MiiInstructions({ instructions }: Props) {

Misc

- {height && ( -
- -
- -
-
-
- )} - {weight && ( -
- -
- -
-
-
- )} + + + {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}}
@@ -241,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 d809588..7a1d965 100644 --- a/src/components/mii/voice-viewer.tsx +++ b/src/components/mii/voice-viewer.tsx @@ -1,48 +1,28 @@ "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; - onClickTone?: (i: number) => 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?.(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 new file mode 100644 index 0000000..3b1b86d --- /dev/null +++ b/src/components/submit-form/mii-editor/enhanced-slider.tsx @@ -0,0 +1,78 @@ +import { Icon } from "@iconify/react"; + +interface SliderProps { + label: string; + value: number; + onChange: (value: number) => void; + min?: number; + max?: number; + mid?: number; + step?: number; + className?: string; +} + +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)); + onChange(clampedValue); + }; + + const nudge = (direction: number) => { + const newValue = value + direction * step; + handleChange(newValue); + }; + + const displayValue = value - mid; + const displayText = displayValue > 0 ? `+${displayValue}` : displayValue.toString(); + const percentage = ((value - min) / (max - min)) * 100; + + return ( +
+
+

{label}

+ + {displayText} + +
+
+ + +
+ {/* Tick mark at center */} +
+ + 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 ${percentage}%, #fed7aa ${percentage}%, #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 }));