diff --git a/index.html b/index.html
index fd922ff..f502c96 100644
--- a/index.html
+++ b/index.html
@@ -39,6 +39,14 @@
- one
- two
+ - three
+ - four
+ - five
+ - six
+ - seven
+ - eight
+ - nine
+ - ten
diff --git a/js/jenot.js b/js/jenot.js
index 0ccbf07..1d3ba79 100644
--- a/js/jenot.js
+++ b/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);
diff --git a/style.css b/style.css
index 082376b..f841642 100644
--- a/style.css
+++ b/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 */