From 6e15cf8c08e73c175107e64fef98b712017899e9 Mon Sep 17 00:00:00 2001 From: krutoo Date: Fri, 20 Mar 2026 00:08:58 +0500 Subject: [PATCH] test(react): Swipeable view example added for useDragAndDrop --- .../use-drag-and-drop/primary.story.tsx | 2 +- .../stories/use-drag-and-drop/swipes.m.css | 50 ++++++++++ .../use-drag-and-drop/swipes.story.tsx | 93 +++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 tests-e2e/stories/use-drag-and-drop/swipes.m.css create mode 100644 tests-e2e/stories/use-drag-and-drop/swipes.story.tsx diff --git a/tests-e2e/stories/use-drag-and-drop/primary.story.tsx b/tests-e2e/stories/use-drag-and-drop/primary.story.tsx index 1256d72..c31370a 100644 --- a/tests-e2e/stories/use-drag-and-drop/primary.story.tsx +++ b/tests-e2e/stories/use-drag-and-drop/primary.story.tsx @@ -36,7 +36,7 @@ export default function Example() { This block is draggable
- + Link
diff --git a/tests-e2e/stories/use-drag-and-drop/swipes.m.css b/tests-e2e/stories/use-drag-and-drop/swipes.m.css new file mode 100644 index 0000000..0eb0b8c --- /dev/null +++ b/tests-e2e/stories/use-drag-and-drop/swipes.m.css @@ -0,0 +1,50 @@ +.list { + margin: 24px; + border-radius: 8px; + display: flex; + flex-direction: column; + background: #ddd; + padding: 8px; + overflow: hidden; +} + +.list .item + .item { + border-top: 1px solid #ddd; +} + +.item { + position: relative; + display: flex; +} + +.draggable { + flex-grow: 1; + padding: 0 12px; + display: flex; + align-items: center; + height: 64px; + background: #fff; + overflow: hidden; + z-index: 1; +} + +.draggable:hover { + cursor: grab; +} + +.draggable:active { + cursor: grabbing; +} + +.rubber { + position: absolute; + top: 0; + right: 0; + bottom: 0; + background: rgb(246, 75, 75); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} diff --git a/tests-e2e/stories/use-drag-and-drop/swipes.story.tsx b/tests-e2e/stories/use-drag-and-drop/swipes.story.tsx new file mode 100644 index 0000000..2ba7305 --- /dev/null +++ b/tests-e2e/stories/use-drag-and-drop/swipes.story.tsx @@ -0,0 +1,93 @@ +import { type CSSProperties, type ReactNode, useRef, useState } from 'react'; +import { useDragAndDrop } from '@krutoo/utils/react'; +import styles from './swipes.m.css'; + +export const meta = { + category: 'React hooks/useDragAndDrop', + title: 'Swipes', +}; + +export default function Example() { + const [items, setItems] = useState(() => + Array(10) + .fill(0) + .map((zero, index) => ({ id: index })), + ); + + const deleteItem = (itemId: number) => { + setItems(prev => prev.filter(item => item.id !== itemId)); + }; + + return ( +
+ {items.map(item => ( + deleteItem(item.id)}>{`Item #${item.id}`} + ))} +
+ ); +} + +function Item({ children, onDelete }: { children?: ReactNode; onDelete?: VoidFunction }) { + const duration = 200; + const limit = -200; + + const ref = useRef(null); + const [grabbed, setGrabbed] = useState(false); + const [deleted, setDeleted] = useState(false); + const [offset, setOffset] = useState({ x: 0, y: 0 }); + + useDragAndDrop(ref, { + onGrab(event) { + setGrabbed(true); + setOffset(event.offset); + }, + onMove(event) { + setOffset(event.offset); + }, + onDrop(event) { + setGrabbed(false); + + if (event.offset.x < limit) { + setDeleted(true); + setTimeout(() => onDelete?.(), duration); + } + }, + }); + + const draggableStyle: CSSProperties = grabbed + ? { + transform: `translateX(${Math.min(0, offset.x)}px)`, + } + : { + transition: `height ${duration}ms ease-in-out, transform ${duration}ms ease-in-out`, + + ...(deleted && { + height: `0px`, + transform: `translateX(-100%)`, + }), + }; + + const rubberStyle: CSSProperties = { + width: !deleted ? -offset.x : '100%', + + ...(!grabbed && { + transition: `width ${duration}ms ease-in-out`, + }), + + ...(offset.x < limit && { + fontSize: '18px', + fontWeight: 'bolder', + }), + }; + + return ( +
+
+ {children} +
+
+ Delete +
+
+ ); +}