Skip to content

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

@connoratmyxt

Description

@connoratmyxt

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/agnostic-draggable@1.4.3";
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/agnostic-draggable@1.4.3";
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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions