diff --git a/src/application/popin/example/index.jsx b/src/application/popin/example/index.jsx index af7df5e92..a52448c41 100644 --- a/src/application/popin/example/index.jsx +++ b/src/application/popin/example/index.jsx @@ -1,7 +1,23 @@ const {MenuLeft} = FocusComponents.components; const Popin = FocusComponents.application.popin.component; +const {Panel} = FocusComponents.components; + +let currentLevel = 0; +const getLevel = function getLevel() { + currentLevel++; + return currentLevel; +}; const MyMenu = React.createClass({ + + getInitialState() { + return ({ + levelPopin1: 0, + levelPopin2: 0, + levelPopin3: 0, + }); + }, + goHome() { window.location.href = '#' }, @@ -9,45 +25,52 @@ const MyMenu = React.createClass({ const self = this; return [ { - icon:'home', + icon: 'home', onClick() { self.goHome(); } }, { - icon:'notifications', + icon: 'notifications', onClick() { + self.setState({levelPopin1:getLevel() }); self.refs.popin.togglePopin(); } }, { - icon:'chat', + icon: 'chat', onClick() { + self.setState({levelPopin3:getLevel() }); self.refs.thirdPopin.togglePopin(); + } } ] }, style: { position: 'fixed', - left:'40%', - top:'30%', - width:'576px' + left: '40%', + top: '30%', + width: '576px' }, titleStyle: { color: 'white', - height:' 176px', + height: ' 176px', backgroundImage: 'url(http://media.dcentertainment.com/sites/default/files/character_bio_576_superman_0.jpg)' }, makeTheSecondPopinPop() { const self = this; + self.setState({levelPopin2:getLevel()}); + self.refs.secondPopin.setState({dynamicPopin: 0}); return self.refs.secondPopin.togglePopin(); }, makeTheThirdPopinPop() { const self = this; + self.setState({levelPopin3:getLevel() }); return self.refs.thirdPopin.togglePopin(); }, render() { + const {levelPopin1,levelPopin2,levelPopin3} = this.state; return (
@@ -58,20 +81,31 @@ const MyMenu = React.createClass({
- Through this example you'll find the different possibilities you got with the Focus Popin component. + Through this example you'll find the different possibilities you got with the Focus Popin + component.

- Click on
notifications
icon to have a menu slidding popin + Click on +
notifications
+ icon to have a menu slidding popin
- Click on
chat
icon to have a right slidding popin + Click on +
chat
+ icon to have a right slidding popin
- And to display a fade-in popin example. + And + + to display a fade-in popin example.
- - - + + + ); } @@ -82,8 +116,9 @@ const MyPopin = React.createClass({ this.refs.popin.toggleOpen(); }, render() { + const {level}= this.props; return ( - +

News

@@ -104,12 +139,35 @@ const MyPopin = React.createClass({ }); const SecondPopin = React.createClass({ + + getInitialState() { + return {dynamicPopin: 0}; + }, togglePopin() { this.refs.secondPopin.toggleOpen(); }, + _renderDynamicPopin(){ + const colors = ['grey', 'Cornsilk', 'Gainsboro', 'LightCyan ']; + const popins = Array.from(Array(this.state.dynamicPopin).keys()) + .map(idx => { + const colorStyle = {backgroundColor: colors[idx % colors.length]}; + const popinTile = `Popin ${idx + 1}`; + return ( + + +

Close this popin with Escape key

+
+
); + }); + return popins; + }, + addPopup(){ + this.setState({dynamicPopin: this.state.dynamicPopin + 1}); + }, render() { + const {level}= this.props; return ( - +

The Modal, The Medium and The Full !

@@ -117,11 +175,17 @@ const SecondPopin = React.createClass({

This Popin is, as described, set the default type named full it defines its animation.
- You have 3 choices which are "full", "from-menu" and "from-right". The modal attribute is, by default, set to true so you can click outside of the Popin to close it. + You have 3 choices which are "full", "from-menu" and "from-right". The modal + attribute is, by default, set to true so you can click outside of the Popin to close it.
Check the notification icon which is in the Focus menu bar now. +

+ You can add as much popin as you want and close them with esc, from newer to older ones.


+ + {this._renderDynamicPopin()}
); } @@ -132,8 +196,9 @@ const ThirdPopin = React.createClass({ this.refs.thirdPopin.toggleOpen(); }, render() { + const {level}= this.props; return ( - +

Material Design Videos

@@ -141,32 +206,37 @@ const ThirdPopin = React.createClass({
Material design
- +

Making Material Design: Crafting Material
- +

Material Design : Palette Perfect
- +

Material Desing : Story
- +
); } }); + module.exports = MyMenu; diff --git a/src/application/popin/index.js b/src/application/popin/index.js index 74ad52409..0bc542890 100644 --- a/src/application/popin/index.js +++ b/src/application/popin/index.js @@ -7,10 +7,11 @@ import builder from 'focus-core/component/builder'; import types from 'focus-core/component/types'; import {ArgumentInvalidException} from 'focus-core/exception'; const includes = require('lodash').includes; +const {max} = require('lodash/math'); /** -* Small overlay component used to listen to scroll and prevent it to leave the Popin component -*/ + * Small overlay component used to listen to scroll and prevent it to leave the Popin component + */ const Overlay = React.createClass({ displayName: 'PopinOverlay', propTypes: { @@ -22,35 +23,36 @@ const Overlay = React.createClass({ return {show: false}; }, /** - * Store the body overgflow property, and set it to hidden - * @private - */ + * Store the body overgflow property, and set it to hidden + * @private + */ _hideBodyOverflow() { document.body.style['overflow-y'] = 'hidden'; }, /** - * Restore body overflow property - * @private - */ + * Restore body overflow property + * @private + */ _restoreBodyOverflow() { document.body.style['overflow-y'] = 'auto'; }, /** - * Component will unmount event handler. - * Remove the mouse wheel listener. - */ + * Component will unmount event handler. + * Remove the mouse wheel listener. + */ componentWillUnmount() { // ReactDOM.findDOMNode(this.refs.overlay).removeEventListener('mousewheel', this._onScroll); this._restoreBodyOverflow(); }, /** - * Render the component - * @return {XML} the rendered HTML - */ + * Render the component + * @return {XML} the rendered HTML + */ render() { const {children, clickHandler, show} = this.props; return ( -
+
{children}
); @@ -58,25 +60,25 @@ const Overlay = React.createClass({ }); /** -* The popin component configuration -* @type {Object} -*/ + * The popin component configuration + * @type {Object} + */ const popin = { /** - * Init the component. - * The popin is closed by default. - * @return {Object} the initial state - */ + * Init the component. + * The popin is closed by default. + * @return {Object} the initial state + */ getInitialState() { return ({ - opened: this.props.open + opened: this.props.open, }); }, /** - * Init the props if not provided. - * By default, a popin is full, medium and modal. - * @return {Object} the default props - */ + * Init the props if not provided. + * By default, a popin is full, medium and modal. + * @return {Object} the default props + */ getDefaultProps() { return ({ modal: true, @@ -88,30 +90,31 @@ const popin = { }); }, /** - * Helper attribute, for React debugging - */ + * Helper attribute, for React debugging + */ displayName: 'Popin', /** - * Properties validation - */ + * Properties validation + */ propTypes: { modal: types('bool'), size: types('string'), types: types('string'), level: types('number'), overlay: types('bool'), - open: types('bool') + open: types('bool'), + size: PropTypes.oneOf(['small', 'medium', 'large']), }, /** - * Wheel event handler. - * @param {object} event wheel event - */ + * Wheel event handler. + * @param {object} event wheel event + */ _onWheel(event) { ReactDOM.findDOMNode(this.refs['popin-window']).scrollTop += 0 < event.deltaY ? 100 : -100; }, /** - * Toggle the popin's open state - */ + * Toggle the popin's open state + */ toggleOpen() { let timeout = 0; const {opened} = this.state; @@ -131,6 +134,11 @@ const popin = { this._openTimeoutID = setTimeout(() => { // Store the current popin state const wasOpened = this.state.opened; + if (wasOpened) { + window.removeEventListener('keydown', this.handleKeyDown, true); + } else { + window.addEventListener('keydown', this.handleKeyDown, true); + } // If it was opened, then we are closing it, so restore the body overflow before closing. if (wasOpened && this.refs['popin-overlay']) { this.refs['popin-overlay']._restoreBodyOverflow(); @@ -147,35 +155,70 @@ const popin = { }); }, timeout); }, + + componentDidMount() { + if (this.state.opened) { + window.addEventListener('keydown', this.handleKeyDown, true); + } + }, componentWillUnmount() { window.clearTimeout(this._openTimeoutID); }, + /** - * Render the component - * @return {XML} the rendered HTML - */ + * keyboard event handler + * close the popin with esc + * @param e keyboard event + */ + handleKeyDown(e) { + e.preventDefault(); + if (e.code === "Escape") { + if (this.state.opened) { + const popins = [...document.querySelectorAll("[data-focus='popin']")].reduce((prec, current) => { + if (current.hasChildNodes()) { + prec.push(+current.getAttribute('data-level')); + } + return prec; + }, []); + + const {level} = this.props; + if (max(popins) === level) { + this.toggleOpen(); + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + } + }, + /** + * Render the component + * @return {XML} the rendered HTML + */ render() { // test for this.state.opened and return an Overlay component if true - const {type, level, modal, overlay, children} = this.props; + const {level,type, modal, overlay, children} = this.props; return ( -
+
{this.state.opened && - -
- close -
- {children} -
+ +
+ close +
+ {children}
- +
+
}
); }, /** - * Compute the animation classes - * @return {Object} the props to attach to the component - * @private - */ + * Compute the animation classes + * @return {Object} the props to attach to the component + * @private + */ _getAnimationProps() { let openingAnimation; let closingAnimation; @@ -200,21 +243,18 @@ const popin = { }); }, /** - * Validate the optional given size - * @return {string} the validated size - * @private - */ + * Validate the optional given size + * @return {string} the validated size + * @private + */ _validateSize() { - if (!includes(['small', 'medium', 'large'], this.props.size)) { - throw new ArgumentInvalidException('Please provide a valid popin size among small, medium and large. Provided ' + this.props.size); - } return this.props.size; }, /** - * Prevent popin close when there's a click on the popin window - * @param {Object} event - raised by the click - * @private - */ + * Prevent popin close when there's a click on the popin window + * @param {Object} event - raised by the click + * @private + */ _preventPopinClose(event) { event.stopPropagation(); }