From 303c9d3f32ed16f10832ef40a2c35d35826e8b2c Mon Sep 17 00:00:00 2001 From: aravind-segu Date: Fri, 14 Aug 2020 10:12:26 -0400 Subject: [PATCH 1/5] Added Tag Menu --- package.json | 2 - src/components/TagMenu.tsx | 173 ++++++++++++++++++++++++++++++++++++ src/index.tsx | 12 +++ src/nodes/Blockquote.ts | 5 ++ src/nodes/BulletList.ts | 5 ++ src/nodes/CheckboxItem.ts | 3 + src/nodes/CheckboxList.ts | 5 ++ src/nodes/CodeFence.ts | 3 + src/nodes/Embed.tsx | 3 + src/nodes/Heading.ts | 12 +++ src/nodes/Image.tsx | 3 + src/nodes/ListItem.ts | 3 + src/nodes/OrderedList.ts | 3 + src/nodes/Paragraph.ts | 4 + src/plugins/SelectedNode.ts | 41 +++++++++ src/server.ts | 2 + yarn.lock | 39 ++------ 17 files changed, 283 insertions(+), 35 deletions(-) create mode 100644 src/components/TagMenu.tsx create mode 100644 src/plugins/SelectedNode.ts diff --git a/package.json b/package.json index b9c0d58fa..c6befebfe 100644 --- a/package.json +++ b/package.json @@ -71,8 +71,6 @@ "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.7.0", "prettier": "^1.19.1", - "react": "^16.8.6", - "react-dom": "^16.8.6", "source-map-loader": "^0.2.4", "ts-loader": "^6.2.1", "webpack": "^4.29.6", diff --git a/src/components/TagMenu.tsx b/src/components/TagMenu.tsx new file mode 100644 index 000000000..3caacf532 --- /dev/null +++ b/src/components/TagMenu.tsx @@ -0,0 +1,173 @@ +import * as React from 'react'; +import { findChildren } from 'prosemirror-utils'; +import { EditorView } from "prosemirror-view"; + +type Props = { + view: EditorView; +}; + +class TagMenu extends React.Component { + + menuRef = React.createRef(); + + state = { + left: 20, + top: -1000, + tag: "", + position: -1000, + existingAttrs: [], + selectedTagIndex: -1 + }; + + componentDidUpdate(prevProps,prevState) { + const nextStyle = this.calculatePosition(this.props); + if(prevState.top != nextStyle.top){ + this.setState({ top: nextStyle.top, left: nextStyle.left, tag: "", position: nextStyle.position, existingAttrs: nextStyle.existingAttrs, selectedTagIndex: -1 }); + } + } + + handleTagChange(event) { + this.setState({tag: event.target.value}) + } + + addTag(event){ + console.log(this.state.tag); + const { view } = this.props; + const { state } = view; + const { tr, selection } = state + const tagMarkType = state.schema.marks.tag; + const tagName = this.state.tag + var prevAttrs = Array.from(this.state.existingAttrs) + prevAttrs.push(tagName) + const transaction = tr.setNodeMarkup(this.state.position, undefined, {tags: prevAttrs}); + view.dispatch(transaction); + this.setState({tag: "", existingAttrs: prevAttrs}) + } + + selectTag(index){ + this.setState({selectedTagIndex: index}) + } + render() { + return ( +
+
+
+ + +
+ +
+
+ ) + } + + getScrollTop() { + return ( + window.pageYOffset || + document.documentElement.scrollTop || + document.body.scrollTop || + 0 + ); + } + + getScrollRight(){ + return ( + window.pageXOffset || + document.documentElement.scrollLeft || + document.body.scrollLeft || + 0 + ); + } + + getOffset(element) { + const rect = element.getBoundingClientRect(); + return { + top: rect.top + this.getScrollTop(), + right: rect.right + } + } + + calculatePosition(props) { + const { view } = props; + const { state } = view; + const { selection } = state; + + if (!selection) { + return { + top: -1000 + } + } + + const { $anchor } = selection; + const resolvedPos = state.doc.resolve($anchor.pos) as any; + const rowNumber = resolvedPos.path[1]; + console.log(rowNumber) + let i = 0; + if ($anchor.pos === 0) { + return { + top: -1000 + } + } + const [firstNode] = findChildren( + state.doc, + _node => { + if (rowNumber === i || rowNumber + 1 === i) { + i++ + return true + } + i++ + return false + }, + false + ) + if (!firstNode) { + return { + top: -1000 + } + } + + const coords = view.coordsAtPos(firstNode.pos); + const dom = view.nodeDOM(firstNode.pos) as HTMLElement; + const element = this.getOffset(dom); + + if (coords.top === 0) { + return { + top: -1000 + } + } + return { + left: element.right - 250, + top: element.top - 40, + position: firstNode.pos, + existingAttrs: firstNode.node.attrs.tags + } + } +} + +export default TagMenu; diff --git a/src/index.tsx b/src/index.tsx index c601f84e9..15a4a8b00 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -17,6 +17,7 @@ import { SearchResult } from "./components/LinkEditor"; import { EmbedDescriptor } from "./types"; import FloatingToolbar from "./components/FloatingToolbar"; import BlockMenu from "./components/BlockMenu"; +import TagMenu from "./components/TagMenu"; import Tooltip from "./components/Tooltip"; import Extension from "./lib/Extension"; import ExtensionManager from "./lib/ExtensionManager"; @@ -53,6 +54,7 @@ import Highlight from "./marks/Highlight"; import Italic from "./marks/Italic"; import Link from "./marks/Link"; import Strikethrough from "./marks/Strikethrough"; +import Tag from "./marks/Tag"; // plugins import BlockMenuTrigger from "./plugins/BlockMenuTrigger"; @@ -63,6 +65,7 @@ import SmartText from "./plugins/SmartText"; import TrailingNode from "./plugins/TrailingNode"; import MarkdownPaste from "./plugins/MarkdownPaste"; import TagFiltering from "./plugins/TagFiltering"; +import SelectedNode from "./plugins/SelectedNode"; export { schema, parser, serializer } from "./server"; @@ -257,6 +260,7 @@ class RichMarkdownEditor extends React.PureComponent { onClickLink: this.props.onClickLink, onClickHashtag: this.props.onClickHashtag, }), + new Tag(), new Strikethrough(), new OrderedList(), new History(), @@ -264,6 +268,7 @@ class RichMarkdownEditor extends React.PureComponent { new TrailingNode(), new MarkdownPaste(), new TagFiltering(), + new SelectedNode(), new Keys({ onSave: this.handleSave, onSaveAndExit: this.handleSaveAndExit, @@ -567,6 +572,9 @@ class RichMarkdownEditor extends React.PureComponent { onShowToast={this.props.onShowToast} embeds={this.props.embeds} /> + )} @@ -620,6 +628,10 @@ const StyledEditor = styled("div")<{ readOnly: boolean }>` opacity: 0.5; } + .selectedNode{ + background: #f2f2f4 + } + .ProseMirror-hideselection *::selection { background: transparent; } diff --git a/src/nodes/Blockquote.ts b/src/nodes/Blockquote.ts index d8d12cb8c..90e48ee6c 100644 --- a/src/nodes/Blockquote.ts +++ b/src/nodes/Blockquote.ts @@ -9,6 +9,11 @@ export default class Blockquote extends Node { get schema() { return { + attrs: { + tags: { + default: [] + } + }, content: "block+", group: "block", parseDOM: [{ tag: "blockquote" }], diff --git a/src/nodes/BulletList.ts b/src/nodes/BulletList.ts index 6c5ce08d8..35ab44770 100644 --- a/src/nodes/BulletList.ts +++ b/src/nodes/BulletList.ts @@ -9,6 +9,11 @@ export default class BulletList extends Node { get schema() { return { + attrs: { + tags: { + default: [] + } + }, content: "list_item+", group: "block", parseDOM: [{ tag: "ul" }], diff --git a/src/nodes/CheckboxItem.ts b/src/nodes/CheckboxItem.ts index 82eef78ec..c6f427c89 100644 --- a/src/nodes/CheckboxItem.ts +++ b/src/nodes/CheckboxItem.ts @@ -20,6 +20,9 @@ export default class CheckboxItem extends Node { checked: { default: false, }, + tags: { + default: [] + } }, content: "paragraph block*", defining: true, diff --git a/src/nodes/CheckboxList.ts b/src/nodes/CheckboxList.ts index 15dcb5129..9bb71e0e6 100644 --- a/src/nodes/CheckboxList.ts +++ b/src/nodes/CheckboxList.ts @@ -9,6 +9,11 @@ export default class CheckboxList extends Node { get schema() { return { + attrs: { + tags: { + default: [] + } + }, group: "block", content: "checkbox_item+", toDOM: () => ["ul", { class: this.name }, 0], diff --git a/src/nodes/CodeFence.ts b/src/nodes/CodeFence.ts index 5b9dd1dfd..c243c7375 100644 --- a/src/nodes/CodeFence.ts +++ b/src/nodes/CodeFence.ts @@ -65,6 +65,9 @@ export default class CodeFence extends Node { language: { default: "javascript", }, + tags: { + default: [] + } }, content: "text*", marks: "", diff --git a/src/nodes/Embed.tsx b/src/nodes/Embed.tsx index 12825ad0e..fc502daae 100644 --- a/src/nodes/Embed.tsx +++ b/src/nodes/Embed.tsx @@ -14,6 +14,9 @@ export default class Embed extends Node { href: {}, component: {}, matches: {}, + tags: { + default: [] + } }, parseDOM: [{ tag: "iframe" }], toDOM: node => [ diff --git a/src/nodes/Heading.ts b/src/nodes/Heading.ts index e83d870cb..a59e9f996 100644 --- a/src/nodes/Heading.ts +++ b/src/nodes/Heading.ts @@ -30,6 +30,9 @@ export default class Heading extends Node { level: { default: 1, }, + tags: { + default: [] + } }, content: "inline*", group: "block", @@ -46,6 +49,15 @@ export default class Heading extends Node { button.className = "heading-anchor"; button.addEventListener("click", this.handleCopyLink()); // TODO (carl) hiding then unhiding changes h level to 1 somehow + var tagsDiv = document.createElement("div"); + var tagString = "" + node.attrs.tags.forEach(element => { + console.log("adding :" + element) + tagString += element + }); + var tagContent = document.createTextNode(tagString); + // add the text node to the newly created div + tagsDiv.appendChild(tagContent); if (node.attrs.hidden) { return [ `h${node.attrs.level + (this.options.offset || 0)}`, diff --git a/src/nodes/Image.tsx b/src/nodes/Image.tsx index c6100e52c..3fd01f570 100644 --- a/src/nodes/Image.tsx +++ b/src/nodes/Image.tsx @@ -75,6 +75,9 @@ export default class Image extends Node { alt: { default: null, }, + tags:{ + default: [] + } }, content: "text*", marks: "", diff --git a/src/nodes/ListItem.ts b/src/nodes/ListItem.ts index cc833cced..7825d02b4 100644 --- a/src/nodes/ListItem.ts +++ b/src/nodes/ListItem.ts @@ -16,6 +16,9 @@ export default class ListItem extends Node { hidden: { default: false, }, + tags: { + default: [] + } }, content: "paragraph block*", defining: true, diff --git a/src/nodes/OrderedList.ts b/src/nodes/OrderedList.ts index ca889c78e..68dbf1aa0 100644 --- a/src/nodes/OrderedList.ts +++ b/src/nodes/OrderedList.ts @@ -13,6 +13,9 @@ export default class OrderedList extends Node { order: { default: 1, }, + tags: { + default: [] + } }, content: "list_item+", group: "block", diff --git a/src/nodes/Paragraph.ts b/src/nodes/Paragraph.ts index 1391325bb..8916c373f 100644 --- a/src/nodes/Paragraph.ts +++ b/src/nodes/Paragraph.ts @@ -12,6 +12,9 @@ export default class Paragraph extends Node { hidden: { default: false, }, + tags: { + default: [] + } }, content: "inline*", group: "block", @@ -25,6 +28,7 @@ export default class Paragraph extends Node { if (node.attrs.hidden) { return ["p", { class: "hidden" }, 0]; } + return ["p", 0]; }, }; diff --git a/src/plugins/SelectedNode.ts b/src/plugins/SelectedNode.ts new file mode 100644 index 000000000..ff031123b --- /dev/null +++ b/src/plugins/SelectedNode.ts @@ -0,0 +1,41 @@ +import { Plugin } from 'prosemirror-state'; +import { Decoration, DecorationSet } from 'prosemirror-view'; +import Extension from "../lib/Extension"; + +export default class SelectedNode extends Extension { + get name() { + return "SelectedNode"; + } + + get plugins() { + return [ + new Plugin({ + props: { + decorations(state) { + const { selection } = state; + const decorations = []; + console.log(selection.from) + console.log(selection.to) + state.doc.nodesBetween( + selection.from, + selection.to, + (node, position) => { + console.log(node.toJSON()); + console.log(node.marks); + if (node.isBlock) { + decorations.push( + Decoration.node(position, position + node.nodeSize, { + class: 'selectedNode' + }) + ) + } + } + ) + return DecorationSet.create(state.doc, decorations); + } + } + }) + ] + } + +} \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index 17d6190ba..607073c99 100644 --- a/src/server.ts +++ b/src/server.ts @@ -29,6 +29,7 @@ import Highlight from "./marks/Highlight"; import Italic from "./marks/Italic"; import Link from "./marks/Link"; import Strikethrough from "./marks/Strikethrough"; +import Tag from "./marks/Tag"; const extensions = new ExtensionManager([ new Doc(), @@ -54,6 +55,7 @@ const extensions = new ExtensionManager([ new Highlight(), new Italic(), new Link(), + new Tag(), new Strikethrough(), new OrderedList(), ]); diff --git a/yarn.lock b/yarn.lock index 0d6c8426b..545af193c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -386,10 +386,10 @@ dependencies: "@types/prismjs" "*" -"@types/styled-components@^4.4.2": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-4.4.3.tgz#74dd00ad760845a98890a8539361d8afc32059de" - integrity sha512-U0udeNOZBfUkJycmGJwmzun0FBt11rZy08weVQmE2xfUNAbX8AGOEWxWna2d+qAUKxKgMlcG+TZT0+K2FfDcnQ== +"@types/styled-components@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.0.tgz#24d3412ba5395aa06e14fbc93c52f9454cebd0d6" + integrity sha512-ZFlLCuwF5r+4Vb7JUmd+Yr2S0UBdBGmI7ctFTgJMypIp3xOHI4LCFVn2dKMvpk6xDB2hLRykrEWMBwJEpUAUIQ== dependencies: "@types/hoist-non-react-statics" "*" "@types/react" "*" @@ -3407,7 +3407,7 @@ loglevelnext@^1.0.1, loglevelnext@^1.0.2: es6-symbol "^3.1.1" object.assign "^4.1.0" -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -4355,7 +4355,7 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.8, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -4594,16 +4594,6 @@ rc@^1.0.1, rc@^1.1.6: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.8.6: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" - integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" - react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -4621,15 +4611,6 @@ react-portal@^4.2.1: dependencies: prop-types "^15.5.8" -react@^16.8.6: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" - integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" @@ -4914,14 +4895,6 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" From 277d52d9d332b20bb074f33dc08391fc35ff83f8 Mon Sep 17 00:00:00 2001 From: aravind-segu Date: Fri, 14 Aug 2020 10:17:11 -0400 Subject: [PATCH 2/5] Removed Tag Mark --- src/index.tsx | 2 -- src/server.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 15a4a8b00..4df3cb747 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -54,7 +54,6 @@ import Highlight from "./marks/Highlight"; import Italic from "./marks/Italic"; import Link from "./marks/Link"; import Strikethrough from "./marks/Strikethrough"; -import Tag from "./marks/Tag"; // plugins import BlockMenuTrigger from "./plugins/BlockMenuTrigger"; @@ -260,7 +259,6 @@ class RichMarkdownEditor extends React.PureComponent { onClickLink: this.props.onClickLink, onClickHashtag: this.props.onClickHashtag, }), - new Tag(), new Strikethrough(), new OrderedList(), new History(), diff --git a/src/server.ts b/src/server.ts index 607073c99..17d6190ba 100644 --- a/src/server.ts +++ b/src/server.ts @@ -29,7 +29,6 @@ import Highlight from "./marks/Highlight"; import Italic from "./marks/Italic"; import Link from "./marks/Link"; import Strikethrough from "./marks/Strikethrough"; -import Tag from "./marks/Tag"; const extensions = new ExtensionManager([ new Doc(), @@ -55,7 +54,6 @@ const extensions = new ExtensionManager([ new Highlight(), new Italic(), new Link(), - new Tag(), new Strikethrough(), new OrderedList(), ]); From cdbbfb8a17d1bf5164949e8f14b660b196769c39 Mon Sep 17 00:00:00 2001 From: aravind-segu Date: Fri, 14 Aug 2020 10:20:06 -0400 Subject: [PATCH 3/5] Removed Print Statements --- src/components/TagMenu.tsx | 2 -- src/nodes/Heading.ts | 8 -------- src/plugins/SelectedNode.ts | 4 ---- 3 files changed, 14 deletions(-) diff --git a/src/components/TagMenu.tsx b/src/components/TagMenu.tsx index 3caacf532..e5736f7f7 100644 --- a/src/components/TagMenu.tsx +++ b/src/components/TagMenu.tsx @@ -31,7 +31,6 @@ class TagMenu extends React.Component { } addTag(event){ - console.log(this.state.tag); const { view } = this.props; const { state } = view; const { tr, selection } = state @@ -127,7 +126,6 @@ class TagMenu extends React.Component { const { $anchor } = selection; const resolvedPos = state.doc.resolve($anchor.pos) as any; const rowNumber = resolvedPos.path[1]; - console.log(rowNumber) let i = 0; if ($anchor.pos === 0) { return { diff --git a/src/nodes/Heading.ts b/src/nodes/Heading.ts index a59e9f996..1c40e93a7 100644 --- a/src/nodes/Heading.ts +++ b/src/nodes/Heading.ts @@ -49,15 +49,7 @@ export default class Heading extends Node { button.className = "heading-anchor"; button.addEventListener("click", this.handleCopyLink()); // TODO (carl) hiding then unhiding changes h level to 1 somehow - var tagsDiv = document.createElement("div"); - var tagString = "" - node.attrs.tags.forEach(element => { - console.log("adding :" + element) - tagString += element - }); - var tagContent = document.createTextNode(tagString); // add the text node to the newly created div - tagsDiv.appendChild(tagContent); if (node.attrs.hidden) { return [ `h${node.attrs.level + (this.options.offset || 0)}`, diff --git a/src/plugins/SelectedNode.ts b/src/plugins/SelectedNode.ts index ff031123b..8f78c8c6c 100644 --- a/src/plugins/SelectedNode.ts +++ b/src/plugins/SelectedNode.ts @@ -14,14 +14,10 @@ export default class SelectedNode extends Extension { decorations(state) { const { selection } = state; const decorations = []; - console.log(selection.from) - console.log(selection.to) state.doc.nodesBetween( selection.from, selection.to, (node, position) => { - console.log(node.toJSON()); - console.log(node.marks); if (node.isBlock) { decorations.push( Decoration.node(position, position + node.nodeSize, { From 17214682375626c1d45a181ed999e8865c0505b4 Mon Sep 17 00:00:00 2001 From: aravind-segu Date: Fri, 14 Aug 2020 15:52:25 -0400 Subject: [PATCH 4/5] Added UUID for tags --- src/components/TagMenu.tsx | 19 +++++++++++-------- src/nodes/Blockquote.ts | 2 +- src/nodes/BulletList.ts | 2 +- src/nodes/CheckboxItem.ts | 2 +- src/nodes/CheckboxList.ts | 2 +- src/nodes/CodeFence.ts | 2 +- src/nodes/Embed.tsx | 2 +- src/nodes/Heading.ts | 2 +- src/nodes/Image.tsx | 2 +- src/nodes/ListItem.ts | 2 +- src/nodes/OrderedList.ts | 2 +- src/nodes/Paragraph.ts | 2 +- 12 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/components/TagMenu.tsx b/src/components/TagMenu.tsx index e5736f7f7..dc4675f89 100644 --- a/src/components/TagMenu.tsx +++ b/src/components/TagMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { findChildren } from 'prosemirror-utils'; import { EditorView } from "prosemirror-view"; - +import { v4 as uuidv4 } from 'uuid'; type Props = { view: EditorView; }; @@ -15,7 +15,7 @@ class TagMenu extends React.Component { top: -1000, tag: "", position: -1000, - existingAttrs: [], + existingAttrs: {}, selectedTagIndex: -1 }; @@ -36,11 +36,14 @@ class TagMenu extends React.Component { const { tr, selection } = state const tagMarkType = state.schema.marks.tag; const tagName = this.state.tag - var prevAttrs = Array.from(this.state.existingAttrs) - prevAttrs.push(tagName) - const transaction = tr.setNodeMarkup(this.state.position, undefined, {tags: prevAttrs}); + var newTags = {} + for (var key in this.state.existingAttrs){ + newTags[key] = this.state.existingAttrs[key] + } + newTags[uuidv4()] = tagName; + const transaction = tr.setNodeMarkup(this.state.position, undefined, {tags: newTags}); view.dispatch(transaction); - this.setState({tag: "", existingAttrs: prevAttrs}) + this.setState({tag: "", existingAttrs: newTags}) } selectTag(index){ @@ -72,12 +75,12 @@ class TagMenu extends React.Component { paddingLeft: "5px", marginTop: "0px", background: "#f2f2f4"}}> - {this.state.existingAttrs.map(function(name, index){ + {Object.keys(this.state.existingAttrs).map((key, index) => { return
  • {name}
  • ; + >{this.state.existingAttrs[key]}; })} diff --git a/src/nodes/Blockquote.ts b/src/nodes/Blockquote.ts index 90e48ee6c..92b7bbfb7 100644 --- a/src/nodes/Blockquote.ts +++ b/src/nodes/Blockquote.ts @@ -11,7 +11,7 @@ export default class Blockquote extends Node { return { attrs: { tags: { - default: [] + default: {} } }, content: "block+", diff --git a/src/nodes/BulletList.ts b/src/nodes/BulletList.ts index 35ab44770..c9653311d 100644 --- a/src/nodes/BulletList.ts +++ b/src/nodes/BulletList.ts @@ -11,7 +11,7 @@ export default class BulletList extends Node { return { attrs: { tags: { - default: [] + default: {} } }, content: "list_item+", diff --git a/src/nodes/CheckboxItem.ts b/src/nodes/CheckboxItem.ts index c6f427c89..3c46a2498 100644 --- a/src/nodes/CheckboxItem.ts +++ b/src/nodes/CheckboxItem.ts @@ -21,7 +21,7 @@ export default class CheckboxItem extends Node { default: false, }, tags: { - default: [] + default: {} } }, content: "paragraph block*", diff --git a/src/nodes/CheckboxList.ts b/src/nodes/CheckboxList.ts index 9bb71e0e6..89a828772 100644 --- a/src/nodes/CheckboxList.ts +++ b/src/nodes/CheckboxList.ts @@ -11,7 +11,7 @@ export default class CheckboxList extends Node { return { attrs: { tags: { - default: [] + default: {} } }, group: "block", diff --git a/src/nodes/CodeFence.ts b/src/nodes/CodeFence.ts index c243c7375..4d925cd78 100644 --- a/src/nodes/CodeFence.ts +++ b/src/nodes/CodeFence.ts @@ -66,7 +66,7 @@ export default class CodeFence extends Node { default: "javascript", }, tags: { - default: [] + default: {} } }, content: "text*", diff --git a/src/nodes/Embed.tsx b/src/nodes/Embed.tsx index fc502daae..c58ace56c 100644 --- a/src/nodes/Embed.tsx +++ b/src/nodes/Embed.tsx @@ -15,7 +15,7 @@ export default class Embed extends Node { component: {}, matches: {}, tags: { - default: [] + default: {} } }, parseDOM: [{ tag: "iframe" }], diff --git a/src/nodes/Heading.ts b/src/nodes/Heading.ts index 1c40e93a7..e9a41f189 100644 --- a/src/nodes/Heading.ts +++ b/src/nodes/Heading.ts @@ -31,7 +31,7 @@ export default class Heading extends Node { default: 1, }, tags: { - default: [] + default: {} } }, content: "inline*", diff --git a/src/nodes/Image.tsx b/src/nodes/Image.tsx index 3fd01f570..d7d82ca4b 100644 --- a/src/nodes/Image.tsx +++ b/src/nodes/Image.tsx @@ -76,7 +76,7 @@ export default class Image extends Node { default: null, }, tags:{ - default: [] + default: {} } }, content: "text*", diff --git a/src/nodes/ListItem.ts b/src/nodes/ListItem.ts index 7825d02b4..8e78299f0 100644 --- a/src/nodes/ListItem.ts +++ b/src/nodes/ListItem.ts @@ -17,7 +17,7 @@ export default class ListItem extends Node { default: false, }, tags: { - default: [] + default: {} } }, content: "paragraph block*", diff --git a/src/nodes/OrderedList.ts b/src/nodes/OrderedList.ts index 68dbf1aa0..d34a18931 100644 --- a/src/nodes/OrderedList.ts +++ b/src/nodes/OrderedList.ts @@ -14,7 +14,7 @@ export default class OrderedList extends Node { default: 1, }, tags: { - default: [] + default: {} } }, content: "list_item+", diff --git a/src/nodes/Paragraph.ts b/src/nodes/Paragraph.ts index 8916c373f..62652516d 100644 --- a/src/nodes/Paragraph.ts +++ b/src/nodes/Paragraph.ts @@ -13,7 +13,7 @@ export default class Paragraph extends Node { default: false, }, tags: { - default: [] + default: {} } }, content: "inline*", From 71551f1a7a6c52a965c2558db6289eebf37a47a7 Mon Sep 17 00:00:00 2001 From: aravind-segu Date: Fri, 14 Aug 2020 16:59:08 -0400 Subject: [PATCH 5/5] Tag Deletion --- src/components/TagMenu.tsx | 80 +++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/src/components/TagMenu.tsx b/src/components/TagMenu.tsx index dc4675f89..24eaf7f4e 100644 --- a/src/components/TagMenu.tsx +++ b/src/components/TagMenu.tsx @@ -2,10 +2,20 @@ import * as React from 'react'; import { findChildren } from 'prosemirror-utils'; import { EditorView } from "prosemirror-view"; import { v4 as uuidv4 } from 'uuid'; +import styled from "styled-components"; +import { CloseIcon } from "outline-icons"; + type Props = { view: EditorView; }; +type TagListProps = { + key: string; + selectedKey: string; + tagName: string; + deleteTag: () => void +} + class TagMenu extends React.Component { menuRef = React.createRef(); @@ -16,13 +26,13 @@ class TagMenu extends React.Component { tag: "", position: -1000, existingAttrs: {}, - selectedTagIndex: -1 + selectedTagKey: "" }; componentDidUpdate(prevProps,prevState) { const nextStyle = this.calculatePosition(this.props); if(prevState.top != nextStyle.top){ - this.setState({ top: nextStyle.top, left: nextStyle.left, tag: "", position: nextStyle.position, existingAttrs: nextStyle.existingAttrs, selectedTagIndex: -1 }); + this.setState({ top: nextStyle.top, left: nextStyle.left, tag: "", position: nextStyle.position, existingAttrs: nextStyle.existingAttrs, selectedTagIndex: "" }); } } @@ -34,7 +44,6 @@ class TagMenu extends React.Component { const { view } = this.props; const { state } = view; const { tr, selection } = state - const tagMarkType = state.schema.marks.tag; const tagName = this.state.tag var newTags = {} for (var key in this.state.existingAttrs){ @@ -46,8 +55,20 @@ class TagMenu extends React.Component { this.setState({tag: "", existingAttrs: newTags}) } - selectTag(index){ - this.setState({selectedTagIndex: index}) + deleteTag(keyToDelete){ + const { view } = this.props; + const { state } = view; + const { tr, selection } = state + const tagName = this.state.tag + var newTags = {} + for (var key in this.state.existingAttrs){ + if(key !== keyToDelete){ + newTags[key] = this.state.existingAttrs[key] + } + } + const transaction = tr.setNodeMarkup(this.state.position, undefined, {tags: newTags}); + view.dispatch(transaction); + this.setState({existingAttrs: newTags}) } render() { return ( @@ -71,16 +92,10 @@ class TagMenu extends React.Component {
      + marginTop: "0px"}}> {Object.keys(this.state.existingAttrs).map((key, index) => { - return
    • {this.state.existingAttrs[key]}
    • ; + return this.deleteTag(key)}/> })}
    @@ -171,4 +186,43 @@ class TagMenu extends React.Component { } } +class TagItem extends React.Component { + render() { + return( +
  • +
    +
    + {this.props.tagName} +
    +
    this.props.deleteTag()} + > + +
    +
    +
  • + ) + } +} export default TagMenu;