improve draggable item style

This commit is contained in:
Arthur Lu 2023-09-13 20:03:36 +00:00
parent ea8d2e5b8c
commit bab1ab8830

View File

@ -1,3 +1,26 @@
// Map valid UUIDs used by draggable-item elements in order to better validate data transfers to ignore random data transfers.
const draggableItemUUIDs = {};
/**
* Validate a data transfer object through its data types. Valid draggable-item events have one type of the format `application/json/${uuid}`.
* The function takes the entire type list from event.dataTransfer.types and returns true if the dataTransfer object is likely to be valid.
* @param {*} formatList from event.dataTransfer.types
* @returns {Boolean} true if dataTransfer is valid (from a draggable-item source on this page), false otherwise
*/
function getDragSource (typesList) {
if (typesList.length !== 1) {
return null;
}
const typeString = typesList[0];
const type = typeString.split("/");
if (type.length === 3 && type[0] === "application" && type[1] === "json" && draggableItemUUIDs[type[2]]) {
return { type: typeString, uuid: type[2], element: draggableItemUUIDs[type[2]] };
}
else {
return null;
}
}
class DraggableContainer extends HTMLElement { class DraggableContainer extends HTMLElement {
constructor () { constructor () {
super(); super();
@ -22,10 +45,14 @@ class DraggableContainer extends HTMLElement {
} }
append (newNode) { append (newNode) {
newNode.uuid = window.crypto.randomUUID();
draggableItemUUIDs[newNode.uuid] = newNode;
this.content.insertBefore(newNode, this.bottom); this.content.insertBefore(newNode, this.bottom);
} }
insertBefore (newNode, referenceNode) { insertBefore (newNode, referenceNode) {
newNode.uuid = window.crypto.randomUUID();
draggableItemUUIDs[newNode.uuid] = newNode;
this.content.insertBefore(newNode, referenceNode); this.content.insertBefore(newNode, referenceNode);
} }
@ -35,6 +62,7 @@ class DraggableContainer extends HTMLElement {
removeChild (node) { removeChild (node) {
if (node && this.content.contains(node)) { if (node && this.content.contains(node)) {
draggableItemUUIDs[node.uuid] = null;
this.content.removeChild(node); this.content.removeChild(node);
return true; return true;
} }
@ -57,20 +85,25 @@ class DraggableContainer extends HTMLElement {
} }
class DraggableItem extends HTMLElement { class DraggableItem extends HTMLElement {
uuid = null;
constructor () { constructor () {
super(); super();
this.attachShadow({ mode: "open" }); this.attachShadow({ mode: "open" });
// for whatever reason, only grid layout seems to respect the parent's content bounds // for whatever reason, only grid layout seems to respect the parent's content bounds
this.shadowRoot.innerHTML = ` this.shadowRoot.innerHTML = `
<style> <style>
#drag { #drag-over {
cursor: move; height: 1.5em;
border: 1px dotted var(--main-text-color);
border-radius: 5px;
background-color: rgba(0,0,0,0.25);
} }
img { img {
height: 1em; height: 1em;
width: 1em; width: 1em;
} }
</style> </style>
<div id="drag-over" style="display: none;"></div>
<div id="wrapper"> <div id="wrapper">
<div style="min-height: 1.5em;"></div> <div style="min-height: 1.5em;"></div>
</div> </div>
@ -80,8 +113,14 @@ class DraggableItem extends HTMLElement {
this.addEventListener("dragstart", (event) => { this.addEventListener("dragstart", (event) => {
this.content.style.opacity = "0.5"; this.content.style.opacity = "0.5";
const data = { id: this.id, data: this.data, content: this.content.innerHTML, value: this.value }; const data = { id: this.id, data: this.data, content: this.content.innerHTML, value: this.value };
event.dataTransfer.setData("application/json", JSON.stringify(data)); event.dataTransfer.setData(`application/json/${this.uuid}`, JSON.stringify(data));
event.dataTransfer.effectAllowed = "move"; event.dataTransfer.effectAllowed = "move";
const blank = document.createElement("img");
event.dataTransfer.setDragImage(blank, 0, 0);
setTimeout(() => {
this.content.style.visibility = "hidden";
this.content.style.height = "0";
}, 0);
}); });
this.addEventListener("dragend", (event) => { this.addEventListener("dragend", (event) => {
if (event.dataTransfer.dropEffect === "move") { if (event.dataTransfer.dropEffect === "move") {
@ -92,13 +131,14 @@ class DraggableItem extends HTMLElement {
} }
}); });
this.addEventListener("dragenter", (event) => { this.addEventListener("dragenter", (event) => {
if (event.target.dropTarget) { const sourceElement = getDragSource(event.dataTransfer.types);
event.target.dragOver = true; if (event.target.dropTarget && sourceElement) {
event.target.dragOver = sourceElement.element.innerHTML;
} }
event.preventDefault(); event.preventDefault();
}); });
this.addEventListener("dragleave", (event) => { this.addEventListener("dragleave", (event) => {
if (event.target.dragOver) { if (event.target.dragOver && getDragSource(event.dataTransfer.types)) {
event.target.dragOver = false; event.target.dragOver = false;
} }
event.preventDefault(); event.preventDefault();
@ -110,8 +150,9 @@ class DraggableItem extends HTMLElement {
if (event.target.dragOver) { if (event.target.dragOver) {
event.target.dragOver = false; event.target.dragOver = false;
} }
if (event.target.dropTarget) { const sourceElement = getDragSource(event.dataTransfer.types);
const transfer = JSON.parse(event.dataTransfer.getData("application/json")); if (event.target.dropTarget && sourceElement) {
const transfer = JSON.parse(event.dataTransfer.getData(sourceElement.type));
const item = document.createElement("draggable-item"); const item = document.createElement("draggable-item");
item.data = transfer.data; item.data = transfer.data;
item.innerHTML = transfer.content; item.innerHTML = transfer.content;
@ -119,6 +160,7 @@ class DraggableItem extends HTMLElement {
item.dropTarget = true; item.dropTarget = true;
item.id = transfer.id; item.id = transfer.id;
item.value = transfer.data.value; item.value = transfer.data.value;
item.uuid = sourceElement.uuid;
event.target.parentElement.insertBefore(item, event.target); event.target.parentElement.insertBefore(item, event.target);
} }
this.content.attributeStyleMap.clear(); this.content.attributeStyleMap.clear();
@ -154,13 +196,13 @@ class DraggableItem extends HTMLElement {
set dragOver (dragOver) { set dragOver (dragOver) {
if (dragOver) { if (dragOver) {
this.classList.add("drag-over"); this.classList.add("drag-over");
// temp hover over effect this.shadowRoot.querySelector("#drag-over").style.display = "block";
this.content.style.borderTop = "1px dotted"; this.shadowRoot.querySelector("#drag-over").innerHTML = dragOver;
} }
else { else {
this.classList.remove("drag-over"); this.classList.remove("drag-over");
// temp hover over effect this.shadowRoot.querySelector("#drag-over").style.display = "none";
this.content.style.borderTop = ""; this.shadowRoot.querySelector("#drag-over").innerHTML = "";
} }
} }
} }