Skip to content

Commit 9bd4281

Browse files
committed
Initial commit.
0 parents  commit 9bd4281

File tree

12 files changed

+433
-0
lines changed

12 files changed

+433
-0
lines changed

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["es2015", "stage-0", "react"]
3+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
lib

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.storybook/config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { configure } from '@kadira/storybook';
2+
3+
function loadStories() {
4+
require('../stories');
5+
}
6+
7+
configure(loadStories, module);
8+
9+
const injectTapEventPlugin = require("react-tap-event-plugin");
10+
injectTapEventPlugin();

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Team Wertarbyte
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# material-ui-fullscreen-dialog
2+
This project provides a [fullscreen dialog][dialogspec] for [Material-UI][mui].
3+
4+
![Demo](demo.gif)
5+
6+
If you want to try the component yourself instead of watching a gif, head over to [the storybook][gh-pages] for a live demo!
7+
8+
## Installation
9+
```shell
10+
npm i --save material-ui-fullscreen-dialog
11+
```
12+
13+
## Usage
14+
15+
16+
```jsx
17+
import FullscreenDialog from 'material-ui-fullscreen-dialog'
18+
19+
<FullscreenDialog
20+
open={this.state.open}
21+
onRequestClose={() => this.setState({ open: false })}
22+
title={'Demo dialog'}
23+
actionButton={<FlatButton
24+
label='Done'
25+
onTouchTap={() => this.setState({ open: false })}
26+
/>}
27+
>
28+
// dialog content here
29+
</FullscreenDialog>
30+
```
31+
32+
## Properties
33+
| Name | Type | Default | Description |
34+
| --- | --- | --- | --- |
35+
| actionButton | `node` | | A `FlatButton` or `IconButton` that is used as affirmative action button. |
36+
| appBarStyle | `object` | |
37+
| children | `node` | | Children elements. |
38+
| closeIcon | `node` | Close icon | Icon element used for the dismissive action. |
39+
| containerStyle | `object` | | Overrides the inline-styles of the dialog's children container. |
40+
| onRequestClose | `function` | | Callback that is invoked when the dismissive action button is touched. |
41+
| open * | `bool` | | Controls whether the dialog is opened or not. |
42+
| style | `object` | | Overrides the inline-styles of the dialog's root element. |
43+
| title | `string` | | The title of the dialog. |
44+
45+
\* required property
46+
47+
## Credits
48+
The code for the animation was adapted from Material UI's [Dialog][mui-auto-complete], although the animation itself is different.
49+
50+
## License
51+
The files included in this repository are licensed under the MIT license.
52+
53+
[dialogspec]: https://material.io/guidelines/components/dialogs.html#dialogs-specs
54+
[mui]: http://www.material-ui.com/#/
55+
[gh-pages]: https://teamwertarbyte.github.io/material-ui-fullscreen-dialog/
56+
[Dialog]: https://github.com/callemall/material-ui/blob/master/src/Dialog/Dialog.js

demo.gif

364 KB
Loading

package.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "material-ui-fullscreen-dialog",
3+
"version": "0.1.0",
4+
"description": "A fullscreen dialog for Material-UI.",
5+
"main": "lib/FullscreenDialog.js",
6+
"scripts": {
7+
"build": "babel src -d lib",
8+
"prepublish": "babel src -d lib",
9+
"storybook": "start-storybook -p 6006",
10+
"deploy-storybook": "storybook-to-ghpages"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "git+https://github.com/TeamWertarbyte/material-ui-fullscreen-dialog.git"
15+
},
16+
"keywords": [
17+
"react",
18+
"material",
19+
"dialog",
20+
"fullscreen",
21+
"mobile"
22+
],
23+
"author": "Wertarbyte <[email protected]> (https://wertarbyte.com)",
24+
"license": "MIT",
25+
"bugs": {
26+
"url": "https://github.com/TeamWertarbyte/material-ui-fullscreen-dialog/issues"
27+
},
28+
"homepage": "https://github.com/TeamWertarbyte/material-ui-fullscreen-dialog#readme",
29+
"devDependencies": {
30+
"@kadira/storybook": "2.15.1",
31+
"@kadira/storybook-deployer": "^1.2.0",
32+
"babel-cli": "^6.14.0",
33+
"babel-core": "^6.8.0",
34+
"babel-preset-es2015": "^6.14.0",
35+
"babel-preset-react": "^6.11.1",
36+
"babel-preset-stage-0": "^6.5.0",
37+
"material-ui": "^0.16.2",
38+
"react": "^15.4.0",
39+
"react-dom": "^15.4.0",
40+
"react-tap-event-plugin": "^2.0.0"
41+
},
42+
"peerDependencies": {
43+
"material-ui": ">= 0.15.0 <= 0.16.x",
44+
"react": "^15.3.2",
45+
"react-dom": "^15.3.2",
46+
"react-tap-event-plugin": "^1.0.0 || ^2.0.0"
47+
}
48+
}

src/FullscreenDialog.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import React, { PropTypes, Component } from 'react'
2+
import ReactTransitionGroup from 'react-addons-transition-group'
3+
4+
import AppBar from 'material-ui/AppBar'
5+
import IconButton from 'material-ui/IconButton'
6+
import NavigationCloseIcon from 'material-ui/svg-icons/navigation/close'
7+
8+
import FullscreenDialogFrame from './FullscreenDialogFrame'
9+
10+
const getStyles = (props, theme) => ({
11+
root: {
12+
display: 'flex',
13+
flexDirection: 'column'
14+
},
15+
title: {
16+
fontSize: 20,
17+
fontWeight: 500
18+
},
19+
appBar: {
20+
height: (props.appBarStyle ? props.appBarStyle.height : null) || theme.appBar.height
21+
},
22+
container: {
23+
flex: 1,
24+
overflow: 'auto',
25+
padding: 16
26+
}
27+
})
28+
29+
export default class FullscreenDialog extends Component {
30+
componentDidMount () {
31+
this.id = `popup-${Date.now()}`
32+
if (this.props.open) {
33+
history.replaceState(this.id, 'popup')
34+
history.pushState(this.id, 'popup')
35+
}
36+
window.addEventListener('popstate', this.onPopState)
37+
}
38+
39+
componentWillUnmount () {
40+
window.removeEventListener('popstate', this.onPopState)
41+
}
42+
43+
onPopState = ({ state }) => {
44+
history.pushState(this.id, 'popup')
45+
if (state === this.id && this.props.onRequestClose) {
46+
this.props.onRequestClose()
47+
}
48+
}
49+
50+
componentWillReceiveProps ({ open }) {
51+
if (open && !this.props.open) {
52+
history.replaceState(this.id, 'popup')
53+
history.pushState(this.id, 'popup')
54+
}
55+
}
56+
57+
render () {
58+
const styles = getStyles(this.props, this.context.muiTheme)
59+
60+
const {
61+
actionButton,
62+
appBarStyle,
63+
children,
64+
closeIcon,
65+
containerStyle,
66+
open,
67+
style,
68+
title,
69+
titleStyle,
70+
} = this.props
71+
72+
return (
73+
<FullscreenDialogFrame
74+
open={open}
75+
style={{ ...style, ...styles.root }}
76+
>
77+
<AppBar
78+
title={title}
79+
titleStyle={{ ...styles.title, ...titleStyle }}
80+
style={{ ...styles.appBar, ...appBarStyle }}
81+
iconElementLeft={(
82+
<IconButton onTouchTap={() => this.props.onRequestClose()}>
83+
{closeIcon || <NavigationCloseIcon />}
84+
</IconButton>
85+
)}
86+
iconElementRight={actionButton}
87+
/>
88+
<div style={{ ...styles.container, ...containerStyle }}>
89+
{this.props.children}
90+
</div>
91+
</FullscreenDialogFrame>
92+
)
93+
}
94+
}
95+
96+
FullscreenDialog.propTypes = {
97+
actionButton: PropTypes.node,
98+
children: PropTypes.node,
99+
closeIcon: PropTypes.node,
100+
onRequestClose: PropTypes.func,
101+
open: PropTypes.bool.isRequired,
102+
style: PropTypes.object,
103+
title: PropTypes.string
104+
}
105+
106+
FullscreenDialog.defaultProps = {
107+
actions: []
108+
}
109+
110+
FullscreenDialog.contextTypes = {
111+
muiTheme: PropTypes.object
112+
}

src/FullscreenDialogFrame.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React, { PropTypes, Component } from 'react'
2+
import ReactTransitionGroup from 'react-addons-transition-group'
3+
4+
const getStyles = (props) => ({
5+
root: {
6+
width: '100vw',
7+
height: '100vh',
8+
position: 'fixed',
9+
top: 0,
10+
left: 0,
11+
zIndex: 1500,
12+
background: '#fafafa'
13+
}
14+
})
15+
16+
class TransitionItem extends Component {
17+
static propTypes = {
18+
children: PropTypes.node,
19+
style: PropTypes.object,
20+
};
21+
22+
state = {
23+
style: {
24+
opacity: 0,
25+
transition: 'all 225ms cubic-bezier(0.0, 0.0, 0.2, 1)',
26+
transform: 'translate(0, 56px)'
27+
},
28+
};
29+
30+
componentWillUnmount() {
31+
clearTimeout(this.enterTimeout);
32+
clearTimeout(this.leaveTimeout);
33+
}
34+
35+
componentWillEnter(callback) {
36+
this.componentWillAppear(callback);
37+
}
38+
39+
componentWillAppear(callback) {
40+
this.enterTimeout = setTimeout(() => {
41+
this.setState({
42+
style: {
43+
opacity: 1,
44+
transition: 'all 225ms cubic-bezier(0.0, 0.0, 0.2, 1)',
45+
transform: 'translate(0, 0px)',
46+
}
47+
})
48+
this.enterTimeout = setTimeout(callback, 225); // matches transition duration
49+
})
50+
}
51+
52+
componentWillLeave(callback) {
53+
this.setState({
54+
style: {
55+
opacity: 0,
56+
transition: 'all 195ms cubic-bezier(0.4, 0.0, 1, 1)',
57+
transform: 'translate(0, 56px)'
58+
},
59+
});
60+
61+
this.leaveTimeout = setTimeout(callback, 195); // matches transition duration
62+
}
63+
64+
render() {
65+
const {
66+
style,
67+
children,
68+
...other
69+
} = this.props;
70+
71+
return (
72+
<div {...other} style={{ ...style, ...this.state.style }}>
73+
{children}
74+
</div>
75+
);
76+
}
77+
}
78+
79+
export default class FullscreenDialogFrame extends Component {
80+
render () {
81+
const styles = getStyles(this.props)
82+
83+
const {
84+
children,
85+
style,
86+
title,
87+
titleStyle,
88+
} = this.props
89+
90+
return (
91+
<ReactTransitionGroup
92+
component='div'
93+
transitionAppear={true}
94+
transitionAppearTimeout={225}
95+
transitionEnter={true}
96+
transitionEnterTimeout={225}
97+
>
98+
{this.props.open && (
99+
<TransitionItem style={{
100+
...styles.root,
101+
...style
102+
}}
103+
>
104+
{this.props.children}
105+
</TransitionItem>
106+
)}
107+
</ReactTransitionGroup>
108+
)
109+
}
110+
}
111+
112+
FullscreenDialogFrame.propTypes = {
113+
children: PropTypes.node,
114+
open: PropTypes.bool.isRequired,
115+
style: PropTypes.object
116+
}

0 commit comments

Comments
 (0)