diff --git a/.flowconfig b/.flowconfig index a8e4b05f..5f2981a1 100644 --- a/.flowconfig +++ b/.flowconfig @@ -5,6 +5,8 @@ .*/node_modules/draft-js/lib/DraftEditorLeaf.react.js.flow .*/node_modules/draft-js/lib/DraftEditorDragHandler.js.flow .*/node_modules/draft-js/lib/DraftEditor.react.js.flow +.*/node_modules/draft-js-plugins-editor/lib/Editor/index.js.flow +.*/node_modules/draft-js-plugins-editor/lib/index.js.flow # .*/node_modules/config-chain suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe diff --git a/.storybook/config.js b/.storybook/config.js index c97c34b3..968e101c 100644 --- a/.storybook/config.js +++ b/.storybook/config.js @@ -44,4 +44,5 @@ configure(() => { require("../examples/docs.story"); require("../examples/performance.story"); require("../examples/tests.story"); + require("../examples/plugins.story"); }, module); diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c8b0b42..605d443d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ > Documentation: [draftail.org/docs/next/getting-started](https://www.draftail.org/docs/next/getting-started) +๐ŸŽ‰ blog post for this release: [Draftail v1.2.0: supporting modern experiences](https://www.draftail.org/blog/2019/03/03/draftail-v1-2-0-supporting-modern-experiences). + +### Added + +- Add [`plugins`](https://www.draftail.org/docs/plugins) API to support extensions of the editor using the [draft-js-plugins](https://github.com/draft-js-plugins/draft-js-plugins) architecture ([#83](https://github.com/springload/draftail/issues/83), [#171](https://github.com/springload/draftail/pull/171)). + +This new API makes it possible to build much more advanced extensions to the editor than ever before, such as autocompletes, [linkify](https://www.draftail.org/docs/extensions-tutorial-linkify), [custom blocks](https://www.draftail.org/docs/blocks#custom-block-rendering), [custom toolbars](https://www.draftail.org/docs/customising-toolbars), and more. Read the [release blog post](https://www.draftail.org/blog/2019/03/03/draftail-v1-2-0-supporting-modern-experiences) to learn more about the motivation for those new APIs. + +- Add data reset parameter to `DraftUtils.resetBlockWithType()`. +- Add ability to disable or customise the editor toolbar with [`topToolbar`](https://www.draftail.org/docs/customising-toolbars). +- Add ability to add a toolbar below the editor with [`bottomToolbar`](https://www.draftail.org/docs/customising-toolbars). + +### Changed + +- Enable list continuation on Enter for custom `*-list-item` blocks. All thatโ€™s required is for the block type to end with `-list-item`. + ## [[v1.1.0]](https://github.com/springload/draftail/releases/tag/v1.1.0) > Documentation: [draftail.org/docs/getting-started](https://www.draftail.org/docs/getting-started) diff --git a/README.md b/README.md index 0f0e4478..d6ea71e1 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,14 @@ Draftail aims for a mouse-free, keyboard-centric experience. Most formatting can Here are important features worth highlighting: - Support for [keyboard shortcuts](https://www.draftail.org/docs/keyboard-shortcuts). Lots of them! -- Paste from Word. Or any other editor. +- Paste from Word. Or any other editor. It just works. - Autolists โ€“ start a line with `-` , `*` , `1.` to create a list item. - Shortcuts for heading levels `##`, code blocks ` ``` `, and more. - Undo / redo โ€“ until the end of times. - Common text types: headings, paragraphs, quotes, lists. - Common text styles: Bold, italic, and many more. - API to build custom controls for links, images, and more. +- Compatibility with the [`draft-js-plugins`](https://www.draft-js-plugins.com) ecosystem to build more advanced extensions. > This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), and measures performance and [code coverage](https://coveralls.io/github/springload/draftail). It uses [Flow](https://flow.org/) types. We also try to follow accessibility best practices (tested with [aXe](https://www.axe-core.org/)) โ€“ please [get in touch](https://github.com/springload/draftail/issues/149#issuecomment-389476151) if you can help us do better in this area. diff --git a/examples/constants/customContentState.js b/examples/constants/customContentState.js index dd7be14e..4759db6b 100644 --- a/examples/constants/customContentState.js +++ b/examples/constants/customContentState.js @@ -1,66 +1,44 @@ // @flow export default { - entityMap: { - "0": { - type: "DOCUMENT", - mutability: "MUTABLE", - data: { - url: "doc.pdf", - title: "Kritik der reinen Vernunft", - }, - }, - "1": { - type: "EMBED", - mutability: "IMMUTABLE", - data: { - url: "http://www.youtube.com/watch?v=y8Kyi0WNg40", - title: "Dramatic Look", - thumbnail: "/static/example-lowres-image2.jpg", - }, - }, - }, blocks: [ { key: "c1gc9", - text: "You can implement custom block types as required.", - type: "tiny-text", - depth: 0, - inlineStyleRanges: [], - entityRanges: [], - data: {}, - }, - { - key: "bldpo", text: - "And also inline styles. Or abuse the entity API to make text decorators.", - type: "unstyled", + "You can implement custom block types as required, and inline styles too, or entities.", + type: "tiny-text", depth: 0, inlineStyleRanges: [ { - offset: 9, + offset: 54, length: 13, style: "REDACTED", }, - { - offset: 27, - length: 5, - style: "REDACTED", - }, - { - offset: 56, - length: 15, - style: "REDACTED", - }, ], entityRanges: [ { - offset: 44, - length: 3, + offset: 76, + length: 8, key: 0, }, ], data: {}, }, + { + key: "7dtlg", + text: + "Draftail also supports the #plugins architecture of draft-js-plugins.", + type: "unstyled", + depth: 0, + inlineStyleRanges: [ + { + offset: 52, + length: 16, + style: "BOLD", + }, + ], + entityRanges: [], + data: {}, + }, { key: "affm4", text: " ", @@ -104,4 +82,22 @@ export default { data: {}, }, ], + entityMap: { + "0": { + type: "DOCUMENT", + mutability: "MUTABLE", + data: { + url: "docs.pdf", + }, + }, + "1": { + type: "EMBED", + mutability: "IMMUTABLE", + data: { + url: "http://www.youtube.com/watch?v=y8Kyi0WNg40", + title: "Dramatic Look", + thumbnail: "/static/example-lowres-image2.jpg", + }, + }, + }, }; diff --git a/examples/docs.story.js b/examples/docs.story.js index 0a06c4d2..1d8d2a62 100644 --- a/examples/docs.story.js +++ b/examples/docs.story.js @@ -222,6 +222,80 @@ storiesOf("Docs", module) /> )) + .add("No toolbar", () => ( +
+ +
+ )) + .add("Custom toolbars", () => ( +
+ ( +
+ +
+ )} + bottomToolbar={({ getEditorState }) => ( +
+ +
+ )} + /> +
+ )) .add("Icons", () => ( (
@@ -80,6 +83,7 @@ storiesOf("Examples", module) entityTypes={[ENTITY_CONTROL.EMBED, ENTITY_CONTROL.DOCUMENT]} decorators={[new PrismDecorator({ defaultLanguage: "css" })]} controls={[ReadingTime]} + plugins={[hashtagPlugin]} /> )) .add("All built-in formats", () => ( diff --git a/examples/main.scss b/examples/main.scss index ed328971..0bd2c8ac 100644 --- a/examples/main.scss +++ b/examples/main.scss @@ -36,6 +36,9 @@ $draftail-editor-chrome-accent: lighten( $draftail-editor-font-family: $FONT_FAMILY_SANS; @import "../node_modules/draft-js/dist/Draft"; +@import "../node_modules/draft-js-hashtag-plugin/lib/plugin"; +@import "../node_modules/draft-js-inline-toolbar-plugin/lib/plugin"; +@import "../node_modules/draft-js-side-toolbar-plugin/lib/plugin"; @import "../lib/index"; @import "./components/editor"; @@ -53,4 +56,7 @@ $draftail-editor-font-family: $FONT_FAMILY_SANS; @import "./blocks/EmbedBlock"; @import "./blocks/ImageBlock"; +@import "./plugins/actionBlockPlugin"; +@import "./plugins/sectionBreakPlugin"; + @import "./utils/utilities"; diff --git a/examples/plugins.story.js b/examples/plugins.story.js new file mode 100644 index 00000000..50fe5d4f --- /dev/null +++ b/examples/plugins.story.js @@ -0,0 +1,200 @@ +import { storiesOf } from "@storybook/react"; +import React, { Component } from "react"; +import { composeDecorators } from "draft-js-plugins-editor"; +import createInlineToolbarPlugin from "draft-js-inline-toolbar-plugin"; +import createSideToolbarPlugin from "draft-js-side-toolbar-plugin"; +import { DraftailEditor } from "../lib"; + +import { INLINE_CONTROL, ENTITY_CONTROL, BLOCK_CONTROL } from "./constants/ui"; + +import EditorWrapper from "./components/EditorWrapper"; +import singleLinePlugin from "./plugins/singleLinePlugin"; +import linkifyPlugin from "./plugins/linkifyPlugin"; +import actionBlockPlugin from "./plugins/actionBlockPlugin"; +import slashCommandPlugin from "./plugins/slashCommandPlugin"; +import sectionBreakPlugin, { + SectionBreakControl, +} from "./plugins/sectionBreakPlugin"; +import createFocusPlugin from "./plugins/draft-js-focus-plugin/index"; + +const singleLine = singleLinePlugin(); +const linkify = linkifyPlugin(); +const actionBlock = actionBlockPlugin(); +const slashCommand = slashCommandPlugin(); +const focusPlugin = createFocusPlugin({ focusableBlocks: ["section-break"] }); +const sectionBreak = sectionBreakPlugin({ + decorator: composeDecorators(focusPlugin.decorator), +}); + +storiesOf("Plugins", module) + .add("Single-line", () => ( + + )) + .add("Linkify", () => ( + + )) + .add("Actions", () => ( + + )) + .add("Slash (/) commands", () => ( + . Then press Enter.", + inlineStyleRanges: [ + { + offset: 35, + length: 3, + style: "CODE", + }, + { + offset: 44, + length: 12, + style: "CODE", + }, + { + offset: 69, + length: 5, + style: "KEYBOARD", + }, + ], + }, + ], + }} + enableHorizontalRule + inlineStyles={[INLINE_CONTROL.CODE, INLINE_CONTROL.KEYBOARD]} + blockTypes={[BLOCK_CONTROL.UNORDERED_LIST_ITEM]} + entityTypes={[ENTITY_CONTROL.LINK, ENTITY_CONTROL.EMBED]} + plugins={[slashCommand]} + /> + )) + .add("Section break", () => ( + + )); + +class CustomToolbarStory extends Component { + constructor(props) { + super(props); + + this.state = { + inlineToolbarPlugin: createInlineToolbarPlugin(), + sideToolbarPlugin: createSideToolbarPlugin(), + }; + } + + render() { + const { inlineToolbarPlugin, sideToolbarPlugin } = this.state; + const { InlineToolbar } = inlineToolbarPlugin; + const { SideToolbar } = sideToolbarPlugin; + + return ( +
+ ( + <> + + + + )} + /> +
+ ); + } +} + +storiesOf("Plugins", module).add("Custom toolbars", () => ( + +)); diff --git a/examples/plugins/actionBlockPlugin.js b/examples/plugins/actionBlockPlugin.js new file mode 100644 index 00000000..6cff0e99 --- /dev/null +++ b/examples/plugins/actionBlockPlugin.js @@ -0,0 +1,157 @@ +// @flow +import React, { Component } from "react"; +import { + ContentState, + ContentBlock, + EditorState, + EditorBlock, + SelectionState, +} from "draft-js"; +import type { DraftDecoratorType } from "draft-js/lib/DraftDecoratorType"; +import type { BidiDirection } from "fbjs/lib/UnicodeBidiDirection"; +import { DraftUtils } from "../../lib/index"; + +// https://github.com/brijeshb42/medium-draft/blob/master/src/components/blocks/todo.js + +type PluginFns = { + setEditorState: (EditorState) => void, + getEditorState: () => EditorState, +}; + +const updateDataOfBlock = (editorState, block, newData) => { + const contentState = editorState.getCurrentContent(); + const newBlock = block.merge({ + data: newData, + }); + const newContentState = contentState.merge({ + blockMap: contentState.getBlockMap().set(block.getKey(), newBlock), + }); + + // forceSelection hack to make sure the selection does not attempt to go where the checkbox is. + return EditorState.forceSelection( + EditorState.push(editorState, newContentState, "change-block-data"), + editorState.getSelection(), + ); +}; + +const preventDefaultStopPropagation = (e) => { + e.preventDefault(); + e.stopPropagation(); +}; + +type Props = {| + block: ContentBlock, + blockProps: PluginFns, + blockStyleFn: (block: ContentBlock) => string, + contentState: ContentState, + customStyleFn: (style: string, block: ContentBlock) => ?{}, + customStyleMap: {}, + decorator: ?DraftDecoratorType, + direction: BidiDirection, + forceSelection: boolean, + offsetKey: string, + selection: SelectionState, + startIndent: boolean, + tree: {}, +|}; + +class ActionBlock extends Component { + constructor(props: Props) { + super(props); + + this.onChange = this.onChange.bind(this); + } + + /* :: onChange: () => void; */ + onChange() { + const { block, blockProps } = this.props; + const { setEditorState, getEditorState } = blockProps; + + const data = block.getData(); + const newData = data.set("checked", !data.get("checked")); + setEditorState(updateDataOfBlock(getEditorState(), block, newData)); + } + + render() { + const { block } = this.props; + const checked = block.getData().get("checked", false); + return ( + <> + + + + + + ); + } +} + +const ACTION_INPUT = { + "[] ": false, + "[ ] ": false, + "- [] ": false, + "- [ ] ": false, + "* [] ": false, + "* [ ] ": false, + "[x] ": true, + "- [x] ": true, + "* [x] ": true, + "[X] ": true, + "- [X] ": true, + "* [X] ": true, +}; + +const actionBlockPlugin = () => ({ + blockRendererFn(block: ContentBlock, pluginFns: PluginFns) { + if (block.getType() === "action-list-item") { + return { + component: ActionBlock, + editable: true, + props: pluginFns, + }; + } + + return null; + }, + + handleBeforeInput( + char: string, + editorState: EditorState, + { setEditorState }: PluginFns, + ) { + const selection = editorState.getSelection(); + + if (selection.isCollapsed()) { + const block = DraftUtils.getSelectedBlock(editorState); + const startOffset = selection.getStartOffset(); + const text = block.getText(); + const beforeBeforeInput = text.slice(0, startOffset); + const mark = `${beforeBeforeInput}${char}`; + + const shouldSwitchBlock = typeof ACTION_INPUT[mark] !== "undefined"; + + if (shouldSwitchBlock) { + setEditorState( + DraftUtils.resetBlockWithType( + editorState, + "action-list-item", + text.replace(beforeBeforeInput, ""), + { + checked: ACTION_INPUT[mark], + }, + ), + ); + return "handled"; + } + } + + return "not-handled"; + }, +}); + +export default actionBlockPlugin; diff --git a/examples/plugins/actionBlockPlugin.scss b/examples/plugins/actionBlockPlugin.scss new file mode 100644 index 00000000..0db8290c --- /dev/null +++ b/examples/plugins/actionBlockPlugin.scss @@ -0,0 +1,17 @@ +.Draftail-block--action-list-item { + [type="checkbox"] { + $above-draft-block: 1; + + cursor: pointer; + float: left; + position: relative; + top: 4px; + z-index: $above-draft-block; + } + + .public-DraftStyleDefault-block { + $default-list-item-spacing: 1.5em; + + padding-left: $default-list-item-spacing; + } +} diff --git a/examples/plugins/draft-js-focus-plugin/.eslintrc.js b/examples/plugins/draft-js-focus-plugin/.eslintrc.js new file mode 100644 index 00000000..d3c885d6 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + "no-warning-comments": [0], + }, +}; diff --git a/examples/plugins/draft-js-focus-plugin/createDecorator.js b/examples/plugins/draft-js-focus-plugin/createDecorator.js new file mode 100755 index 00000000..dc6879cc --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/createDecorator.js @@ -0,0 +1,47 @@ +import React, { Component } from "react"; + +// Get a component's display name +const getDisplayName = (WrappedComponent) => { + const component = WrappedComponent.WrappedComponent || WrappedComponent; + return component.displayName || component.name || "Component"; +}; + +export default ({ blockKeyStore }) => (WrappedComponent) => + class BlockFocusDecorator extends Component { + static displayName = `BlockFocus(${getDisplayName(WrappedComponent)})`; + + static WrappedComponent = + WrappedComponent.WrappedComponent || WrappedComponent; + + componentDidMount() { + const { block } = this.props; + blockKeyStore.add(block.getKey()); + } + + componentWillUnmount() { + const { block } = this.props; + blockKeyStore.remove(block.getKey()); + } + + onClick = (e) => { + const { blockProps } = this.props; + + e.preventDefault(); + + if (!blockProps.isFocused) { + blockProps.setFocusToBlock(); + } + }; + + render() { + const { blockProps } = this.props; + const { isFocused } = blockProps; + return ( + + ); + } + }; diff --git a/examples/plugins/draft-js-focus-plugin/index.js b/examples/plugins/draft-js-focus-plugin/index.js new file mode 100755 index 00000000..3e006893 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/index.js @@ -0,0 +1,242 @@ +import { EditorState } from "draft-js"; +import insertNewLine from "./modifiers/insertNewLine"; +import setSelection from "./modifiers/setSelection"; +import setSelectionToBlock from "./modifiers/setSelectionToBlock"; +import createDecorator from "./createDecorator"; +import createBlockKeyStore from "./utils/createBlockKeyStore"; +import blockInSelection from "./utils/blockInSelection"; +import getBlockMapKeys from "./utils/getBlockMapKeys"; +import removeBlock from "./modifiers/removeBlock"; + +const focusableBlockIsSelected = (editorState, blockKeyStore) => { + const selection = editorState.getSelection(); + if (selection.getAnchorKey() !== selection.getFocusKey()) { + return false; + } + const content = editorState.getCurrentContent(); + const block = content.getBlockForKey(selection.getAnchorKey()); + return blockKeyStore.includes(block.getKey()); +}; + +const deleteCommands = [ + "backspace", + "backspace-word", + "backspace-to-start-of-line", + "delete", + "delete-word", + "delete-to-end-of-block", +]; + +export default (config = {}) => { + const focusableBlocks = config.focusableBlocks || []; + const blockKeyStore = createBlockKeyStore({}); + let lastSelection; + let lastContentState; + + return { + handleReturn: (event, editorState, { setEditorState }) => { + // if a focusable block is selected then overwrite new line behavior to custom + if (focusableBlockIsSelected(editorState, blockKeyStore)) { + setEditorState(insertNewLine(editorState)); + return "handled"; + } + return "not-handled"; + }, + handleKeyCommand: (command, editorState, { setEditorState }) => { + if ( + deleteCommands.includes(command) && + focusableBlockIsSelected(editorState, blockKeyStore) + ) { + const key = editorState.getSelection().getStartKey(); + const newEditorState = removeBlock(editorState, key); + if (newEditorState !== editorState) { + setEditorState(newEditorState); + return "handled"; + } + } + return "not-handled"; + }, + onChange: (editorState) => { + // in case the content changed there is no need to re-render blockRendererFn + // since if a block was added it will be rendered anyway and if it was text + // then the change was not a pure selection change + const contentState = editorState.getCurrentContent(); + if (!contentState.equals(lastContentState)) { + lastContentState = contentState; + return editorState; + } + lastContentState = contentState; + + // if the selection didn't change there is no need to re-render + const selection = editorState.getSelection(); + if (lastSelection && selection.equals(lastSelection)) { + lastSelection = editorState.getSelection(); + return editorState; + } + + // Note: Only if the previous or current selection contained a focusableBlock a re-render is needed. + const focusableBlockKeys = blockKeyStore.getAll(); + if (lastSelection) { + const lastBlockMapKeys = getBlockMapKeys( + contentState, + lastSelection.getStartKey(), + lastSelection.getEndKey(), + ); + if (lastBlockMapKeys.some((key) => focusableBlockKeys.includes(key))) { + lastSelection = selection; + // By forcing the selection the editor will trigger the blockRendererFn which is + // necessary for the blockProps containing isFocus to be passed down again. + return EditorState.forceSelection( + editorState, + editorState.getSelection(), + ); + } + } + + const currentBlockMapKeys = getBlockMapKeys( + contentState, + selection.getStartKey(), + selection.getEndKey(), + ); + if (currentBlockMapKeys.some((key) => focusableBlockKeys.includes(key))) { + lastSelection = selection; + // By forcing the selection the editor will trigger the blockRendererFn which is + // necessary for the blockProps containing isFocus to be passed down again. + return EditorState.forceSelection( + editorState, + editorState.getSelection(), + ); + } + + return editorState; + }, + keyBindingFn(evt, { getEditorState, setEditorState }) { + const editorState = getEditorState(); + // TODO match by entitiy instead of block type + if (focusableBlockIsSelected(editorState, blockKeyStore)) { + // arrow left + if (evt.keyCode === 37) { + setSelection(getEditorState, setEditorState, "up", evt); + } + // arrow right + if (evt.keyCode === 39) { + setSelection(getEditorState, setEditorState, "down", evt); + } + } + + // Don't manually overwrite in case the shift key is used to avoid breaking + // native behaviour that works anyway. + if (evt.shiftKey) { + return; + } + + // arrow left + if (evt.keyCode === 37) { + // Covering the case to select the before block + const selection = editorState.getSelection(); + const selectionKey = selection.getAnchorKey(); + const beforeBlock = editorState + .getCurrentContent() + .getBlockBefore(selectionKey); + // only if the selection caret is a the left most position + if ( + beforeBlock && + selection.getAnchorOffset() === 0 && + blockKeyStore.includes(beforeBlock.getKey()) + ) { + setSelection(getEditorState, setEditorState, "up", evt); + } + } + // arrow right + if (evt.keyCode === 39) { + // Covering the case to select the after block + const selection = editorState.getSelection(); + const selectionKey = selection.getFocusKey(); + const currentBlock = editorState + .getCurrentContent() + .getBlockForKey(selectionKey); + const afterBlock = editorState + .getCurrentContent() + .getBlockAfter(selectionKey); + const notAtomicAndLastPost = + !focusableBlocks.includes(currentBlock.getType()) && + currentBlock.getLength() === selection.getFocusOffset(); + if ( + afterBlock && + notAtomicAndLastPost && + blockKeyStore.includes(afterBlock.getKey()) + ) { + setSelection(getEditorState, setEditorState, "down", evt); + } + } + }, + // Wrap all block-types in block-focus decorator + blockRendererFn: (contentBlock, { getEditorState, setEditorState }) => { + if (!focusableBlocks.includes(contentBlock.getType())) { + return undefined; + } + + const editorState = getEditorState(); + const isFocused = blockInSelection(editorState, contentBlock.getKey()); + + return { + props: { + isFocused, + isCollapsedSelection: editorState.getSelection().isCollapsed(), + setFocusToBlock: () => { + setSelectionToBlock(getEditorState, setEditorState, contentBlock); + }, + }, + }; + }, + // Handle down/up arrow events and set activeBlock/selection if necessary + onDownArrow: (event, { getEditorState, setEditorState }) => { + // TODO edgecase: if one block is selected and the user wants to expand the selection using the shift key + + const editorState = getEditorState(); + if (focusableBlockIsSelected(editorState, blockKeyStore)) { + setSelection(getEditorState, setEditorState, "down", event); + return; + } + + // Don't manually overwrite in case the shift key is used to avoid breaking + // native behaviour that works anyway. + if (event.shiftKey) { + return; + } + + // Covering the case to select the after block with arrow down + const selectionKey = editorState.getSelection().getAnchorKey(); + const afterBlock = editorState + .getCurrentContent() + .getBlockAfter(selectionKey); + if (afterBlock && blockKeyStore.includes(afterBlock.getKey())) { + setSelection(getEditorState, setEditorState, "down", event); + } + }, + onUpArrow: (event, { getEditorState, setEditorState }) => { + // TODO edgecase: if one block is selected and the user wants to expand the selection using the shift key + + const editorState = getEditorState(); + if (focusableBlockIsSelected(editorState, blockKeyStore)) { + setSelection(getEditorState, setEditorState, "up", event); + } + + // Don't manually overwrite in case the shift key is used to avoid breaking + // native behaviour that works anyway. + if (event.shiftKey) { + return; + } + + // Covering the case to select the before block with arrow up + const selectionKey = editorState.getSelection().getAnchorKey(); + const beforeBlock = editorState + .getCurrentContent() + .getBlockBefore(selectionKey); + if (beforeBlock && blockKeyStore.includes(beforeBlock.getKey())) { + setSelection(getEditorState, setEditorState, "up", event); + } + }, + decorator: createDecorator({ blockKeyStore }), + }; +}; diff --git a/examples/plugins/draft-js-focus-plugin/modifiers/insertNewLine.js b/examples/plugins/draft-js-focus-plugin/modifiers/insertNewLine.js new file mode 100755 index 00000000..68af28b2 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/modifiers/insertNewLine.js @@ -0,0 +1,48 @@ +import { List } from "immutable"; +import { + ContentBlock, + EditorState, + BlockMapBuilder, + genKey as generateRandomKey, +} from "draft-js"; + +const insertBlockAfterSelection = (contentState, selectionState, newBlock) => { + const targetKey = selectionState.getStartKey(); + const array = []; + contentState.getBlockMap().forEach((block, blockKey) => { + array.push(block); + if (blockKey !== targetKey) return; + array.push(newBlock); + }); + return contentState.merge({ + blockMap: BlockMapBuilder.createFromArray(array), + selectionBefore: selectionState, + selectionAfter: selectionState.merge({ + anchorKey: newBlock.getKey(), + anchorOffset: newBlock.getLength(), + focusKey: newBlock.getKey(), + focusOffset: newBlock.getLength(), + isBackward: false, + }), + }); +}; + +export default function insertNewLine(editorState) { + const contentState = editorState.getCurrentContent(); + const selectionState = editorState.getSelection(); + const newLineBlock = new ContentBlock({ + key: generateRandomKey(), + type: "unstyled", + text: "", + characterList: List(), + }); + const withNewLine = insertBlockAfterSelection( + contentState, + selectionState, + newLineBlock, + ); + const newContent = withNewLine.merge({ + selectionAfter: withNewLine.getSelectionAfter().set("hasFocus", true), + }); + return EditorState.push(editorState, newContent, "insert-fragment"); +} diff --git a/examples/plugins/draft-js-focus-plugin/modifiers/removeBlock.js b/examples/plugins/draft-js-focus-plugin/modifiers/removeBlock.js new file mode 100755 index 00000000..f03db12b --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/modifiers/removeBlock.js @@ -0,0 +1,53 @@ +import { Modifier, EditorState, SelectionState } from "draft-js"; + +/* NOT USED at the moment, but might be valuable if we want to fix atomic block behaviour */ + +export default function(editorState, blockKey) { + let content = editorState.getCurrentContent(); + + const beforeKey = content.getKeyBefore(blockKey); + const beforeBlock = content.getBlockForKey(beforeKey); + + // Note: if the focused block is the first block then it is reduced to an + // unstyled block with no character + if (beforeBlock === undefined) { + const targetRange = new SelectionState({ + anchorKey: blockKey, + anchorOffset: 0, + focusKey: blockKey, + focusOffset: 1, + }); + // change the blocktype and remove the characterList entry with the sticker + content = Modifier.removeRange(content, targetRange, "backward"); + content = Modifier.setBlockType(content, targetRange, "unstyled"); + const newState = EditorState.push(editorState, content, "remove-block"); + + // force to new selection + const newSelection = new SelectionState({ + anchorKey: blockKey, + anchorOffset: 0, + focusKey: blockKey, + focusOffset: 0, + }); + return EditorState.forceSelection(newState, newSelection); + } + + const targetRange = new SelectionState({ + anchorKey: beforeKey, + anchorOffset: beforeBlock.getLength(), + focusKey: blockKey, + focusOffset: 1, + }); + + content = Modifier.removeRange(content, targetRange, "backward"); + const newState = EditorState.push(editorState, content, "remove-block"); + + // force to new selection + const newSelection = new SelectionState({ + anchorKey: beforeKey, + anchorOffset: beforeBlock.getLength(), + focusKey: beforeKey, + focusOffset: beforeBlock.getLength(), + }); + return EditorState.forceSelection(newState, newSelection); +} diff --git a/examples/plugins/draft-js-focus-plugin/modifiers/setSelection.js b/examples/plugins/draft-js-focus-plugin/modifiers/setSelection.js new file mode 100755 index 00000000..dd633256 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/modifiers/setSelection.js @@ -0,0 +1,47 @@ +import { SelectionState, EditorState } from "draft-js"; +import DraftOffsetKey from "draft-js/lib/DraftOffsetKey"; + +// Set selection of editor to next/previous block +export default (getEditorState, setEditorState, mode, event) => { + const editorState = getEditorState(); + const selectionKey = editorState.getSelection().getAnchorKey(); + const newActiveBlock = + mode === "up" + ? editorState.getCurrentContent().getBlockBefore(selectionKey) + : editorState.getCurrentContent().getBlockAfter(selectionKey); + + if (newActiveBlock && newActiveBlock.get("key") === selectionKey) { + return; + } + + if (newActiveBlock) { + // TODO verify that always a key-0-0 exists + const offsetKey = DraftOffsetKey.encode(newActiveBlock.getKey(), 0, 0); + const node = document.querySelectorAll( + `[data-offset-key="${offsetKey}"]`, + )[0]; + // set the native selection to the node so the caret is not in the text and + // the selectionState matches the native selection + const selection = window.getSelection(); + const range = document.createRange(); + range.setStart(node, 0); + range.setEnd(node, 0); + selection.removeAllRanges(); + selection.addRange(range); + + const offset = mode === "up" ? newActiveBlock.getLength() : 0; + event.preventDefault(); + setEditorState( + EditorState.forceSelection( + editorState, + new SelectionState({ + anchorKey: newActiveBlock.getKey(), + anchorOffset: offset, + focusKey: newActiveBlock.getKey(), + focusOffset: offset, + isBackward: false, + }), + ), + ); + } +}; diff --git a/examples/plugins/draft-js-focus-plugin/modifiers/setSelectionToBlock.js b/examples/plugins/draft-js-focus-plugin/modifiers/setSelectionToBlock.js new file mode 100755 index 00000000..e63fd833 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/modifiers/setSelectionToBlock.js @@ -0,0 +1,32 @@ +import { SelectionState, EditorState } from "draft-js"; +import DraftOffsetKey from "draft-js/lib/DraftOffsetKey"; + +// Set selection of editor to next/previous block +export default (getEditorState, setEditorState, newActiveBlock) => { + const editorState = getEditorState(); + + // TODO verify that always a key-0-0 exists + const offsetKey = DraftOffsetKey.encode(newActiveBlock.getKey(), 0, 0); + const node = document.querySelectorAll(`[data-offset-key="${offsetKey}"]`)[0]; + // set the native selection to the node so the caret is not in the text and + // the selectionState matches the native selection + const selection = window.getSelection(); + const range = document.createRange(); + range.setStart(node, 0); + range.setEnd(node, 0); + selection.removeAllRanges(); + selection.addRange(range); + + setEditorState( + EditorState.forceSelection( + editorState, + new SelectionState({ + anchorKey: newActiveBlock.getKey(), + anchorOffset: 0, + focusKey: newActiveBlock.getKey(), + focusOffset: 0, + isBackward: false, + }), + ), + ); +}; diff --git a/examples/plugins/draft-js-focus-plugin/utils/blockInSelection.js b/examples/plugins/draft-js-focus-plugin/utils/blockInSelection.js new file mode 100755 index 00000000..562c45e6 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/utils/blockInSelection.js @@ -0,0 +1,6 @@ +import getSelectedBlocksMapKeys from "./getSelectedBlocksMapKeys"; + +export default (editorState, blockKey) => { + const selectedBlocksKeys = getSelectedBlocksMapKeys(editorState); + return selectedBlocksKeys.includes(blockKey); +}; diff --git a/examples/plugins/draft-js-focus-plugin/utils/createBlockKeyStore.js b/examples/plugins/draft-js-focus-plugin/utils/createBlockKeyStore.js new file mode 100755 index 00000000..d12210b1 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/utils/createBlockKeyStore.js @@ -0,0 +1,24 @@ +import { List } from "immutable"; + +const createBlockKeyStore = () => { + let keys = List(); + + const add = (key) => { + keys = keys.push(key); + return keys; + }; + + const remove = (key) => { + keys = keys.filter((item) => item !== key); + return keys; + }; + + return { + add, + remove, + includes: (key) => keys.includes(key), + getAll: () => keys, + }; +}; + +export default createBlockKeyStore; diff --git a/examples/plugins/draft-js-focus-plugin/utils/getBlockMapKeys.js b/examples/plugins/draft-js-focus-plugin/utils/getBlockMapKeys.js new file mode 100755 index 00000000..a4a20db5 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/utils/getBlockMapKeys.js @@ -0,0 +1,7 @@ +export default (contentState, startKey, endKey) => { + const blockMapKeys = contentState.getBlockMap().keySeq(); + return blockMapKeys + .skipUntil((key) => key === startKey) + .takeUntil((key) => key === endKey) + .concat([endKey]); +}; diff --git a/examples/plugins/draft-js-focus-plugin/utils/getSelectedBlocksMapKeys.js b/examples/plugins/draft-js-focus-plugin/utils/getSelectedBlocksMapKeys.js new file mode 100755 index 00000000..d6e30995 --- /dev/null +++ b/examples/plugins/draft-js-focus-plugin/utils/getSelectedBlocksMapKeys.js @@ -0,0 +1,11 @@ +import getBlockMapKeys from "./getBlockMapKeys"; + +export default (editorState) => { + const selectionState = editorState.getSelection(); + const contentState = editorState.getCurrentContent(); + return getBlockMapKeys( + contentState, + selectionState.getStartKey(), + selectionState.getEndKey(), + ); +}; diff --git a/examples/plugins/linkifyPlugin.js b/examples/plugins/linkifyPlugin.js new file mode 100644 index 00000000..3fbe5192 --- /dev/null +++ b/examples/plugins/linkifyPlugin.js @@ -0,0 +1,132 @@ +// @flow +import { Modifier, RichUtils, EditorState } from "draft-js"; + +const createEntity = ( + editorState: EditorState, + entityType: string, + entityData: {}, + entityText: string, + entityMutability: "IMMUTABLE" | "MUTABLE" = "IMMUTABLE", +) => { + const contentState = editorState.getCurrentContent(); + const selection = editorState.getSelection(); + const contentStateWithEntity = contentState.createEntity( + // Draft.js Flow types issue. + // See https://github.com/facebook/draft-js/issues/868. + // $FlowFixMe + entityType, + entityMutability, + entityData, + ); + const entityKey = contentStateWithEntity.getLastCreatedEntityKey(); + + let nextContentState; + + if (selection.isCollapsed()) { + nextContentState = Modifier.insertText( + contentState, + selection, + entityText, + null, + entityKey, + ); + } else { + nextContentState = Modifier.replaceText( + contentState, + selection, + entityText, + null, + entityKey, + ); + } + + const nextState = EditorState.push( + editorState, + nextContentState, + "insert-fragment", + ); + + return nextState; +}; + +// https://gist.github.com/dperini/729294 +const LINKIFY_PATTERN = // protocol identifier (optional) + // short syntax // still required + "(?:(?:(?:https?|ftp):)?\\/\\/)" + + // user:pass BasicAuth (optional) + "(?:\\S+(?::\\S*)?@)?" + + "(?:" + + // IP address exclusion + // private & local networks + "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + + "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + + "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + + // IP address dotted notation octets + // excludes loopback network 0.0.0.0 + // excludes reserved space >= 224.0.0.0 + // excludes network & broacast addresses + // (first & last IP address of each class) + "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + + "|" + + // host & domain names, may end with dot + // can be replaced by a shortest alternative + // (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+ + "(?:" + + "(?:" + + "[a-z0-9\\u00a1-\\uffff]" + + "[a-z0-9\\u00a1-\\uffff_-]{0,62}" + + ")?" + + "[a-z0-9\\u00a1-\\uffff]\\." + + ")+" + + // TLD identifier name, may end with dot + "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" + + ")" + + // port number (optional) + "(?::\\d{2,5})?" + + // resource path (optional) + "(?:[/?#]\\S*)?"; +export const LINKIFY_REGEX_EXACT = new RegExp(`^${LINKIFY_PATTERN}$`, "ig"); + +const linkifyPlugin = () => ({ + handlePastedText( + text: string, + html: ?string, + editorState: EditorState, + { setEditorState }: { setEditorState: (EditorState) => void }, + ) { + let nextState = editorState; + if (text.match(LINKIFY_REGEX_EXACT)) { + const selection = nextState.getSelection(); + + if (selection.isCollapsed()) { + nextState = createEntity( + nextState, + "LINK", + { url: text }, + text, + "MUTABLE", + ); + } else { + const content = nextState.getCurrentContent(); + const contentWithEntity = content.createEntity( + // Draft.js Flow types issue. + // See https://github.com/facebook/draft-js/issues/868. + // $FlowFixMe + "LINK", + "MUTABLE", + { url: text }, + ); + const entityKey = contentWithEntity.getLastCreatedEntityKey(); + nextState = RichUtils.toggleLink(nextState, selection, entityKey); + } + setEditorState(nextState); + return "handled"; + } + + return "not-handled"; + }, +}); + +export default linkifyPlugin; diff --git a/examples/plugins/sectionBreakPlugin.js b/examples/plugins/sectionBreakPlugin.js new file mode 100644 index 00000000..70753e95 --- /dev/null +++ b/examples/plugins/sectionBreakPlugin.js @@ -0,0 +1,76 @@ +// @flow +import React from "react"; +import type { Component, Node } from "react"; +import { ContentBlock, EditorState, Modifier } from "draft-js"; + +import { ToolbarButton } from "../../lib"; + +const BREAK_ICON = + "M0 16h4v2h-4zM6 16h6v2h-6zM14 16h4v2h-4zM20 16h6v2h-6zM28 16h4v2h-4zM27.5 0l0.5 14h-24l0.5-14h1l0.5 12h20l0.5-12zM4.5 32l-0.5-12h24l-0.5 12h-1l-0.5-10h-20l-0.5 10zM0 512h128v64H0v-64zm192 0h192v64H192v-64zm256 0h128v64H448v-64zm192 0h192v64H640v-64zm256 0h128v64H896v-64zM880 0l16 448H128L144 0h32l16 384h640L848 0h32zM144 1024l-16-384h768l-16 384h-32l-16-320H192l-16 320h-32z"; + +const insertSectionBreak = (editorState: EditorState) => { + const content = editorState.getCurrentContent(); + + const selection = editorState.getSelection(); + let newContent = Modifier.splitBlock(content, selection); + const blockMap = newContent.getBlockMap(); + const blockKey = selection.getStartKey(); + const insertedBlockKey = newContent.getKeyAfter(blockKey); + + const newBlock = blockMap.get(insertedBlockKey).set("type", "section-break"); + + newContent = newContent.merge({ + blockMap: blockMap.set(insertedBlockKey, newBlock), + }); + + return EditorState.push(editorState, newContent, "split-block"); +}; + +type Props = {| + getEditorState: () => EditorState, + onChange: (EditorState) => void, +|}; + +export const SectionBreakControl = ({ getEditorState, onChange }: Props) => ( + { + onChange(insertSectionBreak(getEditorState())); + }} + /> +); + +type SectionBreakProps = { isFocused: boolean }; + +const SectionBreak = ({ isFocused }: SectionBreakProps) => ( +
+ Section break +
+); + +const sectionBreakPlugin = (config: {| + decorator: ((props: SectionBreakProps) => Node) => Component<{}>, +|}) => { + const component = config.decorator(SectionBreak); + + return { + blockRendererFn(block: ContentBlock) { + if (block.getType() === "section-break") { + return { + component, + editable: false, + }; + } + + return null; + }, + }; +}; + +export default sectionBreakPlugin; diff --git a/examples/plugins/sectionBreakPlugin.scss b/examples/plugins/sectionBreakPlugin.scss new file mode 100644 index 00000000..34970428 --- /dev/null +++ b/examples/plugins/sectionBreakPlugin.scss @@ -0,0 +1,27 @@ +[name="section-break"] { + display: none; +} + +.SectionBreak { + border-bottom: 1px solid #aaa; + text-align: center; + line-height: 0.1em; + margin: 10px 0 20px; + + &__label { + background: #fff; + padding: 0 10px; + } + + &--unfocused:hover { + cursor: default; + border-radius: 2px; + box-shadow: 0 0 0 3px #d2e3f7; + } + + &--focused { + cursor: default; + border-radius: 2px; + box-shadow: 0 0 0 3px #accef7; + } +} diff --git a/examples/plugins/singleLinePlugin.js b/examples/plugins/singleLinePlugin.js new file mode 100644 index 00000000..55e9282f --- /dev/null +++ b/examples/plugins/singleLinePlugin.js @@ -0,0 +1,64 @@ +// @flow +import { ContentBlock, ContentState, EditorState, genKey } from "draft-js"; + +/** + * Condense an array of content blocks into a single block + */ +export function condenseBlocks(editorState: EditorState) { + const blocks = editorState.getCurrentContent().getBlocksAsArray(); + + if (blocks.length < 2) { + return editorState; + } + + let text = ""; + let characterList; + + // Gather all the text/characterList and concat them + blocks.forEach((block) => { + // Atomic blocks should be ignored (stripped) + if (block.getType() !== "atomic") { + text += block.getText(); + characterList = characterList + ? characterList.concat(block.getCharacterList()) + : block.getCharacterList().slice(); + } + }); + + const contentBlock = new ContentBlock({ + key: genKey(), + type: "unstyled", + depth: 0, + text, + characterList, + }); + + // Update the editor state with the compressed version. + const newContentState = ContentState.createFromBlockArray([contentBlock]); + // Create the new state as an undoable action. + const nextState = EditorState.push( + editorState, + newContentState, + "remove-range", + ); + // Move the selection to the end. + return EditorState.moveFocusToEnd(nextState); +} + +/** + * Single Line Plugin + */ +const singleLinePlugin = () => ({ + onChange(editorState: EditorState) { + return condenseBlocks(editorState); + }, + + /** + * Stop new lines being inserted by always handling the return + */ + handleReturn() { + return "handled"; + }, +}); + +export default singleLinePlugin; diff --git a/examples/plugins/slashCommandPlugin.js b/examples/plugins/slashCommandPlugin.js new file mode 100644 index 00000000..396e17ef --- /dev/null +++ b/examples/plugins/slashCommandPlugin.js @@ -0,0 +1,80 @@ +// @flow +import { EditorState, AtomicBlockUtils } from "draft-js"; + +import { DraftUtils } from "../../lib/index"; + +import embedly from "../utils/embedly"; + +import { LINKIFY_REGEX_EXACT } from "./linkifyPlugin"; + +type PluginFns = { + setEditorState: (EditorState) => void, +}; + +const slashCommandPlugin = () => ({ + handleReturn( + e: SyntheticKeyboardEvent<>, + editorState: EditorState, + { setEditorState }: PluginFns, + ) { + const selection = editorState.getSelection(); + const block = DraftUtils.getSelectedBlock(editorState); + const text = block.getText(); + + if ( + !selection.isCollapsed() || + !selection.getStartOffset() === text.length + ) { + return "not-handled"; + } + + if (text === "/hr") { + const nextState = DraftUtils.removeBlock( + DraftUtils.addHorizontalRuleRemovingSelection(editorState), + block.getKey(), + ); + + setEditorState(nextState); + return "handled"; + } + + if ( + text.startsWith("/embed ") && + text.replace("/embed ", "").match(LINKIFY_REGEX_EXACT) + ) { + const url = text.replace("/embed ", ""); + + embedly.get(url, (embed) => { + const content = editorState.getCurrentContent(); + const contentWithEntity = content.createEntity( + // Fixed in https://github.com/facebook/draft-js/commit/6ba124cf663b78c41afd6c361a67bd29724fa617, to be released. + // $FlowFixMe + "EMBED", + "IMMUTABLE", + { + url: embed.url, + title: embed.title, + authorName: embed.author_name, + thumbnail: embed.thumbnail_url, + }, + ); + const nextState = DraftUtils.removeBlock( + AtomicBlockUtils.insertAtomicBlock( + editorState, + contentWithEntity.getLastCreatedEntityKey(), + " ", + ), + block.getKey(), + ); + + setEditorState(nextState); + }); + + return "handled"; + } + + return "not-handled"; + }, +}); + +export default slashCommandPlugin; diff --git a/examples/sources/EmbedSource.js b/examples/sources/EmbedSource.js index fa165496..8a67cea0 100644 --- a/examples/sources/EmbedSource.js +++ b/examples/sources/EmbedSource.js @@ -5,21 +5,7 @@ import type { EntityInstance } from "draft-js"; import Modal from "../components/Modal"; -/* global EMBEDLY_API_KEY */ -const key = typeof EMBEDLY_API_KEY === "undefined" ? "key" : EMBEDLY_API_KEY; -const EMBEDLY_ENDPOINT = `https://api.embedly.com/1/oembed?key=${key}`; - -const getJSON = (endpoint, data, successCallback) => { - const request = new XMLHttpRequest(); - request.open("GET", endpoint, true); - request.setRequestHeader("Content-Type", "application/json; charset=UTF-8"); - request.onload = () => { - if (request.status >= 200 && request.status < 400) { - successCallback(JSON.parse(request.responseText)); - } - }; - request.send(data); -}; +import embedly from "../utils/embedly"; type Props = {| editorState: EditorState, @@ -73,44 +59,36 @@ class EmbedSource extends Component { e.preventDefault(); - getJSON( - `${EMBEDLY_ENDPOINT}&url=${encodeURIComponent(url)}`, - null, - (embed) => { - if (entity && entityKey) { - const nextContent = content.mergeEntityData(entityKey, { + embedly.get(url, (embed) => { + if (entity && entityKey) { + const nextContent = content.mergeEntityData(entityKey, { + url: embed.url, + title: embed.title, + thumbnail: embed.thumbnail_url, + }); + nextState = EditorState.push(editorState, nextContent, "apply-entity"); + } else { + const contentWithEntity = content.createEntity( + // Fixed in https://github.com/facebook/draft-js/commit/6ba124cf663b78c41afd6c361a67bd29724fa617, to be released. + // $FlowFixMe + entityType.type, + "IMMUTABLE", + { url: embed.url, title: embed.title, + authorName: embed.author_name, thumbnail: embed.thumbnail_url, - }); - nextState = EditorState.push( - editorState, - nextContent, - "apply-entity", - ); - } else { - const contentWithEntity = content.createEntity( - // Fixed in https://github.com/facebook/draft-js/commit/6ba124cf663b78c41afd6c361a67bd29724fa617, to be released. - // $FlowFixMe - entityType.type, - "IMMUTABLE", - { - url: embed.url, - title: embed.title, - authorName: embed.author_name, - thumbnail: embed.thumbnail_url, - }, - ); - nextState = AtomicBlockUtils.insertAtomicBlock( - editorState, - contentWithEntity.getLastCreatedEntityKey(), - " ", - ); - } - - onComplete(nextState); - }, - ); + }, + ); + nextState = AtomicBlockUtils.insertAtomicBlock( + editorState, + contentWithEntity.getLastCreatedEntityKey(), + " ", + ); + } + + onComplete(nextState); + }); } /* :: onRequestClose: (e: SyntheticEvent<>) => void; */ diff --git a/examples/utils/_utilities.scss b/examples/utils/_utilities.scss index 1783abf5..9a12ec91 100644 --- a/examples/utils/_utilities.scss +++ b/examples/utils/_utilities.scss @@ -14,12 +14,27 @@ font-style: italic; } -.docs-ui-theming .Draftail-Toolbar { +.docs-ui-theming .Draftail-Toolbar, +.docs-custom-toolbars .Draftail-Toolbar--bottom { border: 0; background: transparent; color: $GREY_999; } +.docs-custom-toolbars .Draftail-Toolbar { + [name="READING_TIME"] { + float: right; + } + + select { + max-width: 50px; + } +} + +.custom-toolbar-plugins { + margin: 2rem 1rem 0 8rem; +} + // See https://snook.ca/archives/html_and_css/hiding-content-for-accessibility. .u-visually-hidden { // stylelint-disable-next-line declaration-no-important diff --git a/examples/utils/embedly.js b/examples/utils/embedly.js new file mode 100644 index 00000000..f360e755 --- /dev/null +++ b/examples/utils/embedly.js @@ -0,0 +1,42 @@ +// @flow + +const getJSON = ( + endpoint: string, + data: ?{}, + successCallback: ({ + url: string, + title: string, + author_name: string, + thumbnail_url: string, + }) => void, +) => { + const request = new XMLHttpRequest(); + request.open("GET", endpoint, true); + request.setRequestHeader("Content-Type", "application/json; charset=UTF-8"); + request.onload = () => { + if (request.status >= 200 && request.status < 400) { + successCallback(JSON.parse(request.responseText)); + } + }; + request.send(data); +}; + +/* global EMBEDLY_API_KEY */ +const key = typeof EMBEDLY_API_KEY === "undefined" ? "key" : EMBEDLY_API_KEY; +const EMBEDLY_ENDPOINT = `https://api.embedly.com/1/oembed?key=${key}`; + +const get = ( + url: string, + callback: ({ + url: string, + title: string, + author_name: string, + thumbnail_url: string, + }) => void, +) => { + getJSON(`${EMBEDLY_ENDPOINT}&url=${encodeURIComponent(url)}`, null, callback); +}; + +export default { + get, +}; diff --git a/flow-typed/npm/decorate-component-with-props_v1.1.0.js b/flow-typed/npm/decorate-component-with-props_v1.1.0.js new file mode 100644 index 00000000..58b9ef39 --- /dev/null +++ b/flow-typed/npm/decorate-component-with-props_v1.1.0.js @@ -0,0 +1,7 @@ +// @flow +// flow-typed signature: 5cb2a0b34b6b80dd5687f8ec49e7cca3 +// flow-typed version: <>/decorate-component-with-props_v1.1.0/flow_v0.91.0 + +declare module "decorate-component-with-props" { + declare module.exports: (React$Component<{}>, {}) => React$Node; +} diff --git a/lib/api/DraftUtils.js b/lib/api/DraftUtils.js index 93cf4842..d1f3e3a7 100644 --- a/lib/api/DraftUtils.js +++ b/lib/api/DraftUtils.js @@ -148,6 +148,7 @@ export default { editorState: EditorState, newType: string, newText: string, + newData: ?{} = {}, ) { const contentState = editorState.getCurrentContent(); const selectionState = editorState.getSelection(); @@ -169,7 +170,7 @@ export default { type: newType, text: newText, characterList: chars, - data: {}, + data: newData, }); const newContentState = contentState.merge({ blockMap: blockMap.set(key, newBlock), @@ -370,10 +371,8 @@ export default { const blockKey = selection.getStartKey(); const block = content.getBlockForKey(blockKey); const blockType = block.getType(); - const isListBlock = [ - BLOCK_TYPE.UNORDERED_LIST_ITEM, - BLOCK_TYPE.ORDERED_LIST_ITEM, - ].includes(blockType); + // Use a loose check to allow custom list item types to reuse the continuation behavior. + const isListBlock = blockType.endsWith("-list-item"); if ( !isListBlock && diff --git a/lib/api/DraftUtils.test.js b/lib/api/DraftUtils.test.js index 42b08ad7..8ac3b261 100644 --- a/lib/api/DraftUtils.test.js +++ b/lib/api/DraftUtils.test.js @@ -333,6 +333,35 @@ describe("DraftUtils", () => { inlineStyleRanges: [{ length: 4, offset: 5, style: "BOLD" }], }); }); + + it("can set data", () => { + const editorState = DraftUtils.resetBlockWithType( + EditorState.createWithContent( + convertFromRaw({ + entityMap: {}, + blocks: [ + { + key: "a", + text: "test bold", + data: {}, + }, + ], + }), + ), + "header-two", + "test bold", + { test: true }, + ); + + expect( + convertToRaw(editorState.getCurrentContent()).blocks[0], + ).toMatchObject({ + key: "a", + type: "header-two", + text: "test bold", + data: { test: true }, + }); + }); }); describe("#removeBlock", () => { @@ -756,6 +785,21 @@ describe("DraftUtils", () => { expect(DraftUtils.handleHardNewline(editorState)).toBe(false); }); + it("non-empty custom list block", () => { + const contentState = convertFromRaw({ + entityMap: {}, + blocks: [ + { + key: "b0ei9", + text: "test", + type: "action-list-item", + }, + ], + }); + const editorState = EditorState.createWithContent(contentState); + expect(DraftUtils.handleHardNewline(editorState)).toBe(false); + }); + it("empty list block non-nested", () => { const contentState = convertFromRaw({ entityMap: {}, diff --git a/lib/api/conversion.js b/lib/api/conversion.js index 25094f5f..cbed74d0 100644 --- a/lib/api/conversion.js +++ b/lib/api/conversion.js @@ -1,37 +1,18 @@ // @flow -import { - EditorState, - convertFromRaw, - convertToRaw, - CompositeDecorator, -} from "draft-js"; +import { EditorState, convertFromRaw, convertToRaw } from "draft-js"; import type { RawDraftContentState } from "draft-js/lib/RawDraftContentState"; -import type { DraftDecorator } from "draft-js/lib/DraftDecorator"; -import type { DraftDecoratorType } from "draft-js/lib/DraftDecoratorType"; const EMPTY_CONTENT_STATE = null; export default { - createEditorState( - rawContentState: ?RawDraftContentState, - decorators: Array, - ) { - // Draft.js flow types are inconsistent with the documented usage of this API. - // See https://github.com/facebook/draft-js/issues/1585. - // $FlowFixMe - const compositeDecorator: DraftDecoratorType = new CompositeDecorator( - decorators, - ); + createEditorState(rawContentState: ?RawDraftContentState) { let editorState; if (rawContentState) { const contentState = convertFromRaw(rawContentState); - editorState = EditorState.createWithContent( - contentState, - compositeDecorator, - ); + editorState = EditorState.createWithContent(contentState); } else { - editorState = EditorState.createEmpty(compositeDecorator); + editorState = EditorState.createEmpty(); } return editorState; diff --git a/lib/api/conversion.test.js b/lib/api/conversion.test.js index 47c332e6..7b93c347 100644 --- a/lib/api/conversion.test.js +++ b/lib/api/conversion.test.js @@ -28,7 +28,7 @@ const stubContent = { describe("conversion", () => { describe("#createEditorState", () => { it("creates state from real content", () => { - const state = conversion.createEditorState(stubContent, []); + const state = conversion.createEditorState(stubContent); const result = convertToRaw(state.getCurrentContent()); expect(result.blocks.length).toEqual(2); expect(result.blocks[0].text).toEqual("Hello, World!"); @@ -37,12 +37,12 @@ describe("conversion", () => { describe("#serialiseEditorState", () => { it("keeps real content", () => { - const state = conversion.createEditorState(stubContent, []); + const state = conversion.createEditorState(stubContent); expect(conversion.serialiseEditorState(state)).toEqual(stubContent); }); it("discards empty content", () => { - const state = conversion.createEditorState(null, []); + const state = conversion.createEditorState(null); expect(conversion.serialiseEditorState(state)).toBeNull(); }); diff --git a/lib/components/DraftailEditor.js b/lib/components/DraftailEditor.js index fbd0d055..2ddb629c 100644 --- a/lib/components/DraftailEditor.js +++ b/lib/components/DraftailEditor.js @@ -1,16 +1,19 @@ // @flow import React, { Component } from "react"; import type { ComponentType } from "react"; -import { Editor, EditorState, RichUtils, ContentBlock } from "draft-js"; +import { EditorState, RichUtils, ContentBlock } from "draft-js"; import type { EntityInstance } from "draft-js"; import type { RawDraftContentState } from "draft-js/lib/RawDraftContentState"; import type { DraftEditorCommand } from "draft-js/lib/DraftEditorCommand"; import type { DraftDecorator } from "draft-js/lib/DraftDecorator"; +// flowlint untyped-import:off +import Editor from "draft-js-plugins-editor"; import { ListNestingStyles, registerCopySource, handleDraftEditorPastedText, } from "draftjs-conductor"; +import decorateComponentWithProps from "decorate-component-with-props"; import { ENTITY_TYPE, @@ -28,9 +31,8 @@ import DraftUtils from "../api/DraftUtils"; import behavior from "../api/behavior"; import conversion from "../api/conversion"; -import getComponentWrapper from "../utils/getComponentWrapper"; - import Toolbar from "./Toolbar"; +import type { ToolbarProps } from "./Toolbar"; import type { IconProp } from "./Icon"; import DividerBlock from "../blocks/DividerBlock"; @@ -101,7 +103,15 @@ type Props = {| onChange: (EditorState) => void, |}>, >, + // List of plugins of the draft-js-plugins architecture. + plugins: $ReadOnlyArray<{}>, + // Optionally override the default Draftail toolbar, removing or replacing it. + topToolbar: ?ComponentType, + // Optionally add a custom toolbar underneath the editor, e.g. for metrics. + bottomToolbar: ?ComponentType, + // Max level of nesting for list items. 0 = no nesting. Maximum = 10. maxListNesting: number, + // Frequency at which to call the save callback (ms). stateSaveInterval: number, |}; @@ -156,6 +166,12 @@ const defaultProps = { decorators: [], // List of extra toolbar controls. controls: [], + // List of plugins of the draft-js-plugins architecture. + plugins: [], + // Optionally override the default Draftail toolbar, removing or replacing it. + topToolbar: Toolbar, + // Optionally add a custom toolbar underneath the editor, e.g. for metrics. + bottomToolbar: null, // Max level of nesting for list items. 0 = no nesting. Maximum = 10. maxListNesting: 1, // Frequency at which to call the save callback (ms). @@ -229,24 +245,10 @@ class DraftailEditor extends Component { this.renderSource = this.renderSource.bind(this); - const { rawContentState, decorators, entityTypes } = props; - - const entityDecorators = entityTypes - .filter((type) => !!type.decorator) - .map((type) => ({ - strategy: DraftUtils.getEntityTypeStrategy(type.type), - // $FlowFixMe - component: getComponentWrapper(type.decorator, { - onEdit: this.onEditEntity, - onRemove: this.onRemoveEntity, - }), - })); + const { rawContentState } = props; this.state = { - editorState: conversion.createEditorState( - rawContentState, - decorators.concat(entityDecorators), - ), + editorState: conversion.createEditorState(rawContentState), hasFocus: false, readOnly: false, sourceOptions: null, @@ -254,7 +256,7 @@ class DraftailEditor extends Component { } componentDidMount() { - this.copySource = registerCopySource(this.editorRef); + this.copySource = registerCopySource(this.editorRef.editor); } componentWillUnmount() { @@ -486,18 +488,18 @@ class DraftailEditor extends Component { }); } - /* :: handleReturn: (e: SyntheticKeyboardEvent<>) => void; */ + /* :: handleReturn: (e: SyntheticKeyboardEvent<>) => 'not-handled' | 'handled'; */ handleReturn(e: SyntheticKeyboardEvent<>) { const { enableLineBreak } = this.props; const { editorState } = this.state; const contentState = editorState.getCurrentContent(); - let ret = false; + let ret = NOT_HANDLED; // alt + enter opens links and other entities with a `url` property. if (e.altKey) { // Mark the return as handled even if there is no entity. // alt + enter should never create a newline anyway. - ret = true; + ret = HANDLED; const entityKey = DraftUtils.getSelectionEntity(editorState); @@ -517,7 +519,7 @@ class DraftailEditor extends Component { const newState = DraftUtils.handleNewLine(editorState, e); if (newState) { - ret = true; + ret = HANDLED; this.onChange(newState); } } @@ -525,23 +527,23 @@ class DraftailEditor extends Component { return ret; } - /* :: handleKeyCommand: (command: DraftEditorCommand) => boolean; */ + /* :: handleKeyCommand: (command: DraftEditorCommand) => 'handled' | 'not-handled'; */ handleKeyCommand(command: DraftEditorCommand) { const { editorState } = this.state; if (ENTITY_TYPES.includes(command)) { this.onRequestSource(command); - return true; + return HANDLED; } if (BLOCK_TYPES.includes(command)) { this.toggleBlockType(command); - return true; + return HANDLED; } if (INLINE_STYLES.includes(command)) { this.toggleInlineStyle(command); - return true; + return HANDLED; } // Special case โ€“ some delete commands on atomic blocks are not covered by RichUtils. @@ -550,17 +552,17 @@ class DraftailEditor extends Component { if (newState) { this.onChange(newState); - return true; + return HANDLED; } } const newState = RichUtils.handleKeyCommand(editorState, command); if (newState) { this.onChange(newState); - return true; + return HANDLED; } - return false; + return NOT_HANDLED; } /* :: handleBeforeInput: (char: string) => 'handled' | 'not-handled'; */ @@ -606,23 +608,23 @@ class DraftailEditor extends Component { return NOT_HANDLED; } - /* :: handlePastedText: (text: string, html: ?string, editorState: EditorState) => boolean; */ + /* :: handlePastedText: (text: string, html: ?string, editorState: EditorState) => 'handled' | 'not-handled'; */ handlePastedText(text: string, html: ?string, editorState: EditorState) { const { stripPastedStyles } = this.props; // Leave paste handling to Draft.js when stripping styles is desirable. if (stripPastedStyles) { - return false; + return NOT_HANDLED; } const pastedState = handleDraftEditorPastedText(html, editorState); if (pastedState) { this.onChange(pastedState); - return true; + return HANDLED; } - return false; + return NOT_HANDLED; } /* :: toggleBlockType: (blockType: string) => void; */ @@ -763,11 +765,49 @@ class DraftailEditor extends Component { blockTypes, inlineStyles, entityTypes, + decorators, controls, maxListNesting, + plugins, + topToolbar, + bottomToolbar, } = this.props; const { editorState, hasFocus, readOnly } = this.state; const hidePlaceholder = DraftUtils.shouldHidePlaceholder(editorState); + const entityDecorators = entityTypes + .filter((type) => !!type.decorator) + .map((type) => ({ + strategy: DraftUtils.getEntityTypeStrategy(type.type), + // $FlowFixMe + component: decorateComponentWithProps(type.decorator, { + onEdit: this.onEditEntity, + onRemove: this.onRemoveEntity, + }), + })); + + const TopToolbar = topToolbar; + const BottomToolbar = bottomToolbar; + const toolbarProps = { + currentStyles: editorState.getCurrentInlineStyle(), + currentBlock: DraftUtils.getSelectedBlock(editorState).getType(), + enableHorizontalRule, + enableLineBreak, + showUndoControl, + showRedoControl, + blockTypes, + inlineStyles, + entityTypes, + controls, + readOnly, + toggleBlockType: this.toggleBlockType, + toggleInlineStyle: this.toggleInlineStyle, + addHR: this.addHR, + addBR: this.addBR, + onUndoRedo: this.onUndoRedo, + onRequestSource: this.onRequestSource, + getEditorState: this.getEditorState, + onChange: this.onChange, + }; return (
{ hasFocus ? " Draftail-Editor--focus" : "" }`} > - + {TopToolbar ? : null} { blockRendererFn={this.blockRenderer} blockRenderMap={behavior.getBlockRenderMap(blockTypes)} blockStyleFn={behavior.blockStyleFn} + plugins={plugins} + // $FlowFixMe + decorators={decorators.concat(entityDecorators)} /> + {BottomToolbar ? : null} + {this.renderSource()} diff --git a/lib/components/DraftailEditor.test.js b/lib/components/DraftailEditor.test.js index 7069b69c..d486d8d9 100644 --- a/lib/components/DraftailEditor.test.js +++ b/lib/components/DraftailEditor.test.js @@ -2,18 +2,19 @@ import { OrderedSet } from "immutable"; import React from "react"; import { shallow, mount } from "enzyme"; import { - Editor, convertFromRaw, EditorState, SelectionState, ContentBlock, RichUtils, } from "draft-js"; +import Editor from "draft-js-plugins-editor"; import behavior from "../api/behavior"; import DraftUtils from "../api/DraftUtils"; import DividerBlock from "../blocks/DividerBlock"; import DraftailEditor from "./DraftailEditor"; +import Toolbar from "./Toolbar"; import { ENTITY_TYPE } from "../api/constants"; jest.mock("draft-js/lib/generateRandomKey", () => () => "a"); @@ -132,6 +133,124 @@ describe("DraftailEditor", () => { ).toEqual("test"); }); + describe("#topToolbar", () => { + it("defaults to top static Toolbar", () => { + expect( + shallowNoLifecycle() + .find(Toolbar) + .exists(), + ).toBe(true); + }); + + it("can be disabled", () => { + expect( + shallowNoLifecycle() + .find(Toolbar) + .exists(), + ).toBe(false); + }); + + it("can be customised", () => { + expect( + shallowNoLifecycle( +
Test
} + />, + ) + .find("topToolbar") + .dive() + .find(".CustomTopToolbar") + .exists(), + ).toBe(true); + }); + + it("receives default toolbar props", () => { + expect( + Object.keys( + shallowNoLifecycle(
} />) + .find("topToolbar") + .props(), + ), + ).toEqual([ + "currentStyles", + "currentBlock", + "enableHorizontalRule", + "enableLineBreak", + "showUndoControl", + "showRedoControl", + "blockTypes", + "inlineStyles", + "entityTypes", + "controls", + "readOnly", + "toggleBlockType", + "toggleInlineStyle", + "addHR", + "addBR", + "onUndoRedo", + "onRequestSource", + "getEditorState", + "onChange", + ]); + }); + }); + + describe("#bottomToolbar", () => { + it("defaults to no bottom toolbar", () => { + expect( + shallowNoLifecycle() + .find("bottomToolbar") + .exists(), + ).toBe(false); + }); + + it("can be customised", () => { + expect( + shallowNoLifecycle( + ( +
Test
+ )} + />, + ) + .find("bottomToolbar") + .dive() + .find(".CustomBottomToolbar") + .exists(), + ).toBe(true); + }); + + it("receives default toolbar props", () => { + expect( + Object.keys( + shallowNoLifecycle(
} />) + .find("bottomToolbar") + .props(), + ), + ).toEqual([ + "currentStyles", + "currentBlock", + "enableHorizontalRule", + "enableLineBreak", + "showUndoControl", + "showRedoControl", + "blockTypes", + "inlineStyles", + "entityTypes", + "controls", + "readOnly", + "toggleBlockType", + "toggleInlineStyle", + "addHR", + "addBR", + "onUndoRedo", + "onRequestSource", + "getEditorState", + "onChange", + ]); + }); + }); + it("#maxListNesting", () => { expect( shallowNoLifecycle(), @@ -289,7 +408,7 @@ describe("DraftailEditor", () => { wrapper.instance().handleReturn({ keyCode: 13, }), - ).toBe(true); + ).toBe("handled"); DraftUtils.handleNewLine.mockRestore(); }); @@ -300,7 +419,7 @@ describe("DraftailEditor", () => { wrapper.instance().handleReturn({ keyCode: 13, }), - ).toBe(false); + ).toBe("not-handled"); }); it("alt + enter on text", () => { const wrapper = shallowNoLifecycle(); @@ -309,7 +428,7 @@ describe("DraftailEditor", () => { wrapper.instance().handleReturn({ altKey: true, }), - ).toBe(true); + ).toBe("handled"); }); it("alt + enter on entity without url", () => { const wrapper = shallowNoLifecycle( @@ -354,7 +473,7 @@ describe("DraftailEditor", () => { wrapper.instance().handleReturn({ altKey: true, }), - ).toBe(true); + ).toBe("handled"); }); it("alt + enter on entity", () => { @@ -396,7 +515,7 @@ describe("DraftailEditor", () => { wrapper.instance().handleReturn({ altKey: true, }), - ).toBe(true); + ).toBe("handled"); expect(window.open).toHaveBeenCalled(); window.open.mockRestore(); @@ -478,7 +597,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("backspace"), - ).toBe(true); + ).toBe("handled"); RichUtils.handleKeyCommand.mockRestore(); }); @@ -490,7 +609,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("backspace"), - ).toBe(false); + ).toBe("not-handled"); RichUtils.handleKeyCommand.mockRestore(); }); @@ -500,7 +619,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("LINK"), - ).toBe(true); + ).toBe("handled"); }); it("block type", () => { @@ -508,7 +627,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("header-one"), - ).toBe(true); + ).toBe("handled"); }); it("inline style", () => { @@ -516,7 +635,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("BOLD"), - ).toBe(true); + ).toBe("handled"); }); describe("delete", () => { @@ -529,7 +648,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("delete"), - ).toBe(true); + ).toBe("handled"); expect(DraftUtils.handleDeleteAtomic).toHaveBeenCalled(); DraftUtils.handleDeleteAtomic.mockRestore(); @@ -542,7 +661,7 @@ describe("DraftailEditor", () => { shallowNoLifecycle() .instance() .handleKeyCommand("delete"), - ).toBe(false); + ).toBe("not-handled"); expect(DraftUtils.handleDeleteAtomic).toHaveBeenCalled(); DraftUtils.handleDeleteAtomic.mockRestore(); @@ -625,7 +744,7 @@ describe("DraftailEditor", () => { "this is plain text paste", wrapper.state("editorState"), ), - ).toBe(false); + ).toBe("not-handled"); }); it("stripPastedStyles", () => { @@ -639,7 +758,7 @@ describe("DraftailEditor", () => { "

bold

", wrapper.state("editorState"), ), - ).toBe(false); + ).toBe("not-handled"); }); it("handled by handleDraftEditorPastedText", () => { @@ -667,7 +786,7 @@ describe("DraftailEditor", () => { wrapper .instance() .handlePastedText(text, html, wrapper.state("editorState")), - ).toBe(true); + ).toBe("handled"); }); }); @@ -1225,4 +1344,14 @@ describe("DraftailEditor", () => { expect(focus).toHaveBeenCalled(); }); }); + + describe("#plugins", () => { + it("forwards to draft-js-plugins-editor", () => { + expect( + shallowNoLifecycle() + .find(Editor) + .prop("plugins"), + ).toEqual([{ test: true }]); + }); + }); }); diff --git a/lib/components/Toolbar.js b/lib/components/Toolbar.js index 88273c05..c8b58d12 100644 --- a/lib/components/Toolbar.js +++ b/lib/components/Toolbar.js @@ -12,13 +12,13 @@ type ControlProps = {| onChange: (EditorState) => void, |}; -type Props = { +export type ToolbarProps = { controls: $ReadOnlyArray>, getEditorState: () => EditorState, onChange: (EditorState) => void, } & ToolbarDefaultProps; -const Toolbar = (props: Props) => { +const Toolbar = (props: ToolbarProps) => { const { controls, getEditorState, onChange } = props; return (
diff --git a/lib/components/__snapshots__/DraftailEditor.test.js.snap b/lib/components/__snapshots__/DraftailEditor.test.js.snap index 564e01bf..f9d998dd 100644 --- a/lib/components/__snapshots__/DraftailEditor.test.js.snap +++ b/lib/components/__snapshots__/DraftailEditor.test.js.snap @@ -25,7 +25,7 @@ exports[`DraftailEditor #maxListNesting 1`] = ` toggleBlockType={[Function]} toggleInlineStyle={[Function]} /> - - =0.10.0" @@ -12324,8 +14699,7 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.7.0", @@ -12394,6 +14768,12 @@ } } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "jsome": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/jsome/-/jsome-2.5.0.tgz", @@ -12557,7 +14937,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -12739,6 +15119,12 @@ "integrity": "sha512-pku5zscbIr9YsA6lFU1nhFGSAXsdJtEQ2WilCL40d0YCoDofBlNohMUq32wyt7tpiiaZ09GKyLZFrB1ijx6+WA==", "dev": true }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, "lazy-universal-dotenv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-2.0.0.tgz", @@ -12753,9 +15139,9 @@ }, "dependencies": { "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", "dev": true } } @@ -12913,6 +15299,12 @@ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -13016,7 +15408,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, "requires": { "js-tokens": "^3.0.0" } @@ -13176,6 +15567,12 @@ "extend": "3.0.1" } }, + "mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13233,6 +15630,61 @@ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, + "merge-deep": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz", + "integrity": "sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + }, + "dependencies": { + "clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", + "dev": true, + "requires": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + } + }, + "shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", + "dev": true, + "requires": { + "is-buffer": "^1.0.2" + } + }, + "lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", + "dev": true + } + } + } + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -13328,9 +15780,9 @@ } }, "mini-css-extract-plugin": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz", - "integrity": "sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.5.tgz", + "integrity": "sha512-dqBanNfktnp2hwL2YguV9Jh91PFX7gu7nRLs4TGsbAfAG6WOtlynFRYzwDwmmeSb5uIwHo9nx1ta0f7vAZVp2w==", "dev": true, "requires": { "loader-utils": "^1.1.0", @@ -13615,7 +16067,6 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz", "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==", - "dev": true, "requires": { "encoding": "^0.1.11", "is-stream": "^1.0.1" @@ -13708,6 +16159,12 @@ "semver": "^5.3.0" } }, + "node-version": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-version/-/node-version-1.2.0.tgz", + "integrity": "sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ==", + "dev": true + }, "nomnom": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.6.2.tgz", @@ -13822,8 +16279,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -13927,16 +16383,39 @@ }, "dependencies": { "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", + "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + }, + "dependencies": { + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + } + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "function-bind": { @@ -13944,6 +16423,27 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true } } }, @@ -14775,14 +17275,25 @@ } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "has-flag": { @@ -14792,14 +17303,14 @@ "dev": true }, "postcss": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.5.tgz", - "integrity": "sha512-HBNpviAUFCKvEh7NZhw1e8MBPivRszIiUnhrJ+sBFVSYSqubrzwX3KG51mYgcRHX8j/cAgZJedONZcm5jTBdgQ==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz", + "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==", "dev": true, "requires": { - "chalk": "^2.4.1", + "chalk": "^2.4.2", "source-map": "^0.6.1", - "supports-color": "^5.5.0" + "supports-color": "^6.1.0" } }, "source-map": { @@ -14809,9 +17320,9 @@ "dev": true }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -15462,7 +17973,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, "requires": { "asap": "~2.0.3" } @@ -15473,6 +17983,12 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "promise-polyfill": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", + "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=", + "dev": true + }, "promise.prototype.finally": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.0.tgz", @@ -15485,16 +18001,28 @@ }, "dependencies": { "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", + "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "function-bind": { @@ -15502,6 +18030,36 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true } } }, @@ -15519,13 +18077,21 @@ "version": "15.6.0", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", - "dev": true, "requires": { "fbjs": "^0.8.16", "loose-envify": "^1.3.1", "object-assign": "^4.1.1" } }, + "property-information": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.0.1.tgz", + "integrity": "sha512-nAtBDVeSwFM3Ot/YxT7s4NqZmqXI7lLzf46BThvotEtYf2uk2yH0ACYuWQkJ7gxKs49PPtKVY0UlDGkyN9aJlw==", + "dev": true, + "requires": { + "xtend": "^4.0.1" + } + }, "proxy-addr": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", @@ -15663,6 +18229,12 @@ } } }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", @@ -15801,26 +18373,44 @@ "dev": true }, "react": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.6.0.tgz", - "integrity": "sha512-zJPnx/jKtuOEXCbQ9BKaxDMxR0001/hzxXwYxG8septeyYGfsgAei6NgfbVgOhbY1WOP2o3VPs/E9HaN+9hV3Q==", + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react/-/react-16.8.3.tgz", + "integrity": "sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.10.0" + "scheduler": "^0.13.3" }, "dependencies": { "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "dev": true, "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==", + "dev": true } } }, @@ -16484,26 +19074,6 @@ "text-table": "0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -16531,9 +19101,9 @@ } }, "caniuse-lite": { - "version": "1.0.30000907", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000907.tgz", - "integrity": "sha512-No5sQ/OB2Nmka8MNOOM6nJx+Hxt6MQ6h7t7kgJFu9oTuwjykyKRSBP/+i/QAyFHxeHB+ddE0Da1CG5ihx9oehQ==", + "version": "1.0.30000940", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000940.tgz", + "integrity": "sha512-rp/086IBUfCsNgBpko6DGQv674jRjeXPesDatDB2kxrkmDfD+S5Gesw+uT8YjpRWvLKLMRBy72SLRZ8I0EgQFw==", "dev": true }, "chalk": { @@ -16558,9 +19128,9 @@ } }, "electron-to-chromium": { - "version": "1.3.83", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.83.tgz", - "integrity": "sha512-DqJoDarxq50dcHsOOlMLNoy+qQitlMNbYb6wwbE0oUw2veHdRkpNrhmngiUYKMErdJ8SJ48rpJsZTQgy5SoEAA==", + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", "dev": true }, "find-up": { @@ -16593,12 +19163,6 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -16610,9 +19174,9 @@ } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -16660,9 +19224,9 @@ } }, "react-docgen": { - "version": "3.0.0-rc.2", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-rc.2.tgz", - "integrity": "sha512-tXbIvq7Hxdc92jW570rztqsz0adtWEM5FX8bShJYozT2Y6L/LeHvBMQcED6mSqJ72niiNMPV8fi3S37OHrGMEw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0.tgz", + "integrity": "sha512-2UseoLWabFNXuk1Foz4VDPSIAkxz+1Hmmq4qijzUmYHDq0ZSloKDLXtGLpQRcAi/M76hRpPtH1rV4BI5jNAOnQ==", "dev": true, "requires": { "@babel/parser": "^7.1.3", @@ -16675,9 +19239,9 @@ }, "dependencies": { "@babel/parser": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.1.5.tgz", - "integrity": "sha512-WXKf5K5HT6X0kKiCOezJZFljsfxKV1FpU8Tf1A7ZpGvyd/Q4hlrJm2EwoH2onaUq3O4tLDp+4gk0hHPsMyxmOg==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz", + "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==", "dev": true }, "commander": { @@ -16694,37 +19258,79 @@ "requires": { "esutils": "^2.0.2" } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "recast": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.16.2.tgz", + "integrity": "sha512-O/7qXi51DPjRVdbrpNzoBQH5dnAPQNbfoOFyRiUwreTMJfIHYOEBzwuH+c0+/BTSJ3CQyKs6ILSWXhESH6Op3A==", + "dev": true, + "requires": { + "ast-types": "0.11.7", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, "react-dom": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.0.tgz", - "integrity": "sha512-Stm2D9dXEUUAQdvpvhvFj/DEXwC2PAL/RwEMhoN4dvvD2ikTlJegEXf97xryg88VIAU22ZAP7n842l+9BTz6+w==", + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.3.tgz", + "integrity": "sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==", "dev": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.10.0" + "scheduler": "^0.13.3" }, "dependencies": { "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "dev": true, "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==", + "dev": true } } }, "react-error-overlay": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.0.tgz", - "integrity": "sha512-akMy/BQT5m1J3iJIHkSb4qycq2wzllWsmmolaaFVnb+LPV9cIJ/nTud40ZsiiT0H3P+/wXIdbjx2fzF61OaeOQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.3.tgz", + "integrity": "sha512-GoqeM3Xadie7XUApXOjkY3Qhs8RkwB/Za4WMedBGrOKH1eTuKGyoAECff7jiVonJchOx6KZ9i8ILO5XIoHB+Tg==", "dev": true }, "react-fast-compare": { @@ -16820,26 +19426,42 @@ } }, "react-test-renderer": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.0.tgz", - "integrity": "sha512-w+Y3YT7OX1LP5KO7HCd0YR34Ol1qmISHaooPNMRYa6QzmwtcWhEGuZPr34wO8UCBIokswuhyLQUq7rjPDcEtJA==", + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.3.tgz", + "integrity": "sha512-rjJGYebduKNZH0k1bUivVrRLX04JfIQ0FKJLPK10TAb06XWhfi4gTobooF9K/DEFNW98iGac3OSxkfIJUN9Mdg==", "dev": true, "requires": { "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "scheduler": "^0.10.0" + "react-is": "^16.8.3", + "scheduler": "^0.13.3" }, "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "dev": true, "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==", + "dev": true } } }, @@ -16853,9 +19475,9 @@ } }, "react-transition-group": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.0.tgz", - "integrity": "sha512-qYB3JBF+9Y4sE4/Mg/9O6WFpdoYjeeYqx0AFb64PTazVy8RPMiE3A47CG9QmM4WJ/mzDiZYslV+Uly6O1Erlgw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.6.0.tgz", + "integrity": "sha512-VzZ+6k/adL3pJHo4PU/MHEPjW59/TGQtRsXC+wnxsx2mxjQKNHnDdJL/GpYuPJIsyHGjYbBQfIJ2JNOAdPc8GQ==", "dev": true, "requires": { "dom-helpers": "^3.3.1", @@ -16874,14 +19496,21 @@ } }, "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "dev": true, "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==", + "dev": true } } }, @@ -16900,15 +19529,31 @@ "velocity-react": "^1.4.1" }, "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "dev": true, "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" } + }, + "react-is": { + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", + "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==", + "dev": true } } }, @@ -17021,17 +19666,23 @@ } }, "recast": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.16.0.tgz", - "integrity": "sha512-cm2jw4gCBatvs404ZJrxmGirSgWswW+S1U3SQTPHKNqdlUMg+V3J2XAOUvdAAgD7Hg2th2nxZ4wmYUekHI2Qmg==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.14.7.tgz", + "integrity": "sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A==", "dev": true, "requires": { - "ast-types": "0.11.6", + "ast-types": "0.11.3", "esprima": "~4.0.0", "private": "~0.1.5", "source-map": "~0.6.1" }, "dependencies": { + "ast-types": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.3.tgz", + "integrity": "sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA==", + "dev": true + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -17174,6 +19825,12 @@ } } }, + "regexp-tree": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz", + "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==", + "dev": true + }, "regexp.prototype.flags": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", @@ -17255,6 +19912,25 @@ } } }, + "rehype-parse": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.0.tgz", + "integrity": "sha512-V2OjMD0xcSt39G4uRdMTqDXXm6HwkUbLMDayYKA/d037j8/OtVSQ+tqKwYWOuyBeoCs/3clXRe30VUjeMDTBSA==", + "dev": true, + "requires": { + "hast-util-from-parse5": "^5.0.0", + "parse5": "^5.0.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + } + } + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -19025,9 +21701,9 @@ "dev": true }, "scheduler": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.10.0.tgz", - "integrity": "sha512-+TSTVTCBAA3h8Anei3haDc1IRwMeDmtI/y/o3iBe3Mjl2vwYF9DtPDt929HyRmV/e7au7CLu8sc4C4W0VOs29w==", + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.3.tgz", + "integrity": "sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ==", "dev": true, "requires": { "loose-envify": "^1.1.0", @@ -19168,8 +21844,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { "version": "1.0.3", @@ -19240,9 +21915,9 @@ } }, "shelljs": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.2.tgz", - "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", "dev": true, "requires": { "glob": "^7.0.0", @@ -19463,6 +22138,24 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "space-separated-tokens": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz", + "integrity": "sha512-G3jprCEw+xFEs0ORweLmblJ3XLymGGr6hxZYTYZjIlvDti9vOBUjRQa1Rzjt012aRrocKstHwdNi+F7HguPsEA==", + "dev": true, + "requires": { + "trim": "0.0.1" + } + }, + "spawn-promise": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/spawn-promise/-/spawn-promise-0.1.8.tgz", + "integrity": "sha512-pTkEOFxvYLq9SaI1d8bwepj0yD9Yyz65+4e979YZLv/L3oYPxZpDTabcm6e+KIZniGK9mQ+LGrwB5s1v2z67nQ==", + "dev": true, + "requires": { + "co": "^4.6.0" + } + }, "spdx-correct": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", @@ -19597,6 +22290,12 @@ "safe-buffer": "^5.1.1" } }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, "stack-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", @@ -19777,29 +22476,50 @@ } }, "string.prototype.matchall": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-3.0.0.tgz", - "integrity": "sha512-/g0YW/cEfXASRHAaLR7VZbTUlxgP14fmCsfSRFG2gvlG2S1q9rBpjYnEy/EIIzY+bjzs2nTfAHJYXmQ+zTnXSQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-3.0.1.tgz", + "integrity": "sha512-NSiU0ILQr9PQ1SZmM1X327U5LsM+KfDTassJfqN1al1+0iNpKzmQ4BfXOJwRnTEqv8nKJ67mFpqRoPaGWwvy5A==", "dev": true, "requires": { - "define-properties": "^1.1.2", + "define-properties": "^1.1.3", "es-abstract": "^1.12.0", "function-bind": "^1.1.1", "has-symbols": "^1.0.0", "regexp.prototype.flags": "^1.2.0" }, "dependencies": { + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", + "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" } }, "function-bind": { @@ -19807,6 +22527,36 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true } } }, @@ -21315,53 +24065,247 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - } + "has-flag": "^3.0.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + } + } + } + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svg-url-loader": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz", + "integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=", + "dev": true, + "requires": { + "file-loader": "1.1.11", + "loader-utils": "1.1.0" + }, + "dependencies": { + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "svgo": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.0.tgz", + "integrity": "sha512-xBfxJxfk4UeVN8asec9jNxHiv3UAMv/ujwBWGYvQhhMb2u3YTGKkiybPcLFDLq7GLLWE9wa73e0/m8L5nTzQbw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.28", + "css-url-regex": "^1.1.0", + "csso": "^3.5.1", + "js-yaml": "^3.12.0", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "css-select": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", + "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^2.1.2", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" } - } - } - }, - "svg-tags": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", - "dev": true - }, - "svg-url-loader": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz", - "integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=", - "dev": true, - "requires": { - "file-loader": "1.1.11", - "loader-utils": "1.1.0" - }, - "dependencies": { - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + }, + "js-yaml": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz", + "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==", "dev": true, "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "boolbase": "~1.0.0" + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } } } @@ -21506,6 +24450,224 @@ "execa": "^0.7.0" } }, + "terser": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", + "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.9" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz", + "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.16.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, "test-exclude": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", @@ -21724,6 +24886,12 @@ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -21936,8 +25104,7 @@ "ua-parser-js": { "version": "0.7.14", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", - "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=", - "dev": true + "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=" }, "uglify-js": { "version": "3.4.9", @@ -22071,6 +25238,11 @@ "x-is-string": "^0.1.0" } }, + "union-class-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-class-names/-/union-class-names-1.0.0.tgz", + "integrity": "sha1-kllgitrMOQlKKwz+FseOYgBheEc=" + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -22199,6 +25371,12 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", @@ -22310,9 +25488,9 @@ }, "dependencies": { "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true } } @@ -22540,6 +25718,12 @@ "defaults": "^1.0.3" } }, + "web-namespaces": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.2.tgz", + "integrity": "sha512-II+n2ms4mPxK+RnIxRPOw3zwF2jRscdJIUE9BfkKHm4FYEg9+biIoTMnaZF5MpemE3T+VhMLrhbyD4ilkPCSbg==", + "dev": true + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -22961,21 +26145,21 @@ } }, "webpack-dev-middleware": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", - "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.0.tgz", + "integrity": "sha512-oeXA3m+5gbYbDBGo4SvKpAHJJEGMoekUbHgo1RK7CP1sz7/WOSeu/dWJtSTk+rzDCLkPwQhGocgIq6lQqOyOwg==", "dev": true, "requires": { - "memory-fs": "~0.4.1", + "memory-fs": "^0.4.1", "mime": "^2.3.1", "range-parser": "^1.0.3", "webpack-log": "^2.0.0" }, "dependencies": { "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", "dev": true } } @@ -23000,14 +26184,6 @@ "requires": { "ansi-colors": "^3.0.0", "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - } } }, "webpack-sources": { @@ -23067,8 +26243,7 @@ "whatwg-fetch": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", - "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=", - "dev": true + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" }, "whatwg-mimetype": { "version": "2.3.0", diff --git a/package.json b/package.json index 0b5ddb09..ffc987a2 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "not OperaMini all" ], "dependencies": { + "decorate-component-with-props": "^1.0.2", + "draft-js-plugins-editor": "^2.1.1", "draftjs-conductor": "^0.4.1", "draftjs-filters": "^2.2.3" }, @@ -48,7 +50,7 @@ "@babel/preset-react": "^7.0.0", "@sentry/browser": "^4.2.3", "@storybook/addon-viewport": "^4.0.4", - "@storybook/react": "^4.0.4", + "@storybook/react": "^4.1.13", "@thibaudcolas/eslint-plugin-cookbook": "^4.0.1", "@thibaudcolas/stylelint-config-cookbook": "^2.0.1", "autoprefixer": "^7.1.2", @@ -60,6 +62,9 @@ "dotenv": "^6.0.0", "draft-convert": "^2.1.4", "draft-js": "0.10.5", + "draft-js-hashtag-plugin": "^2.0.3", + "draft-js-inline-toolbar-plugin": "^3.0.0", + "draft-js-side-toolbar-plugin": "^3.0.1", "enzyme": "^3.7.0", "enzyme-adapter-react-16": "^1.6.0", "enzyme-to-json": "^3.3.4", @@ -82,13 +87,13 @@ "prettier": "^1.16.4", "prismjs": "^1.8.4", "puppeteer": "^1.11.0", - "react": "^16.6.0", + "react": "^16.8.3", "react-benchmark": "^2.1.0", "react-component-benchmark": "0.0.4", - "react-dom": "^16.6.0", + "react-dom": "^16.8.3", "react-intl": "^2.7.2", "react-modal": "^3.1.5", - "react-test-renderer": "^16.6.0", + "react-test-renderer": "^16.8.3", "reading-time": "^1.1.0", "rimraf": "^2.6.2", "rollup": "^0.66.2", diff --git a/tests/performance/markov_draftjs_41.test.js b/tests/performance/markov_draftjs_41.test.js index 4fb9e57b..e61e8949 100644 --- a/tests/performance/markov_draftjs_41.test.js +++ b/tests/performance/markov_draftjs_41.test.js @@ -31,7 +31,7 @@ describe("performance", () => { component.instance().start(); expect(results.mean).toBeLessThan(87 * PERFORMANCE_BUFFER); expect(results.min).toBeLessThan(49 * PERFORMANCE_BUFFER); - expect(results.median).toBeLessThan(61 * PERFORMANCE_BUFFER); + expect(results.median).toBeLessThan(70 * PERFORMANCE_BUFFER); expect(results.max).toBeLessThan(278 * PERFORMANCE_BUFFER); });