diff --git a/dist/react-modal.js b/dist/react-modal.js new file mode 100644 index 00000000..16801652 --- /dev/null +++ b/dist/react-modal.js @@ -0,0 +1,437 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.ReactModal=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) + this.closeWithTimeout(); + else + this.closeWithoutTimeout(); + }, + + componentDidUpdate: function() { + this.maybeFocus(); + }, + + maybeFocus: function() { + if (this.props.isOpen) + this.focusContent(); + }, + + focusContent: function() { + this.refs.content.getDOMNode().focus(); + }, + + closeWithTimeout: function() { + this.setState({beforeClose: true}, function() { + setTimeout(this.closeWithoutTimeout, this.props.closeTimeoutMS); + }.bind(this)); + }, + + closeWithoutTimeout: function() { + this.setState({ + afterOpen: false, + beforeClose: false + }, this.afterClose); + }, + + afterClose: function() { + focusManager.returnFocus(); + focusManager.teardownScopedFocus(); + }, + + handleKeyDown: function(event) { + if (event.key == 9 /*tab*/) scopeTab(this.getDOMNode(), event); + if (event.key == 27 /*esc*/) this.requestClose(); + }, + + handleOverlayClick: function() { + if (this.ownerHandlesClose()) + this.requestClose(); + else + this.focusContent(); + }, + + requestClose: function() { + if (this.ownerHandlesClose) + this.props.onRequestClose(); + }, + + ownerHandlesClose: function() { + return this.props.onRequestClose; + }, + + shouldBeClosed: function() { + return !this.props.isOpen && !this.state.beforeClose; + }, + + overlayStyles: { position: 'fixed', left: 0, right: 0, top: 0, bottom: 0 }, + + buildClassName: function(which) { + var className = CLASS_NAMES[which].base; + if (this.state.afterOpen) + className += ' '+CLASS_NAMES[which].afterOpen; + if (this.state.beforeClose) + className += ' '+CLASS_NAMES[which].beforeClose; + return className; + }, + + render: function() { + return this.shouldBeClosed() ? div() : ( + div({ + className: this.buildClassName('overlay'), + style: this.overlayStyles, + onClick: this.handleOverlayClick + }, + div({ + ref: "content", + className: this.buildClassName('content'), + tabIndex: "-1", + onClick: stopPropagation, + onKeyDown: this.handleKeyDown + }, + this.props.children + ) + ) + ); + } +}); + + +},{"../helpers/focusManager":4,"../helpers/scopeTab":6}],3:[function(_dereq_,module,exports){ +var _element = null; + +function setElement(element) { + _element = element; +} + +function hide(appElement) { + validateElement(appElement); + (appElement || _element).setAttribute('aria-hidden', 'true'); +} + +function show(appElement) { + validateElement(appElement); + (appElement || _element).removeAttribute('aria-hidden'); +} + +function toggle(shouldHide, appElement) { + if (shouldHide) + hide(appElement); + else + show(appElement); +} + +function validateElement(appElement) { + if (!appElement && !_element) + throw new Error('react-modal: You must set an element with `Modal.setAppElement(el)` to make this accessible'); +} + +function resetForTesting() { + _element = null; +} + +exports.toggle = toggle; +exports.setElement = setElement; +exports.show = show; +exports.hide = hide; +exports.resetForTesting = resetForTesting; + + +},{}],4:[function(_dereq_,module,exports){ +var findTabbable = _dereq_('../helpers/tabbable'); +var modalElement = null; +var focusLaterElement = null; +var needToFocus = false; + +function handleBlur(event) { + needToFocus = true; +} + +function handleFocus(event) { + if (needToFocus) { + needToFocus = false; + // need to see how jQuery shims document.on('focusin') so we don't need the + // setTimeout, firefox doesn't support focusin, if it did, we could focus + // the the element outisde of a setTimeout. Side-effect of this + // implementation is that the document.body gets focus, and then we focus + // our element right after, seems fine. + setTimeout(function() { + if (modalElement.contains(document.activeElement)) + return; + var el = (findTabbable(modalElement)[0] || modalElement); + el.focus(); + }, 0); + } +} + +exports.markForFocusLater = function() { + focusLaterElement = document.activeElement; +}; + +exports.returnFocus = function() { + try { + focusLaterElement.focus(); + } + catch (e) { + console.warn('You tried to return focus to '+focusLaterElement+' but it is not in the DOM anymore'); + } + focusLaterElement = null; +}; + +exports.setupScopedFocus = function(element) { + modalElement = element; + window.addEventListener('blur', handleBlur, false); + document.addEventListener('focus', handleFocus, true); +}; + +exports.teardownScopedFocus = function() { + modalElement = null; + window.removeEventListener('blur', handleBlur); + document.removeEventListener('focus', handleFocus); +}; + + +},{"../helpers/tabbable":7}],5:[function(_dereq_,module,exports){ +module.exports = function() { + injectStyle([ + '.ReactModal__Overlay {', + ' background-color: rgba(255, 255, 255, 0.75);', + '}', + '.ReactModal__Content {', + ' position: absolute;', + ' top: 40px;', + ' left: 40px;', + ' right: 40px;', + ' bottom: 40px;', + ' border: 1px solid #ccc;', + ' background: #fff;', + ' overflow: auto;', + ' -webkit-overflow-scrolling: touch;', + ' border-radius: 4px;', + ' outline: none;', + ' padding: 20px;', + '}', + '@media (max-width: 768px) {', + ' .ReactModal__Content {', + ' top: 10px;', + ' left: 10px;', + ' right: 10px;', + ' bottom: 10px;', + ' padding: 10px;', + ' }', + '}' + ].join('\n')); +}; + +function injectStyle(css) { + var style = document.getElementById('rackt-style'); + if (!style) { + style = document.createElement('style'); + style.setAttribute('id', 'rackt-style'); + var head = document.getElementsByTagName('head')[0]; + head.insertBefore(style, head.firstChild); + } + style.innerHTML = style.innerHTML+'\n'+css; +} + + +},{}],6:[function(_dereq_,module,exports){ +var findTabbable = _dereq_('../helpers/tabbable'); + +module.exports = function(node, event) { + var tabbable = findTabbable(node); + var finalTabbable = tabbable[event.shiftKey ? 0 : tabbable.length - 1]; + var leavingFinalTabbable = ( + finalTabbable === document.activeElement || + // handle immediate shift+tab after opening with mouse + node === document.activeElement + ); + if (!leavingFinalTabbable) return; + event.preventDefault(); + var target = tabbable[event.shiftKey ? tabbable.length - 1 : 0]; + target.focus(); +}; + +},{"../helpers/tabbable":7}],7:[function(_dereq_,module,exports){ +/*! + * Adapted from jQuery UI core + * + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/ui-core/ + */ + +function focusable(element, isTabIndexNotNaN) { + var nodeName = element.nodeName.toLowerCase(); + return (/input|select|textarea|button|object/.test(nodeName) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && visible(element); +} + +function hidden(el) { + return (el.offsetWidth <= 0 && el.offsetHeight <= 0) || + el.style.display === 'none'; +} + +function visible(element) { + while (element) { + if (element === document.body) break; + if (hidden(element)) return false; + element = element.parentNode; + } + return true; +} + +function tabbable(element) { + var tabIndex = element.getAttribute('tabindex'); + if (tabIndex === null) tabIndex = undefined; + var isTabIndexNaN = isNaN(tabIndex); + return (isTabIndexNaN || tabIndex >= 0) && focusable(element, !isTabIndexNaN); +} + +function findTabbableDescendants(element) { + return [].slice.call(element.querySelectorAll('*'), 0).filter(function(el) { + return tabbable(el); + }); +} + +module.exports = findTabbableDescendants; + + +},{}],8:[function(_dereq_,module,exports){ +module.exports = _dereq_('./components/Modal'); + + +},{"./components/Modal":1}]},{},[8]) +(8) +}); \ No newline at end of file diff --git a/dist/react-modal.min.js b/dist/react-modal.min.js new file mode 100644 index 00000000..14a4c56d --- /dev/null +++ b/dist/react-modal.min.js @@ -0,0 +1 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.ReactModal=e()}}(function(){return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}for(var i="function"==typeof require&&require,o=0;o0?this.closeWithTimeout():this.closeWithoutTimeout())},componentDidUpdate:function(){this.maybeFocus()},maybeFocus:function(){this.props.isOpen&&this.focusContent()},focusContent:function(){this.refs.content.getDOMNode().focus()},closeWithTimeout:function(){this.setState({beforeClose:!0},function(){setTimeout(this.closeWithoutTimeout,this.props.closeTimeoutMS)}.bind(this))},closeWithoutTimeout:function(){this.setState({afterOpen:!1,beforeClose:!1},this.afterClose)},afterClose:function(){focusManager.returnFocus(),focusManager.teardownScopedFocus()},handleKeyDown:function(event){9==event.key&&scopeTab(this.getDOMNode(),event),27==event.key&&this.requestClose()},handleOverlayClick:function(){this.ownerHandlesClose()?this.requestClose():this.focusContent()},requestClose:function(){this.ownerHandlesClose&&this.props.onRequestClose()},ownerHandlesClose:function(){return this.props.onRequestClose},shouldBeClosed:function(){return!this.props.isOpen&&!this.state.beforeClose},overlayStyles:{position:"fixed",left:0,right:0,top:0,bottom:0},buildClassName:function(which){var className=CLASS_NAMES[which].base;return this.state.afterOpen&&(className+=" "+CLASS_NAMES[which].afterOpen),this.state.beforeClose&&(className+=" "+CLASS_NAMES[which].beforeClose),className},render:function(){return this.shouldBeClosed()?div():div({className:this.buildClassName("overlay"),style:this.overlayStyles,onClick:this.handleOverlayClick},div({ref:"content",className:this.buildClassName("content"),tabIndex:"-1",onClick:stopPropagation,onKeyDown:this.handleKeyDown},this.props.children))}})}},{"../helpers/focusManager":4,"../helpers/scopeTab":6}],3:[function(_dereq_,module,exports){function setElement(element){_element=element}function hide(appElement){validateElement(appElement),(appElement||_element).setAttribute("aria-hidden","true")}function show(appElement){validateElement(appElement),(appElement||_element).removeAttribute("aria-hidden")}function toggle(shouldHide,appElement){shouldHide?hide(appElement):show(appElement)}function validateElement(appElement){if(!appElement&&!_element)throw new Error("react-modal: You must set an element with `Modal.setAppElement(el)` to make this accessible")}function resetForTesting(){_element=null}var _element=null;exports.toggle=toggle,exports.setElement=setElement,exports.show=show,exports.hide=hide,exports.resetForTesting=resetForTesting},{}],4:[function(_dereq_,module,exports){function handleBlur(){needToFocus=!0}function handleFocus(){needToFocus&&(needToFocus=!1,setTimeout(function(){if(!modalElement.contains(document.activeElement)){var el=findTabbable(modalElement)[0]||modalElement;el.focus()}},0))}var findTabbable=_dereq_("../helpers/tabbable"),modalElement=null,focusLaterElement=null,needToFocus=!1;exports.markForFocusLater=function(){focusLaterElement=document.activeElement},exports.returnFocus=function(){try{focusLaterElement.focus()}catch(e){console.warn("You tried to return focus to "+focusLaterElement+" but it is not in the DOM anymore")}focusLaterElement=null},exports.setupScopedFocus=function(element){modalElement=element,window.addEventListener("blur",handleBlur,!1),document.addEventListener("focus",handleFocus,!0)},exports.teardownScopedFocus=function(){modalElement=null,window.removeEventListener("blur",handleBlur),document.removeEventListener("focus",handleFocus)}},{"../helpers/tabbable":7}],5:[function(_dereq_,module){function injectStyle(css){var style=document.getElementById("rackt-style");if(!style){style=document.createElement("style"),style.setAttribute("id","rackt-style");var head=document.getElementsByTagName("head")[0];head.insertBefore(style,head.firstChild)}style.innerHTML=style.innerHTML+"\n"+css}module.exports=function(){injectStyle([".ReactModal__Overlay {"," background-color: rgba(255, 255, 255, 0.75);","}",".ReactModal__Content {"," position: absolute;"," top: 40px;"," left: 40px;"," right: 40px;"," bottom: 40px;"," border: 1px solid #ccc;"," background: #fff;"," overflow: auto;"," -webkit-overflow-scrolling: touch;"," border-radius: 4px;"," outline: none;"," padding: 20px;","}","@media (max-width: 768px) {"," .ReactModal__Content {"," top: 10px;"," left: 10px;"," right: 10px;"," bottom: 10px;"," padding: 10px;"," }","}"].join("\n"))}},{}],6:[function(_dereq_,module){var findTabbable=_dereq_("../helpers/tabbable");module.exports=function(node,event){var tabbable=findTabbable(node),finalTabbable=tabbable[event.shiftKey?0:tabbable.length-1],leavingFinalTabbable=finalTabbable===document.activeElement||node===document.activeElement;if(leavingFinalTabbable){event.preventDefault();var target=tabbable[event.shiftKey?tabbable.length-1:0];target.focus()}}},{"../helpers/tabbable":7}],7:[function(_dereq_,module){function focusable(element,isTabIndexNotNaN){var nodeName=element.nodeName.toLowerCase();return(/input|select|textarea|button|object/.test(nodeName)?!element.disabled:"a"===nodeName?element.href||isTabIndexNotNaN:isTabIndexNotNaN)&&visible(element)}function hidden(el){return el.offsetWidth<=0&&el.offsetHeight<=0||"none"===el.style.display}function visible(element){for(;element&&element!==document.body;){if(hidden(element))return!1;element=element.parentNode}return!0}function tabbable(element){var tabIndex=element.getAttribute("tabindex");null===tabIndex&&(tabIndex=void 0);var isTabIndexNaN=isNaN(tabIndex);return(isTabIndexNaN||tabIndex>=0)&&focusable(element,!isTabIndexNaN)}function findTabbableDescendants(element){return[].slice.call(element.querySelectorAll("*"),0).filter(function(el){return tabbable(el)})}module.exports=findTabbableDescendants},{}],8:[function(_dereq_,module){module.exports=_dereq_("./components/Modal")},{"./components/Modal":1}]},{},[8])(8)}); \ No newline at end of file diff --git a/script/build b/script/build index f425beb9..b9cd5395 100755 --- a/script/build +++ b/script/build @@ -6,6 +6,6 @@ NODE_ENV=production node_modules/.bin/browserify lib/index.js \ -t envify \ --detect-globals false \ -s ReactModal > dist/react-modal.js -node_modules/.bin/uglifyjs dist/react-modal \ +node_modules/.bin/uglifyjs dist/react-modal.js \ --compress warnings=false > dist/react-modal.min.js