diff --git a/src/components/controls/ColorKeyboard.tsx b/src/components/controls/ColorKeyboard.tsx new file mode 100644 index 0000000..8e25918 --- /dev/null +++ b/src/components/controls/ColorKeyboard.tsx @@ -0,0 +1,101 @@ +import { Toggle } from "@/components/ui/toggle"; +import { colorScale } from "@/constants/colorScale"; +import { cn } from "@/lib/utils"; + +const WHITE_KEYS = ["C", "D", "E", "F", "G", "A", "B"]; +const BLACK_KEYS = [ + { note: "C#", position: 0 }, + { note: "D#", position: 1 }, + { note: "F#", position: 3 }, + { note: "G#", position: 4 }, + { note: "A#", position: 5 }, +]; + +export default function ColorKeyboard({ + value, + onChange, +}: { + value: string; + onChange: (color: string) => void; +}) { + const colorByNote = Object.fromEntries(colorScale.map((entry) => [entry.note, entry.color])); + + return ( +
+ {WHITE_KEYS.map((note) => { + const color = colorByNote[note]; + const isActive = color.toLowerCase() === value.toLowerCase(); + + return ( + onChange(color)} + className={cn( + "relative flex flex-1 items-end justify-center rounded-none border border-foreground dark:border-background cursor-pointer", + "h-36 min-w-0 px-0", + note === "C" ? "rounded-l-md" : "", + note === "B" ? "rounded-r-md" : "", + isActive ? "z-10" : "", + )} + style={{ backgroundColor: color }} + aria-label={`Select ${note}`} + > + + {note} + + + ); + })} + + {BLACK_KEYS.map((key) => { + const color = colorByNote[key.note]; + const isActive = color.toLowerCase() === value.toLowerCase(); + + return ( + onChange(color)} + className={cn( + "absolute top-0 z-20 h-24 rounded-none border border-black min-w-0 px-0 cursor-pointer", + isActive ? "" : "", + )} + style={{ + width: "8%", + left: `${(key.position + 1) * (100 / WHITE_KEYS.length) - 4}%`, + backgroundColor: color, + }} + aria-label={`Select ${key.note}`} + > + + + {key.note} + + + + ); + })} +
+ ); +}