"use client"; import { useEffect, useId, useState } from "react"; import { motion, useReducedMotion, useSpring, useTransform } from "motion/react"; type MoodSliderProps = { /** The question, shown on the sky. */ label?: string; /** Mood names, lowest first. */ moods?: string[]; /** Value (controlled), 1 to the number of moods. */ value?: number; /** Value at start (uncontrolled). */ defaultValue?: number; /** Called with the new value and its mood name. */ onChange?: (value: number, mood: string) => void; /** Form field name. Sends the number. */ name?: string; /** Set the width here. Everything scales with it. */ className?: string; }; const MOODS = ["Awful", "Low", "Okay", "Good", "Great"]; const RAYS = Array.from({ length: 12 }, (_, i) => i * 30); const RAIN = Array.from({ length: 22 }, (_, i) => [24 + i * 17 + (i % 3) * 5, 70 + ((i * 37) % 90)]); const TRACK = [44, 376]; /* MoodSlider: your mood is the weather. A cut-paper sun climbs out of the rain as you slide up, the clouds part, the hills turn green and its face comes round to a smile. A native range input does the work, so keyboard and touch are free. */ export function MoodSlider({ label = "How is today?", moods = MOODS, value, defaultValue, onChange, name, className = "w-full max-w-lg" }: MoodSliderProps) { const reduce = useReducedMotion(); const id = useId().replace(/:/g, ""); const [inner, setInner] = useState(defaultValue ?? Math.ceil(moods.length / 2)); const v = value ?? inner; const n = moods.length; const mood = moods[v - 1]; const m = useSpring((v - 1) / (n - 1), reduce ? { stiffness: 10000, damping: 500 } : { stiffness: 90, damping: 16 }); useEffect(() => m.set((v - 1) / (n - 1)), [m, v, n]); const top = useTransform(m, [0, 0.5, 1], ["#2c3648", "#6f8fae", "#8fd3ea"]); const low = useTransform(m, [0, 0.5, 1], ["#55637a", "#e8c9a0", "#fdeaa8"]); const sunY = useTransform(m, [0, 1], [50, 0]); const sunFill = useTransform(m, [0, 0.5, 1], ["#a6adb8", "#f4c35a", "#ffb21e"]); const rayScale = useTransform(m, [0.25, 1], [0.6, 1]); const rayOpacity = useTransform(m, [0.25, 0.6], [0, 1]); const cloudLeft = useTransform(m, [0, 1], [0, -190]); const cloudRight = useTransform(m, [0, 1], [0, 200]); const cloudFill = useTransform(m, [0, 0.6, 1], ["#46526a", "#d5dbe5", "#ffffff"]); const rainOpacity = useTransform(m, [0, 0.42], [1, 0]); const farHill = useTransform(m, [0, 0.5, 1], ["#3a4557", "#5f8a6c", "#6dbb6a"]); const nearHill = useTransform(m, [0, 0.5, 1], ["#2a3342", "#3f6f54", "#3f9a58"]); const mouth = useTransform(m, (t) => `M186 ${138 - (t - 0.5) * 8} Q210 ${138 + (t - 0.5) * 56} 234 ${138 - (t - 0.5) * 8}`); const browOuter = useTransform(m, [0, 0.5, 1], [102, 96, 90]); const browInner = useTransform(m, [0, 0.5, 1], [90, 96, 92]); const browL = useTransform([browOuter, browInner], ([o, i]) => `M176 ${o} L198 ${i}`); const browR = useTransform([browOuter, browInner], ([o, i]) => `M244 ${o} L222 ${i}`); const eye = useTransform(m, [0, 0.5, 1], [3, 7, 8]); const blush = useTransform(m, [0.6, 1], [0, 0.7]); const tear = useTransform(m, [0, 0.2], [1, 0]); const thumb = useTransform(m, [0, 1], TRACK); return (