From 7eddfe09799ae2be45fb389f5a88cf3e64fc5ac4 Mon Sep 17 00:00:00 2001 From: aarushivyas-15 Date: Sat, 30 May 2026 21:14:44 +0530 Subject: [PATCH 1/3] feat : add roving tabintax and radiogroup to RotateControl --- src/components/RotateControl.tsx | 165 ++++++++++++++++++++++++++++++- src/components/ui/BaseButton.tsx | 28 ++++-- 2 files changed, 182 insertions(+), 11 deletions(-) diff --git a/src/components/RotateControl.tsx b/src/components/RotateControl.tsx index e86d6b0c..3ace73c6 100644 --- a/src/components/RotateControl.tsx +++ b/src/components/RotateControl.tsx @@ -1,9 +1,9 @@ "use client"; +import { useRef } from "react"; import { EditRecipe } from "@/lib/types"; import { RotateCw } from "lucide-react"; import BaseButton from "./ui/BaseButton"; -import { cn } from "@/lib/utils"; interface Props { recipe: EditRecipe; @@ -13,21 +13,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" > - ); @@ -35,3 +78,115 @@ export default function RotateControl({ recipe, onChange }: Props) {
); } + +// "use client"; + +// import { useRef } from "react"; +// import { EditRecipe } from "@/lib/types"; +// import { RotateCw } from "lucide-react"; +// import BaseButton from "./ui/BaseButton"; +// import { cn } from "@/lib/utils"; + +// interface Props { +// recipe: EditRecipe; +// onChange: (patch: Partial) => void; +// } + +// const ROTATIONS = [0, 90, 180, 270] as const; + +// export default function RotateControl({ recipe, onChange }: Props) { +// const refs = useRef<(HTMLButtonElement | 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] }); +// } + +// }; +// return ( +// //
+//
+// {ROTATIONS.map((deg) => { +// const active = recipe.rotate === deg; +// return ( +// (refs.current[index] = el)} +// onClick={() => onChange({ rotate: deg })} +// // aria-label={`Rotate video to ${deg} degrees`} +// // aria-pressed={active} +// role="radio" +// aria-checked={active} +// tabIndex={active ? 0 : -1} +// onKeyDown={(e) => handleKeyDown(e, ROTATIONS.indexOf(deg))} + +// active={active} +// aria-label={`Rotate video to ${deg} degrees`} +// className="flex-1 flex flex-col items-center gap-1.5 py-3" +// > +// +// ); +// })} +//
+// ); +// } + +// "use client"; + +// import { EditRecipe } from "@/lib/types"; +// import { RotateCw } from "lucide-react"; +// import BaseButton from "./ui/BaseButton"; +// import { cn } from "@/lib/utils"; + +// interface Props { +// recipe: EditRecipe; +// onChange: (patch: Partial) => void; +// } + +// const ROTATIONS = [0, 90, 180, 270] as const; + +// export default function RotateControl({ recipe, onChange }: Props) { +// return ( +//
+// {ROTATIONS.map((deg) => { +// const active = recipe.rotate === deg; +// return ( +// onChange({ rotate: deg })} +// 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" +// > +// +// ); +// })} +//
+// ); +// } 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"; From d9d0c3eccf91a59495af2bdac8286f31c3078fc4 Mon Sep 17 00:00:00 2001 From: aarushivyas-15 Date: Sat, 30 May 2026 21:25:04 +0530 Subject: [PATCH 2/3] fix: restore cn import in RotateControl --- src/components/RotateControl.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/RotateControl.tsx b/src/components/RotateControl.tsx index 3ace73c6..f01cfeda 100644 --- a/src/components/RotateControl.tsx +++ b/src/components/RotateControl.tsx @@ -4,6 +4,7 @@ import { useRef } from "react"; import { EditRecipe } from "@/lib/types"; import { RotateCw } from "lucide-react"; import BaseButton from "./ui/BaseButton"; +import { cn } from "@/lib/utils"; interface Props { recipe: EditRecipe; From 0f005dafc350eb09229bdf8fa522bfe6ff9502e0 Mon Sep 17 00:00:00 2001 From: aarushivyas-15 Date: Sat, 30 May 2026 21:36:04 +0530 Subject: [PATCH 3/3] fix : remove commented lines --- src/components/RotateControl.tsx | 112 ------------------------------- 1 file changed, 112 deletions(-) diff --git a/src/components/RotateControl.tsx b/src/components/RotateControl.tsx index f01cfeda..a5bb6198 100644 --- a/src/components/RotateControl.tsx +++ b/src/components/RotateControl.tsx @@ -79,115 +79,3 @@ export default function RotateControl({ recipe, onChange }: Props) {
); } - -// "use client"; - -// import { useRef } from "react"; -// import { EditRecipe } from "@/lib/types"; -// import { RotateCw } from "lucide-react"; -// import BaseButton from "./ui/BaseButton"; -// import { cn } from "@/lib/utils"; - -// interface Props { -// recipe: EditRecipe; -// onChange: (patch: Partial) => void; -// } - -// const ROTATIONS = [0, 90, 180, 270] as const; - -// export default function RotateControl({ recipe, onChange }: Props) { -// const refs = useRef<(HTMLButtonElement | 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] }); -// } - -// }; -// return ( -// //
-//
-// {ROTATIONS.map((deg) => { -// const active = recipe.rotate === deg; -// return ( -// (refs.current[index] = el)} -// onClick={() => onChange({ rotate: deg })} -// // aria-label={`Rotate video to ${deg} degrees`} -// // aria-pressed={active} -// role="radio" -// aria-checked={active} -// tabIndex={active ? 0 : -1} -// onKeyDown={(e) => handleKeyDown(e, ROTATIONS.indexOf(deg))} - -// active={active} -// aria-label={`Rotate video to ${deg} degrees`} -// className="flex-1 flex flex-col items-center gap-1.5 py-3" -// > -// -// ); -// })} -//
-// ); -// } - -// "use client"; - -// import { EditRecipe } from "@/lib/types"; -// import { RotateCw } from "lucide-react"; -// import BaseButton from "./ui/BaseButton"; -// import { cn } from "@/lib/utils"; - -// interface Props { -// recipe: EditRecipe; -// onChange: (patch: Partial) => void; -// } - -// const ROTATIONS = [0, 90, 180, 270] as const; - -// export default function RotateControl({ recipe, onChange }: Props) { -// return ( -//
-// {ROTATIONS.map((deg) => { -// const active = recipe.rotate === deg; -// return ( -// onChange({ rotate: deg })} -// 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" -// > -// -// ); -// })} -//
-// ); -// }