mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-24 10:33:58 +03:00
parent
d8da887da8
commit
f99c76f9d3
4
packages/react-dock/.babelrc
Normal file
4
packages/react-dock/.babelrc
Normal file
|
@ -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"]
|
||||
}
|
22
packages/react-dock/LICENSE
Normal file
22
packages/react-dock/LICENSE
Normal file
|
@ -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.
|
||||
|
44
packages/react-dock/README.md
Normal file
44
packages/react-dock/README.md
Normal file
|
@ -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 (
|
||||
<Dock position='right' isVisible={this.state.isVisible}>
|
||||
{/* you can pass a function as a child here */}
|
||||
<div onClick={() => this.setState({ isVisible: !this.state.isVisible })}>X</div>
|
||||
</Dock>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 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 }` |
|
13
packages/react-dock/demo/index.html
Normal file
13
packages/react-dock/demo/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>React Dock</title>
|
||||
<link href="http://fonts.googleapis.com/css?family=Noto+Sans|Roboto:400,300,500" rel="stylesheet" type="text/css">
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.5/paper/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/alexkuz/react-dock"><img style="z-index: 999999999; position: fixed; top: 0; left: 0; border: 0;" src="https://camo.githubusercontent.com/121cd7cbdc3e4855075ea8b558508b91ac463ac2/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f6c6566745f677265656e5f3030373230302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_left_green_007200.png"></a>
|
||||
<div id="root"></div>
|
||||
<script src="static/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
186
packages/react-dock/demo/src/App.jsx
Normal file
186
packages/react-dock/demo/src/App.jsx
Normal file
|
@ -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 (
|
||||
<Root>
|
||||
<Main>
|
||||
<h1>Main Content</h1>
|
||||
<div>
|
||||
<div>
|
||||
Position: {positions[this.state.positionIdx]}
|
||||
<Button
|
||||
onClick={this.handlePositionClick}
|
||||
style={{ marginLeft: '20px' }}
|
||||
>
|
||||
Change
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
Dim Mode: {dimModes[this.state.dimModeIdx]}
|
||||
<Button
|
||||
onClick={this.handleDimModeClick}
|
||||
style={{ marginLeft: '20px' }}
|
||||
>
|
||||
Change
|
||||
</Button>
|
||||
</div>
|
||||
<Form.Check
|
||||
label="is visible"
|
||||
checked={this.state.isVisible}
|
||||
onChange={() =>
|
||||
this.setState({
|
||||
isVisible: !this.state.isVisible,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<Form.Check
|
||||
label="custom animation"
|
||||
checked={this.state.customAnimation}
|
||||
onChange={() =>
|
||||
this.setState({
|
||||
customAnimation: !this.state.customAnimation,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<Form.Check
|
||||
label="slow"
|
||||
checked={this.state.slow}
|
||||
onChange={() =>
|
||||
this.setState({
|
||||
slow: !this.state.slow,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<Form.Check
|
||||
label="fluid"
|
||||
checked={this.state.fluid}
|
||||
onChange={() =>
|
||||
this.setState({
|
||||
fluid: !this.state.fluid,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Main>
|
||||
<Dock
|
||||
position={positions[this.state.positionIdx]}
|
||||
size={this.state.size}
|
||||
dimMode={dimModes[this.state.dimModeIdx]}
|
||||
isVisible={this.state.isVisible}
|
||||
onVisibleChange={this.handleVisibleChange}
|
||||
onSizeChange={this.handleSizeChange}
|
||||
fluid={this.state.fluid}
|
||||
dimStyle={{ background: 'rgba(0, 0, 100, 0.2)' }}
|
||||
dockStyle={
|
||||
this.state.customAnimation ? { transition: transitions } : null
|
||||
}
|
||||
dockHiddenStyle={
|
||||
this.state.customAnimation
|
||||
? {
|
||||
transition: [
|
||||
transitions,
|
||||
`opacity 0.01s linear ${dur}s`,
|
||||
].join(','),
|
||||
}
|
||||
: null
|
||||
}
|
||||
duration={duration}
|
||||
>
|
||||
{({ position, isResizing }) => (
|
||||
<DockContent>
|
||||
<h1>Dock Content</h1>
|
||||
<div>Position: {position}</div>
|
||||
<div>Resizing: {isResizing ? 'true' : 'false'}</div>
|
||||
<Remove onClick={() => this.setState({ isVisible: false })} />
|
||||
</DockContent>
|
||||
)}
|
||||
</Dock>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
5
packages/react-dock/demo/src/index.js
vendored
Normal file
5
packages/react-dock/demo/src/index.js
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
64
packages/react-dock/package.json
Normal file
64
packages/react-dock/package.json
Normal file
|
@ -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 <alexkuz@gmail.com> (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"
|
||||
}
|
||||
}
|
432
packages/react-dock/src/Dock.js
vendored
Normal file
432
packages/react-dock/src/Dock.js
vendored
Normal file
|
@ -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 (
|
||||
<div style={Object.assign({}, styles.wrapper, { zIndex })}>
|
||||
{dimMode !== 'none' && !isDimHidden && (
|
||||
<div style={dimStyles} onClick={this.handleDimClick} />
|
||||
)}
|
||||
<div style={dockStyles}>
|
||||
<div style={resizerStyles} onMouseDown={this.handleMouseDown} />
|
||||
<div style={styles.dockContent}>
|
||||
{typeof children === 'function'
|
||||
? children({
|
||||
position,
|
||||
isResizing,
|
||||
size,
|
||||
isVisible,
|
||||
})
|
||||
: children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
};
|
||||
}
|
57
packages/react-dock/src/autoprefix.js
vendored
Normal file
57
packages/react-dock/src/autoprefix.js
vendored
Normal file
|
@ -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
|
||||
);
|
||||
}
|
1
packages/react-dock/src/index.js
vendored
Normal file
1
packages/react-dock/src/index.js
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export default from './Dock';
|
23
packages/react-dock/test/index.test.js
Normal file
23
packages/react-dock/test/index.test.js
Normal file
|
@ -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 = <Dock />;
|
||||
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');
|
||||
});
|
||||
});
|
50
packages/react-dock/webpack.config.js
Normal file
50
packages/react-dock/webpack.config.js
Normal file
|
@ -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,
|
||||
},
|
||||
};
|
20
yarn.lock
20
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"
|
||||
|
|
Loading…
Reference in New Issue
Block a user