Skip to content

Commit a10a9da

Browse files
committed
feat: add loop and drag events
1 parent 0b2c717 commit a10a9da

File tree

7 files changed

+630
-2
lines changed

7 files changed

+630
-2
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ function blurTransform(position) {
278278
| approve | `item: T` | Emitted when a card is approved (swiped right or approved via actions) |
279279
| reject | `item: T` | Emitted when a card is rejected (swiped left or rejected via actions) |
280280
| restore | `item: T` | Emitted when a card is restored (returned to the stack via restore action) |
281+
| loop | - | Emitted when a new loop cycle starts in loop mode (all cards have been swiped) |
282+
| dragstart | `item: T` | Emitted when user starts dragging a card |
283+
| dragmove | `item: T, type: SwipeAction \| null, delta: number` | Emitted during card dragging with movement details |
284+
| dragend | `item: T` | Emitted when user stops dragging a card |
281285

282286
## Exposed
283287
| Method/Property | Type | Description |

docs/api/flashcards.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,28 @@ function blurTransform(position) {
362362
- **Payload:** `item: T`
363363
- **Description:** Emitted when a card is restored (returned to the stack via restore action).
364364

365+
### `loop`
366+
367+
- **Payload:** None
368+
- **Description:** Emitted when a new loop cycle starts in loop mode. This event fires when all cards have been swiped and the component starts over from the beginning. Only triggered when `loop` prop is enabled.
369+
370+
### `dragstart`
371+
372+
- **Payload:** `item: T`
373+
- **Description:** Emitted when user starts dragging a card. Fires immediately when dragging begins, before any movement threshold is reached.
374+
375+
### `dragmove`
376+
377+
- **Payload:** `item: T, type: SwipeAction | null, delta: number`
378+
- **Description:** Emitted during card dragging with real-time movement details. The `type` indicates the swipe direction (`'approve'` for right/up, `'reject'` for left/down, or `null` if within threshold). The `delta` is a normalized value (-1 to 1) representing drag progress relative to swipe threshold.
379+
380+
### `dragend`
381+
382+
- **Payload:** `item: T`
383+
- **Description:** Emitted when user stops dragging a card, regardless of whether the swipe completed or was cancelled.
384+
385+
**Usage example with all events:**
386+
365387
```vue
366388
<script setup>
367389
function handleApprove(item) {
@@ -378,14 +400,39 @@ function handleRestore(item) {
378400
console.log('Restored:', item)
379401
// Remove from approved/rejected lists, etc.
380402
}
403+
404+
function handleLoop() {
405+
console.log('New loop cycle started!')
406+
// Analytics, reset counters, etc.
407+
}
408+
409+
function handleDragStart(item) {
410+
console.log('Started dragging:', item)
411+
// Show drag hints, haptic feedback, etc.
412+
}
413+
414+
function handleDragMove(item, type, delta) {
415+
console.log('Dragging:', item, 'Direction:', type, 'Progress:', delta)
416+
// Update UI indicators, play sounds, etc.
417+
}
418+
419+
function handleDragEnd(item) {
420+
console.log('Stopped dragging:', item)
421+
// Hide drag hints, etc.
422+
}
381423
</script>
382424
383425
<template>
384426
<FlashCards
385427
:items="cards"
428+
:loop="true"
386429
@approve="handleApprove"
387430
@reject="handleReject"
388431
@restore="handleRestore"
432+
@loop="handleLoop"
433+
@dragstart="handleDragStart"
434+
@dragmove="handleDragMove"
435+
@dragend="handleDragEnd"
389436
>
390437
<!-- slots -->
391438
</FlashCards>

src/FlashCard.vue

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ const emit = defineEmits<{
4848
* Event fired when animation ends
4949
*/
5050
animationend: []
51+
52+
/**
53+
* Event fired when drag starts
54+
*/
55+
dragstart: []
56+
57+
/**
58+
* Event fired during dragging movement
59+
*/
60+
dragmove: [type: SwipeAction | null, delta: number]
61+
62+
/**
63+
* Event fired when drag ends
64+
*/
65+
dragend: []
5166
}>()
5267
5368
defineSlots<{
@@ -82,6 +97,15 @@ const {
8297
} = useDragSetup(el, () => ({
8398
...params,
8499
...animation,
100+
onDragStart() {
101+
emit('dragstart')
102+
},
103+
onDragMove(type, delta) {
104+
emit('dragmove', type, delta)
105+
},
106+
onDragEnd() {
107+
emit('dragend')
108+
},
85109
onDragComplete(action) {
86110
emit('complete', action, position)
87111
},

src/FlashCards.vue

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ const emit = defineEmits<{
6060
approve: [item: T]
6161
reject: [item: T]
6262
restore: [item: T]
63+
loop: []
64+
dragstart: [item: T]
65+
dragmove: [item: T, type: SwipeAction | null, delta: number]
66+
dragend: [item: T]
6367
}>()
6468
6569
defineSlots<{
@@ -109,7 +113,11 @@ const {
109113
removeAnimatingCard,
110114
reset,
111115
currentItemId,
112-
} = useStackList<T>(() => ({ ...config.value, renderLimit: renderLimit.value }))
116+
} = useStackList<T>(() => ({
117+
...config.value,
118+
renderLimit: renderLimit.value,
119+
onLoop: () => emit('loop'),
120+
}))
113121
114122
/**
115123
* STACK TRANSFORM
@@ -209,6 +217,9 @@ defineExpose({
209217
:class="{ 'flashcards__card--active': itemId === currentItemId }"
210218
@complete="(action, pos) => handleCardSwipe(itemId, action, pos)"
211219
@mounted="containerHeight = Math.max($event, 0)"
220+
@dragstart="emit('dragstart', item)"
221+
@dragmove="(type, delta) => emit('dragmove', item, type, delta)"
222+
@dragend="emit('dragend', item)"
212223
>
213224
<template #default>
214225
<slot :item="item" />
@@ -235,6 +246,9 @@ defineExpose({
235246
class="flashcards__card flashcards__card--animating"
236247
:animation="animation"
237248
@animationend="() => removeAnimatingCard(itemId)"
249+
@dragstart="emit('dragstart', item)"
250+
@dragmove="(type, delta) => emit('dragmove', item, type, delta)"
251+
@dragend="emit('dragend', item)"
238252
>
239253
<template #default>
240254
<slot :item="item" />

src/utils/useStackList.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface StackListOptions<T> {
1818
renderLimit: number
1919
itemKey?: keyof T | 'id'
2020
waitAnimationEnd?: boolean
21+
onLoop?: () => void
2122
}
2223

2324
export interface ResetOptions {
@@ -68,9 +69,10 @@ export function useStackList<T>(_options: MaybeRefOrGetter<StackListOptions<T>>)
6869

6970
// For loop mode reset history on new cycle (when index points outside of source array)
7071
watch(currentIndex, (ci) => {
71-
const { loop, items } = options.value
72+
const { loop, items, onLoop } = options.value
7273
if (loop && ci === items.length) {
7374
history.clear()
75+
onLoop?.() // New loop cycle started
7476
}
7577
})
7678

0 commit comments

Comments
 (0)