mirror of
https://github.com/zoldar/jenot.git
synced 2026-01-05 07:02:55 +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>
|
<ul>
|
||||||
<li draggable="true"><task-list-item checked>one</task-list-item></li>
|
<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>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>
|
</ul>
|
||||||
</task-list>
|
</task-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
102
js/jenot.js
102
js/jenot.js
|
|
@ -113,6 +113,7 @@ class TaskListItem extends HTMLElement {
|
||||||
this.appendChild(child.cloneNode(true));
|
this.appendChild(child.cloneNode(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.taskList = this.closest("task-list");
|
||||||
this.handleElement = this.querySelector(".handle");
|
this.handleElement = this.querySelector(".handle");
|
||||||
this.checkboxElement = this.querySelector(".checkbox input");
|
this.checkboxElement = this.querySelector(".checkbox input");
|
||||||
this.contentElement = this.querySelector("editable-area");
|
this.contentElement = this.querySelector("editable-area");
|
||||||
|
|
@ -161,6 +162,68 @@ class TaskListItem extends HTMLElement {
|
||||||
this.parentNode.classList.remove("dragging");
|
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();
|
this.#updateChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -205,6 +268,9 @@ class TaskListItem extends HTMLElement {
|
||||||
customElements.define("task-list-item", TaskListItem);
|
customElements.define("task-list-item", TaskListItem);
|
||||||
|
|
||||||
class TaskList extends HTMLElement {
|
class TaskList extends HTMLElement {
|
||||||
|
dragPlaceholder = null;
|
||||||
|
dragActiveElement = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
@ -278,7 +344,7 @@ class TaskList extends HTMLElement {
|
||||||
this.listElement.addEventListener("dragover", (e) => {
|
this.listElement.addEventListener("dragover", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const afterElement = this.#getDragAfterElement(e.clientY);
|
const afterElement = getDragAfterElement(this.listElement, e.clientY);
|
||||||
const draggable = document.querySelector(".dragging");
|
const draggable = document.querySelector(".dragging");
|
||||||
|
|
||||||
if (afterElement == null) {
|
if (afterElement == null) {
|
||||||
|
|
@ -288,26 +354,26 @@ class TaskList extends HTMLElement {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#getDragAfterElement(y) {
|
function getDragAfterElement(container, y) {
|
||||||
const draggableElements = [
|
const draggableElements = [
|
||||||
...this.listElement.querySelectorAll("li:not(.dragging)"),
|
...container.querySelectorAll("li:not(.dragging)"),
|
||||||
];
|
];
|
||||||
|
|
||||||
return draggableElements.reduce(
|
return draggableElements.reduce(
|
||||||
(closest, containerChild) => {
|
(closest, containerChild) => {
|
||||||
const box = containerChild.getBoundingClientRect();
|
const box = containerChild.getBoundingClientRect();
|
||||||
const offset = y - box.top - box.height / 2;
|
const offset = y - box.top - box.height / 2;
|
||||||
|
|
||||||
if (offset < 0 && offset > closest.offset) {
|
if (offset < 0 && offset > closest.offset) {
|
||||||
return { offset: offset, element: containerChild };
|
return { offset: offset, element: containerChild };
|
||||||
} else {
|
} else {
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ offset: Number.NEGATIVE_INFINITY },
|
{ offset: Number.NEGATIVE_INFINITY },
|
||||||
).element;
|
).element;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("task-list", TaskList);
|
customElements.define("task-list", TaskList);
|
||||||
|
|
|
||||||
19
style.css
19
style.css
|
|
@ -88,7 +88,7 @@ editable-area textarea {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
-webkit-text-fill-color: transparent;
|
-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;
|
-webkit-tap-highlight-color: transparent;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -123,13 +123,28 @@ task-list-item {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task-list-item .handle button {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
task-list-item .checkbox input {
|
task-list-item .checkbox input {
|
||||||
width: 1.1em;
|
width: 1.1em;
|
||||||
height: 1.1em;
|
height: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dragging {
|
li.dragging {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-placeholder {
|
||||||
|
opacity: 0.5;
|
||||||
|
background-color: beige;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable,
|
||||||
|
task-list ul {
|
||||||
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles */
|
/* Styles */
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue