From fc9f70b480fb9f8179b31f54420e00332d2e1b38 Mon Sep 17 00:00:00 2001 From: yousefed Date: Tue, 22 Nov 2022 11:30:15 +0100 Subject: [PATCH 1/3] fix animations for headings and indents --- lexical-submodule/lexical | 1 + .../Blocks/PreviousBlockTypePlugin.ts | 46 ++++++++----- .../extensions/Blocks/nodes/Block.module.css | 69 ++++++++++++------- .../core/src/extensions/Blocks/nodes/Block.ts | 9 +-- .../extensions/Blocks/nodes/HeadingBlock.ts | 26 ++++--- .../src/extensions/Blocks/nodes/TextBlock.ts | 3 +- 6 files changed, 98 insertions(+), 56 deletions(-) create mode 160000 lexical-submodule/lexical diff --git a/lexical-submodule/lexical b/lexical-submodule/lexical new file mode 160000 index 0000000000..73aa323947 --- /dev/null +++ b/lexical-submodule/lexical @@ -0,0 +1 @@ +Subproject commit 73aa323947eab19fe5e329f45cf5cc0b614a3076 diff --git a/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts b/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts index 1db218a4e3..e8d1403b14 100644 --- a/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts +++ b/packages/core/src/extensions/Blocks/PreviousBlockTypePlugin.ts @@ -5,14 +5,9 @@ import { } from "@tiptap/core"; import { Plugin, PluginKey } from "prosemirror-state"; import { Decoration, DecorationSet } from "prosemirror-view"; -import BlockAttributes from "./BlockAttributes"; const PLUGIN_KEY = new PluginKey(`previous-blocks`); -// Inserts "prev-" string into an HTML attribute name with a "data-" prefix, e.g. "data-depth" -> "data-prev-depth". -// Assumes "data-" prefix is in the attribute name. -const insertPrev = (attr: string) => attr.slice(0, 5) + "prev-" + attr.slice(5); - /** * This plugin tracks transformation of Block node attributes, so we can support CSS transitions. * @@ -75,7 +70,6 @@ export const PreviousBlockTypePlugin = () => { changes.forEach(() => { const oldNodes = findChildren(oldState.doc, (node) => node.attrs.id); - const oldNodesById = new Map( oldNodes.map((node) => [node.node.attrs.id, node]) ); @@ -84,28 +78,38 @@ export const PreviousBlockTypePlugin = () => { for (let node of newNodes) { const oldNode = oldNodesById.get(node.node.attrs.id); - if (oldNode) { + const oldContentNode = oldNode?.node.firstChild; + const newContentNode = node.node.firstChild; + if (oldNode && oldContentNode && newContentNode) { const newAttrs = { - listType: node.node.attrs.listType, - blockColor: node.node.attrs.blockColor, - blockStyle: node.node.attrs.blockStyle, - headingType: node.node.attrs.headingType, + // listType: node.node.attrs.listType, + level: newContentNode.attrs.level, + type: newContentNode.type.name, depth: newState.doc.resolve(node.pos).depth, }; const oldAttrs = { - listType: oldNode.node.attrs.listType, - blockColor: oldNode.node.attrs.blockColor, - blockStyle: oldNode.node.attrs.blockStyle, - headingType: oldNode.node.attrs.headingType, + // listType: oldNode.node.attrs.listType, + level: oldContentNode.attrs.level, + type: oldContentNode.type.name, depth: oldState.doc.resolve(oldNode.pos).depth, }; if ( JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs) // TODO: faster deep equal? ) { - (oldAttrs as any).depthChange = oldAttrs.depth - newAttrs.depth; + (oldAttrs as any)["depth-change"] = + oldAttrs.depth - newAttrs.depth; prev.prevBlockAttrs[node.node.attrs.id] = oldAttrs; + + // for debugging: + // console.log( + // "previousBlockTypePlugin changes detected, oldAttrs", + // oldAttrs, + // "new", + // newAttrs + // ); + prev.needsUpdate = true; } } @@ -135,9 +139,15 @@ export const PreviousBlockTypePlugin = () => { const decorationAttributes: any = {}; for (let [nodeAttr, val] of Object.entries(prevAttrs)) { - decorationAttributes[insertPrev(BlockAttributes[nodeAttr])] = - val || "none"; + decorationAttributes["data-prev-" + nodeAttr] = val || "none"; } + + // for debugging: + // console.log( + // "previousBlockTypePlugin committing decorations", + // decorationAttributes + // ); + const decoration = Decoration.node(pos, pos + node.nodeSize, { ...decorationAttributes, }); diff --git a/packages/core/src/extensions/Blocks/nodes/Block.module.css b/packages/core/src/extensions/Blocks/nodes/Block.module.css index 140c8711f4..af2fe14dc3 100644 --- a/packages/core/src/extensions/Blocks/nodes/Block.module.css +++ b/packages/core/src/extensions/Blocks/nodes/Block.module.css @@ -23,11 +23,22 @@ BASIC STYLES } .blockContent { + font-size: 1em; padding: 3px 0; transition: font-size 0.2s; /* display: inline-block; */ } +/* reset styles, they will be set on blockContent */ +.blockContent p, +.blockContent h1, +.blockContent h2, +.blockContent h3 { + margin: 0; + padding: 0; + font-size: inherit; +} + /* NESTED BLOCKS */ @@ -54,7 +65,7 @@ NESTED BLOCKS height: 0; } -/* NESTED BLOCK ANIMATIONS */ +/* NESTED BLOCK ANIMATIONS (change in indent) */ [data-prev-depth-change="1"] { --x: 1; @@ -97,31 +108,39 @@ NESTED BLOCKS } /* HEADINGS*/ -.blockOuter[data-prev-heading-type="1"] > .block > div:first-child, -.blockOuter[data-heading-type="1"]:not([data-prev-heading-type]) - > .block - > div:first-child { - font-size: 3em; - font-weight: bold; +[data-level="1"] { + --level: 3em; +} +[data-level="2"] { + --level: 2em; +} +[data-level="3"] { + --level: 1.3em; } -.blockOuter[data-prev-heading-type="2"] > .block > div:first-child, -.blockOuter[data-heading-type="2"]:not([data-prev-heading-type]) - > .block - > div:first-child { - font-size: 2em; +[data-prev-level="1"] { + --prev-level: 3em; +} +[data-prev-level="2"] { + --prev-level: 2em; +} +[data-prev-level="3"] { + --prev-level: 1.3em; +} + +.blockOuter[data-prev-type="headingBlock"] > .block > .blockContent { + font-size: var(--prev-level); font-weight: bold; } -.blockOuter[data-prev-heading-type="3"] > .block > div:first-child, -.blockOuter[data-heading-type="3"]:not([data-prev-heading-type]) +.blockOuter:not([data-prev-type]) > .block - > div:first-child { - font-size: 1.3em; + > .blockContent[data-content-type="headingBlock"] { + font-size: var(--level); font-weight: bold; } -/* LISTS */ +/* LISTS (TODO) */ .block > div:first-child::before { content: ""; @@ -191,12 +210,12 @@ NESTED BLOCKS /* PLACEHOLDERS*/ -.blockContent > div { +.blockContent > :first-child { display: inline; } -.blockContent.isEmpty div::before, -.blockContent.isFilter div::before { +.blockContent.isEmpty > :first-child:before, +.blockContent.isFilter > :first-child:before { /*float: left; */ content: ""; color: #aeb8c2; @@ -209,18 +228,18 @@ NESTED BLOCKS /* TODO: would be nicer if defined from code */ -.blockContent.isEmpty.hasAnchor div::before { +.blockContent.isEmpty.hasAnchor > :first-child:before { content: "Enter text or type '/' for commands"; } -.blockContent.isFilter.hasAnchor div::before { +.blockContent.isFilter.hasAnchor > :first-child:before { content: "Type to filter"; } -[data-heading-type] > .blockContent.isEmpty div::before { +.blockContent[data-content-type="headingBlock"].isEmpty > :first-child::before { content: "Heading"; } -[data-list-type] > .blockContent.isEmpty div::before { +.blockContent[data-content-type="listBlock"] > :first-child div::before { content: "List"; -} \ No newline at end of file +} diff --git a/packages/core/src/extensions/Blocks/nodes/Block.ts b/packages/core/src/extensions/Blocks/nodes/Block.ts index 3db97338d2..c5b87ef326 100644 --- a/packages/core/src/extensions/Blocks/nodes/Block.ts +++ b/packages/core/src/extensions/Blocks/nodes/Block.ts @@ -1,12 +1,12 @@ import { InputRule, mergeAttributes, Node } from "@tiptap/core"; -import { TextSelection } from "prosemirror-state"; import { Slice } from "prosemirror-model"; -import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin"; -import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos"; +import { TextSelection } from "prosemirror-state"; import BlockAttributes from "../BlockAttributes"; +import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos"; +import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin"; +import styles from "./Block.module.css"; import { HeadingBlockAttributes } from "./HeadingBlock"; import { ListItemBlockAttributes } from "./ListItemBlock"; -import styles from "./Block.module.css"; export interface IBlock { HTMLAttributes: Record; @@ -112,6 +112,7 @@ export const Block = Node.create({ [ "div", mergeAttributes(attrs, { + // TODO: maybe remove html attributes from inner block class: styles.block, "data-node-type": this.name, }), diff --git a/packages/core/src/extensions/Blocks/nodes/HeadingBlock.ts b/packages/core/src/extensions/Blocks/nodes/HeadingBlock.ts index 62f31b7fdb..28d6984736 100644 --- a/packages/core/src/extensions/Blocks/nodes/HeadingBlock.ts +++ b/packages/core/src/extensions/Blocks/nodes/HeadingBlock.ts @@ -1,4 +1,4 @@ -import { Node, NodeViewRendererProps } from "@tiptap/core"; +import { mergeAttributes, Node } from "@tiptap/core"; import styles from "./Block.module.css"; export type HeadingBlockAttributes = { @@ -11,10 +11,20 @@ export const HeadingBlock = Node.create({ addAttributes() { return { - level: { default: "1" }, + level: { + default: "1", + parseHTML: (element) => element.getAttribute("data-level"), + renderHTML: (attributes) => { + return { + "data-level": attributes.level, + }; + }, + }, }; }, + /* + TODO: this is not necessary? addNodeView() { return (props: NodeViewRendererProps) => { const element = document.createElement("div"); @@ -32,7 +42,7 @@ export const HeadingBlock = Node.create({ contentDOM: editableElement, }; }; - }, + },*/ parseHTML() { return [ @@ -51,15 +61,15 @@ export const HeadingBlock = Node.create({ ]; }, - renderHTML({ HTMLAttributes }) { + renderHTML({ node, HTMLAttributes }) { return [ "div", - { - "data-node-type": "block-content", + mergeAttributes(HTMLAttributes, { + "data-node-type": "block-content", // TODO: only for testing? if so, rename to data-test-*? "data-content-type": this.name, class: styles.blockContent, - }, - ["h" + HTMLAttributes["level"], 0], + }), + ["h" + node.attrs.level, 0], ]; }, }); diff --git a/packages/core/src/extensions/Blocks/nodes/TextBlock.ts b/packages/core/src/extensions/Blocks/nodes/TextBlock.ts index 6c6bad746e..066181d4fc 100644 --- a/packages/core/src/extensions/Blocks/nodes/TextBlock.ts +++ b/packages/core/src/extensions/Blocks/nodes/TextBlock.ts @@ -13,6 +13,7 @@ export const TextBlock = Node.create({ name: "textBlock", content: "inline*", + // TODO: not necessary because we use renderHTML? addNodeView() { return (_props: NodeViewRendererProps) => { const element = document.createElement("div"); @@ -43,7 +44,7 @@ export const TextBlock = Node.create({ return [ "div", { - "data-node-type": "block-content", + "data-node-type": "block-content", // TODO: only for testing? if so, rename to data-test-*? "data-content-type": this.name, class: styles.blockContent, }, From 1c33c638ed0126ef5f46b41f2049013a0ba845e6 Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 28 Nov 2022 13:23:17 +0100 Subject: [PATCH 2/3] remove submodule --- lexical-submodule/lexical | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lexical-submodule/lexical diff --git a/lexical-submodule/lexical b/lexical-submodule/lexical deleted file mode 160000 index 73aa323947..0000000000 --- a/lexical-submodule/lexical +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73aa323947eab19fe5e329f45cf5cc0b614a3076 From bde95f2a781a0cb20d26f3c51c351d0551688ebe Mon Sep 17 00:00:00 2001 From: yousefed Date: Mon, 28 Nov 2022 13:58:20 +0100 Subject: [PATCH 3/3] fix css --- .../core/src/extensions/Blocks/nodes/Block.module.css | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/extensions/Blocks/nodes/Block.module.css b/packages/core/src/extensions/Blocks/nodes/Block.module.css index af2fe14dc3..a2a4ca0b09 100644 --- a/packages/core/src/extensions/Blocks/nodes/Block.module.css +++ b/packages/core/src/extensions/Blocks/nodes/Block.module.css @@ -128,14 +128,14 @@ NESTED BLOCKS --prev-level: 1.3em; } -.blockOuter[data-prev-type="headingBlock"] > .block > .blockContent { +.blockOuter[data-prev-type="headingContent"] > .block > .blockContent { font-size: var(--prev-level); font-weight: bold; } .blockOuter:not([data-prev-type]) > .block - > .blockContent[data-content-type="headingBlock"] { + > .blockContent[data-content-type="headingContent"] { font-size: var(--level); font-weight: bold; } @@ -236,10 +236,11 @@ NESTED BLOCKS content: "Type to filter"; } -.blockContent[data-content-type="headingBlock"].isEmpty > :first-child::before { +.blockContent[data-content-type="headingContent"].isEmpty + > :first-child::before { content: "Heading"; } -.blockContent[data-content-type="listBlock"] > :first-child div::before { +.blockContent[data-content-type="listContent"] > :first-child div::before { content: "List"; }