diff --git a/packages/editor/src/extensions/outline-list-item/component.tsx b/packages/editor/src/extensions/outline-list-item/component.tsx
deleted file mode 100644
index 4f291930e..000000000
--- a/packages/editor/src/extensions/outline-list-item/component.tsx
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
-This file is part of the Notesnook project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-
-import { Box, Flex, Text } from "@theme-ui/components";
-import { GetPosNode, ReactNodeViewProps } from "../react";
-import { Icon } from "../../toolbar/components/icon";
-import { Icons } from "../../toolbar/icons";
-import { Node as ProsemirrorNode } from "prosemirror-model";
-import { findChildren } from "@tiptap/core";
-import { OutlineList } from "../outline-list/outline-list";
-import { useIsMobile } from "../../toolbar/stores/toolbar-store";
-import { Editor } from "../../types";
-import { TextDirections } from "../text-direction";
-
-export function OutlineListItemComponent(props: ReactNodeViewProps) {
- const { editor, node, getPos, forwardRef } = props;
-
- const isMobile = useIsMobile();
- const isNested = node.lastChild?.type.name === OutlineList.name;
- const isCollapsed = isNested && node.lastChild?.attrs.collapsed;
-
- return (
-
-
- {isNested ? (
- <>
-
-
- >
- ) : (
-
- )}
-
- {isNested && !isCollapsed && (
-
- )}
-
-
-
- );
-}
-
-function toggleOutlineList(
- editor: Editor,
- node: ProsemirrorNode,
- isCollapsed: boolean,
- nodePos: number
-) {
- const [subList] = findChildren(
- node,
- (node) => node.type.name === OutlineList.name
- );
- if (!subList) return;
- const { pos } = subList;
-
- editor.current?.commands.toggleOutlineCollapse(
- pos + nodePos + 1,
- !isCollapsed
- );
-}
-
-type ToggleIconButtonProps = {
- textDirection: TextDirections;
- isCollapsed: boolean;
- isMobile: boolean;
-
- editor: Editor;
- node: ProsemirrorNode;
- getPos: GetPosNode;
-};
-function ToggleIconButton(props: ToggleIconButtonProps) {
- const { textDirection, isCollapsed, isMobile, editor, node, getPos } = props;
-
- return (
- e.preventDefault()}
- onTouchEnd={(e) => {
- e.preventDefault();
- toggleOutlineList(editor, node, isCollapsed, getPos());
- }}
- onClick={() => {
- toggleOutlineList(editor, node, isCollapsed, getPos());
- }}
- />
- );
-}
diff --git a/packages/editor/src/extensions/outline-list-item/outline-list-item.ts b/packages/editor/src/extensions/outline-list-item/outline-list-item.ts
index 478d13ff0..cef020848 100644
--- a/packages/editor/src/extensions/outline-list-item/outline-list-item.ts
+++ b/packages/editor/src/extensions/outline-list-item/outline-list-item.ts
@@ -22,8 +22,6 @@ import { NodeType } from "prosemirror-model";
import { findParentNodeOfTypeClosestToPos } from "../../utils/prosemirror";
import { onArrowUpPressed, onBackspacePressed } from "../list-item/commands";
import { OutlineList } from "../outline-list/outline-list";
-import { createNodeView } from "../react";
-import { OutlineListItemComponent } from "./component";
export interface ListItemOptions {
HTMLAttributes: Record;
@@ -46,6 +44,19 @@ export const OutlineListItem = Node.create({
};
},
+ addAttributes() {
+ return {
+ collapsed: {
+ default: false,
+ keepOnSplit: false,
+ parseHTML: (element) => element.dataset.collapsed === "true",
+ renderHTML: (attributes) => ({
+ "data-collapsed": attributes.collapsed === true
+ })
+ }
+ };
+ },
+
content: "heading* paragraph block*",
defining: true,
@@ -114,10 +125,69 @@ export const OutlineListItem = Node.create({
},
addNodeView() {
- return createNodeView(OutlineListItemComponent, {
- contentDOMFactory: true,
- wrapperFactory: () => document.createElement("li")
- });
+ return ({ node, getPos, editor }) => {
+ const isNested = node.lastChild?.type.name === OutlineList.name;
+
+ const li = document.createElement("li");
+
+ if (node.attrs.collapsed) li.classList.add("collapsed");
+ else li.classList.remove("collapsed");
+
+ if (isNested) li.classList.add("nested");
+ else li.classList.remove("nested");
+
+ function onClick(e: MouseEvent | TouchEvent) {
+ if (!(e.target instanceof HTMLParagraphElement)) return;
+ if (!li.classList.contains("nested")) return;
+
+ const clientX =
+ e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
+ const clientY =
+ e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
+
+ const { x, y } = li.getBoundingClientRect();
+
+ const hitArea = { width: 26, height: 24 };
+ if (
+ clientX >= x - hitArea.width &&
+ clientX <= x &&
+ clientY >= y &&
+ clientY <= y + hitArea.height
+ ) {
+ const pos = typeof getPos === "function" ? getPos() : 0;
+ if (!pos) return;
+
+ e.preventDefault();
+ editor.commands.toggleOutlineCollapse(
+ pos,
+ !li.classList.contains("collapsed")
+ );
+ }
+ }
+
+ li.onmousedown = onClick;
+ li.ontouchstart = onClick;
+
+ return {
+ dom: li,
+ contentDOM: li,
+ update: (updatedNode) => {
+ if (updatedNode.type !== this.type) {
+ return false;
+ }
+ const isNested =
+ updatedNode.lastChild?.type.name === OutlineList.name;
+
+ if (updatedNode.attrs.collapsed) li.classList.add("collapsed");
+ else li.classList.remove("collapsed");
+
+ if (isNested) li.classList.add("nested");
+ else li.classList.remove("nested");
+
+ return true;
+ }
+ };
+ };
}
});
@@ -139,17 +209,4 @@ function findSublist(editor: Editor, type: NodeType) {
const subListPos = listItem.pos + subList.pos + 1;
return { isCollapsed, isNested, subListPos };
- // return (
- // this.editor
- // .chain()
- // .command(({ tr }) => {
- // tr.setNodeMarkup(listItem.pos + subList.pos + 1, undefined, {
- // collapsed: !isCollapsed,
- // });
- // return true;
- // })
- // //.setTextSelection(listItem.pos + subList.pos + 1)
- // //.splitListItem(this.name)
- // .run()
- // );
}
diff --git a/packages/editor/src/extensions/outline-list/component.tsx b/packages/editor/src/extensions/outline-list/component.tsx
deleted file mode 100644
index 0500eb79a..000000000
--- a/packages/editor/src/extensions/outline-list/component.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-This file is part of the Notesnook project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-
-import { Text } from "@theme-ui/components";
-import { ReactNodeViewProps } from "../react";
-import { useMemo } from "react";
-import { OutlineListAttributes } from "./outline-list";
-import { OutlineListItem } from "../outline-list-item";
-
-export function OutlineListComponent(
- props: ReactNodeViewProps
-) {
- const { editor, getPos, node, forwardRef } = props;
- const { collapsed, textDirection } = node.attrs;
-
- const isNested = useMemo(() => {
- const pos = editor.state.doc.resolve(getPos());
- return pos.parent?.type.name === OutlineListItem.name;
- }, [editor, getPos]);
-
- return (
- <>
-
- >
- );
-}
diff --git a/packages/editor/src/extensions/outline-list/outline-list.ts b/packages/editor/src/extensions/outline-list/outline-list.ts
index 459519d4c..4fc73efe5 100644
--- a/packages/editor/src/extensions/outline-list/outline-list.ts
+++ b/packages/editor/src/extensions/outline-list/outline-list.ts
@@ -18,8 +18,6 @@ along with this program. If not, see .
*/
import { Node, mergeAttributes, wrappingInputRule } from "@tiptap/core";
-import { createNodeView } from "../react";
-import { OutlineListComponent } from "./component";
export type OutlineListAttributes = {
collapsed: boolean;
@@ -51,19 +49,6 @@ export const OutlineList = Node.create({
};
},
- addAttributes() {
- return {
- collapsed: {
- default: false,
- keepOnSplit: false,
- parseHTML: (element) => element.dataset.collapsed === "true",
- renderHTML: (attributes) => ({
- "data-collapsed": attributes.collapsed === true
- })
- }
- };
- },
-
group: "block list",
content: `${outlineListItemName}+`,
@@ -113,13 +98,13 @@ export const OutlineList = Node.create({
},
addNodeView() {
- return createNodeView(OutlineListComponent, {
- contentDOMFactory: () => {
- const content = document.createElement("ul");
- content.classList.add(`${this.name.toLowerCase()}-content-wrapper`);
- content.style.whiteSpace = "inherit";
- return { dom: content };
- }
- });
+ return () => {
+ const ul = document.createElement("ul");
+ ul.classList.add("outline-list");
+ return {
+ dom: ul,
+ contentDOM: ul
+ };
+ };
}
});
diff --git a/packages/editor/styles/styles.css b/packages/editor/styles/styles.css
index 77ec0bf6a..fee6edf99 100644
--- a/packages/editor/styles/styles.css
+++ b/packages/editor/styles/styles.css
@@ -41,7 +41,7 @@
border: 2px solid var(--primary);
}
-.ProseMirror code:not(pre code){
+.ProseMirror code:not(pre code) {
background-color: var(--bgSecondary);
border: 1px solid var(--border);
border-radius: 5px;
@@ -51,7 +51,7 @@
font-size: 10pt !important;
}
-.ProseMirror code > span {
+.ProseMirror code > span {
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono,
Menlo, monospace !important;
}
@@ -64,7 +64,8 @@
margin-bottom: 5px;
}
-.ProseMirror ul ul, .ProseMirror ol ol {
+.ProseMirror ul ul,
+.ProseMirror ol ol {
margin-top: 5px;
}
@@ -90,8 +91,7 @@
}
.ProseMirror > div.codeblock-view-content-wrap,
-.ProseMirror > div.taskList-view-content-wrap,
-.ProseMirror > div.outlineList-view-content-wrap {
+.ProseMirror > div.taskList-view-content-wrap {
margin-top: 1em;
margin-bottom: 1em;
}
@@ -471,6 +471,71 @@ p > *::selection {
padding-top: 2px;
} */
+/* Outline lists */
+
+.ProseMirror > .outline-list {
+ padding-left: 18px;
+}
+
+.outline-list {
+ list-style-type: none;
+ position: relative;
+}
+
+.outline-list li ul {
+ padding-left: 25px;
+}
+
+.outline-list li.collapsed .outline-list {
+ display: none;
+}
+.outline-list li > p {
+ position: relative;
+}
+
+.outline-list li > p::before {
+ position: absolute;
+ top: 4px;
+ left: -22px;
+ cursor: pointer;
+ content: "";
+ background-size: 18px;
+ width: 18px;
+ height: 18px;
+
+ background-color: var(--icon);
+ mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik03LjQxIDguNThMMTIgMTMuMTdsNC41OS00LjU5TDE4IDEwbC02IDZsLTYtNmwxLjQxLTEuNDJaIi8+PC9zdmc+)
+ no-repeat 50% 50%;
+ mask-size: cover;
+ border: 1px solid var(--background);
+
+ transform: rotate(0);
+ transition: transform 250ms ease;
+}
+
+.outline-list li:not(.nested) > p::before {
+ mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik0xMiAyQTEwIDEwIDAgMCAwIDIgMTJhMTAgMTAgMCAwIDAgMTAgMTBhMTAgMTAgMCAwIDAgMTAtMTBBMTAgMTAgMCAwIDAgMTIgMloiLz48L3N2Zz4=)
+ no-repeat 50% 50%;
+ scale: 0.4;
+}
+
+.outline-list li.collapsed > p::before {
+ transform: rotate(-90deg);
+}
+
+.outline-list li .outline-list::before {
+ content: " ";
+ position: absolute;
+ height: 100%;
+ left: -14px;
+ border-left: 1px solid var(--border);
+ transition: border 200ms ease-in-out;
+}
+
+.outline-list li .outline-list:hover:before {
+ border-left: 1px solid var(--bgSecondaryHover);
+}
+
/* RTL */
[dir="rtl"] * {
@@ -498,4 +563,4 @@ blockquote[dir="rtl"] {
[dir="rtl"] .outline-list-item-toggle:not(.rtl) {
display: none;
-}
\ No newline at end of file
+}