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
+
+
+
+
+
+
+
+
+
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]}
+
+ Change
+
+
+
+ Dim Mode: {dimModes[this.state.dimModeIdx]}
+
+ Change
+
+
+
+ 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"