From c20b43aa6c28a69237b02d3c68f893fa08ddd628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Mon, 8 Mar 2021 13:19:05 +0100 Subject: [PATCH] try to improve react support --- docs/babel.config.js | 1 + docs/gridsome.server.js | 1 + docs/package.json | 2 + .../Examples/Default/React/index-nope.jsx | 143 +++++++++++ .../Examples/Default/React/index-ori.jsx | 167 +++++++++++++ .../demos/Examples/Default/React/index.jsx | 229 ++++++------------ packages/react/package.json | 6 +- packages/react/src/Editor.ts | 5 + packages/react/src/EditorContent.jsx | 71 ++++++ packages/react/src/ReactNodeViewRenderer.ts | 210 +++++++++++++++- packages/react/src/ReactRenderer.ts | 59 ++++- packages/react/src/index.ts | 12 +- packages/react/src/test.jsx | 7 + yarn.lock | 81 ++++--- 14 files changed, 797 insertions(+), 197 deletions(-) create mode 100644 docs/src/demos/Examples/Default/React/index-nope.jsx create mode 100644 docs/src/demos/Examples/Default/React/index-ori.jsx create mode 100644 packages/react/src/Editor.ts create mode 100644 packages/react/src/EditorContent.jsx create mode 100644 packages/react/src/test.jsx diff --git a/docs/babel.config.js b/docs/babel.config.js index 30a180020..7e7cba28d 100644 --- a/docs/babel.config.js +++ b/docs/babel.config.js @@ -5,5 +5,6 @@ module.exports = { ], plugins: [ '@babel/plugin-proposal-optional-chaining', + '@babel/plugin-proposal-class-properties', ], } diff --git a/docs/gridsome.server.js b/docs/gridsome.server.js index 737872a20..83bb46dcf 100644 --- a/docs/gridsome.server.js +++ b/docs/gridsome.server.js @@ -57,6 +57,7 @@ module.exports = function (api) { api.chainWebpack(config => { config.resolve.extensions .add('.ts') + .add('.tsx') .add('.jsx') config.module diff --git a/docs/package.json b/docs/package.json index 86e049b42..1cca1e83f 100644 --- a/docs/package.json +++ b/docs/package.json @@ -35,7 +35,9 @@ "yjs": "^13.5.1" }, "devDependencies": { + "@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-optional-chaining": "^7.13.0", + "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/preset-env": "^7.13.5", "@babel/preset-react": "^7.12.13", "html-loader": "^1.3.2", diff --git a/docs/src/demos/Examples/Default/React/index-nope.jsx b/docs/src/demos/Examples/Default/React/index-nope.jsx new file mode 100644 index 000000000..d654737d1 --- /dev/null +++ b/docs/src/demos/Examples/Default/React/index-nope.jsx @@ -0,0 +1,143 @@ +import * as React from 'react' +import ReactDOM from 'react-dom' +import { EditorState } from 'prosemirror-state' +import { EditorView } from 'prosemirror-view' +import { Node, Schema } from 'prosemirror-model' +// import applyDevTools from 'prosemirror-dev-tools' +// import styled from 'styled-components' + +// Here we have the (too simple) React component which +// we'll be rendering content into. +// +class Underlined extends React.Component { + constructor(props) { + super(props) + this.hole = React.createRef() + } + // We'll put the content into what we render using + // this function, which appends a given node to + // a ref HTMLElement, if present. + // + append(node) { + if (this.hole) { + this.hole.current.appendChild(node) + } + } + + render() { + // Just really wanted to prove I could get React AND + // styled-component abilities at the same time. + // + // const UnderlinedText = styled.p` + // text-decoration: underline; + // ` + + // The styled components version is basically just a wrapper to do SCSS styling. + // Questionable if it's even needed for such simple styling and because you can't clearly see the + // DOM structure from the code (hence making `& > ${Component}` selectors quite unintuitive) + // return + return

+ } +} + +// This class is our actual interactor for ProseMirror itself. +// It glues DOM rendering, React, and ProseMirror nodes together. +// +class Underline { + constructor(node) { + // We'll use this to access our Underlined component's + // instance methods. + // + this.ref = React.createRef() + + // Here, we'll provide a container to render React into. + // Coincidentally, this is where ProseMirror will put its + // generated contentDOM. React will throw out that content + // once rendered, and at the same time we'll append it into + // the component tree, like a fancy shell game. This isn't + // obvious to the user, but would it be more obvious on an + // expensive render? + // + this.dom = document.createElement('span') + + // Finally, we provide an element to render content into. + // We will be moving this node around as we need to. + // + this.contentDOM = document.createElement('span') + + // Better way of doing this would be portals https://reactjs.org/docs/portals.html + ReactDOM.render( + , + this.dom, + this.putContentDomInRef + ) + } + + update(node) { + return true + } + + // This is the least complex part. Now we've put + // all of our interlocking pieces behind refs and + // instance properties, this becomes the callback + // which performs the actual shell game. + // + putContentDomInRef = () => { + this.ref.current.append(this.contentDOM) + } + + // Required to not to leave the React nodes orphaned. + destroy() { + ReactDOM.unmountComponentAtNode(this.dom) + } +} + +export default class Editor extends React.Component { + constructor(props) { + super(props) + this.editorState = EditorState.create({ + schema: new Schema({ + nodes: { + doc: { + content: 'block+' + }, + underline: { + group: 'block', + content: 'inline*', + parseDOM: [{ tag: 'p' }], + toDOM() { return ['p', 0] } + }, + text: { + group: 'inline' + }, + } + }) + }) + } + + createEditorView = (element) => { + if (element != null) { + this.editorView = new EditorView(element, { + nodeViews: { + underline: (node) => new Underline(node) + }, + state: this.editorState, + }) + // applyDevTools(this.editorView) + } + } + + componentWillUnmount() { + if (this.editorView) { + this.editorView.destroy() + } + } + + shouldComponentUpdate() { + return false + } + + render() { + return
{ this.createEditorView(ref) }} /> + } +} diff --git a/docs/src/demos/Examples/Default/React/index-ori.jsx b/docs/src/demos/Examples/Default/React/index-ori.jsx new file mode 100644 index 000000000..e3893834c --- /dev/null +++ b/docs/src/demos/Examples/Default/React/index-ori.jsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react' +import { defaultExtensions } from '@tiptap/starter-kit' +import { useEditor, Editor } from '@tiptap/react' +import './styles.scss' + +// useEditor only works for child components of +const MenuBar = () => { + const editor = useEditor() + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default () => { + const [value, setValue] = useState(` +

+ Hi there, +

+

+ this is a basic basic example of tiptap. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists: +

+
    +
  • + That’s a bullet list with one … +
  • +
  • + … or two list items. +
  • +
+

+ Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block: +

+
body {
+  display: none;
+}
+

+ I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too. +

+
+ Wow, that’s amazing. Good work, boy! 👏 +
+ — Mom +
+`) + + return ( + <> + + + + + ) +} diff --git a/docs/src/demos/Examples/Default/React/index.jsx b/docs/src/demos/Examples/Default/React/index.jsx index e3893834c..8274e29f1 100644 --- a/docs/src/demos/Examples/Default/React/index.jsx +++ b/docs/src/demos/Examples/Default/React/index.jsx @@ -1,167 +1,84 @@ -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import { defaultExtensions } from '@tiptap/starter-kit' -import { useEditor, Editor } from '@tiptap/react' +import Paragraph from '@tiptap/extension-paragraph' +import { Editor, EditorContent, ReactNodeViewRenderer } from '@tiptap/react' import './styles.scss' +import { render, unmountComponentAtNode } from 'react-dom' -// useEditor only works for child components of -const MenuBar = () => { - const editor = useEditor() +const useEditor = (options = {}) => { + const [editor, setEditor] = useState(null) - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - ) + useEffect(() => { + const instance = new Editor(options) + setEditor(instance) + + return () => { + instance.destroy() + } + }, []) + + return editor +} + + + +function reactNodeView(Component) { + const renderComponent = (props, dom) => render(, dom) + + return (node, view, getPos, decorations) => { + let dom = document.createElement("div") + renderComponent({ node, view, decorations, getPos }, dom) + + console.log(dom) + return { + dom, + contentDOM: dom.querySelector('[data-node-view-content]'), + update(node, decorations) { + renderComponent({ node, view, decorations, getPos }, dom) + return true + }, + destroy() { + unmountComponentAtNode(dom) + }, + } + } } export default () => { - const [value, setValue] = useState(` -

- Hi there, -

-

- this is a basic basic example of tiptap. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists: -

-
    -
  • - That’s a bullet list with one … -
  • -
  • - … or two list items. -
  • -
-

- Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block: -

-
body {
-  display: none;
-}
-

- I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too. -

-
- Wow, that’s amazing. Good work, boy! 👏 -
- — Mom -
-`) + const editor = useEditor({ + content: '

hello react

', + extensions: [ + ...defaultExtensions(), + Paragraph.extend({ + addNodeView() { + return reactNodeView(() => { + // useEffect(() => { + // console.log('effect') + // }, []); + + return ( +

+ +

+ ) + }) + return ReactNodeViewRenderer(() => { + // useEffect(() => { + // console.log('effect') + // }, []); + + return ( +

+ +

+ ) + }) + }, + }), + ] + }) return ( - <> - - - - + ) } diff --git a/packages/react/package.json b/packages/react/package.json index 707be9035..bebb84465 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,9 +23,13 @@ ], "peerDependencies": { "@tiptap/core": "^2.0.0-beta.1", - "react": "^17.0.1" + "react": "^17.0.1", + "react-dom": "^17.0.1" }, "dependencies": { "prosemirror-view": "^1.17.8" + }, + "devDependencies": { + "@types/react-dom": "^17.0.1" } } diff --git a/packages/react/src/Editor.ts b/packages/react/src/Editor.ts new file mode 100644 index 000000000..e4c174f54 --- /dev/null +++ b/packages/react/src/Editor.ts @@ -0,0 +1,5 @@ +import { Editor as CoreEditor } from '@tiptap/core' + +export class Editor extends CoreEditor { + public contentComponent: any | null = null +} diff --git a/packages/react/src/EditorContent.jsx b/packages/react/src/EditorContent.jsx new file mode 100644 index 000000000..e73950376 --- /dev/null +++ b/packages/react/src/EditorContent.jsx @@ -0,0 +1,71 @@ +import React, { useState, useRef, useEffect } from 'react' +import ReactDOM from 'react-dom' +import { Editor } from './Editor' + +// export const EditorContent = ({ editor }) => { +// const editorContentRef = useRef(null) + +// useEffect(() => { +// if (editor && editor.options.element) { +// console.log('set editorContent element') + +// const element = editorContentRef.current + +// element.appendChild(editor.options.element.firstChild) + +// editor.setOptions({ +// element, +// }) + +// console.log({instance: this}) + +// // TODO: why setTimeout? +// setTimeout(() => { +// editor.createNodeViews() +// }, 0) +// } +// }) + +// return ( +//
+// ) +// } + +export class EditorContent extends React.Component { + constructor(props) { + super(props) + this.editorContentRef = React.createRef() + this.editorPortalRef = React.createRef() + + this.state = { + editor: this.props.editor + } + } + + componentDidUpdate() { + const { editor } = this.props + + if (editor && editor.options.element) { + const element = this.editorContentRef.current + + element.appendChild(editor.options.element.firstChild) + + editor.setOptions({ + element, + }) + + editor.contentComponent = this + + // TODO: why setTimeout? + setTimeout(() => { + editor.createNodeViews() + }, 0) + } + } + + render() { + return ( +
+ ) + } +} diff --git a/packages/react/src/ReactNodeViewRenderer.ts b/packages/react/src/ReactNodeViewRenderer.ts index 7fb1a1a89..c4af67b4c 100644 --- a/packages/react/src/ReactNodeViewRenderer.ts +++ b/packages/react/src/ReactNodeViewRenderer.ts @@ -1 +1,209 @@ -export default class ReactNodeViewRenderer {} +// @ts-nocheck +import { Node, NodeViewRenderer, NodeViewRendererProps } from '@tiptap/core' +import { Decoration, NodeView } from 'prosemirror-view' +import { NodeSelection } from 'prosemirror-state' +import { Node as ProseMirrorNode } from 'prosemirror-model' +import React from 'react' +import ReactDOM from 'react-dom' +import { Editor } from './Editor' +import { ReactRenderer } from './ReactRenderer' +import test from './test' + +interface ReactNodeViewRendererOptions { + stopEvent: ((event: Event) => boolean) | null, + update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, +} + +class ReactNodeView implements NodeView { + + renderer!: ReactRenderer + + editor: Editor + + extension!: Node + + node!: ProseMirrorNode + + decorations!: Decoration[] + + getPos!: any + + isDragging = false + + options: ReactNodeViewRendererOptions = { + stopEvent: null, + update: null, + } + + constructor(component: any, props: NodeViewRendererProps, options?: Partial) { + this.options = { ...this.options, ...options } + this.editor = props.editor as Editor + this.extension = props.extension + this.node = props.node + this.getPos = props.getPos + this.mount(component) + } + + mount(component: any) { + const props = {} + + if (!component.displayName) { + component.displayName = this.extension.config.name + } + + this.renderer = new ReactRenderer(component, { + editor: this.editor, + props, + }) + } + + get dom() { + // if (!this.renderer.element) { + // return null + // } + + // if (!this.renderer.element.hasAttribute('data-node-view-wrapper')) { + // throw Error('Please use the NodeViewWrapper component for your node view.') + // } + + return this.renderer.element + } + + get contentDOM() { + // console.log(this.dom) + // return null + // if (!this.renderer.element) { + // return null + // } + + const hasContent = !this.node.type.isAtom + + if (!hasContent) { + return null + } + + const contentElement = this.dom.querySelector('[data-node-view-content]') + + return contentElement || this.dom + } + + stopEvent(event: Event) { + if (typeof this.options.stopEvent === 'function') { + return this.options.stopEvent(event) + } + + const target = (event.target as HTMLElement) + const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target) + + // ignore all events from child nodes + if (!isInElement) { + return false + } + + const { isEditable } = this.editor + const { isDragging } = this + const isDraggable = !!this.node.type.spec.draggable + const isSelectable = NodeSelection.isSelectable(this.node) + const isCopyEvent = event.type === 'copy' + const isPasteEvent = event.type === 'paste' + const isCutEvent = event.type === 'cut' + const isClickEvent = event.type === 'mousedown' + const isDragEvent = event.type.startsWith('drag') || event.type === 'drop' + + // ProseMirror tries to drag selectable nodes + // even if `draggable` is set to `false` + // this fix prevents that + if (!isDraggable && isSelectable && isDragEvent) { + event.preventDefault() + } + + if (isDraggable && isDragEvent && !isDragging) { + event.preventDefault() + return false + } + + // we have to store that dragging started + if (isDraggable && isEditable && !isDragging && isClickEvent) { + const dragHandle = target.closest('[data-drag-handle]') + const isValidDragHandle = dragHandle + && (this.dom === dragHandle || (this.dom.contains(dragHandle))) + + if (isValidDragHandle) { + this.isDragging = true + document.addEventListener('dragend', () => { + this.isDragging = false + }, { once: true }) + } + } + + // these events are handled by prosemirror + if ( + isDragging + || isCopyEvent + || isPasteEvent + || isCutEvent + || (isClickEvent && isSelectable) + ) { + return false + } + + return true + } + + ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) { + if (mutation.type === 'selection') { + if (this.node.isLeaf) { + return true + } + + return false + } + + if (!this.contentDOM) { + return true + } + + const contentDOMHasChanged = !this.contentDOM.contains(mutation.target) + || this.contentDOM === mutation.target + + return contentDOMHasChanged + } + + destroy() { + this.renderer.destroy() + } + + update(node: ProseMirrorNode, decorations: Decoration[]) { + if (typeof this.options.update === 'function') { + return this.options.update(node, decorations) + } + + if (node.type !== this.node.type) { + return false + } + + if (node === this.node && this.decorations === decorations) { + return true + } + + this.node = node + this.decorations = decorations + // this.renderer.updateProps({ node, decorations }) + this.renderer.render() + + return true + } +} + +export function ReactNodeViewRenderer(component: any, options?: Partial): NodeViewRenderer { + return (props: NodeViewRendererProps) => { + // try to get the parent component + // this is important for vue devtools to show the component hierarchy correctly + // maybe it’s `undefined` because isn’t rendered yet + if (!(props.editor as Editor).contentComponent) { + return {} + } + + return new ReactNodeView(component, props, options) as NodeView + } +} diff --git a/packages/react/src/ReactRenderer.ts b/packages/react/src/ReactRenderer.ts index d2672c602..6f7ece456 100644 --- a/packages/react/src/ReactRenderer.ts +++ b/packages/react/src/ReactRenderer.ts @@ -1 +1,58 @@ -export default class ReactRenderer {} +// @ts-nocheck + +import React from 'react' +import { render, unmountComponentAtNode } from 'react-dom' + +import { Editor } from './Editor' + +export interface VueRendererOptions { + as?: string; + editor: Editor; + props?: { [key: string]: any }; +} + +export class ReactRenderer { + id: string + + editor: Editor + + component: any + + teleportElement: Element + + element: Element + + props: { [key: string]: any } + + constructor(component: any, { props = {}, editor }: VueRendererOptions) { + this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString() + this.component = component + this.editor = editor + this.props = props + + this.teleportElement = document.createElement('div') + // this.teleportElement.setAttribute('data-bla', '') + // render(React.createElement(component), this.teleportElement) + // render(React.createElement(component), this.teleportElement) + this.render() + // this.element = this.teleportElement.firstElementChild as Element + this.element = this.teleportElement + } + + render() { + render(React.createElement(this.component), this.teleportElement) + } + + updateProps(props: { [key: string]: any } = {}) { + // TODO + } + + destroy() { + // TODO + // console.log('DESTROY', { bla: this.teleportElement }) + // console.log(document.querySelector('[data-bla]')) + unmountComponentAtNode(this.teleportElement) + // unmountComponentAtNode(document.querySelector('[data-bla]')) + } + +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 2fdac48e7..27fc78c4a 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,7 +1,9 @@ // @ts-nocheck export * from '@tiptap/core' -export { default as ReactRenderer } from './ReactRenderer' -export { default as ReactNodeViewRenderer } from './ReactNodeViewRenderer' -export { - Editor, EditorContext, useEditor, -} from './components/Editor' +export { Editor } from './Editor' +// export { +// Editor, EditorContext, useEditor, +// } from './components/Editor' +export * from './ReactRenderer' +export * from './ReactNodeViewRenderer' +export * from './EditorContent' diff --git a/packages/react/src/test.jsx b/packages/react/src/test.jsx new file mode 100644 index 000000000..0c78dc434 --- /dev/null +++ b/packages/react/src/test.jsx @@ -0,0 +1,7 @@ +import React from 'react' + +export default () => { + return ( +
+ ) +} diff --git a/yarn.lock b/yarn.lock index b5900c7ef..bf0101102 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1952,9 +1952,9 @@ universal-user-agent "^6.0.0" "@octokit/openapi-types@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.3.0.tgz#29e3faa119da90082dc653ea74c8bb345d197bf7" - integrity sha512-5q2qBz4iZ0xS/DEJ0ROusFbN4cVlbJE9GvOByen+mv7artuGXfVhONqcuRd7jYN2glTmCnzcZw+X6LrjRVqs0A== + version "5.3.1" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.3.1.tgz#a49d119a1b9e47b7a9f5133ab14be2d8afaa183d" + integrity sha512-TvVk2QuIA0lQZcIMd6xbdGaGDVeNYIOa3l1ZVagAIk5K3t/WMYbcg4BISNDhzdVhm/TgQB26frAgd/GV81aHJA== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -2313,6 +2313,21 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/react-dom@^17.0.1": + version "17.0.1" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.1.tgz#d92d77d020bfb083e07cc8e0ac9f933599a4d56a" + integrity sha512-yIVyopxQb8IDZ7SOHeTovurFq+fXiPICa+GV3gp0Xedsl+MwQlMLKmvrnEjFbQxjliH5YVAEWFh975eVNmKj7Q== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "17.0.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8" + integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/react@^16.8.12": version "16.14.4" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.4.tgz#365f6a1e117d1eec960ba792c7e1e91ecad38e6f" @@ -3393,9 +3408,9 @@ bcrypt-pbkdf@^1.0.0: tweetnacl "^0.14.3" before-after-hook@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.1.tgz#99ae36992b5cfab4a83f6bee74ab27835f28f405" - integrity sha512-5ekuQOvO04MDj7kYZJaMab2S8SPjGJbotVNyv7QYFCOAwrGZs/YnoDNlh1U+m5hl7H2D/+n0taaAV/tfyd3KMA== + version "2.2.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.0.tgz#09c40d92e936c64777aa385c4e9b904f8147eaf0" + integrity sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ== big.js@^3.1.3: version "3.2.0" @@ -4195,9 +4210,9 @@ cli-width@^2.0.0: integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== clipboard@^2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" - integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== + version "2.0.7" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.7.tgz#da927f817b1859426df39212ac81feb07dbaeadc" + integrity sha512-8M8WEZcIvs0hgOma+wAPkrUxpv0PMY1L6VsAJh/2DOKARIMpyWe6ZLcEoe1qktl6/ced5ceYHs+oGedSbgZ3sg== dependencies: good-listener "^1.2.2" select "^1.1.2" @@ -4299,9 +4314,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.5.3, color-string@^1.5.4: - version "1.5.4" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" - integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== + version "1.5.5" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" + integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -5792,9 +5807,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.649: - version "1.3.681" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.681.tgz#facd915ae46a020e8be566a2ecdc0b9444439be9" - integrity sha512-W6uYvSUTHuyX2DZklIESAqx57jfmGjUkd7Z3RWqLdj9Mmt39ylhBuvFXlskQnvBHj0MYXIeQI+mjiwVddZLSvA== + version "1.3.682" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.682.tgz#f4b5c8d4479df96b61e508a721d6c32c1262ef23" + integrity sha512-zok2y37qR00U14uM6qBz/3iIjWHom2eRfC2S1StA0RslP7x34jX+j4mxv80t8OEOHLJPVG54ZPeaFxEI7gPrwg== elegant-spinner@^1.0.1: version "1.0.1" @@ -7063,9 +7078,9 @@ gitconfiglocal@^1.0.0: ini "^1.3.2" github-buttons@^2.8.0: - version "2.14.3" - resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.14.3.tgz#421691619d6becc451fe1ad175d5e29fe3ae2651" - integrity sha512-kOXAjak/qEZYKORe1W80q+v8Ehas2yjHjyVWe4OO/MvmaRVH30m6n2xL4OM1+RBq3thVWxdCeVt3LIzbdATsKA== + version "2.14.4" + resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.14.4.tgz#f4b335d1af235bfb4a4598162e8f2ca190635124" + integrity sha512-Fikb6lylJ2skQnoBaEv9m1yc6QwA8qbRFMRFWU08tMKXpbaql1LMKCghBAiuNpiWiGA25NiKuFO0oeD63030Kw== github-from-package@0.0.0: version "0.0.0" @@ -7088,9 +7103,9 @@ glob-parent@^3.1.0: path-dirname "^1.0.0" glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -10072,9 +10087,9 @@ no-case@^3.0.4: tslib "^2.0.3" node-abi@^2.7.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.20.0.tgz#0659ee1a4a04dacabd3ac4429fac6297ed58e92e" - integrity sha512-6ldtfVR5l3RS8D0aT+lj/uM2Vv/PGEkeWzt2tl8DFBsGY/IuVnAIHl+dG6C14NlWClVv7Rn2+ZDvox+35Hx2Kg== + version "2.21.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.21.0.tgz#c2dc9ebad6f4f53d6ea9b531e7b8faad81041d48" + integrity sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg== dependencies: semver "^5.4.1" @@ -14689,9 +14704,9 @@ uuid@^3.0.1, uuid@^3.3.2, uuid@^3.4.0: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== v8-compile-cache@^2.0.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" - integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" @@ -15189,9 +15204,9 @@ ws@^6.2.1: async-limiter "~1.0.0" ws@^7.2.0: - version "7.4.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" - integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== + version "7.4.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" + integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== x-is-string@^0.1.0: version "0.1.0" @@ -15232,9 +15247,9 @@ y-leveldb@^0.1.0: lib0 "^0.2.31" y-prosemirror@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/y-prosemirror/-/y-prosemirror-1.0.6.tgz#75cfdd3ec65638f4c3112dd1a5ccd2daeb147f12" - integrity sha512-rbYA/kssa/4yMlvdfYrKtZwG15UCdwCIO6fCitua4VaJSVN2ruLvR1OrPl8txhKzyiPiBAphFzjj+ZXkm4D/6w== + version "1.0.7" + resolved "https://registry.yarnpkg.com/y-prosemirror/-/y-prosemirror-1.0.7.tgz#e0d3bbbc8b88bc4942d9720fd8413da393999200" + integrity sha512-AnlwGoz6+o6PkrBm09kY3+dXP3Yt6QHDIMHhtn0mGeUnSQRVcI0/raQwh/kYD7z1EUPghKfDaH8DoI0duAu5sg== dependencies: lib0 "^0.2.34"