+
{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();
}