diff --git a/packages/react-dock/.babelrc b/packages/react-dock/.babelrc new file mode 100644 index 00000000..a53745a0 --- /dev/null +++ b/packages/react-dock/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@babel/preset-env", "@babel/preset-react"], + "plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "react-hot-loader/babel"] +} diff --git a/packages/react-dock/LICENSE b/packages/react-dock/LICENSE new file mode 100644 index 00000000..01ffd8f2 --- /dev/null +++ b/packages/react-dock/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alexander Kuznetsov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/packages/react-dock/README.md b/packages/react-dock/README.md new file mode 100644 index 00000000..f4491a25 --- /dev/null +++ b/packages/react-dock/README.md @@ -0,0 +1,44 @@ +# react-dock + +Resizable dockable react component. + +#### Demo + +[http://alexkuz.github.io/react-dock/demo/](http://alexkuz.github.io/react-dock/demo/) + +#### Install + +``` +$ npm i -S react-dock +``` + +#### Example + +```jsx +render() { + return ( + + {/* you can pass a function as a child here */} +
this.setState({ isVisible: !this.state.isVisible })}>X
+
+ ); +} +``` + +#### Dock Props + +| Prop Name | Description | +|-----------|-------------| +| position | Side to dock (`left`, `right`, `top` or `bottom`). Default is `left`. | +| fluid | If `true`, resize dock proportionally on window resize. | +| size | Size of dock panel (width or height, depending on `position`). If this prop is set, `Dock` is considered as a controlled component, so you need to use `onSizeChange` to track dock resizing. Value is a fraction of window width/height, if `fluid` is true, or pixels otherwise | +| defaultSize | Default size of dock panel (used for uncontrolled `Dock` component) | +| isVisible | If `true`, dock is visible | +| dimMode | If `none` - content is not dimmed, if `transparent` - pointer events are disabled (so you can click through it), if `opaque` - click on dim area closes the dock. Default is `opaque` | +| duration | Animation duration. Should be synced with transition animation in style properties | +| dimStyle | Style for dim area | +| dockStyle | Style for dock | +| zIndex | Z-index for wrapper | +| onVisibleChange | Fires when `Dock` wants to change `isVisible` (when opaque dim is clicked, in particular) | +| onSizeChange | Fires when `Dock` wants to change `size` | +| children | Dock content - react elements or function that returns an element. Function receives an object with these state values: `{ position, isResizing, size, isVisible }` | diff --git a/packages/react-dock/demo/index.html b/packages/react-dock/demo/index.html new file mode 100644 index 00000000..c6c57bb1 --- /dev/null +++ b/packages/react-dock/demo/index.html @@ -0,0 +1,13 @@ + + + + React Dock + + + + +Fork me on GitHub +
+ + + diff --git a/packages/react-dock/demo/src/App.jsx b/packages/react-dock/demo/src/App.jsx new file mode 100644 index 00000000..995b404c --- /dev/null +++ b/packages/react-dock/demo/src/App.jsx @@ -0,0 +1,186 @@ +import React, { Component } from 'react'; +import { hot } from 'react-hot-loader/root'; +import Button from 'react-bootstrap/Button'; +import Form from 'react-bootstrap/Form'; +import { BsX } from 'react-icons/bs'; +import styled from 'styled-components'; + +import Dock from '../../src/Dock'; + +const Root = styled.div` + font-size: 16px; + color: #999; + height: 100vh; +`; + +const Main = styled.div` + width: 100%; + height: 150%; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 30vh; +`; + +const DockContent = styled.div` + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +`; + +const Remove = styled(BsX)` + position: absolute; + z-index: 1; + right: 10px; + top: 10px; + cursor: pointer; +`; + +const positions = ['left', 'top', 'right', 'bottom']; +const dimModes = ['transparent', 'none', 'opaque']; + +class App extends Component { + constructor(props) { + super(props); + this.state = { + positionIdx: 0, + dimModeIdx: 0, + isVisible: true, + fluid: true, + customAnimation: false, + slow: false, + size: 0.25, + }; + } + + componentDidMount() {} + + render() { + const duration = this.state.slow ? 2000 : 200; + const dur = duration / 1000; + const transitions = ['left', 'top', 'width', 'height'] + .map((p) => `${p} ${dur}s cubic-bezier(0, 1.5, 0.5, 1)`) + .join(','); + + return ( + +
+

Main Content

+
+
+ Position: {positions[this.state.positionIdx]} + +
+
+ Dim Mode: {dimModes[this.state.dimModeIdx]} + +
+ + this.setState({ + isVisible: !this.state.isVisible, + }) + } + /> + + + this.setState({ + customAnimation: !this.state.customAnimation, + }) + } + /> + + + this.setState({ + slow: !this.state.slow, + }) + } + /> + + + this.setState({ + fluid: !this.state.fluid, + }) + } + /> +
+
+ + {({ position, isResizing }) => ( + +

Dock Content

+
Position: {position}
+
Resizing: {isResizing ? 'true' : 'false'}
+ this.setState({ isVisible: false })} /> +
+ )} +
+
+ ); + } + + handleVisibleChange = (isVisible) => { + this.setState({ isVisible }); + }; + + handleSizeChange = (size) => { + this.setState({ size }); + }; + + handlePositionClick = () => { + this.setState({ positionIdx: (this.state.positionIdx + 1) % 4 }); + }; + + handleDimModeClick = () => { + this.setState({ dimModeIdx: (this.state.dimModeIdx + 1) % 3 }); + }; +} + +export default hot(App); diff --git a/packages/react-dock/demo/src/index.js b/packages/react-dock/demo/src/index.js new file mode 100644 index 00000000..b597a442 --- /dev/null +++ b/packages/react-dock/demo/src/index.js @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +ReactDOM.render(, document.getElementById('root')); diff --git a/packages/react-dock/package.json b/packages/react-dock/package.json new file mode 100644 index 00000000..60ea6b47 --- /dev/null +++ b/packages/react-dock/package.json @@ -0,0 +1,64 @@ +{ + "name": "react-dock", + "version": "0.2.4", + "description": "Resizable dockable react component", + "scripts": { + "build-lib": "babel src --out-dir lib", + "build-demo": "NODE_ENV=production webpack -p", + "stats": "webpack --profile --json > stats.json", + "start": "webpack-dev-server", + "preversion": "npm run lint && npm run test", + "version": "npm run build-demo && git add -A .", + "postversion": "git push", + "prepublish": "npm run build-lib", + "test": "jest" + }, + "main": "lib/index.js", + "files": [ + "lib", + "src" + ], + "repository": { + "type": "git", + "url": "https://github.com/reduxjs/redux-devtools.git" + }, + "keywords": [ + "react", + "reactjs", + "dock", + "sidebar" + ], + "author": "Alexander (http://kuzya.org/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/reduxjs/redux-devtools/issues" + }, + "homepage": "https://github.com/reduxjs/redux-devtools", + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.1", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-export-default-from": "^7.10.4", + "@babel/preset-env": "^7.11.0", + "@babel/preset-react": "^7.10.4", + "babel-loader": "^8.1.0", + "react": "^16.13.1", + "react-bootstrap": "^1.3.0", + "react-dom": "^16.13.1", + "react-hot-loader": "^4.12.21", + "react-icons": "^3.10.0", + "react-pure-render": "^1.0.2", + "react-test-renderer": "^16.13.1", + "styled-components": "^5.1.1", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0" + }, + "peerDependencies": { + "react": "^16.3.0" + }, + "dependencies": { + "lodash.debounce": "^4.0.8", + "prop-types": "^15.7.2" + } +} diff --git a/packages/react-dock/src/Dock.js b/packages/react-dock/src/Dock.js new file mode 100644 index 00000000..92ecea0f --- /dev/null +++ b/packages/react-dock/src/Dock.js @@ -0,0 +1,432 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import debounce from 'lodash.debounce'; +import autoprefix from './autoprefix'; + +function autoprefixes(styles) { + return Object.keys(styles).reduce( + (obj, key) => ((obj[key] = autoprefix(styles[key])), obj), + {} + ); +} + +const styles = autoprefixes({ + wrapper: { + position: 'fixed', + width: 0, + height: 0, + top: 0, + left: 0, + }, + + dim: { + position: 'fixed', + left: 0, + right: 0, + top: 0, + bottom: 0, + zIndex: 0, + background: 'rgba(0, 0, 0, 0.2)', + opacity: 1, + }, + + dimAppear: { + opacity: 0, + }, + + dimTransparent: { + pointerEvents: 'none', + }, + + dimHidden: { + opacity: 0, + }, + + dock: { + position: 'fixed', + zIndex: 1, + boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)', + background: 'white', + left: 0, + top: 0, + width: '100%', + height: '100%', + }, + + dockHidden: { + opacity: 0, + }, + + dockResizing: { + transition: 'none', + }, + + dockContent: { + width: '100%', + height: '100%', + overflow: 'auto', + }, + + resizer: { + position: 'absolute', + zIndex: 2, + opacity: 0, + }, +}); + +function getTransitions(duration) { + return ['left', 'top', 'width', 'height'].map( + (p) => `${p} ${duration / 1000}s ease-out` + ); +} + +function getDockStyles( + { fluid, dockStyle, dockHiddenStyle, duration, position, isVisible }, + { size, isResizing, fullWidth, fullHeight } +) { + let posStyle; + const absSize = fluid ? size * 100 + '%' : size + 'px'; + + function getRestSize(fullSize) { + return fluid ? 100 - size * 100 + '%' : fullSize - size + 'px'; + } + + switch (position) { + case 'left': + posStyle = { + width: absSize, + left: isVisible ? 0 : '-' + absSize, + }; + break; + case 'right': + posStyle = { + left: isVisible ? getRestSize(fullWidth) : fullWidth, + width: absSize, + }; + break; + case 'top': + posStyle = { + top: isVisible ? 0 : '-' + absSize, + height: absSize, + }; + break; + case 'bottom': + posStyle = { + top: isVisible ? getRestSize(fullHeight) : fullHeight, + height: absSize, + }; + break; + } + + const transitions = getTransitions(duration); + + return [ + styles.dock, + autoprefix({ + transition: [ + ...transitions, + !isVisible && `opacity 0.01s linear ${duration / 1000}s`, + ] + .filter((t) => t) + .join(','), + }), + dockStyle, + autoprefix(posStyle), + isResizing && styles.dockResizing, + !isVisible && styles.dockHidden, + !isVisible && dockHiddenStyle, + ]; +} + +function getDimStyles( + { dimMode, dimStyle, duration, isVisible }, + { isTransitionStarted } +) { + return [ + styles.dim, + autoprefix({ + transition: `opacity ${duration / 1000}s ease-out`, + }), + dimStyle, + dimMode === 'transparent' && styles.dimTransparent, + !isVisible && styles.dimHidden, + isTransitionStarted && isVisible && styles.dimAppear, + isTransitionStarted && !isVisible && styles.dimDisappear, + ]; +} + +function getResizerStyles(position) { + let resizerStyle; + const size = 10; + + switch (position) { + case 'left': + resizerStyle = { + right: -size / 2, + width: size, + top: 0, + height: '100%', + cursor: 'col-resize', + }; + break; + case 'right': + resizerStyle = { + left: -size / 2, + width: size, + top: 0, + height: '100%', + cursor: 'col-resize', + }; + break; + case 'top': + resizerStyle = { + bottom: -size / 2, + height: size, + left: 0, + width: '100%', + cursor: 'row-resize', + }; + break; + case 'bottom': + resizerStyle = { + top: -size / 2, + height: size, + left: 0, + width: '100%', + cursor: 'row-resize', + }; + break; + } + + return [styles.resizer, autoprefix(resizerStyle)]; +} + +function getFullSize(position, fullWidth, fullHeight) { + return position === 'left' || position === 'right' ? fullWidth : fullHeight; +} + +export default class Dock extends Component { + constructor(props) { + super(props); + this.state = { + isControlled: typeof props.size !== 'undefined', + size: props.size || props.defaultSize, + isDimHidden: !props.isVisible, + fullWidth: typeof window !== 'undefined' && window.innerWidth, + fullHeight: typeof window !== 'undefined' && window.innerHeight, + isTransitionStarted: false, + isWindowResizing: false, + }; + } + + static propTypes = { + position: PropTypes.oneOf(['left', 'right', 'top', 'bottom']), + zIndex: PropTypes.number, + fluid: PropTypes.bool, + size: PropTypes.number, + defaultSize: PropTypes.number, + dimMode: PropTypes.oneOf(['none', 'transparent', 'opaque']), + isVisible: PropTypes.bool, + onVisibleChange: PropTypes.func, + onSizeChange: PropTypes.func, + dimStyle: PropTypes.object, + dockStyle: PropTypes.object, + duration: PropTypes.number, + }; + + static defaultProps = { + position: 'left', + zIndex: 99999999, + fluid: true, + defaultSize: 0.3, + dimMode: 'opaque', + duration: 200, + }; + + componentDidMount() { + window.addEventListener('mouseup', this.handleMouseUp); + window.addEventListener('mousemove', this.handleMouseMove); + window.addEventListener('resize', this.handleResize); + + if (!window.fullWidth) { + this.updateWindowSize(); + } + } + + componentWillUnmount() { + window.removeEventListener('mouseup', this.handleMouseUp); + window.removeEventListener('mousemove', this.handleMouseMove); + window.removeEventListener('resize', this.handleResize); + } + + UNSAFE_componentWillReceiveProps(nextProps) { + const isControlled = typeof nextProps.size !== 'undefined'; + + this.setState({ isControlled }); + + if (isControlled && this.props.size !== nextProps.size) { + this.setState({ size: nextProps.size }); + } else if (this.props.fluid !== nextProps.fluid) { + this.updateSize(nextProps); + } + + if (this.props.isVisible !== nextProps.isVisible) { + this.setState({ + isTransitionStarted: true, + }); + } + } + + updateSize(props) { + const { fullWidth, fullHeight } = this.state; + + this.setState({ + size: props.fluid + ? this.state.size / getFullSize(props.position, fullWidth, fullHeight) + : getFullSize(props.position, fullWidth, fullHeight) * this.state.size, + }); + } + + componentDidUpdate(prevProps) { + if (this.props.isVisible !== prevProps.isVisible) { + if (!this.props.isVisible) { + window.setTimeout(() => this.hideDim(), this.props.duration); + } else { + this.setState({ isDimHidden: false }); + } + + window.setTimeout(() => this.setState({ isTransitionStarted: false }), 0); + } + } + + transitionEnd = () => { + this.setState({ isTransitionStarted: false }); + }; + + hideDim = () => { + if (!this.props.isVisible) { + this.setState({ isDimHidden: true }); + } + }; + + render() { + const { children, zIndex, dimMode, position, isVisible } = this.props; + const { isResizing, size, isDimHidden } = this.state; + + const dimStyles = Object.assign( + {}, + ...getDimStyles(this.props, this.state) + ); + const dockStyles = Object.assign( + {}, + ...getDockStyles(this.props, this.state) + ); + const resizerStyles = Object.assign({}, ...getResizerStyles(position)); + + return ( +
+ {dimMode !== 'none' && !isDimHidden && ( +
+ )} +
+
+
+ {typeof children === 'function' + ? children({ + position, + isResizing, + size, + isVisible, + }) + : children} +
+
+
+ ); + } + + handleDimClick = () => { + if (this.props.dimMode === 'opaque') { + this.props.onVisibleChange && this.props.onVisibleChange(false); + } + }; + + handleResize = () => { + if (window.requestAnimationFrame) { + window.requestAnimationFrame(this.updateWindowSize.bind(this, true)); + } else { + this.updateWindowSize(true); + } + }; + + updateWindowSize = (windowResize) => { + const sizeState = { + fullWidth: window.innerWidth, + fullHeight: window.innerHeight, + }; + + if (windowResize) { + this.setState({ + ...sizeState, + isResizing: true, + isWindowResizing: windowResize, + }); + + this.debouncedUpdateWindowSizeEnd(); + } else { + this.setState(sizeState); + } + }; + + updateWindowSizeEnd = () => { + this.setState({ + isResizing: false, + isWindowResizing: false, + }); + }; + + debouncedUpdateWindowSizeEnd = debounce(this.updateWindowSizeEnd, 30); + + handleWrapperLeave = () => { + this.setState({ isResizing: false }); + }; + + handleMouseDown = () => { + this.setState({ isResizing: true }); + }; + + handleMouseUp = () => { + this.setState({ isResizing: false }); + }; + + handleMouseMove = (e) => { + if (!this.state.isResizing || this.state.isWindowResizing) return; + e.preventDefault(); + + const { position, fluid } = this.props; + const { fullWidth, fullHeight, isControlled } = this.state; + const { clientX: x, clientY: y } = e; + let size; + + switch (position) { + case 'left': + size = fluid ? x / fullWidth : x; + break; + case 'right': + size = fluid ? (fullWidth - x) / fullWidth : fullWidth - x; + break; + case 'top': + size = fluid ? y / fullHeight : y; + break; + case 'bottom': + size = fluid ? (fullHeight - y) / fullHeight : fullHeight - y; + break; + } + + this.props.onSizeChange && this.props.onSizeChange(size); + + if (!isControlled) { + this.setState({ size }); + } + }; +} diff --git a/packages/react-dock/src/autoprefix.js b/packages/react-dock/src/autoprefix.js new file mode 100644 index 00000000..f4c57d16 --- /dev/null +++ b/packages/react-dock/src/autoprefix.js @@ -0,0 +1,57 @@ +// Same as https://github.com/SimenB/react-vendor-prefixes/blob/master/src/index.js, +// but dumber + +const vendorSpecificProperties = [ + 'animation', + 'animationDelay', + 'animationDirection', + 'animationDuration', + 'animationFillMode', + 'animationIterationCount', + 'animationName', + 'animationPlayState', + 'animationTimingFunction', + 'appearance', + 'backfaceVisibility', + 'backgroundClip', + 'borderImage', + 'borderImageSlice', + 'boxSizing', + 'boxShadow', + 'contentColumns', + 'transform', + 'transformOrigin', + 'transformStyle', + 'transition', + 'transitionDelay', + 'transitionDuration', + 'transitionProperty', + 'transitionTimingFunction', + 'perspective', + 'perspectiveOrigin', + 'userSelect', +]; + +const prefixes = ['Moz', 'Webkit', 'ms', 'O']; + +function prefixProp(key, value) { + return prefixes.reduce( + (obj, pre) => ( + (obj[pre + key[0].toUpperCase() + key.substr(1)] = value), obj + ), + {} + ); +} + +export default function autoprefix(style) { + return Object.keys(style).reduce( + (obj, key) => + vendorSpecificProperties.indexOf(key) !== -1 + ? { + ...obj, + ...prefixProp(key, style[key]), + } + : obj, + style + ); +} diff --git a/packages/react-dock/src/index.js b/packages/react-dock/src/index.js new file mode 100644 index 00000000..9b56b45c --- /dev/null +++ b/packages/react-dock/src/index.js @@ -0,0 +1 @@ +export default from './Dock'; diff --git a/packages/react-dock/test/index.test.js b/packages/react-dock/test/index.test.js new file mode 100644 index 00000000..036cd9c8 --- /dev/null +++ b/packages/react-dock/test/index.test.js @@ -0,0 +1,23 @@ +import React from 'react'; +import ShallowRenderer from 'react-test-renderer/shallow'; +import Dock from '../src/Dock'; + +describe('Dock component', function () { + it('should have shallow rendering', function () { + const shallowRenderer = new ShallowRenderer(); + const DockEl = ; + shallowRenderer.render(DockEl); + + const result = shallowRenderer.getRenderOutput(); + + expect(DockEl.props).toEqual({ + position: 'left', + zIndex: 99999999, + fluid: true, + defaultSize: 0.3, + dimMode: 'opaque', + duration: 200, + }); + expect(result.type).toBe('div'); + }); +}); diff --git a/packages/react-dock/webpack.config.js b/packages/react-dock/webpack.config.js new file mode 100644 index 00000000..4387df1d --- /dev/null +++ b/packages/react-dock/webpack.config.js @@ -0,0 +1,50 @@ +var path = require('path'); +var webpack = require('webpack'); + +var isProduction = process.env.NODE_ENV === 'production'; + +module.exports = { + mode: isProduction ? 'production' : 'development', + devtool: 'eval', + entry: isProduction + ? ['./demo/src/index'] + : [ + 'webpack-dev-server/client?http://localhost:3000', + 'webpack/hot/only-dev-server', + './demo/src/index', + ], + output: { + path: path.join(__dirname, 'demo/static'), + filename: 'bundle.js', + publicPath: isProduction ? 'static/' : '/static/', + }, + plugins: isProduction ? [] : [new webpack.HotModuleReplacementPlugin()], + resolve: { + extensions: ['.js', '.jsx'], + }, + module: { + rules: [ + { + test: /\.jsx?$/, + loader: 'babel-loader', + include: [ + path.join(__dirname, 'src'), + path.join(__dirname, 'demo/src'), + ], + }, + ], + }, + devServer: isProduction + ? null + : { + quiet: true, + publicPath: '/static/', + port: 3000, + contentBase: './demo/', + hot: true, + stats: { + colors: true, + }, + historyApiFallback: true, + }, +}; diff --git a/yarn.lock b/yarn.lock index dc04512a..b9506b28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10472,11 +10472,6 @@ lodash-es@^4.17.15, lodash-es@^4.17.4: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -10492,13 +10487,6 @@ lodash.curry@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA= -lodash.debounce@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-3.1.1.tgz#812211c378a94cc29d5aa4e3346cf0bfce3a7df5" - integrity sha1-gSIRw3ipTMKdWqTjNGzwv846ffU= - dependencies: - lodash._getnative "^3.0.0" - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -13053,14 +13041,6 @@ react-docgen@^3.0.0: node-dir "^0.1.10" recast "^0.16.0" -react-dock@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/react-dock/-/react-dock-0.2.4.tgz#e727dc7550b3b73116635dcb9c0e04d0b7afe17c" - integrity sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw= - dependencies: - lodash.debounce "^3.1.1" - prop-types "^15.5.8" - react-dom@^16.13.1, react-dom@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"