Code Monkey home page Code Monkey logo

agnostic-draggable's People

Contributors

marcospont avatar rozek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

agnostic-draggable's Issues

Droppable doesn't work if used on an element with `display: contents`

Hi, I found this bug while using your library. I am willing to create a PR to fix this if you'd like.

Steps to reproduce

  1. Use display: contents on an element
  2. Make a Droppable with it
  3. It doesn't work (the Droppable only uses the bounding box of the container to calculate the droppable area, which is always [0,0,0,0] on elements with display: contents)

Expected behavior

If the bounding box is [0,0,0,0], the Droppable should use the union of the bounding boxes of any not-absolutely-positioned children as the droppable area.

Code

This code doesn't work
import agnosticDraggable from "https://cdn.skypack.dev/[email protected]";
const { Draggable, Droppable } = agnosticDraggable

const app = document.createElement("div")

const data = [
	{ name: "foo", number: 100, date: new Date(Date.now() - 2 * 60 * 1000).toLocaleString() },
	{ name: "bar", number: 200, date: new Date(Date.now() - 60 * 1000).toLocaleString() },
	{ name: "baz", number: 300, date: new Date(Date.now()).toLocaleString() }
]

const table = document.createElement("table")
table.style.width = "calc(100% - 40px)"
table.style.margin = "20px"
table.style.display = "grid"
table.style.gridTemplateColumns = "auto auto auto"
const thead = document.createElement("thead")
thead.style.display = "contents"
const tbody = document.createElement("tbody")
tbody.style.display = "contents"

const tr = document.createElement("tr")
tr.style.display = "contents"
for (const col of Object.keys(data[0])) {
	const th = document.createElement("th")
	th.style.background = "rgba(127, 127, 127, .1)"
	th.style.padding = "8px"
	th.style.border = "solid rgb(127,127,127)"
	th.style.borderWidth = "1px 0"
	th.textContent = col
	tr.appendChild(th)
}
thead.appendChild(tr)

for (const row of data) {
	const tr = document.createElement("tr")
	tr.style.display = "contents"
	const cells = Object.values(row).map((cell) => {
		const td = document.createElement("td")
		td.style.padding = "8px"
		td.style.border = "solid rgb(127,127,127)"
		td.style.borderWidth = "1px 0"
		td.textContent = cell
		tr.appendChild(td)
	})
	tbody.appendChild(tr)

	new Droppable(
		tr,
		{
			tolerance: "pointer",
		},
		{
			"droppable:over": () => {
				tr.style.background = "blue"
			},
			"droppable:out": () => {
				tr.style.background = ""
			},
			"droppable:drop": () => {
				tr.style.background = ""
				const td = tr.querySelector("td:nth-child(2)")
				td.textContent = parseInt(td.textContent) + 1
			}
		}
	)
}

table.appendChild(thead)
table.appendChild(tbody)
app.appendChild(table)

const addDiv = document.createElement("div")
addDiv.style.display = "inline-block"
addDiv.style.margin = "20px"
addDiv.style.padding = "8px"
addDiv.style.background = "rgb(127,127,127)"
addDiv.textContent = "Drop me on a row to add 1"

app.appendChild(addDiv)

document.body.appendChild(app)

let listener = (e) => {
	e.preventDefault()
}
new Draggable(
	addDiv,
	{
		revert: true,
	},
	{
		"drag:start": () => {
			document.body.addEventListener("selectstart", listener)
		},
		"drag:stop": () => {
			document.body.removeEventListener("selectstart", listener)
		}
	}
)
This code works
import agnosticDraggable from "https://cdn.skypack.dev/[email protected]";
const { Draggable, Droppable } = agnosticDraggable

const app = document.createElement("div")

const data = [
	{ name: "foo", number: 100, date: new Date(Date.now() - 2 * 60 * 1000).toLocaleString() },
	{ name: "bar", number: 200, date: new Date(Date.now() - 60 * 1000).toLocaleString() },
	{ name: "baz", number: 300, date: new Date(Date.now()).toLocaleString() }
]

const table = document.createElement("table")
table.style.width = "calc(100% - 40px)"
table.style.margin = "20px"
const thead = document.createElement("thead")
const tbody = document.createElement("tbody")

const tr = document.createElement("tr")
for (const col of Object.keys(data[0])) {
	const th = document.createElement("th")
	th.style.background = "rgba(127, 127, 127, .1)"
	th.style.padding = "8px"
	th.style.border = "solid rgb(127,127,127)"
	th.style.borderWidth = "1px 0"
	th.textContent = col
	tr.appendChild(th)
}
thead.appendChild(tr)

for (const row of data) {
	const tr = document.createElement("tr")
	const cells = Object.values(row).map((cell) => {
		const td = document.createElement("td")
		td.style.padding = "8px"
		td.style.border = "solid rgb(127,127,127)"
		td.style.borderWidth = "1px 0"
		td.textContent = cell
		tr.appendChild(td)
	})
	tbody.appendChild(tr)

	new Droppable(
		tr,
		{
			tolerance: "pointer",
		},
		{
			"droppable:over": () => {
				tr.style.background = "blue"
			},
			"droppable:out": () => {
				tr.style.background = ""
			},
			"droppable:drop": () => {
				tr.style.background = ""
				const td = tr.querySelector("td:nth-child(2)")
				td.textContent = parseInt(td.textContent) + 1
			}
		}
	)
}

table.appendChild(thead)
table.appendChild(tbody)
app.appendChild(table)

const addDiv = document.createElement("div")
addDiv.style.display = "inline-block"
addDiv.style.margin = "20px"
addDiv.style.padding = "8px"
addDiv.style.background = "rgb(127,127,127)"
addDiv.textContent = "Drop me on a row to add 1"

app.appendChild(addDiv)

document.body.appendChild(app)

let listener = (e) => {
	e.preventDefault()
}
new Draggable(
	addDiv,
	{
		revert: true,
	},
	{
		"drag:start": () => {
			document.body.addEventListener("selectstart", listener)
		},
		"drag:stop": () => {
			document.body.removeEventListener("selectstart", listener)
		}
	}
)

Suggested solution

  1. If the Droppable element has a bounding box of [0,0,0,0], search deeply for any children (excluding position: absolute or position: fixed) that have a not-zero bounding box
  2. Use those bounding boxes to test whether a Draggable is over the Droppable

Documentation for data returned by events

Could you build upon the documentation to clarify some of the data that is returned?

Immediately, I am looking to use peerSortable but I do not know the nature of its data and if it is always present and/or reliable, and if it is always the previously connected sortable or something else.

Text selection is weird when using the library

Hi, I found this bug while using your library. I don't know how to fix this one.

Steps to reproduce

Text won't deselect:

  1. Select text from lorem ipsum paragraph
  2. Click on text selection to deselect it
  3. It doesn't deselect the text

Selection can't be used:

  1. Select text from lorem ipsum paragraph
  2. Try to drag text, for example into a new tab
  3. Text does not get dragged

Expected behavior

Text selection should stay normal when interacting with other elements when using the library.

Code

import agnosticDraggable from "https://cdn.skypack.dev/[email protected]";
const { Sortable } = agnosticDraggable

const app = document.createElement("div")

const ipsum = document.createElement("div")
ipsum.textContent = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. In tincidunt pretium quam, a pellentesque erat tempor eget. Ut nec ex porttitor, interdum dolor nec, consequat lectus. Sed orci orci, sodales ac tincidunt in, accumsan in neque. Phasellus rutrum eros eget purus aliquet, vitae tincidunt lacus eleifend. Proin ultricies efficitur dui sit amet feugiat. Sed vel suscipit augue, posuere scelerisque massa. In vulputate laoreet lacinia. Aliquam erat volutpat. Etiam scelerisque dictum sodales. Mauris auctor egestas nunc in suscipit. Nullam hendrerit neque a feugiat eleifend. Integer et ornare velit, vitae sodales sapien. Vestibulum sollicitudin pulvinar ligula, at scelerisque ex molestie vel.`

app.appendChild(ipsum)
document.body.appendChild(app)

const container = document.createElement("div")

const els = new Array(10).fill(null).map(() => document.createElement("div"))
els.forEach((el, i) => {
	el.textContent = "Item: " + (i + 1)

	container.appendChild(el)
})

app.appendChild(container)

new Sortable(container)

Investigation

  • happens with both Sortable and Draggable
  • disabling the mousedown event on the html element via devtools makes text selection normal again
  • running sortable.destroy() after creating a Sortable doesn't fix the issue, so it's possible that an event listener also isn't properly removed

Update disabled

Is it possible to update disabled option on draggable component ? It doesnt seem to update if i create it like this (this.lock = prop):
new Draggable(this.$refs.Item, { helper: "clone", containment: "body", zIndex: 5, disabled: this.lock });

Sortable element size is not applied correctly if element has padding

Hi, I found this bug while using your library. I am willing to create a PR to fix this if you'd like.

Steps to reproduce

  1. Drag element in list
  2. Notice it is larger than before dragging
  3. Drop element
  4. Notice element being larger than its siblings, and overflowing the container

Expected behavior

Item should stay the same size before, during, and after dragging.

Code

import agnosticDraggable from "https://cdn.skypack.dev/[email protected]";
const { Sortable } = agnosticDraggable

const container = document.createElement("div")
container.style.padding = "4px 0"

const els = new Array(10).fill(null).map(() => document.createElement("div"))
els.forEach((el, i) => {
	el.textContent = "Item: " + (i + 1)
	el.style.padding = "8px"
	el.style.margin = "4px 8px"
	el.style.background = "rgba(127, 127, 127, .1)"

	container.appendChild(el)
})

document.body.appendChild(container)

new Sortable(container)

Suggested solution(s)

Option 1: Don't compute a height and width for dragged elements (would have effects on elements that inherit size from the positioning in the container, for example flexbox and grid elements)

Option 2: Account for padding+border in getting the size of the element (has to account for box-sizing)

Add event for re-ordering element within sortable

I have been exploring all the events currently available, and it appears the most relevant event to an element being reordered in the same sortable is sortable:update, however that returns all the data for the sortable the element is connected to, not the element itself.

More appropriately, the sortable:remove and sortable:receive events return data about the elements themselves when they are added/removed from a sortable, but there appears to be no equivalent event for when an element is reordered within the sortable it is already a part of. I understand that technically, many elements are reordered simultaneously when one element is reordered, however, if there were some separate sortable:reorder event fired for the element actually dragged and dropped including its previous and new position/index within the list, the rest could be easily inferred.

In the case of linked arrays, this would be helpful so I could splice the element from its previous position/index to its new one.

Vue3

Hey, how do i properly listen to events?

  new Draggable(document.querySelector(`#Item${index}`),null, {
    revert: "invalid",
    helper: "clone",
    zIndex: 1000,
     
    "drag:start": function (event) {
      event.cancel();
    },
  });

gives me an error if i dont delete revert, helper and zIndex

Also what does that null mean?

Drag n Drop on mobile

Will you ever add support for mobile devices by any chance? Or is there a way to make dragndrop working on mobile?

Smaller bundle size

Hi @marcospont

I really appreciate work done for agnostic-draggable. It's easy to use and it's filling all the gaps that I had after using packages like sortablejs or @shopify/draggable ๐Ÿš€

Just wondering, if there is a way to make the bundle size smaller? Currently, it's quite big like showing on bundlephobia below:
https://bundlephobia.com/package/[email protected]

image

Similar packages seems to be smaller, so maybe there is a chance to optimize? (ESM output?)

image

Thank you in advance for all your work done for this package ๐Ÿป

Position/index of sorted element(s) before/after moved

It would be helpful to see both the previously selected sortable and the moved element's previous position/index in that list as well as the new sortable it is a part of and its new position/index.

I see a position property, but it returns all null values. โ€” see attached
E8c09D3X0AccQI_

how to update options during interaction, how to stop interaction?

Hello,

at first, thanks a lot for this wonderful contribution: for quite a while, I thought about implementing s.th. similar myself - but always withdrew that idea because of the foreseeable effort.

But now, everything I had to do was to write a trivial wrapper around your module in order to use it with Svelte.

However, that work raised a few questions, two of them are:

  • is it possible to update the initially given options while an interaction is ongoing? (e.g., the "containment" of a "draggable")
  • is it possible to stop (i.e. abort) an ongoing interaction? (e.g. an ongoing drag-and-drop operation by means of the ESC key)

Thanks in advance for your effort!

Kind regards,

Andreas Rozek

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.