diff --git a/src/components/RotateControl.tsx b/src/components/RotateControl.tsx
index e86d6b0c..a5bb6198 100644
--- a/src/components/RotateControl.tsx
+++ b/src/components/RotateControl.tsx
@@ -1,5 +1,6 @@
"use client";
+import { useRef } from "react";
import { EditRecipe } from "@/lib/types";
import { RotateCw } from "lucide-react";
import BaseButton from "./ui/BaseButton";
@@ -13,21 +14,64 @@ interface Props {
const ROTATIONS = [0, 90, 180, 270] as const;
export default function RotateControl({ recipe, onChange }: Props) {
+ const refs = useRef<(HTMLButtonElement | HTMLAnchorElement | null)[]>([]);
+ const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
+ let nextIndex = index;
+
+ if (e.key === "ArrowRight") {
+ nextIndex = (index + 1) % ROTATIONS.length;
+ e.preventDefault();
+ }
+
+ if (e.key === "ArrowLeft") {
+ nextIndex = (index - 1 + ROTATIONS.length) % ROTATIONS.length;
+ e.preventDefault();
+ }
+
+ if (nextIndex !== index) {
+ onChange({ rotate: ROTATIONS[nextIndex] });
+
+ requestAnimationFrame(() => {
+ refs.current[nextIndex]?.focus();
+ });
+ }
+ };
+
return (
-
- {ROTATIONS.map((deg) => {
+
+ {ROTATIONS.map((deg, index) => {
const active = recipe.rotate === deg;
+ const noneSelected = !ROTATIONS.includes(
+ recipe.rotate as 0 | 90 | 180 | 270,
+ );
+
return (
{
+ refs.current[index] = el;
+ }}
onClick={() => onChange({ rotate: deg })}
+ role="radio"
+ aria-checked={active}
+ tabIndex={active || (noneSelected && index === 0) ? 0 : -1}
+ // tabIndex={active ? 0 : -1}
+
+ onKeyDown={(e) => handleKeyDown(e, index)}
aria-label={`Rotate video to ${deg} degrees`}
- aria-pressed={active}
active={active}
className="flex-1 flex flex-col items-center gap-1.5 py-3"
>
-
+
{deg}
);
diff --git a/src/components/ui/BaseButton.tsx b/src/components/ui/BaseButton.tsx
index e889f045..b8910249 100644
--- a/src/components/ui/BaseButton.tsx
+++ b/src/components/ui/BaseButton.tsx
@@ -14,16 +14,32 @@ interface BaseButtonProps extends ButtonHTMLAttributes {
rel?: string;
}
-const BaseButton = forwardRef(
- ({ className, variant = "secondary", size = "md", active, children, as = "button", ...props }, ref) => {
+const BaseButton = forwardRef<
+ HTMLButtonElement | HTMLAnchorElement,
+ BaseButtonProps
+>(
+ (
+ {
+ className,
+ variant = "secondary",
+ size = "md",
+ active,
+ children,
+ as = "button",
+ ...props
+ },
+ ref,
+ ) => {
const Component = as as any;
const variants = {
- primary: "bg-film-600 text-white shadow-lg shadow-film-200 dark:shadow-none hover:bg-film-700",
+ primary:
+ "bg-film-600 text-white shadow-lg shadow-film-200 dark:shadow-none hover:bg-film-700",
secondary: active
? "border-film-500 bg-film-50 text-film-700 font-heading font-semibold dark:bg-film-900/20 dark:text-film-400 dark:border-film-700"
: "border-[var(--border)] bg-[var(--surface)] text-[var(--muted)] hover:border-film-300 hover:bg-film-50/30 dark:hover:bg-film-900/10",
ghost: "bg-transparent hover:bg-[var(--surface)]",
- outline: "border border-[var(--border)] bg-transparent hover:border-film-300",
+ outline:
+ "border border-[var(--border)] bg-transparent hover:border-film-300",
};
const sizes = {
@@ -41,14 +57,14 @@ const BaseButton = forwardRef
{children}
);
- }
+ },
);
BaseButton.displayName = "BaseButton";