diff --git a/packages/editor/src/extensions/check-list-item/check-list-item.ts b/packages/editor/src/extensions/check-list-item/check-list-item.ts
index 6f9b97a24..e7e277d58 100644
--- a/packages/editor/src/extensions/check-list-item/check-list-item.ts
+++ b/packages/editor/src/extensions/check-list-item/check-list-item.ts
@@ -17,8 +17,14 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import { keybindings } from "@notesnook/common";
-import { KeyboardShortcutCommand, mergeAttributes, Node } from "@tiptap/core";
+import {
+ findParentNodeClosestToPos,
+ KeyboardShortcutCommand,
+ mergeAttributes,
+ Node
+} from "@tiptap/core";
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
+import { CheckList } from "../check-list/check-list";
export interface CheckListItemOptions {
onReadOnlyChecked?: (node: ProseMirrorNode, checked: boolean) => boolean;
@@ -97,94 +103,76 @@ export const CheckListItem = Node.create({
addNodeView() {
return ({ node, getPos, editor }) => {
- const listItem = document.createElement("li");
- const checkboxWrapper = document.createElement("label");
- const checkboxStyler = document.createElement("span");
- const checkbox = document.createElement("input");
- const content = document.createElement("div");
+ const li = document.createElement("li");
+ if (node.attrs.checked) li.classList.add("checked");
+ else li.classList.remove("checked");
- checkboxWrapper.contentEditable = "false";
- checkbox.type = "checkbox";
+ function onClick(e: MouseEvent | TouchEvent) {
+ if (e instanceof MouseEvent && e.button !== 0) return;
+ if (!(e.target instanceof HTMLElement)) return;
- checkbox.addEventListener("mousedown", (event) => {
- if (globalThis.keyboardShown) {
- event.preventDefault();
- }
- });
+ const pos = typeof getPos === "function" ? getPos() : 0;
+ if (typeof pos !== "number") return;
+ const resolvedPos = editor.state.doc.resolve(pos);
- checkbox.addEventListener("change", (event) => {
- event.preventDefault();
- // if the editor isn’t editable and we don't have a handler for
- // readonly checks we have to undo the latest change
- if (!editor.isEditable && !this.options.onReadOnlyChecked) {
- checkbox.checked = !checkbox.checked;
+ const { x, y, right } = li.getBoundingClientRect();
- return;
+ const clientX =
+ e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
+
+ const clientY =
+ e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
+
+ const hitArea = { width: 40, height: 40 };
+
+ const isRtl =
+ e.target.dir === "rtl" ||
+ findParentNodeClosestToPos(
+ resolvedPos,
+ (node) => !!node.attrs.textDirection
+ )?.node.attrs.textDirection === "rtl";
+
+ let xStart = clientX >= x - hitArea.width;
+ let xEnd = clientX <= x;
+ const yStart = clientY >= y;
+ const yEnd = clientY <= y + hitArea.height;
+
+ if (isRtl) {
+ xEnd = clientX <= right + hitArea.width;
+ xStart = clientX >= right;
}
- const { checked } = event.target as any;
-
- if (editor.isEditable && typeof getPos === "function") {
- editor
- .chain()
- .command(({ tr }) => {
- const position = getPos();
- const currentNode = tr.doc.nodeAt(position);
-
- tr.setNodeMarkup(position, undefined, {
- ...currentNode?.attrs,
- checked
- });
-
- return true;
- })
- .run();
+ if (xStart && xEnd && yStart && yEnd) {
+ e.preventDefault();
+ editor.commands.command(({ tr }) => {
+ tr.setNodeAttribute(
+ pos,
+ "checked",
+ !li.classList.contains("checked")
+ );
+ return true;
+ });
}
- if (!editor.isEditable && this.options.onReadOnlyChecked) {
- // Reset state if onReadOnlyChecked returns false
- if (!this.options.onReadOnlyChecked(node, checked)) {
- checkbox.checked = !checkbox.checked;
- }
- }
- });
-
- if (node.attrs.checked) {
- checkbox.setAttribute("checked", "checked");
}
- checkboxWrapper.append(checkbox, checkboxStyler);
- listItem.append(checkboxWrapper, content);
+ li.onmousedown = onClick;
+ li.ontouchstart = onClick;
return {
- dom: listItem,
- contentDOM: content,
+ dom: li,
+ contentDOM: li,
update: (updatedNode) => {
if (updatedNode.type !== this.type) {
return false;
}
+ const isNested = updatedNode.lastChild?.type.name === CheckList.name;
- listItem.dataset.checked = updatedNode.attrs.checked;
- if (updatedNode.attrs.checked) {
- checkbox.setAttribute("checked", "checked");
- } else {
- checkbox.removeAttribute("checked");
- }
+ if (updatedNode.attrs.checked) li.classList.add("checked");
+ else li.classList.remove("checked");
return true;
}
};
};
}
-
- // addInputRules() {
- // return [
- // wrappingInputRule({
- // find: inputRegex,
- // type: this.type,
- // getAttributes: (match) => ({
- // checked: match[match.length - 1] === "x"
- // })
- // })
- // ];
- // }
});
diff --git a/packages/editor/src/extensions/text-direction/text-direction.ts b/packages/editor/src/extensions/text-direction/text-direction.ts
index 9547eb22b..bbc67a1eb 100644
--- a/packages/editor/src/extensions/text-direction/text-direction.ts
+++ b/packages/editor/src/extensions/text-direction/text-direction.ts
@@ -30,6 +30,7 @@ const TEXT_DIRECTION_TYPES = [
"orderedList",
"bulletList",
"outlineList",
+ "checkList",
"taskList",
"table",
"blockquote",
diff --git a/packages/editor/styles/styles.css b/packages/editor/styles/styles.css
index e676c1ee2..9450f8545 100644
--- a/packages/editor/styles/styles.css
+++ b/packages/editor/styles/styles.css
@@ -76,6 +76,14 @@
margin-bottom: 5px;
}
+.ProseMirror li:last-of-type {
+ margin-bottom: 0px;
+}
+
+.ProseMirror li:first-of-type {
+ margin-top: 5px;
+}
+
.ProseMirror ul.tasklist-content-wrapper {
padding-left: 0px;
}
@@ -638,40 +646,71 @@ p > *::selection {
transform: rotate(90deg);
}
+.simple-checklist[dir="rtl"] li::after {
+ left: unset;
+ right: -24px;
+}
+
+.simple-checklist[dir="rtl"] li.checked::before {
+ left: unset;
+ right: -22px;
+}
+
[dir="rtl"] .taskItemTools { right: unset; left: 0 }
/* Check list */
.ProseMirror ul.simple-checklist {
list-style: none;
- padding: 0;
+ margin-block: 0px !important;
+ padding-inline: 0px !important;
+ padding-inline-start: 24px !important;
}
-.ProseMirror ul.simple-checklist > li {
- display: flex;
+.ProseMirror li.nested > ul.simple-checklist {
+ padding-inline-start: 15px !important;
}
-.ProseMirror ul.simple-checklist > li input {
- flex: 0 0 auto;
- margin-right: 0.5rem;
- user-select: none;
- height: 1rem;
- width: 1rem;
- accent-color: var(--accent);
+.simple-checklist li {
+ position: relative;
}
-.ProseMirror ul.simple-checklist > li > div {
- flex: 1 1 auto;
+.simple-checklist > li::after {
+ position: absolute;
+ top: 0px;
+ cursor: pointer;
+ content: "";
+ background-size: 18px;
+ width: 14px;
+ height: 14px;
+
+ border: 2px solid var(--icon);
+ border-radius: 5px;
+ left: -24px;
}
-@media screen and (max-width: 480px) {
- .ProseMirror ul.simple-checklist > li > input {
- height: 21px;
- width: 21px;
- }
+.simple-checklist > li.checked::after {
+ border: 2px solid var(--accent);
+}
- .ProseMirror ul.simple-checklist > li > div {
- margin-top: 2px;
- }
+.simple-checklist > li.checked::before {
+ position: absolute;
+ top: 2px;
+ cursor: pointer;
+ content: "";
+ background-size: 18px;
+ width: 14px;
+ height: 14px;
+ left: -22px;
+
+ background-color: var(--accent);
+ mask: url(data:image/svg+xml;base64,ICA8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICAgIDxwYXRoIGQ9Ik0yMSw3TDksMTlMMy41LDEzLjVMNC45MSwxMi4wOUw5LDE2LjE3TDE5LjU5LDUuNTlMMjEsN1oiLz4KICA8L3N2Zz4K)
+ no-repeat 50% 50%;
+ mask-size: cover;
+}
+
+.simple-checklist > li.checked > p {
+ opacity: 0.8;
+ text-decoration-line: line-through;
}
/* Callout */