fix issues in boot order drag and drop selector

This commit is contained in:
Arthur Lu 2024-07-17 19:49:13 +00:00
parent 6de16ec656
commit 77a060db6b
4 changed files with 30 additions and 130 deletions

View File

@ -12,6 +12,8 @@
<link rel="stylesheet" href="css/form.css"> <link rel="stylesheet" href="css/form.css">
<script src="scripts/config.js" type="module"></script> <script src="scripts/config.js" type="module"></script>
<script src="scripts/draggable.js" type="module"></script> <script src="scripts/draggable.js" type="module"></script>
<script src="modules/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
</head> </head>
<body> <body>
<header> <header>

2
modules/Sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig } from "./utils.js"; import { requestPVE, requestAPI, goToPage, getURIData, resourcesConfig, setTitleAndHeader, bootConfig, setAppearance } from "./utils.js";
import { alert, dialog } from "./dialog.js"; import { alert, dialog } from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second
@ -14,6 +14,7 @@ let vmid;
let config; let config;
async function init () { async function init () {
setAppearance();
setTitleAndHeader(); setTitleAndHeader();
const cookie = document.cookie; const cookie = document.cookie;
if (cookie === "") { if (cookie === "") {

View File

@ -1,39 +1,30 @@
// Map valid UUIDs used by draggable-item elements in order to better validate data transfers to ignore random data transfers. const blank = document.createElement("img");
const draggableItemUUIDs = {};
/**
* Get the data transfer source object by parsing its 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 the source object if valid, or null if invalid.
* @param {*} typesList from event.dataTransfer.types
* @returns {Object} Object containing the type, uuid, and element of the dataTransfer source or null
*/
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();
this.attachShadow({ mode: "open" }); this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML = ` this.shadowRoot.innerHTML = `
<style>
draggable-item.ghost::part(wrapper) {
border: 1px dashed var(--main-text-color);
border-radius: 5px;
margin: -1px;
}
</style>
<label id="title"></label> <label id="title"></label>
<div id="wrapper"> <div id="wrapper" style="padding-bottom: 1em;"></div>
<draggable-item id="bottom" class="drop-target"></draggable-item>
</div>
`; `;
this.content = this.shadowRoot.querySelector("#wrapper"); this.content = this.shadowRoot.querySelector("#wrapper");
this.bottom = this.shadowRoot.querySelector("#bottom");
this.titleElem = this.shadowRoot.querySelector("#title"); this.titleElem = this.shadowRoot.querySelector("#title");
window.Sortable.create(this.content, {
group: "boot",
ghostClass: "ghost",
setData: function (dataTransfer, dragEl) {
dataTransfer.setDragImage(blank, 0, 0);
}
});
} }
get title () { get title () {
@ -45,14 +36,10 @@ class DraggableContainer extends HTMLElement {
} }
append (newNode) { append (newNode) {
newNode.uuid = window.crypto.randomUUID(); this.content.appendChild(newNode, this.bottom);
draggableItemUUIDs[newNode.uuid] = newNode;
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);
} }
@ -62,7 +49,6 @@ 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;
} }
@ -92,80 +78,19 @@ class DraggableItem extends HTMLElement {
// 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-over {
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;
} }
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
</style> </style>
<div id="drag-over" style="display: none;"></div> <div id="wrapper" part="wrapper"></div>
<div id="wrapper">
<div style="min-height: 1.5em;"></div>
</div>
`; `;
this.content = this.shadowRoot.querySelector("#wrapper"); this.content = this.shadowRoot.querySelector("#wrapper");
// add drag and drop listeners
this.addEventListener("dragstart", (event) => {
this.content.style.opacity = "0.5";
const data = { id: this.id, data: this.data, content: this.content.innerHTML, value: this.value };
event.dataTransfer.setData(`application/json/${this.uuid}`, JSON.stringify(data));
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) => {
if (event.dataTransfer.dropEffect === "move") {
this.parentElement.removeChild(this);
}
else {
this.content.attributeStyleMap.clear();
}
});
this.addEventListener("dragenter", (event) => {
const sourceElement = getDragSource(event.dataTransfer.types);
if (event.target.dropTarget && sourceElement) {
event.target.dragOver = sourceElement.element.innerHTML;
}
event.preventDefault();
});
this.addEventListener("dragleave", (event) => {
if (event.target.dragOver && getDragSource(event.dataTransfer.types)) {
event.target.dragOver = false;
}
event.preventDefault();
});
this.addEventListener("dragover", (event) => {
event.preventDefault();
});
this.addEventListener("drop", (event) => {
if (event.target.dragOver) {
event.target.dragOver = false;
}
const sourceElement = getDragSource(event.dataTransfer.types);
if (event.target.dropTarget && sourceElement) {
const transfer = JSON.parse(event.dataTransfer.getData(sourceElement.type));
const item = document.createElement("draggable-item");
item.data = transfer.data;
item.innerHTML = transfer.content;
item.draggable = true;
item.dropTarget = true;
item.id = transfer.id;
item.value = transfer.data.value;
item.uuid = sourceElement.uuid;
event.target.parentElement.insertBefore(item, event.target);
}
this.content.attributeStyleMap.clear();
event.preventDefault();
});
} }
get innerHTML () { get innerHTML () {
@ -175,36 +100,6 @@ class DraggableItem extends HTMLElement {
set innerHTML (innerHTML) { set innerHTML (innerHTML) {
this.content.innerHTML = innerHTML; this.content.innerHTML = innerHTML;
} }
get dropTarget () {
return this.classList.contains("drop-target");
}
set dropTarget (dropTarget) {
if (dropTarget) {
this.classList.add("drop-target");
}
else {
this.classList.remove("drop-target");
}
}
get dragOver () {
return this.classList.contains("drag-over");
}
set dragOver (dragOver) {
if (dragOver) {
this.classList.add("drag-over");
this.shadowRoot.querySelector("#drag-over").style.display = "block";
this.shadowRoot.querySelector("#drag-over").innerHTML = dragOver;
}
else {
this.classList.remove("drag-over");
this.shadowRoot.querySelector("#drag-over").style.display = "none";
this.shadowRoot.querySelector("#drag-over").innerHTML = "";
}
}
} }
customElements.define("draggable-container", DraggableContainer); customElements.define("draggable-container", DraggableContainer);