From 8211284c4a50dd2b6747d7573f5cc6cba174c484 Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Sun, 24 May 2026 15:43:04 +0200 Subject: [PATCH] feat(shopping): composant ItemRow avec swipe-to-delete et mode magasin --- frontend/src/components/shopping/ItemRow.tsx | 124 +++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 frontend/src/components/shopping/ItemRow.tsx diff --git a/frontend/src/components/shopping/ItemRow.tsx b/frontend/src/components/shopping/ItemRow.tsx new file mode 100644 index 0000000..7de768a --- /dev/null +++ b/frontend/src/components/shopping/ItemRow.tsx @@ -0,0 +1,124 @@ +import { useRef, useState } from 'react' +import type { ShoppingItem } from '../../api/shopping' + +interface ItemRowProps { + item: ShoppingItem + onCheck: () => void + onDelete: () => void + storeMode?: boolean +} + +const SWIPE_THRESHOLD = 80 + +export default function ItemRow({ item, onCheck, onDelete, storeMode = false }: ItemRowProps) { + const [offsetX, setOffsetX] = useState(0) + const [isDragging, setIsDragging] = useState(false) + const startX = useRef(null) + + function onTouchStart(e: React.TouchEvent) { + if (storeMode) return + startX.current = e.touches[0].clientX + setIsDragging(true) + } + + function onTouchMove(e: React.TouchEvent) { + if (startX.current === null) return + const dx = e.touches[0].clientX - startX.current + setOffsetX(Math.max(Math.min(dx, 0), -120)) + } + + function onTouchEnd() { + if (offsetX < -SWIPE_THRESHOLD) onDelete() + setOffsetX(0) + setIsDragging(false) + startX.current = null + } + + const minHeight = storeMode ? 64 : 52 + + return ( +
+ {!storeMode && ( +
+ +
+ )} + +
+
+ {item.is_checked && } +
+ +
+
+ {item.display_name} + {item.carried_over && ( + + )} +
+ {(item.quantity || item.unit) && ( +
+ {item.quantity}{item.unit ? ` ${item.unit}` : ''} +
+ )} +
+ + {storeMode && !item.is_checked && ( + + )} +
+
+ ) +}