diff --git a/docs/concepts/06-editor.md b/docs/concepts/06-editor.md index 69c87754d8..746d950f19 100644 --- a/docs/concepts/06-editor.md +++ b/docs/concepts/06-editor.md @@ -9,7 +9,7 @@ interface Editor { isInline: (element: Element) => boolean isVoid: (element: Element) => boolean normalizeNode: (entry: NodeEntry) => void - onChange: (children: Node[], operations: Operation[]) => void + onChange: () => void children: Node[] operations: Operation[] selection: Range | null diff --git a/package.json b/package.json index bb0ee644a7..9f475e825f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@types/mocha": "^5.2.7", "@types/node": "^12.12.14", "@types/react": "^16.9.13", + "@types/react-dom": "^16.9.4", "@typescript-eslint/eslint-plugin": "^2.9.0", "@typescript-eslint/parser": "^2.9.0", "babel-eslint": "^10.0.3", diff --git a/packages/slate-react/package.json b/packages/slate-react/package.json index 625aa8c246..c9c0c977a7 100644 --- a/packages/slate-react/package.json +++ b/packages/slate-react/package.json @@ -18,7 +18,6 @@ "@types/debug": "^4.1.5", "@types/is-hotkey": "^0.1.1", "@types/lodash": "^4.14.149", - "@types/react": "^16.9.13", "debounce": "^1.2.0", "direction": "^1.0.3", "is-hotkey": "^0.1.6", diff --git a/packages/slate-react/src/components/slate.tsx b/packages/slate-react/src/components/slate.tsx new file mode 100644 index 0000000000..5d9e4a1b9e --- /dev/null +++ b/packages/slate-react/src/components/slate.tsx @@ -0,0 +1,41 @@ +import React, { useMemo } from 'react' +import { Editor, Node, Range } from 'slate' + +import { ReactEditor } from '../plugin/react-editor' +import { FocusedContext } from '../hooks/use-focused' +import { EditorContext } from '../hooks/use-editor' +import { SlateContext } from '../hooks/use-slate' +import { EDITOR_TO_ON_CHANGE } from '../utils/weak-maps' + +/** + * A wrapper around the provider to handle `onChange` events, because the editor + * is a mutable singleton so it won't ever register as "changed" otherwise. + */ + +export const Slate = (props: { + editor: Editor + value: Node[] + selection: Range | null + children: React.ReactNode + onChange: (children: Node[], selection: Range | null) => void + [key: string]: any +}) => { + const { editor, children, onChange, value, selection, ...rest } = props + const context: [Editor] = useMemo(() => { + editor.children = value + editor.selection = selection + return [editor] + }, [value, selection, ...Object.values(rest)]) + + EDITOR_TO_ON_CHANGE.set(editor, onChange) + + return ( + + + + {children} + + + + ) +} diff --git a/packages/slate-react/src/hooks/use-slate.tsx b/packages/slate-react/src/hooks/use-slate.tsx index 5fb30544cb..4e9b14c3bc 100644 --- a/packages/slate-react/src/hooks/use-slate.tsx +++ b/packages/slate-react/src/hooks/use-slate.tsx @@ -1,18 +1,5 @@ -import React, { useState, useMemo } from 'react' -import { Editor, Node, Operation } from 'slate' +import { Editor } from 'slate' import { createContext, useContext } from 'react' -import { ReactEditor } from '../react-editor' -import { FocusedContext } from './use-focused' -import { EditorContext } from './use-editor' - -/** - * Associate the context change listener with the editor. - */ - -export const EDITOR_TO_CONTEXT_LISTENER = new WeakMap< - Editor, - (children: Node[], operations: Operation[]) => void ->() /** * A React context for sharing the `Editor` class, in a way that re-renders the @@ -21,42 +8,6 @@ export const EDITOR_TO_CONTEXT_LISTENER = new WeakMap< export const SlateContext = createContext<[Editor] | null>(null) -/** - * A wrapper around the provider to handle `onChange` events, because the editor - * is a mutable singleton so it won't ever register as "changed" otherwise. - */ - -export const Slate = (props: { - editor: Editor - children: React.ReactNode - defaultValue?: Node[] - onChange?: (children: Node[], operations: Operation[]) => void -}) => { - const { editor, children, defaultValue = [], onChange = () => {} } = props - const [context, setContext] = useState([editor]) - const value: [Editor] = useMemo(() => [editor], [context, editor]) - const listener = useMemo(() => { - editor.children = defaultValue - - return (children: Node[], operations: Operation[]) => { - onChange(children, operations) - setContext([editor]) - } - }, [editor]) - - EDITOR_TO_CONTEXT_LISTENER.set(editor, listener) - - return ( - - - - {children} - - - - ) -} - /** * Get the current `Editor` class that the component lives under. */ diff --git a/packages/slate-react/src/index.ts b/packages/slate-react/src/index.ts index eeeea97042..6c811c1714 100644 --- a/packages/slate-react/src/index.ts +++ b/packages/slate-react/src/index.ts @@ -1,11 +1,21 @@ -export * from './components/editable' +// Components +export { + RenderElementProps, + RenderLeafProps, + Editable, +} from './components/editable' export { DefaultElement } from './components/element' export { DefaultLeaf } from './components/leaf' -export * from './hooks/use-editor' -export * from './hooks/use-focused' -export * from './hooks/use-read-only' -export * from './hooks/use-selected' -export * from './hooks/use-slate' -export * from './react-command' -export * from './react-editor' -export * from './with-react' +export { Slate } from './components/slate' + +// Hooks +export { useEditor } from './hooks/use-editor' +export { useFocused } from './hooks/use-focused' +export { useReadOnly } from './hooks/use-read-only' +export { useSelected } from './hooks/use-selected' +export { useSlate } from './hooks/use-slate' + +// Plugin +export { InsertDataCommand, ReactCommand } from './plugin/react-command' +export { ReactEditor } from './plugin/react-editor' +export { withReact } from './plugin/with-react' diff --git a/packages/slate-react/src/react-command.ts b/packages/slate-react/src/plugin/react-command.ts similarity index 100% rename from packages/slate-react/src/react-command.ts rename to packages/slate-react/src/plugin/react-command.ts diff --git a/packages/slate-react/src/react-editor.ts b/packages/slate-react/src/plugin/react-editor.ts similarity index 99% rename from packages/slate-react/src/react-editor.ts rename to packages/slate-react/src/plugin/react-editor.ts index be185d3108..a6cd697a72 100644 --- a/packages/slate-react/src/react-editor.ts +++ b/packages/slate-react/src/plugin/react-editor.ts @@ -1,6 +1,6 @@ import { Editor, Element, Node, Path, Point, Range } from 'slate' -import { Key } from './utils/key' +import { Key } from '../utils/key' import { EDITOR_TO_ELEMENT, ELEMENT_TO_NODE, @@ -10,8 +10,7 @@ import { NODE_TO_INDEX, NODE_TO_KEY, NODE_TO_PARENT, - PLACEHOLDER_SYMBOL, -} from './utils/weak-maps' +} from '../utils/weak-maps' import { DOMElement, DOMNode, @@ -21,7 +20,7 @@ import { DOMStaticRange, isDOMElement, normalizeDOMPoint, -} from './utils/dom' +} from '../utils/dom' export interface ReactEditor extends Editor {} diff --git a/packages/slate-react/src/with-react.ts b/packages/slate-react/src/plugin/with-react.ts similarity index 70% rename from packages/slate-react/src/with-react.ts rename to packages/slate-react/src/plugin/with-react.ts index 7ba3a680eb..65a8c6896c 100644 --- a/packages/slate-react/src/with-react.ts +++ b/packages/slate-react/src/plugin/with-react.ts @@ -1,9 +1,10 @@ +import { unstable_batchedUpdates } from 'react-dom' import { Editor, Node, Path, Operation, Command } from 'slate' -import { ReactEditor, ReactCommand } from '.' -import { Key } from './utils/key' -import { NODE_TO_KEY } from './utils/weak-maps' -import { EDITOR_TO_CONTEXT_LISTENER } from './hooks/use-slate' +import { ReactEditor } from './react-editor' +import { ReactCommand } from './react-command' +import { Key } from '../utils/key' +import { EDITOR_TO_ON_CHANGE, NODE_TO_KEY } from '../utils/weak-maps' /** * `withReact` adds React and DOM specific behaviors to the editor. @@ -87,14 +88,21 @@ export const withReact = (editor: Editor): Editor => { } } - editor.onChange = (children: Node[], operations: Operation[]) => { - const contextOnChange = EDITOR_TO_CONTEXT_LISTENER.get(editor) - - if (contextOnChange) { - contextOnChange(children, operations) - } + editor.onChange = () => { + // COMPAT: React doesn't batch `setState` hook calls, which means that the + // children and selection can get out of sync for one render pass. So we + // have to use this unstable API to ensure it batches them. (2019/12/03) + // https://github.com/facebook/react/issues/14259#issuecomment-439702367 + unstable_batchedUpdates(() => { + const contextOnChange = EDITOR_TO_ON_CHANGE.get(editor) + + if (contextOnChange) { + const { children, selection } = editor + contextOnChange(children, selection) + } - onChange(children, operations) + onChange() + }) } return editor diff --git a/packages/slate-react/src/utils/weak-maps.ts b/packages/slate-react/src/utils/weak-maps.ts index c2074fb3eb..11a8e0e963 100644 --- a/packages/slate-react/src/utils/weak-maps.ts +++ b/packages/slate-react/src/utils/weak-maps.ts @@ -1,4 +1,4 @@ -import { Node, Ancestor, Editor, Text } from 'slate' +import { Node, Ancestor, Editor, Range } from 'slate' import { Key } from './key' @@ -31,4 +31,17 @@ export const IS_FOCUSED: WeakMap = new WeakMap() export const IS_DRAGGING: WeakMap = new WeakMap() export const IS_CLICKING: WeakMap = new WeakMap() +/** + * Weak map for associating the context `onChange` prop with the plugin. + */ + +export const EDITOR_TO_ON_CHANGE = new WeakMap< + Editor, + (children: Node[], selection: Range | null) => void +>() + +/** + * Symbols. + */ + export const PLACEHOLDER_SYMBOL = (Symbol('placeholder') as unknown) as string diff --git a/packages/slate/src/create-editor.ts b/packages/slate/src/create-editor.ts index 55993d4c53..df1a8e4ff9 100755 --- a/packages/slate/src/create-editor.ts +++ b/packages/slate/src/create-editor.ts @@ -78,7 +78,7 @@ export const createEditor = (): Editor => { Promise.resolve().then(() => { FLUSHING.set(editor, false) - editor.onChange(editor.children, editor.operations) + editor.onChange() editor.operations = [] }) } diff --git a/packages/slate/src/interfaces/editor/index.ts b/packages/slate/src/interfaces/editor/index.ts index d6a8aafab2..e4f196979d 100755 --- a/packages/slate/src/interfaces/editor/index.ts +++ b/packages/slate/src/interfaces/editor/index.ts @@ -22,7 +22,7 @@ export interface Editor { isInline: (element: Element) => boolean isVoid: (element: Element) => boolean normalizeNode: (entry: NodeEntry) => void - onChange: (children: Node[], operations: Operation[]) => void + onChange: () => void operations: Operation[] selection: Range | null [key: string]: any diff --git a/site/examples/check-lists.js b/site/examples/check-lists.js index 9f2bce2fcb..d08a44eb1f 100644 --- a/site/examples/check-lists.js +++ b/site/examples/check-lists.js @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback } from 'react' +import React, { useState, useMemo, useCallback } from 'react' import { Slate, Editable, @@ -12,13 +12,24 @@ import { css } from 'emotion' import { withHistory } from 'slate-history' const CheckListsExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const editor = useMemo( () => withChecklists(withHistory(withReact(createEditor()))), [] ) + return ( - + { + setValue(value) + setSelection(selection) + }} + > { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo(() => withEmbeds(withReact(createEditor())), []) return ( - + { + setValue(value) + setSelection(selection) + }} + > } placeholder="Enter some text..." diff --git a/site/examples/forced-layout.js b/site/examples/forced-layout.js index 5c15e6079b..0252759f7c 100644 --- a/site/examples/forced-layout.js +++ b/site/examples/forced-layout.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react' +import React, { useState, useCallback, useMemo } from 'react' import { Slate, Editable, withReact } from 'slate-react' import { Editor, createEditor } from 'slate' import { withHistory } from 'slate-history' @@ -39,13 +39,23 @@ const schema = [ ] const ForcedLayoutExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const editor = useMemo( () => withSchema(withHistory(withReact(createEditor())), schema), [] ) return ( - + { + setValue(value) + setSelection(selection) + }} + > { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo( () => withFormatting(withHistory(withReact(createEditor()))), [] ) + return ( - + { + setValue(value) + setSelection(selection) + }} + > } diff --git a/site/examples/huge-document.js b/site/examples/huge-document.js index f37fa4ccc6..a63b81ed78 100644 --- a/site/examples/huge-document.js +++ b/site/examples/huge-document.js @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback } from 'react' +import React, { useState, useMemo, useCallback } from 'react' import faker from 'faker' import { createEditor } from 'slate' import { Slate, Editable, withReact } from 'slate-react' @@ -21,10 +21,20 @@ for (let h = 0; h < HEADINGS; h++) { } const HugeDocumentExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const editor = useMemo(() => withReact(createEditor()), []) return ( - + { + setValue(value) + setSelection(selection) + }} + > ) diff --git a/site/examples/images.js b/site/examples/images.js index b62a2d1535..7696063b08 100644 --- a/site/examples/images.js +++ b/site/examples/images.js @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import React, { useState, useMemo } from 'react' import imageExtensions from 'image-extensions' import isUrl from 'is-url' import { Editor, createEditor } from 'slate' @@ -16,12 +16,23 @@ import { css } from 'emotion' import { Button, Icon, Toolbar } from '../components' const ImagesExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo( () => withImages(withHistory(withReact(createEditor()))), [] ) + return ( - + { + setValue(value) + setSelection(selection) + }} + > diff --git a/site/examples/links.js b/site/examples/links.js index a244bd50f2..2986adfd35 100644 --- a/site/examples/links.js +++ b/site/examples/links.js @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import React, { useState, useMemo } from 'react' import isUrl from 'is-url' import { Slate, Editable, withReact, useSlate } from 'slate-react' import { Editor, createEditor } from 'slate' @@ -7,12 +7,23 @@ import { withHistory } from 'slate-history' import { Button, Icon, Toolbar } from '../components' const LinkExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo( () => withLinks(withHistory(withReact(createEditor()))), [] ) + return ( - + { + setValue(value) + setSelection(selection) + }} + > diff --git a/site/examples/markdown-preview.js b/site/examples/markdown-preview.js index db0547bdb3..8f7046d003 100644 --- a/site/examples/markdown-preview.js +++ b/site/examples/markdown-preview.js @@ -1,5 +1,5 @@ import Prism from 'prismjs' -import React, { useCallback, useMemo } from 'react' +import React, { useState, useCallback, useMemo } from 'react' import { Slate, Editable, withReact } from 'slate-react' import { Text, createEditor } from 'slate' import { withHistory } from 'slate-history' @@ -9,6 +9,8 @@ import { css } from 'emotion' ;Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore const MarkdownPreviewExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderLeaf = useCallback(props => , []) const editor = useMemo(() => withHistory(withReact(createEditor())), []) const decorate = useCallback(([node, path]) => { @@ -50,7 +52,15 @@ const MarkdownPreviewExample = () => { }, []) return ( - + { + setValue(value) + setSelection(selection) + }} + > { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const editor = useMemo( () => withShortcuts(withReact(withHistory(createEditor()))), [] ) return ( - + { + setValue(value) + setSelection(selection) + }} + > { const ref = useRef() + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const [target, setTarget] = useState() const [index, setIndex] = useState(0) const [search, setSearch] = useState('') @@ -72,9 +74,11 @@ const MentionExample = () => { return ( { - const { selection } = editor + value={value} + selection={selection} + onChange={(value, selection) => { + setValue(value) + setSelection(selection) if (selection && Range.isCollapsed(selection)) { const [start] = Range.edges(selection) diff --git a/site/examples/paste-html.js b/site/examples/paste-html.js index db45ca1ef7..eb5033e20f 100644 --- a/site/examples/paste-html.js +++ b/site/examples/paste-html.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react' +import React, { useState, useCallback, useMemo } from 'react' import { jsx } from 'slate-hyperscript' import { Editor, createEditor } from 'slate' import { withHistory } from 'slate-history' @@ -79,6 +79,8 @@ export const deserialize = el => { } const PasteHtmlExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const renderLeaf = useCallback(props => , []) const editor = useMemo( @@ -86,7 +88,15 @@ const PasteHtmlExample = () => { [] ) return ( - + { + setValue(value) + setSelection(selection) + }} + > { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo(() => withHistory(withReact(createEditor())), []) return ( - + { + setValue(value) + setSelection(selection) + }} + > ) diff --git a/site/examples/read-only.js b/site/examples/read-only.js index c6a0152ed1..2ca37a8c97 100644 --- a/site/examples/read-only.js +++ b/site/examples/read-only.js @@ -1,11 +1,21 @@ -import React, { useMemo } from 'react' +import React, { useState, useMemo } from 'react' import { createEditor } from 'slate' import { Slate, Editable, withReact } from 'slate-react' const ReadOnlyExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const editor = useMemo(() => withReact(createEditor()), []) return ( - + { + setValue(value) + setSelection(selection) + }} + > ) diff --git a/site/examples/rich-text.js b/site/examples/rich-text.js index 34847c5781..328f9bc82d 100644 --- a/site/examples/rich-text.js +++ b/site/examples/rich-text.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import isHotkey from 'is-hotkey' import { Editable, withReact, useSlate, Slate } from 'slate-react' import { Editor, createEditor } from 'slate' @@ -23,6 +23,8 @@ const BLOCK_FORMATS = [ ] const RichTextExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const renderLeaf = useCallback(props => , []) const editor = useMemo( @@ -31,7 +33,15 @@ const RichTextExample = () => { ) return ( - + { + setValue(value) + setSelection(selection) + }} + > diff --git a/site/examples/search-highlighting.js b/site/examples/search-highlighting.js index a3a1273081..a5661aac5a 100644 --- a/site/examples/search-highlighting.js +++ b/site/examples/search-highlighting.js @@ -7,6 +7,8 @@ import { withHistory } from 'slate-history' import { Icon, Toolbar } from '../components' const SearchHighlightingExample = () => { + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const [search, setSearch] = useState() const editor = useMemo(() => withHistory(withReact(createEditor())), []) const decorate = useCallback( @@ -37,7 +39,15 @@ const SearchHighlightingExample = () => { ) return ( - + { + setValue(value) + setSelection(selection) + }} + >
{ + const [value, setValue] = useState(initialValue) + const [selection, setSelection] = useState(null) const renderElement = useCallback(props => , []) const renderLeaf = useCallback(props => , []) const editor = useMemo( @@ -11,7 +13,15 @@ const TablesExample = () => { [] ) return ( - + { + setValue(value) + setSelection(selection) + }} + > ) diff --git a/yarn.lock b/yarn.lock index fd57a9865d..d657c15e07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2551,6 +2551,21 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/react-dom@^16.9.4": + version "16.9.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.4.tgz#0b58df09a60961dcb77f62d4f1832427513420df" + integrity sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "16.9.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.14.tgz#7f1158ce450b4b5aa83b1c5e1324fa75f348bdd1" + integrity sha512-Q4tW4RGmR+u/CgzR8VqZcsUWjP4Pz/LcHfs9AzSG+aBnwq8As3Bid3vG1eGGsXg/xuR2k2tqNlI8pzyV8kxe0g== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/react@^16.9.13": version "16.9.13" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.13.tgz#b3ea5dd443f4a680599e2abba8cc66f5e1ce0059"