Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
connoratmyxt opened this issue Mar 29, 2022 · 1 comment
Open
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@connoratmyxt
Copy link

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
@marcospont
Copy link
Owner

Hi, @connoratmyxt

I will try to come up with a solution based on your suggestion.

Thanks for the detailed information!

@marcospont marcospont added the bug Something isn't working label Apr 6, 2022
@marcospont marcospont added the help wanted Extra attention is needed label Sep 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants