From 13fe0ee852a23739257133a2649b001351d92e77 Mon Sep 17 00:00:00 2001 From: araemer Date: Tue, 21 Oct 2025 07:54:13 +0200 Subject: [PATCH] Extract NumberStepControl --- frontend/src/App.css | 4 +- .../components/basics/NumberStepControl.tsx | 74 +++++++++++++ .../InstructionStepDesktopListItem.tsx | 100 +++++++++--------- .../components/recipes/RecipeDetailPage.tsx | 39 ++----- 4 files changed, 134 insertions(+), 83 deletions(-) create mode 100644 frontend/src/components/basics/NumberStepControl.tsx diff --git a/frontend/src/App.css b/frontend/src/App.css index 4ba5a12..06b80ca 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -107,8 +107,8 @@ .ingredient-group-card { @apply py-4 border-b border-gray-400; } - - .enumeration-indicator { + + .circular-container { @apply flex-shrink-0 w-7 h-7 rounded-full bg-blue-300 text-white flex items-center justify-center shadow-sm; } diff --git a/frontend/src/components/basics/NumberStepControl.tsx b/frontend/src/components/basics/NumberStepControl.tsx new file mode 100644 index 0000000..67b56fb --- /dev/null +++ b/frontend/src/components/basics/NumberStepControl.tsx @@ -0,0 +1,74 @@ +import {Minus, Plus} from "lucide-react"; +import {defaultIconSize} from "./SvgIcon.tsx"; + +type NumberStepControlProps = { + /** Current numeric value */ + value: number; + /** Callback when value changes */ + onChange: (newValue: number) => void; + /** Optional: minimum allowed value */ + min?: number; + /** Optional: maximum allowed value */ + max?: number; + /** Optional: step increment (default = 1) */ + step?: number; + /** Optional: additional Tailwind classes */ + className?: string; +}; + +/** + * A compact number input with +/– buttons. + * Removes native browser spinners and supports min/max limits. + */ +export function NumberStepControl({ + value, + onChange, + min = Number.NEGATIVE_INFINITY, + max = Number.POSITIVE_INFINITY, + step = 1, + className = "", + }: NumberStepControlProps) { + const handleDecrease = () => { + const newValue = Math.max(value - step, min); + onChange(newValue); + }; + + const handleIncrease = () => { + const newValue = Math.min(value + step, max); + onChange(newValue); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const num = Number(e.target.value); + if (!isNaN(num)) onChange(Math.min(Math.max(num, min), max)); + }; + + return ( +
+ + + + + +
+ ); +} diff --git a/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx b/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx index 37f953b..aff0952 100644 --- a/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx +++ b/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx @@ -2,64 +2,64 @@ * Single step component for Desktop editor with drag-and-drop support. */ -import { useSortable } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; -import type { InstructionStepModel } from "../../models/InstructionStepModel"; +import {useSortable} from "@dnd-kit/sortable"; +import {CSS} from "@dnd-kit/utilities"; +import type {InstructionStepModel} from "../../models/InstructionStepModel"; import Button from "../basics/Button"; -import { X } from "lucide-react" -import { ButtonType } from "../basics/BasicButtonDefinitions"; +import {X} from "lucide-react" +import {ButtonType} from "../basics/BasicButtonDefinitions"; type InstructionStepDesktopListItemProps = { - id: string | number; - index: number; - step: InstructionStepModel; - onUpdate: (index: number, value: string) => void; - onRemove: (index: number) => void; + id: string | number; + index: number; + step: InstructionStepModel; + onUpdate: (index: number, value: string) => void; + onRemove: (index: number) => void; }; export function InstructionStepDesktopListItem({ - id, - index, - step, - onUpdate, - onRemove, -}: InstructionStepDesktopListItemProps) { - const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + id, + index, + step, + onUpdate, + onRemove, + }: InstructionStepDesktopListItemProps) { + const {attributes, listeners, setNodeRef, transform, transition} = useSortable({id}); - const style = { transform: CSS.Transform.toString(transform), transition }; + const style = {transform: CSS.Transform.toString(transform), transition}; - return ( -
-
- {index + 1} -
+ return ( +
+
+ {index + 1} +
-