From 575eecfc691a085d5c43237aba4327eb47483a10 Mon Sep 17 00:00:00 2001 From: Anika Raemer Date: Sat, 11 Oct 2025 12:08:56 +0200 Subject: [PATCH] add dragging to step list --- frontend/package-lock.json | 62 +++++++++ frontend/package.json | 3 + frontend/src/App.css | 11 +- frontend/src/components/basics/Button.tsx | 4 + frontend/src/components/basics/SvgIcon.tsx | 4 +- .../InstructionStepDesktopListItem.tsx | 65 ++++++++++ .../InstructionStepListDesktopEditor.tsx | 86 +++++++++++++ .../recipes/InstructionStepListEditor.tsx | 120 +++++++++++------- .../InstructionStepListMobileEditor.tsx | 86 +++++++++++++ .../components/recipes/RecipeDetailPage.tsx | 10 +- .../src/components/recipes/RecipeEditPage.tsx | 2 +- .../{recipeMapper.ts => RecipeMapper.ts} | 8 +- frontend/src/models/InstructionStepModel.ts | 5 + 13 files changed, 406 insertions(+), 60 deletions(-) create mode 100644 frontend/src/components/recipes/InstructionStepDesktopListItem.tsx create mode 100644 frontend/src/components/recipes/InstructionStepListDesktopEditor.tsx create mode 100644 frontend/src/components/recipes/InstructionStepListMobileEditor.tsx rename frontend/src/mappers/{recipeMapper.ts => RecipeMapper.ts} (88%) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 66ad2f9..7c7cea3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,9 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.8.2" @@ -327,6 +330,59 @@ "node": ">=6.9.0" } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", @@ -3990,6 +4046,12 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index e092d2e..05a2c30 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "react": "^19.1.1", "react-dom": "^19.1.1", "react-router-dom": "^7.8.2" diff --git a/frontend/src/App.css b/frontend/src/App.css index b015a65..cc584c0 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -43,7 +43,7 @@ @apply text-gray-600 } .primary-button-bg { - @apply bg-blue-300 hover:bg-blue-400 text-gray-600 + @apply bg-blue-300 hover:bg-blue-400 } .primary-button-text { @apply text-gray-600 @@ -55,6 +55,13 @@ @apply text-white } + .transparent-button-bg { + @apply bg-transparent hover:bg-transparent + } + .transparent-button-text { + @apply text-gray-600 + } + /* input fields like input and textarea */ .input-field { @apply p-2 w-full border rounded-md placeholder-gray-400 border-gray-600 hover:border-blue-600 transition-colors text-gray-600 focus:outline-none focus:border-blue-800; @@ -79,7 +86,7 @@ } .enumeration-indicator{ - @apply flex-shrink-0 w-7 h-7 rounded-full bg-blue-300 text-white flex items-center justify-center + @apply flex-shrink-0 w-7 h-7 rounded-full bg-blue-300 text-white flex items-center justify-center shadow-sm } } \ No newline at end of file diff --git a/frontend/src/components/basics/Button.tsx b/frontend/src/components/basics/Button.tsx index c186039..2423833 100644 --- a/frontend/src/components/basics/Button.tsx +++ b/frontend/src/components/basics/Button.tsx @@ -20,6 +20,10 @@ export const ButtonType = { DefaultButton: { textColor: "default-button-text", backgroundColor: "default-button-bg" + }, + TransparentButton: { + textColor: "transparent-button-text", + backgroundColor: "transparent-button-bg" } } as const; diff --git a/frontend/src/components/basics/SvgIcon.tsx b/frontend/src/components/basics/SvgIcon.tsx index 8a86c8a..75a5d4c 100644 --- a/frontend/src/components/basics/SvgIcon.tsx +++ b/frontend/src/components/basics/SvgIcon.tsx @@ -9,7 +9,9 @@ export const Icon = { LookingGlass: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z", X: "M6 18L18 6M6 6l12 12", - Plus: "M3 12L21 12M12 3L12 21" + Plus: "M3 12L21 12M12 3L12 21", + ArrowUp: "M3 18L12 6L21 18", + ArrowDown: "M3 6L12 18L21 6", } as const; export type Icon = typeof Icon[keyof typeof Icon]; diff --git a/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx b/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx new file mode 100644 index 0000000..4e32531 --- /dev/null +++ b/frontend/src/components/recipes/InstructionStepDesktopListItem.tsx @@ -0,0 +1,65 @@ +/** + * 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 Button, { ButtonType } from "../basics/Button"; +import { Icon } from "../basics/SvgIcon"; + +type InstructionStepDesktopListItemProps = { + 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 }); + + const style = { transform: CSS.Transform.toString(transform), transition }; + + return ( +
+
+ {index + 1} +
+ +