diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..79dc239 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +name: Release +on: + push: + branches: + - master + - "releasetest/**" +jobs: + release: + if: "!contains(github.event.head_commit.message, 'skip ci')" + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Setup Node.js + uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Install dependencies + run: npm install + - name: Build + run: npm run build + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release diff --git a/README.md b/README.md index 53a4f19..e54424a 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ - # React NLP Annotate +# React NLP Annotate + +> If you just want to edit NLP data, it's easier to just use the [Universal Data Tool (MIT)](https://github.com/UniversalDataTool/universal-data-tool). This library is a module of the Universal Data Tool for use in custom react applications. Interface for doing various NLP tasks. [Here's a code playground](https://codesandbox.io/s/react-nlp-annotate-example-0jwms?file=/src/App.js). Please help this repository by adding documentation and issues! diff --git a/package.json b/package.json index 3e95e4a..8f68fe1 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,13 @@ "chroma-js": "^2.0.3", "color-alpha": "^1.0.4", "react-hotkeys": "^2.0.0", - "react-material-workspace-layout": "^1.0.1", + "react-material-workspace-layout": "^1.0.9", "react-select": "^3.0.8", "react-use": "^15.3.3", "spelling": "^2.0.1", "use-event-callback": "^0.1.0" }, + "repository": "github:UniversalDataTool/react-nlp-annotate", "scripts": { "build": "rimraf dist && npm run build:babel", "build:babel": "NODE_ENV=production babel ./src --out-dir=./dist", @@ -39,6 +40,7 @@ "@material-ui/core": "^4.10.0", "@material-ui/icons": "^4.9.1", "@material-ui/styles": "^4.10.0", + "@semantic-release/git": "^9.0.0", "@storybook/addon-actions": "^5.3.19", "@storybook/addon-links": "^5.3.19", "@storybook/addons": "^5.3.19", diff --git a/releaserc.js b/releaserc.js new file mode 100644 index 0000000..f3a6e4e --- /dev/null +++ b/releaserc.js @@ -0,0 +1,19 @@ +module.exports = { + branch: "master", + plugins: [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + ["@semantic-release/npm", { npmPublish: true }], + "@semantic-release/github", + [ + "@semantic-release/git", + { + assets: ["package.json"], + message: + "chore(release): ${nextRelease.version} [skip ci] + +${nextRelease.notes}", + }, + ], + ], +} diff --git a/src/components/Document/index.js b/src/components/Document/index.js index 940d936..739664b 100644 --- a/src/components/Document/index.js +++ b/src/components/Document/index.js @@ -6,13 +6,14 @@ import type { Relationship } from "../../types" import { styled } from "@material-ui/styles" -import stringToSequence from "../../string-to-sequence.js" -import Tooltip from "@material-ui/core/Tooltip" import RelationshipArrows from "../RelationshipArrows" import colors from "../../colors" import ArrowToMouse from "../ArrowToMouse" import { useTimeout, useWindowSize } from "react-use" +import SequenceItem from "../SequenceItem" import classNames from "classnames" +import stringToSequence from "../../string-to-sequence" +import useEventCallback from "use-event-callback" const Container = styled("div")(({ relationshipsOn }) => ({ lineHeight: 1.5, @@ -21,50 +22,6 @@ const Container = styled("div")(({ relationshipsOn }) => ({ flexWrap: "wrap" })) -const SequenceItem = styled("span")(({ color, relationshipsOn }) => ({ - display: "inline-flex", - cursor: "pointer", - backgroundColor: color, - color: "#fff", - padding: 4, - margin: 4, - marginBottom: relationshipsOn ? 64 : 4, - paddingLeft: 10, - paddingRight: 10, - borderRadius: 4, - userSelect: "none", - boxSizing: "border-box", - "&.unlabeled": { - color: "#333", - paddingTop: 4, - paddingBottom: 4, - paddingLeft: 2, - paddingRight: 2, - ".notSpace:hover": { - paddingTop: 2, - paddingBottom: 2, - paddingLeft: 0, - paddingRight: 0, - border: `2px dashed #ccc` - } - } -})) - -const LabeledText = styled("div")({ - display: "inline-flex", - cursor: "pointer", - alignSelf: "center", - fontSize: 11, - width: 18, - height: 18, - alignItems: "center", - justifyContent: "center", - marginLeft: 4, - borderRadius: 9, - color: "#fff", - backgroundColor: "rgba(0,0,0,0.2)" -}) - type Props = { sequence: Array, relationships: Array, @@ -126,6 +83,16 @@ export default function Document({ highlightedItems.push(i) } + const onRemoveLabel = useEventCallback(sequenceItemIndex => { + onSequenceChange( + sequence + .flatMap((s, i) => + i !== sequenceItemIndex ? s : stringToSequence(s.text) + ) + .filter(s => s.text.length > 0) + ) + }) + return ( {sequence.map((seq, i) => ( { - if (!elm) return - sequenceItemPositionsRef.current[seq.textId] = { - offset: { - left: elm.offsetLeft, - top: elm.offsetTop, - width: elm.offsetWidth, - height: elm.offsetHeight - } - } - }} + {...seq} + sequenceItemIndex={i} + sequenceItemPositionsRef={sequenceItemPositionsRef} relationshipsOn={Boolean(relationships)} - onMouseUp={e => { - if (!createRelationshipsMode) return - if (!secondSequenceItem) { - setFirstSequenceItem(null) - setSecondSequenceItem(null) - onCreateEmptyRelationship([firstSequenceItem, seq.textId]) - } else { - setFirstSequenceItem(null) - setSecondSequenceItem(null) - } - }} - onMouseDown={() => { - if (createRelationshipsMode) { - if (!firstSequenceItem) { - setFirstSequenceItem(seq.textId) - } - } else { - if (seq.label) return - changeHighlightedRange([i, i]) - } - }} - onMouseMove={() => { - if (!mouseDown) return - if (!createRelationshipsMode) { - if (seq.label) return - if (i !== lastSelected) { - changeHighlightedRange([ - firstSelected === null ? i : firstSelected, - i - ]) - } - } - }} - className={classNames( - seq.label ? "label" : "unlabeled", - seq.text.trim().length > 0 && "notSpace" - )} - color={ - seq.label - ? seq.color || colorLabelMap[seq.label] || "#333" - : !createRelationshipsMode && - seq.text !== " " && - highlightedItems.includes(i) - ? "#ccc" - : "inherit" - } + createRelationshipsMode={createRelationshipsMode} + onChangeFirstSequenceItem={setFirstSequenceItem} + onChangeSecondSequenceItem={setSecondSequenceItem} + onCreateEmptyRelationship={onCreateEmptyRelationship} + onChangeHighlightedRange={changeHighlightedRange} + firstSequenceItem={firstSequenceItem} + secondSequenceItem={secondSequenceItem} + mouseDown={mouseDown} + firstSelected={firstSelected} + lastSelected={lastSelected} + isHighlighted={highlightedItems.includes(i)} + onRemoveLabel={onRemoveLabel} + color={seq.color || colorLabelMap[seq.label]} key={i} - > - {seq.label ? ( - -
{seq.text}
-
- ) : ( -
{seq.text}
- )} - {seq.label && !createRelationshipsMode && ( - { - e.stopPropagation() - onSequenceChange( - sequence - .flatMap(s => (s !== seq ? s : stringToSequence(s.text))) - .filter(s => s.text.length > 0) - ) - }} - > - {"\u2716"} - - )} -
+ /> ))} {firstSequenceItem && !secondSequenceItem && ( )) @@ -41,15 +41,15 @@ storiesOf("Document", module) Math.random() < 0.9 ? { text: text + " ", textId: `l${i}` } : { - text: text + " ", - textId: `l${i}`, - label: - "somelabel" + - Math.random() - .toString() - .slice(-4), - color: "#9638F9" - } + text: text + " ", + textId: `l${i}`, + label: + "somelabel" + + Math.random() + .toString() + .slice(-4), + color: "#9638F9" + } )} relationships={[ { @@ -60,3 +60,15 @@ storiesOf("Document", module) ]} /> )) + .add("Character Sequence", () => ( + ({ + text: c + })) + } + /> + )) \ No newline at end of file diff --git a/src/components/DocumentLabeler/index.js b/src/components/DocumentLabeler/index.js index 9731f34..c339de4 100644 --- a/src/components/DocumentLabeler/index.js +++ b/src/components/DocumentLabeler/index.js @@ -12,9 +12,10 @@ export default function DocumentLabeler(props: LabelDocumentProps) { const [selectedLabels, changeSelectedLabels] = useState( props.initialLabels || (props.initialLabel ? [props.initialLabel] : []) ) - const sequence = useMemo(() => stringToSequence(props.document), [ - props.document - ]) + const sequence = useMemo( + () => stringToSequence(props.document, props.separatorRegex), + [props.document] + ) return (
@@ -58,7 +59,11 @@ export default function DocumentLabeler(props: LabelDocumentProps) { ) })}
- +
) diff --git a/src/components/NLPAnnotator/index.story.js b/src/components/NLPAnnotator/index.story.js index 5551269..2b4a5fc 100644 --- a/src/components/NLPAnnotator/index.story.js +++ b/src/components/NLPAnnotator/index.story.js @@ -32,6 +32,30 @@ storiesOf("NLPAnnotator", module) ]} /> )) + .add("Sequence Labeler with Custom Regex", () => ( + + )) .add("Document Labeler", () => (
{ diff --git a/src/components/SequenceAnnotator/index.js b/src/components/SequenceAnnotator/index.js index 0f16b77..fdb0907 100644 --- a/src/components/SequenceAnnotator/index.js +++ b/src/components/SequenceAnnotator/index.js @@ -18,7 +18,7 @@ export default function SequenceAnnotator(props: SequenceAnnotatorProps) { ? [entity] : stringToSequence(entity.text, props.separatorRegex) ) - : stringToSequence(props.document) + : stringToSequence(props.document, props.separatorRegex) ) const colorLabelMap = useMemo( () => @@ -71,6 +71,7 @@ export default function SequenceAnnotator(props: SequenceAnnotatorProps) {
diff --git a/src/components/SequenceItem/index.js b/src/components/SequenceItem/index.js new file mode 100644 index 0000000..b151342 --- /dev/null +++ b/src/components/SequenceItem/index.js @@ -0,0 +1,153 @@ +import React, { memo } from "react" +import classNames from "classnames" +import { styled, Tooltip } from "@material-ui/core" +import stringToSequence from "../../string-to-sequence.js" + +const SequenceItemContainer = styled("span")(({ color, relationshipsOn }) => ({ + display: "inline-flex", + cursor: "pointer", + backgroundColor: color, + color: "#fff", + padding: 4, + margin: 4, + marginBottom: relationshipsOn ? 64 : 4, + paddingLeft: 10, + paddingRight: 10, + borderRadius: 4, + userSelect: "none", + boxSizing: "border-box", + "&.unlabeled": { + color: "#333", + paddingTop: 4, + paddingBottom: 4, + paddingLeft: 2, + paddingRight: 2, + ".notSpace:hover": { + paddingTop: 2, + paddingBottom: 2, + paddingLeft: 0, + paddingRight: 0, + border: `2px dashed #ccc` + } + } +})) + +const XContainer = styled("div")({ + display: "inline-flex", + cursor: "pointer", + alignSelf: "center", + fontSize: 11, + width: 18, + height: 18, + alignItems: "center", + justifyContent: "center", + marginLeft: 4, + borderRadius: 9, + color: "#fff", + backgroundColor: "rgba(0,0,0,0.2)" +}) + +export const SequenceItem = ({ + textId, + text, + label, + color, + sequenceItemIndex, + sequenceItemPositionsRef, + relationshipsOn, + createRelationshipsMode, + onChangeFirstSequenceItem, + onChangeSecondSequenceItem, + onCreateEmptyRelationship, + onChangeHighlightedRange, + firstSequenceItem, + secondSequenceItem, + mouseDown, + firstSelected, + lastSelected, + colorLabelMap, + isHighlighted, + onRemoveLabel +}) => { + return ( + { + if (!elm) return + sequenceItemPositionsRef.current[textId] = { + offset: { + left: elm.offsetLeft, + top: elm.offsetTop, + width: elm.offsetWidth, + height: elm.offsetHeight + } + } + }} + relationshipsOn={relationshipsOn} + onMouseUp={e => { + if (!createRelationshipsMode) return + if (!secondSequenceItem) { + onChangeFirstSequenceItem(null) + onChangeSecondSequenceItem(null) + onCreateEmptyRelationship([firstSequenceItem, textId]) + } else { + onChangeFirstSequenceItem(null) + onChangeSecondSequenceItem(null) + } + }} + onMouseDown={() => { + if (createRelationshipsMode) { + if (!firstSequenceItem) { + onChangeFirstSequenceItem(textId) + } + } else { + if (label) return + onChangeHighlightedRange([sequenceItemIndex, sequenceItemIndex]) + } + }} + onMouseMove={() => { + if (!mouseDown) return + if (!createRelationshipsMode) { + if (label) return + if (sequenceItemIndex !== lastSelected) { + onChangeHighlightedRange([ + firstSelected === null ? sequenceItemIndex : firstSelected, + sequenceItemIndex + ]) + } + } + }} + className={classNames( + label ? "label" : "unlabeled", + text.trim().length > 0 && "notSpace" + )} + color={ + label + ? color || "#333" + : !createRelationshipsMode && text !== " " && isHighlighted + ? "#ccc" + : "inherit" + } + > + {label ? ( + +
{text}
+
+ ) : ( +
{text}
+ )} + {label && !createRelationshipsMode && ( + { + e.stopPropagation() + onRemoveLabel(sequenceItemIndex) + }} + > + + + )} +
+ ) +} + +export default memo(SequenceItem) diff --git a/src/components/SequenceItem/index.story.js b/src/components/SequenceItem/index.story.js new file mode 100644 index 0000000..ae54c0c --- /dev/null +++ b/src/components/SequenceItem/index.story.js @@ -0,0 +1,33 @@ +// @flow + +import React from "react" + +import { storiesOf } from "@storybook/react" +import { action } from "@storybook/addon-actions" +import SequenceItem from "./" + +storiesOf("SequenceItem", module).add("Basic", () => { + return ( + + ) +}) diff --git a/src/string-to-sequence.js b/src/string-to-sequence.js index 395e513..a4385a9 100644 --- a/src/string-to-sequence.js +++ b/src/string-to-sequence.js @@ -1,8 +1,11 @@ // @flow -const stringToSequence = (doc: string, sepRe: RegExp = /[a-zA-ZÀ-ÿ]+/g) => { +const stringToSequence = ( + doc: string, + sepRe: RegExp | string = /[a-zA-ZÀ-ÿ]+/g +) => { if (typeof sepRe === "string") { - sepRe = new RegExp(sepRe) + sepRe = new RegExp(sepRe, "g") } let m let indices = [0] @@ -15,7 +18,6 @@ const stringToSequence = (doc: string, sepRe: RegExp = /[a-zA-ZÀ-ÿ]+/g) => { } while (m) indices = indices.concat([doc.length]) return indices - .filter((_, i) => indices[i] !== indices[i + 1]) .map((_, i) => ({ text: doc.slice(indices[i], indices[i + 1]), textId: Math.random() diff --git a/src/types.js b/src/types.js index c952c54..19f49de 100644 --- a/src/types.js +++ b/src/types.js @@ -32,6 +32,7 @@ export type LabelDocumentProps = { multipleLabels?: boolean, document: string, initialLabels?: Array, + separatorRegex?: string, onChange: (Array | string | null) => any } diff --git a/yarn.lock b/yarn.lock index 8f04b71..889e061 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1381,6 +1381,35 @@ prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" +"@semantic-release/error@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-2.2.0.tgz#ee9d5a09c9969eade1ec864776aeda5c5cddbbf0" + integrity sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg== + +"@semantic-release/git@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@semantic-release/git/-/git-9.0.0.tgz#304c4883c87d095b1faaae93300f1f1e0466e9a5" + integrity sha512-AZ4Zha5NAPAciIJH3ipzw/WU9qLAn8ENaoVAhD6srRPxTpTzuV3NhNh14rcAo8Paj9dO+5u4rTKcpetOBluYVw== + dependencies: + "@semantic-release/error" "^2.1.0" + aggregate-error "^3.0.0" + debug "^4.0.0" + dir-glob "^3.0.0" + execa "^4.0.0" + lodash "^4.17.4" + micromatch "^4.0.0" + p-reduce "^2.0.0" + +"@seveibar/react-resize-panel@^0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@seveibar/react-resize-panel/-/react-resize-panel-0.3.7.tgz#7c7bf449b0f2d83697e23008a5bc869c033fcc4c" + integrity sha512-oglctCuyCb04Ps4f1nY+IRmy5IyoGPjCv42rNAWdfDHzGb/wf81mvwTegHbouuaKWbmRH2E/c76ypSl2aGZFzQ== + dependencies: + cash-dom "^4.1.5" + classnames "^2.2.6" + lodash.debounce "^4.0.8" + react-draggable "^4.0.3" + "@storybook/addon-actions@^5.3.19": version "5.3.19" resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.3.19.tgz#50548fa6e84bc79ad95233ce23ade4878fc7cfac" @@ -3873,6 +3902,11 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +cash-dom@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/cash-dom/-/cash-dom-4.1.5.tgz#0ef0cf205bc7603aa4e2dfada5808442a7a0e6ca" + integrity sha512-E6MO0A6ms5iZPtexznQXWRkFEvqdPqCmdx/SiJr2PnhOQNhZNfALkLG5t83Hk3J5JELzED7PJuzhMoS2tT64XA== + chalk@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" @@ -4820,6 +4854,13 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + debug@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -5030,6 +5071,13 @@ dir-glob@2.0.0: arrify "^1.0.1" path-type "^3.0.0" +dir-glob@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -5696,6 +5744,21 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" + integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -6440,6 +6503,13 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -7033,6 +7103,11 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + humanize-url@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/humanize-url/-/humanize-url-1.0.1.tgz#f4ab99e0d288174ca4e1e50407c55fbae464efff" @@ -7729,6 +7804,11 @@ is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-string@^1.0.4, is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" @@ -9051,7 +9131,7 @@ micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2: +micromatch@^4.0.0, micromatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== @@ -9277,7 +9357,7 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -9546,6 +9626,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -9877,6 +9964,11 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-reduce@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-2.1.0.tgz#09408da49507c6c274faa31f28df334bc712b64a" + integrity sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw== + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -10052,7 +10144,7 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -11418,15 +11510,16 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-material-workspace-layout@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/react-material-workspace-layout/-/react-material-workspace-layout-1.0.1.tgz#f67988558eaa370c5f053557f76a0dc837470fcd" - integrity sha512-CK3TWkP8bCGMg35V2xTKAzRCQPiHjkO7mpLZDn1eJWbsee1pSGRVirWX2N6qc0CNKEAN8xEYnTWyOIb1BK/rFA== +react-material-workspace-layout@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/react-material-workspace-layout/-/react-material-workspace-layout-1.0.9.tgz#b8ca0b52fe6a2b61878e4d6fe6cd5f9db36f861a" + integrity sha512-/hFfOvcQTzYImxI892UDhaVAex5JSx5jYumIXkULwYJ7CdXhypmD2QfeQB/9BHInZOchgLyNCDygOTT4m1HDHg== dependencies: "@material-ui/core" "^4.10.0" "@material-ui/icons" "^4.9.1" + "@seveibar/react-resize-panel" "^0.3.7" classnames "^2.2.6" - react "^16.13.1" + react-resize-panel "^0.3.5" react-use-dimensions "^1.2.1" use-event-callback "^0.1.0" @@ -11460,6 +11553,16 @@ react-popper@^1.3.7: typed-styles "^0.0.7" warning "^4.0.2" +react-resize-panel@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/react-resize-panel/-/react-resize-panel-0.3.5.tgz#43aa3450bf5b5a2566b40c4201445ced96c2a905" + integrity sha512-iyHOFTrSt+WV4Ilzi81x6KH3FU7VsGP736rmxepwGrgAEATmCvXzZdluTm3NpsptP7aC3hLODmXwnxusyA393A== + dependencies: + cash-dom "^4.1.5" + classnames "^2.2.6" + lodash.debounce "^4.0.8" + react-draggable "^4.0.3" + react-scripts@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-2.1.3.tgz#6e49be279f4039fb9f330d2b3529b933b8e90945" @@ -12991,6 +13094,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001"