mirror of
https://github.com/hex248/tsos.git
synced 2026-02-07 18:23:05 +00:00
keyboard component
This commit is contained in:
101
src/components/controls/ColorKeyboard.tsx
Normal file
101
src/components/controls/ColorKeyboard.tsx
Normal file
@@ -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 (
|
||||
<div className="relative flex w-full select-none">
|
||||
{WHITE_KEYS.map((note) => {
|
||||
const color = colorByNote[note];
|
||||
const isActive = color.toLowerCase() === value.toLowerCase();
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
key={note}
|
||||
pressed={isActive}
|
||||
onPressedChange={() => 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}`}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"mb-2 inline-flex size-6 items-center justify-center rounded-full text-xs font-medium leading-none",
|
||||
"text-black",
|
||||
)}
|
||||
style={
|
||||
isActive
|
||||
? {
|
||||
backgroundColor: note === "E" ? "#000000" : "#ffffff",
|
||||
color: note === "E" ? "#ffffff" : "#000000",
|
||||
}
|
||||
: { backgroundColor: color }
|
||||
}
|
||||
>
|
||||
{note}
|
||||
</span>
|
||||
</Toggle>
|
||||
);
|
||||
})}
|
||||
|
||||
{BLACK_KEYS.map((key) => {
|
||||
const color = colorByNote[key.note];
|
||||
const isActive = color.toLowerCase() === value.toLowerCase();
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
key={key.note}
|
||||
size="sm"
|
||||
pressed={isActive}
|
||||
onPressedChange={() => 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}`}
|
||||
>
|
||||
<span className={cn("flex h-full w-full items-end justify-center pb-2")}>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex size-5 items-center justify-center rounded-full text-[10px] font-medium leading-none",
|
||||
isActive ? "bg-white text-black" : "text-white",
|
||||
)}
|
||||
style={!isActive ? { backgroundColor: color } : undefined}
|
||||
>
|
||||
{key.note}
|
||||
</span>
|
||||
</span>
|
||||
</Toggle>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user