diff --git a/packages/editor/src/extensions/image/__tests__/__snapshots__/image.test.ts.snap b/packages/editor/src/extensions/image/__tests__/__snapshots__/image.test.ts.snap
new file mode 100644
index 000000000..acd067595
--- /dev/null
+++ b/packages/editor/src/extensions/image/__tests__/__snapshots__/image.test.ts.snap
@@ -0,0 +1,171 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`migration > inline image in outline list 1`] = `
+{
+ "content": [
+ {
+ "content": [
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "item 1",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "hello",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ {
+ "attrs": {
+ "align": undefined,
+ "aspectRatio": 1,
+ "filename": undefined,
+ "hash": undefined,
+ "height": null,
+ "mime": undefined,
+ "progress": 0,
+ "size": undefined,
+ "src": "image.png",
+ "type": "image",
+ "width": null,
+ },
+ "type": "image",
+ },
+ {
+ "content": [
+ {
+ "text": "world",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ {
+ "content": [
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "sub item 2",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "sub item 3",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ ],
+ "type": "outlineList",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "item 4",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ ],
+ "type": "outlineList",
+ },
+ ],
+ "type": "doc",
+}
+`;
+
+exports[`migration > inline image in paragraph 1`] = `
+{
+ "content": [
+ {
+ "content": [
+ {
+ "text": "hello",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ {
+ "attrs": {
+ "align": undefined,
+ "aspectRatio": 1,
+ "filename": undefined,
+ "hash": undefined,
+ "height": null,
+ "mime": undefined,
+ "progress": 0,
+ "size": undefined,
+ "src": "image.png",
+ "type": "image",
+ "width": null,
+ },
+ "type": "image",
+ },
+ {
+ "content": [
+ {
+ "text": "world",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "doc",
+}
+`;
diff --git a/packages/editor/src/extensions/image/__tests__/image.test.ts b/packages/editor/src/extensions/image/__tests__/image.test.ts
new file mode 100644
index 000000000..00b80bb92
--- /dev/null
+++ b/packages/editor/src/extensions/image/__tests__/image.test.ts
@@ -0,0 +1,75 @@
+/*
+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 {
+ createEditor,
+ h,
+ p,
+ outlineList,
+ outlineListItem
+} from "../../../../test-utils/index.js";
+import { test, expect, describe } from "vitest";
+import { ImageNode } from "../index.js";
+import { OutlineList } from "../../outline-list/outline-list.js";
+import { OutlineListItem } from "../../outline-list-item/outline-list-item.js";
+
+describe("migration", () => {
+ test(`inline image in paragraph`, async () => {
+ const el = p(["hello", h("img", [], { src: "image.png" }), "world"]);
+ const {
+ builder: { image },
+ editor
+ } = createEditor({
+ initialContent: el.outerHTML,
+ extensions: {
+ image: ImageNode.configure({})
+ }
+ });
+
+ expect(editor.getJSON()).toMatchSnapshot();
+ });
+
+ test(`inline image in outline list`, async () => {
+ const el = outlineList(
+ outlineListItem(["item 1"]),
+ outlineListItem(
+ ["hello", h("img", [], { src: "image.png" }), "world"],
+ outlineList(
+ outlineListItem(["sub item 2"]),
+ outlineListItem(["sub item 3"])
+ )
+ ),
+ outlineListItem(["item 4"])
+ );
+
+ const {
+ builder: { image },
+ editor
+ } = createEditor({
+ initialContent: el.outerHTML,
+ extensions: {
+ outlineList: OutlineList,
+ outlineListItem: OutlineListItem,
+ image: ImageNode
+ }
+ });
+
+ expect(editor.getJSON()).toMatchSnapshot();
+ });
+});
diff --git a/packages/editor/src/extensions/image/image.ts b/packages/editor/src/extensions/image/image.ts
index b59554d2a..85f7a8ce7 100644
--- a/packages/editor/src/extensions/image/image.ts
+++ b/packages/editor/src/extensions/image/image.ts
@@ -131,39 +131,6 @@ export const ImageNode = Node.create({
getAttrs(node) {
if (node.querySelectorAll("img").length <= 0) return false;
return null;
- },
- getContent: (dom, schema) => {
- const wrapper = document.createElement("div");
- let buffer = "";
-
- const flushBuffer = () => {
- if (buffer.trim().length > 0) {
- const pEl = document.createElement("p");
- pEl.innerHTML = buffer;
- wrapper.appendChild(pEl);
- buffer = "";
- }
- };
-
- for (const child of dom.childNodes) {
- if (
- child.nodeType === globalThis.Node.ELEMENT_NODE &&
- (child as HTMLElement).tagName === "IMG"
- ) {
- flushBuffer();
- wrapper.appendChild(child);
- } else {
- if (child.nodeType === globalThis.Node.ELEMENT_NODE) {
- buffer += (child as HTMLElement).outerHTML;
- } else if (child.nodeType === globalThis.Node.TEXT_NODE) {
- buffer += child.textContent;
- }
- }
- }
- flushBuffer();
-
- const parser = DOMParser.fromSchema(schema);
- return parser.parse(wrapper).content;
}
},
{
diff --git a/packages/editor/src/extensions/outline-list-item/__tests__/__snapshots__/outline-list-item.test.ts.snap b/packages/editor/src/extensions/outline-list-item/__tests__/__snapshots__/outline-list-item.test.ts.snap
new file mode 100644
index 000000000..100c63fd2
--- /dev/null
+++ b/packages/editor/src/extensions/outline-list-item/__tests__/__snapshots__/outline-list-item.test.ts.snap
@@ -0,0 +1,130 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`outline list item > code block in outline list item 1`] = `
+{
+ "content": [
+ {
+ "content": [
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "item 1",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "hello",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ {
+ "attrs": {
+ "caretPosition": undefined,
+ "id": "codeblock-test-id-123456",
+ "indentLength": 2,
+ "indentType": "space",
+ "language": null,
+ "lines": [],
+ },
+ "content": [
+ {
+ "text": "const x = 1;",
+ "type": "text",
+ },
+ ],
+ "type": "codeblock",
+ },
+ {
+ "content": [
+ {
+ "text": "world",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ {
+ "content": [
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "sub item 2",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "sub item 3",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ ],
+ "type": "outlineList",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ {
+ "attrs": {
+ "collapsed": false,
+ },
+ "content": [
+ {
+ "content": [
+ {
+ "text": "item 4",
+ "type": "text",
+ },
+ ],
+ "type": "paragraph",
+ },
+ ],
+ "type": "outlineListItem",
+ },
+ ],
+ "type": "outlineList",
+ },
+ ],
+ "type": "doc",
+}
+`;
diff --git a/packages/editor/src/extensions/outline-list-item/__tests__/outline-list-item.test.ts b/packages/editor/src/extensions/outline-list-item/__tests__/outline-list-item.test.ts
new file mode 100644
index 000000000..e083e89fb
--- /dev/null
+++ b/packages/editor/src/extensions/outline-list-item/__tests__/outline-list-item.test.ts
@@ -0,0 +1,73 @@
+/*
+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 {
+ createEditor,
+ h,
+ li,
+ outlineList,
+ outlineListItem
+} from "../../../../test-utils/index.js";
+import { test, expect, describe, beforeAll, vi } from "vitest";
+import { OutlineList } from "../../outline-list/outline-list.js";
+import { OutlineListItem } from "../outline-list-item.js";
+import { CodeBlock } from "../../code-block/code-block.js";
+
+describe("outline list item", () => {
+ beforeAll(() => {
+ vi.mock("nanoid", () => ({
+ nanoid: () => "test-id-123456"
+ }));
+ });
+
+ test(`code block in outline list item`, async () => {
+ const subList = outlineList(
+ outlineListItem(["sub item 2"]),
+ outlineListItem(["sub item 3"])
+ );
+ const listItemWithCodeBlock = li(
+ [
+ h("p", ["hello"]),
+ h("pre", [h("code", ["const x = 1;"])]),
+ h("p", ["world"]),
+ subList
+ ],
+ { "data-type": "outlineListItem" }
+ );
+ const el = outlineList(
+ outlineListItem(["item 1"]),
+ listItemWithCodeBlock,
+ outlineListItem(["item 4"])
+ );
+
+ const {
+ builder: { codeBlock },
+ editor
+ } = createEditor({
+ initialContent: el.outerHTML,
+ extensions: {
+ outlineList: OutlineList,
+ outlineListItem: OutlineListItem,
+ codeBlock: CodeBlock
+ }
+ });
+
+ expect(editor.getJSON()).toMatchSnapshot();
+ });
+});
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 86fe24405..14165ee14 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
@@ -25,6 +25,7 @@ import {
import { findParentNodeOfTypeClosestToPos } from "../../utils/prosemirror.js";
import { OutlineList } from "../outline-list/outline-list.js";
import { keybindings, tiptapKeys } from "@notesnook/common";
+import { Paragraph } from "../paragraph/paragraph.js";
export interface ListItemOptions {
HTMLAttributes: Record;
@@ -52,13 +53,14 @@ export const OutlineListItem = Node.create({
};
},
- content: "paragraph+ list?",
+ content: "block+",
defining: true,
parseHTML() {
return [
{
+ priority: 100,
tag: `li[data-type="${this.name}"]`
}
];
@@ -92,21 +94,37 @@ export const OutlineListItem = Node.create({
return true;
});
},
- Enter: () => {
- // const subList = findSublist(editor, this.type);
- // if (!subList) return this.editor.commands.splitListItem(this.name);
-
- // const { isCollapsed, subListPos } = subList;
-
- // if (isCollapsed) {
- // return this.editor.commands.toggleOutlineCollapse(subListPos, false);
- // }
+ Enter: ({ editor }) => {
+ const { $anchor } = editor.state.selection;
+ if (
+ $anchor.parent.type.name !== Paragraph.name ||
+ ($anchor.parent.type.name === Paragraph.name &&
+ $anchor.node($anchor.depth - 1)?.type.name !== this.type.name)
+ )
+ return false;
return this.editor.commands.splitListItem(this.name);
},
- Tab: () => this.editor.commands.sinkListItem(this.name),
- [keybindings.liftListItem.keys]: () =>
- this.editor.commands.liftListItem(this.name)
+ Tab: ({ editor }) => {
+ const { $anchor } = editor.state.selection;
+ if (
+ $anchor.parent.type.name !== Paragraph.name ||
+ ($anchor.parent.type.name === Paragraph.name &&
+ $anchor.node($anchor.depth - 1)?.type.name !== this.type.name)
+ )
+ return false;
+ return this.editor.commands.sinkListItem(this.name);
+ },
+ [keybindings.liftListItem.keys]: ({ editor }) => {
+ const { $anchor } = editor.state.selection;
+ if (
+ $anchor.parent.type.name !== Paragraph.name ||
+ ($anchor.parent.type.name === Paragraph.name &&
+ $anchor.node($anchor.depth - 1)?.type.name !== this.type.name)
+ )
+ return false;
+ return this.editor.commands.liftListItem(this.name);
+ }
};
},
@@ -124,7 +142,7 @@ export const OutlineListItem = Node.create({
function onClick(e: MouseEvent | TouchEvent) {
if (e instanceof MouseEvent && e.button !== 0) return;
- if (!(e.target instanceof HTMLParagraphElement)) return;
+ if (!(e.target instanceof HTMLElement)) return;
if (!li.classList.contains("nested")) return;
const pos = typeof getPos === "function" ? getPos() : 0;
diff --git a/packages/editor/styles/styles.css b/packages/editor/styles/styles.css
index 3ff2ec936..e676c1ee2 100644
--- a/packages/editor/styles/styles.css
+++ b/packages/editor/styles/styles.css
@@ -536,11 +536,11 @@ p > *::selection {
.outline-list li.collapsed .outline-list {
display: none;
}
-.outline-list li > :first-child {
+.outline-list li {
position: relative;
}
-.outline-list > li > :first-child::before {
+.outline-list > li::before {
position: absolute;
top: 0px;
cursor: pointer;
@@ -560,13 +560,13 @@ p > *::selection {
left: -22px;
}
-.outline-list li:not(.nested) > :first-child::before {
+.outline-list li:not(.nested)::before {
mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM4ODg4ODgiIGQ9Ik0xMiAyQTEwIDEwIDAgMCAwIDIgMTJhMTAgMTAgMCAwIDAgMTAgMTBhMTAgMTAgMCAwIDAgMTAtMTBBMTAgMTAgMCAwIDAgMTIgMloiLz48L3N2Zz4=)
no-repeat 50% 50%;
scale: 0.4;
}
-.outline-list li.collapsed > :first-child::before {
+.outline-list li.collapsed::before {
transform: rotate(-90deg);
}
diff --git a/packages/editor/test-utils/index.ts b/packages/editor/test-utils/index.ts
index 208577dbe..3474c87de 100644
--- a/packages/editor/test-utils/index.ts
+++ b/packages/editor/test-utils/index.ts
@@ -92,3 +92,19 @@ export const p = elem("p");
export function text(text: string) {
return document.createTextNode(text);
}
+
+export function outlineList(...children: HTMLLIElement[]) {
+ return ul(children, { "data-type": "outlineList" });
+}
+
+export function outlineListItem(
+ paragraphChildren: (string | HTMLElement)[],
+ subList?: HTMLUListElement
+) {
+ const children: HTMLElement[] = [h("p", paragraphChildren)];
+ if (subList) children.push(subList);
+
+ return li(children, {
+ "data-type": "outlineListItem"
+ });
+}