mirror of
https://github.com/zoldar/jenot.git
synced 2026-01-03 14:32:54 +00:00
Implement touch-aware drag and drop of tasks
This commit is contained in:
parent
1330f230c4
commit
aad16d48ad
3 changed files with 109 additions and 20 deletions
|
|
@ -39,6 +39,14 @@
|
|||
<ul>
|
||||
<li draggable="true"><task-list-item checked>one</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>two</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>three</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>four</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>five</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>six</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>seven</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>eight</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>nine</task-list-item></li>
|
||||
<li draggable="true"><task-list-item>ten</task-list-item></li>
|
||||
</ul>
|
||||
</task-list>
|
||||
</div>
|
||||
|
|
|
|||
102
js/jenot.js
102
js/jenot.js
|
|
@ -113,6 +113,7 @@ class TaskListItem extends HTMLElement {
|
|||
this.appendChild(child.cloneNode(true));
|
||||
});
|
||||
|
||||
this.taskList = this.closest("task-list");
|
||||
this.handleElement = this.querySelector(".handle");
|
||||
this.checkboxElement = this.querySelector(".checkbox input");
|
||||
this.contentElement = this.querySelector("editable-area");
|
||||
|
|
@ -161,6 +162,68 @@ class TaskListItem extends HTMLElement {
|
|||
this.parentNode.classList.remove("dragging");
|
||||
});
|
||||
|
||||
this.parentNode.addEventListener("touchstart", (e) => {
|
||||
if (e.target.closest("div").classList.contains("handle")) {
|
||||
this.parentNode.classList.add("dragging");
|
||||
this.taskList.dragActiveElement = this.parentNode;
|
||||
if (!this.taskList.dragPlaceholder) {
|
||||
this.taskList.dragPlaceholder = document.createElement("div");
|
||||
this.taskList.dragPlaceholder.classList.add("drag-placeholder");
|
||||
this.taskList.dragPlaceholder.textContent = ">";
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
const taskListUL = this.taskList.querySelector("ul");
|
||||
|
||||
this.parentNode.addEventListener("touchmove", (e) => {
|
||||
if (this.taskList.dragActiveElement) {
|
||||
const touch = e.touches[0];
|
||||
this.parentNode.style.position = "absolute";
|
||||
this.parentNode.style.left = `${touch.clientX}px`;
|
||||
this.parentNode.style.top = `${touch.clientY}px`;
|
||||
this.parentNode.style.width = "240px";
|
||||
|
||||
if (this.taskList.dragPlaceholder) {
|
||||
const afterElement = getDragAfterElement(taskListUL, touch.clientY);
|
||||
if (afterElement) {
|
||||
taskListUL.insertBefore(
|
||||
this.taskList.dragPlaceholder,
|
||||
afterElement,
|
||||
);
|
||||
} else {
|
||||
taskListUL.appendChild(this.taskList.dragPlaceholder);
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
this.parentNode.addEventListener("touchend", () => {
|
||||
if (this.taskList.dragActiveElement) {
|
||||
this.parentNode.classList.remove("dragging");
|
||||
if (
|
||||
this.taskList.dragActiveElement &&
|
||||
this.taskList.dragPlaceholder &&
|
||||
this.taskList.dragPlaceholder.parentNode
|
||||
) {
|
||||
this.taskList.dragPlaceholder.parentNode.insertBefore(
|
||||
this.taskList.dragActiveElement,
|
||||
this.taskList.dragPlaceholder,
|
||||
);
|
||||
this.taskList.dragPlaceholder.remove();
|
||||
this.taskList.dragActiveElement.style.position = "static";
|
||||
this.taskList.dragActiveElement.style.left = "";
|
||||
this.taskList.dragActiveElement.style.top = "";
|
||||
this.taskList.dragActiveElement.style.width = "";
|
||||
}
|
||||
this.taskList.dragActiveElement = null;
|
||||
this.taskList.dragPlaceholder = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.#updateChecked();
|
||||
}
|
||||
|
||||
|
|
@ -205,6 +268,9 @@ class TaskListItem extends HTMLElement {
|
|||
customElements.define("task-list-item", TaskListItem);
|
||||
|
||||
class TaskList extends HTMLElement {
|
||||
dragPlaceholder = null;
|
||||
dragActiveElement = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -278,7 +344,7 @@ class TaskList extends HTMLElement {
|
|||
this.listElement.addEventListener("dragover", (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const afterElement = this.#getDragAfterElement(e.clientY);
|
||||
const afterElement = getDragAfterElement(this.listElement, e.clientY);
|
||||
const draggable = document.querySelector(".dragging");
|
||||
|
||||
if (afterElement == null) {
|
||||
|
|
@ -288,26 +354,26 @@ class TaskList extends HTMLElement {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#getDragAfterElement(y) {
|
||||
const draggableElements = [
|
||||
...this.listElement.querySelectorAll("li:not(.dragging)"),
|
||||
];
|
||||
function getDragAfterElement(container, y) {
|
||||
const draggableElements = [
|
||||
...container.querySelectorAll("li:not(.dragging)"),
|
||||
];
|
||||
|
||||
return draggableElements.reduce(
|
||||
(closest, containerChild) => {
|
||||
const box = containerChild.getBoundingClientRect();
|
||||
const offset = y - box.top - box.height / 2;
|
||||
return draggableElements.reduce(
|
||||
(closest, containerChild) => {
|
||||
const box = containerChild.getBoundingClientRect();
|
||||
const offset = y - box.top - box.height / 2;
|
||||
|
||||
if (offset < 0 && offset > closest.offset) {
|
||||
return { offset: offset, element: containerChild };
|
||||
} else {
|
||||
return closest;
|
||||
}
|
||||
},
|
||||
{ offset: Number.NEGATIVE_INFINITY },
|
||||
).element;
|
||||
}
|
||||
if (offset < 0 && offset > closest.offset) {
|
||||
return { offset: offset, element: containerChild };
|
||||
} else {
|
||||
return closest;
|
||||
}
|
||||
},
|
||||
{ offset: Number.NEGATIVE_INFINITY },
|
||||
).element;
|
||||
}
|
||||
|
||||
customElements.define("task-list", TaskList);
|
||||
|
|
|
|||
19
style.css
19
style.css
|
|
@ -88,7 +88,7 @@ editable-area textarea {
|
|||
top: 0;
|
||||
left: 0;
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
|
@ -123,13 +123,28 @@ task-list-item {
|
|||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
task-list-item .handle button {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
task-list-item .checkbox input {
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
li.dragging {
|
||||
opacity: 0.5;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.drag-placeholder {
|
||||
opacity: 0.5;
|
||||
background-color: beige;
|
||||
}
|
||||
|
||||
.draggable,
|
||||
task-list ul {
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* Styles */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue