TahaSh / swapy

✨ A framework-agnostic tool that converts any layout into a drag-to-swap one with just a few lines of code https://swapy.tahazsh.com/
MIT License
5.04k stars 94 forks source link

Feature Request: onSwapEnd Event for Dragging Detection #31

Open Way opened 1 month ago

Way commented 1 month ago

First of all thank you for your great tiny and helpful library!

My request proposes adding an onSwapEnd event. This event would trigger when a drag-and-swap action is completed, allowing developers to detect the end of the drag operation and perform additional actions or updates as needed. This feature would enhance the flexibility and interactivity of the drag-to-swap experience.

For instance, when reordering items, I don't need an event triggered for each individual swap. Instead, I require a single event that fires once the user has finished dragging and the final order is set.

Way commented 1 month ago

For anyone who is interested in a intermediate solution or kind of workaround to this request, I've figured out the following approach:

Debounce the onSwap event to trigger the event after an amount of time but only once.

I'm using Angular, so the following code is Angular related. Anyhow, the idea will work with any other framework:

const change = new Subject<any>();

change.pipe(debounceTime(500)).subscribe((event) => {
  // This event will be triggered only once every 500ms and contains the `SwapEventData`
});

const swapy = createSwapy(this.el.nativeElement);
swapy.onSwap((event) => this.change.next(event));
TahaSh commented 4 weeks ago

@Way Thank you! I think what you're looking for is disabling continuous mode:

createSwapy(container, { continuousMode: false })
TahaSh commented 3 weeks ago

Closing this issue for now — @Way let me know if this isn't working for you, and I'll be happy to take another look!

chanmathew commented 1 week ago

@TahaSh I tried to disable continuousMode but I do noticing the onSwap event still firing on every swap, even when I haven't released the drag yet. Is that a bug or is it intentional? It would be great to have this supported as this would allow making an API call to a database to update sort orders for example, and you only want to make a call once the drag has finalized, and not across every item it's dragging across.

Also tagging https://github.com/TahaSh/swapy/issues/53 as it's related.

jurios commented 1 week ago

I'm with @chanmathew , would be really interesting if swapy emitted more events besides the onSwap event. Like onDrag or onDrop so we can, as @chanmathew said, make some tasks at the precise moment.

bfourgeaud commented 1 week ago

Same here, I tried continuousMode: false but it still fires events even when the item is not dropped. Would be great to have onSwapEnd and onSwapStart event

For now I added a debounce so that it only fires after an amount of inactive time

import { SwapArray } from "@/lib/types"
import { useLayoutEffect, useRef } from "react"
import { createSwapy } from "swapy"
import { useDebouncedCallback } from "use-debounce"

interface Props {
  onSwap?: (data: SwapArray) => void
  debounce?: number
}

/**
 *
 * @param onSwap
 * @returns containerRef
 */
const useDraggable = ({ onSwap, debounce = 2000 }: Props) => {
  const containerRef = useRef<HTMLDivElement>(null)
  const swapy = useRef<ReturnType<typeof createSwapy>>()

  const debounced = useDebouncedCallback(
    (data: SwapArray) => onSwap?.(data),
    debounce
  )

  useLayoutEffect(() => {
    if (containerRef.current && !swapy.current) {
      const _swapy = createSwapy(containerRef.current, {
        animation: "dynamic",
      })
      swapy.current = _swapy
      swapy.current.onSwap((event) => {
        debounced(event.data.array)
      })
    }
  }, [containerRef, debounced])

  return containerRef
}

export default useDraggable
TahaSh commented 6 days ago

@chanmathew Disabling continuousMode doesn't swap on drop; instead, it waits about 100ms after you stop before swapping. I'll be adding a new mode soon that will allow swapping right on drop.

TahaSh commented 1 day ago

@chanmathew In v0.3.0, you can swap on drop using swapMode: 'drop'.

Example:

createSwapy(container, {
  swapMode: 'drop'
})