diff --git a/docs/index.html b/docs/index.html index fb5ad3a..f3d2898 100644 --- a/docs/index.html +++ b/docs/index.html @@ -175,14 +175,14 @@

Dialog - v1.0.0 + v1.1.0

- coverage + coverage tag: ui

diff --git a/docs/odo-dialog/dist/odo-dialog.js b/docs/odo-dialog/dist/odo-dialog.js index e40ebea..0d3bd38 100644 --- a/docs/odo-dialog/dist/odo-dialog.js +++ b/docs/odo-dialog/dist/odo-dialog.js @@ -209,16 +209,15 @@ var ScrollFix$1 = { var FOCUSABLE_ELEMENTS = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])'].join(','); -/** - * Dialog that can contain static images, carousels, or videos - * @param {Element} element Main element. - * - * @constructor - */ - var Dialog = function (_TinyEmitter) { inherits(Dialog, _TinyEmitter); + /** + * Dialog that can contain static images, carousels, or videos + * @param {Element} element Main element. + * @param {object} [opts] Instance options. + * @constructor + */ function Dialog(element, opts) { classCallCheck(this, Dialog); @@ -234,6 +233,10 @@ var Dialog = function (_TinyEmitter) { */ _this.element = element; + /** + * Options object. + * @type {object} + */ _this.options = Object.assign({}, Dialog.Defaults, opts); /** @@ -324,6 +327,7 @@ var Dialog = function (_TinyEmitter) { _this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS); _this._bindContexts(); + _this.onResize(); _this._addA11yAttributes(); _this._ensureBodyChild(); return _this; @@ -349,6 +353,9 @@ var Dialog = function (_TinyEmitter) { this.onKeyPress = this.onKeyPress.bind(this); this.onClick = this.onClick.bind(this); this.close = this.close.bind(this); + // Bind undefined as the first parameter so that the event object will be + // the second parameter and the optional viewportHeight parameter will work. + this.onWindowResize = this.onResize.bind(this, undefined); }; /** @@ -436,6 +443,23 @@ var Dialog = function (_TinyEmitter) { } }; + /** + * The dialog has a height of 100vh, which, in mobile safari, is incorrect + * when the toolbars are visible, not allowing the user to scroll the full + * height of the content within it. + * The viewportHeight parameter is optional so that it can be read in the open() + * method with all the other DOM reads. This avoids read->write->read #perfmatters. + * @param {number} [viewportHeight=window.innerHeight] Height of the viewport. + * @protected + */ + + + Dialog.prototype.onResize = function onResize() { + var viewportHeight = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.innerHeight; + + this.element.style.height = viewportHeight + 'px'; + }; + /** * Checks to see if a dialog is already open or animating If not, opens dialog. * @param {boolean} [sync=false] Whether to open with transitions or not. @@ -451,6 +475,7 @@ var Dialog = function (_TinyEmitter) { return; } + var viewportHeight = window.innerHeight; Dialog.focusedBeforeDialog = document.activeElement; this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth; this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN); @@ -468,6 +493,7 @@ var Dialog = function (_TinyEmitter) { }); this.isOpen = true; + this.onResize(viewportHeight); this.element.removeAttribute('aria-hidden'); this.element.classList.add(Dialog.Classes.OPEN); this.element.classList.add(Dialog.Classes.ENTER); @@ -483,6 +509,7 @@ var Dialog = function (_TinyEmitter) { this.element.focus(); document.addEventListener('keydown', this.onKeyPress); + window.addEventListener('resize', this.onWindowResize); this.element.addEventListener('click', this.onClick); this._closers.forEach(function (element) { element.addEventListener('click', _this2.close); @@ -563,11 +590,15 @@ var Dialog = function (_TinyEmitter) { ScrollFix$1.remove(this._scrollFixId); - if (Dialog.focusedBeforeDialog) { + // Support: IE11 + // Clicking on an SVG element inside an will set the `focusedBeforeDialog` + // to the SVG, but SVG doesn't have a `focus()` method in IE. + if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') { Dialog.focusedBeforeDialog.focus(); } document.removeEventListener('keydown', this.onKeyPress); + window.removeEventListener('resize', this.onWindowResize); this.element.removeEventListener('click', this.onClick); this._closers.forEach(function (element) { element.removeEventListener('click', _this3.close); @@ -613,8 +644,7 @@ var Dialog = function (_TinyEmitter) { }; /** - * Disposes of global Dialog variables - * @public + * Close the dialog, remove event listeners and element references. */ @@ -656,12 +686,14 @@ var Dialog = function (_TinyEmitter) { Dialog._handleTriggerClick = function _handleTriggerClick(evt) { - var elem = evt.target.closest('[data-odo-dialog-open]'); + var trigger = evt.target.closest('[data-odo-dialog-open]'); - if (elem !== null) { + if (trigger !== null) { evt.preventDefault(); - var id = elem.getAttribute('data-odo-dialog-open'); - Dialog.getDialogById(id).open(); + var id = trigger.getAttribute('data-odo-dialog-open'); + var instance = Dialog.getDialogById(id); + instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger); + instance.open(); } }; @@ -779,8 +811,7 @@ var Dialog = function (_TinyEmitter) { /** * Instantiates all instances of dialogs with the same settings * @param {Object} options Object of all dialog options. Is optional. - * @return {Array.} - * @public + * @return {Dialog[]} */ @@ -793,9 +824,7 @@ var Dialog = function (_TinyEmitter) { }; /** - * Clear all references to dialogs so there are no duplicates - * @param {Object} options Object of all dialog options. Is optional. - * @public + * Clear all references to dialogs so there are no duplicates. */ @@ -843,7 +872,8 @@ Dialog.Classes = { /** @enum {string} */ Dialog.EventType = { OPENED: 'ododialog:open', - CLOSED: 'ododialog:closed' + CLOSED: 'ododialog:closed', + TRIGGER_CLICKED: 'ododialog:triggerclicked' }; /** @enum {number} */ @@ -858,7 +888,7 @@ Dialog.Defaults = { scrollableElement: '.odo-dialog' }; -/** @enum {Array} */ +/** @enum {Dialog[]} */ Dialog.Instances = []; Dialog.ScrollFix = ScrollFix$1; diff --git a/docs/odo-dialog/dist/odo-dialog.js.map b/docs/odo-dialog/dist/odo-dialog.js.map index f9c043c..2bd5e10 100644 --- a/docs/odo-dialog/dist/odo-dialog.js.map +++ b/docs/odo-dialog/dist/odo-dialog.js.map @@ -1 +1 @@ -{"version":3,"file":"odo-dialog.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\n/**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n *\n * @constructor\n */\nclass Dialog extends TinyEmitter {\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n if (Dialog.focusedBeforeDialog) {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Disposes of global Dialog variables\n * @public\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const elem = evt.target.closest('[data-odo-dialog-open]');\n\n if (elem !== null) {\n evt.preventDefault();\n const id = elem.getAttribute('data-odo-dialog-open');\n Dialog.getDialogById(id).open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Array.}\n * @public\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates\n * @param {Object} options Object of all dialog options. Is optional.\n * @public\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Array} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","open","sync","focusedBeforeDialog","activeElement","clientWidth","window","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","style","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","elem","closest","getDialogById","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","children","ignore","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","clone","slice","find","instance","TinyEmitter"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"odo-dialog.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\nclass Dialog extends TinyEmitter {\n /**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n * @param {object} [opts] Instance options.\n * @constructor\n */\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","children","ignore","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","clone","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file diff --git a/docs/odo-dialog/dist/odo-dialog.min.js b/docs/odo-dialog/dist/odo-dialog.min.js index 143014f..0e6e968 100644 --- a/docs/odo-dialog/dist/odo-dialog.min.js +++ b/docs/odo-dialog/dist/odo-dialog.min.js @@ -1,2 +1,2 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("tiny-emitter"),require("@odopod/odo-helpers"),require("@odopod/odo-device")):"function"==typeof define&&define.amd?define(["tiny-emitter","@odopod/odo-helpers","@odopod/odo-device"],t):e.OdoDialog=t(e.TinyEmitter,e.OdoHelpers,e.OdoDevice)}(this,function(e,t,i){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,i=i&&i.hasOwnProperty("default")?i.default:i;var o=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},s=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},l=document.body,r=function(){function e(t,i){o(this,e),this.element=t,this.id=i,this.startY=null,this.scrollY=null,this._createBoundEvents(),this._registerEvents()}return e.prototype._createBoundEvents=function(){this._touchStartBound=this._onTouchStart.bind(this),this._touchMoveBound=this._onTouchMove.bind(this),this._preventDefaultBound=this._preventDefault.bind(this)},e.prototype._registerEvents=function(){l.addEventListener("touchstart",this._touchStartBound),l.addEventListener("touchmove",this._touchMoveBound),document.addEventListener("touchmove",this._preventDefaultBound)},e.prototype._onTouchStart=function(e){this.startY=e.changedTouches[0].pageY,this.scrollY=this.element.scrollTop},e.prototype._onTouchMove=function(e){var t=this.startY-e.changedTouches[0].pageY,i=this.scrollY+t;i<0||i+this.element.offsetHeight>this.element.scrollHeight?e.preventDefault():e.stopPropagation()},e.prototype._preventDefault=function(e){e.preventDefault()},e.prototype.dispose=function(){l.removeEventListener("touchstart",this._touchStartBound),l.removeEventListener("touchmove",this._touchMoveBound),document.removeEventListener("touchmove",this._preventDefaultBound),this.element=null,this.id=null},e}(),a={_fixes:new Map,add:function(e){if(i.HAS_TOUCH_EVENTS){var o=t.string.random();return this._fixes.set(o,new r(e,o)),o}return""},remove:function(e){this._fixes.has(e)&&(this._fixes.get(e).dispose(),this._fixes.delete(e))}},d=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex^="-"])'].join(","),c=function(e){function i(t,n){o(this,i);var l=s(this,e.call(this));if(!(t instanceof Element))throw new TypeError('OdoDialog requires an element. Got: "'+t+'"');return l.element=t,l.options=Object.assign({},i.Defaults,n),l.id=t.getAttribute("id"),l.backdrop=document.createElement("div"),l.backdrop.className=i.Classes.BACKDROP,l.content=l.getByClass(i.Classes.CONTENT),l._closers=Array.from(l.element.querySelectorAll("[data-odo-dialog-close]")),l._resizeId=null,l._scrollFixId=null,l.isOpen=!1,l.isAnimating=!1,l._hasBodyScrollbar=null,l._originalBodyPadding=-1,l._isFullscreen=null,i.Instances.push(l),1===i.Instances.length&&document.body.addEventListener("click",i._handleTriggerClick),l.element.classList.toggle(i.Classes.NO_AUTO_MARGIN,!i.SUPPORTS_AUTO_MARGINS),l._bindContexts(),l._addA11yAttributes(),l._ensureBodyChild(),l}return n(i,e),i.prototype.getByClass=function(e){return this.element.getElementsByClassName(e)[0]},i.prototype._bindContexts=function(){this.onKeyPress=this.onKeyPress.bind(this),this.onClick=this.onClick.bind(this),this.close=this.close.bind(this)},i.prototype._addA11yAttributes=function(){this.element.tabIndex=-1,this.element.setAttribute("aria-hidden",!0),this.element.setAttribute("role","dialog"),this.content.setAttribute("role","document")},i.prototype._ensureBodyChild=function(){this.element.parentNode!==document.body&&document.body.appendChild(this.element)},i.prototype._applyScrollFix=function(){if(this.options.scrollableElement){var e=this.element.matches(this.options.scrollableElement)?this.element:this.element.querySelector(this.options.scrollableElement);this._scrollFixId=a.add(e)}},i.prototype._getScrollbarOffset=function(){var e=this.element.scrollHeight>document.documentElement.clientHeight;return this._hasBodyScrollbar&&!e?i.SCROLLBAR_WIDTH:0},i.prototype.onClick=function(e){this.options.dismissable&&e.target===this.element&&this.close()},i.prototype.onKeyPress=function(e){this.options.dismissable&&e.which===i.Keys.ESC&&this.close(),e.which===i.Keys.TAB&&i._trapTabKey(this.element,e)},i.prototype.open=function(){var e=this,o=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.isAnimating&&!this.isOpen){i.focusedBeforeDialog=document.activeElement,this._hasBodyScrollbar=document.body.clientWidth0&&(this.element.style.paddingRight=e+"px"),this.element.classList.remove(i.Classes.ENTER),this.element.classList.add(i.Classes.ENTERING)},i.prototype._opened=function(){this.element.classList.remove(i.Classes.ENTERING),this.element.classList.add(i.Classes.VISIBLE),this.isAnimating=!1,this.emit(i.EventType.OPENED)},i.prototype.close=function(){var e=this,o=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.isAnimating&&this.isOpen){var n=i._getSiblings(this.element),s=n.map(function(e){return e.getAttribute("data-odo-dialog-original")});n.forEach(function(e,t){s[t]?(e.setAttribute("aria-hidden",s[t]),e.removeAttribute("data-odo-dialog-original")):e.removeAttribute("aria-hidden")}),this.isOpen=!1,this.element.classList.add(i.Classes.LEAVE),this.element.classList.remove(i.Classes.VISIBLE),a.remove(this._scrollFixId),i.focusedBeforeDialog&&i.focusedBeforeDialog.focus(),document.removeEventListener("keydown",this.onKeyPress),this.element.removeEventListener("click",this.onClick),this._closers.forEach(function(t){t.removeEventListener("click",e.close)}),!0===o?(this._closeNext(),this._closed()):i._nextFrame(function(){e._closeNext(),t.animation.onTransitionEnd(e.element,e._closed,e,null,1e3)})}},i.prototype._closeNext=function(){this.isAnimating=!0,this.element.classList.remove(i.Classes.LEAVE),this.element.classList.add(i.Classes.LEAVING)},i.prototype._closed=function(){this.isAnimating=!1,this.element.style.paddingRight="",this.element.setAttribute("aria-hidden",!0),this.element.classList.remove(i.Classes.OPEN),this.element.classList.remove(i.Classes.LEAVING),document.body.style.paddingRight="",document.body.classList.remove(i.Classes.BODY_OPEN),document.body.removeChild(this.backdrop),this.emit(i.EventType.CLOSED)},i.prototype.dispose=function(){this.isOpen&&this.close(!0),this.element=null,this.content=null,this.backdrop=null,this._closers.length=0,t.array.remove(i.Instances,this),0===i.Instances.length&&document.body.removeEventListener("click",i._handleTriggerClick)},i._nextFrame=function(e){window.requestAnimationFrame(window.requestAnimationFrame.bind(null,e))},i._handleTriggerClick=function(e){var t=e.target.closest("[data-odo-dialog-open]");if(null!==t){e.preventDefault();var o=t.getAttribute("data-odo-dialog-open");i.getDialogById(o).open()}},i._trapTabKey=function(e,t){var o=i._getFocusableChildren(e),n=o.indexOf(document.activeElement);t.shiftKey&&0===n?(o[o.length-1].focus(),t.preventDefault()):t.shiftKey||n!==o.length-1||(o[0].focus(),t.preventDefault())},i._getFocusableChildren=function(e){return Array.from(e.querySelectorAll(d)).filter(i._isVisibleElement)},i._isVisibleElement=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},i._getSiblings=function(e){var t=["script","link","meta"];return Array.from(e.parentNode.children).filter(function(i){return i!==e&&!t.includes(i.nodeName.toLowerCase())})},i._getScrollbarWidth=function(){var e=document.createElement("div");e.style.cssText="width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;",document.body.appendChild(e);var t=e.offsetWidth-e.clientWidth;return document.body.removeChild(e),t},i._autoMarginTest=function(){var e=document.createElement("div"),t=document.createElement("div");e.style.cssText="display:flex;height:50px;width:50px;position:absolute;",t.style.cssText="margin:auto;",t.innerHTML="a",e.appendChild(t),document.body.appendChild(e);var i=t.offsetTop>0;return document.body.removeChild(e),i},i.initializeAll=function(e){return i.disposeAll(),Array.from(document.querySelectorAll("."+i.Classes.BASE)).map(function(t){return new i(t,e)})},i.disposeAll=function(){i.Instances.slice().forEach(function(e){e.dispose()})},i.getDialogById=function(e){return i.Instances.find(function(t){return t.id===e})},i}(e);return c.Classes={BODY_OPEN:"odo-dialog-open",BASE:"odo-dialog",OPEN:"odo-dialog--open",ENTER:"odo-dialog--enter",ENTERING:"odo-dialog--enter-active",LEAVE:"odo-dialog--leave",LEAVING:"odo-dialog--leave-active",VISIBLE:"odo-dialog--visible",FULLSCREEN:"odo-dialog--full",NO_AUTO_MARGIN:"odo-dialog--no-auto-margin",BACKDROP:"odo-dialog-backdrop",CONTENT:"odo-dialog__content"},c.EventType={OPENED:"ododialog:open",CLOSED:"ododialog:closed"},c.Keys={ESC:27,TAB:9},c.Defaults={dismissable:!0,scrollableElement:".odo-dialog"},c.Instances=[],c.ScrollFix=a,c.focusedBeforeDialog=null,c.SUPPORTS_AUTO_MARGINS=c._autoMarginTest(),c.SCROLLBAR_WIDTH=c._getScrollbarWidth(),c}); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("tiny-emitter"),require("@odopod/odo-helpers"),require("@odopod/odo-device")):"function"==typeof define&&define.amd?define(["tiny-emitter","@odopod/odo-helpers","@odopod/odo-device"],t):e.OdoDialog=t(e.TinyEmitter,e.OdoHelpers,e.OdoDevice)}(this,function(e,t,i){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,i=i&&i.hasOwnProperty("default")?i.default:i;var o=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)},s=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},l=document.body,r=function(){function e(t,i){o(this,e),this.element=t,this.id=i,this.startY=null,this.scrollY=null,this._createBoundEvents(),this._registerEvents()}return e.prototype._createBoundEvents=function(){this._touchStartBound=this._onTouchStart.bind(this),this._touchMoveBound=this._onTouchMove.bind(this),this._preventDefaultBound=this._preventDefault.bind(this)},e.prototype._registerEvents=function(){l.addEventListener("touchstart",this._touchStartBound),l.addEventListener("touchmove",this._touchMoveBound),document.addEventListener("touchmove",this._preventDefaultBound)},e.prototype._onTouchStart=function(e){this.startY=e.changedTouches[0].pageY,this.scrollY=this.element.scrollTop},e.prototype._onTouchMove=function(e){var t=this.startY-e.changedTouches[0].pageY,i=this.scrollY+t;i<0||i+this.element.offsetHeight>this.element.scrollHeight?e.preventDefault():e.stopPropagation()},e.prototype._preventDefault=function(e){e.preventDefault()},e.prototype.dispose=function(){l.removeEventListener("touchstart",this._touchStartBound),l.removeEventListener("touchmove",this._touchMoveBound),document.removeEventListener("touchmove",this._preventDefaultBound),this.element=null,this.id=null},e}(),a={_fixes:new Map,add:function(e){if(i.HAS_TOUCH_EVENTS){var o=t.string.random();return this._fixes.set(o,new r(e,o)),o}return""},remove:function(e){this._fixes.has(e)&&(this._fixes.get(e).dispose(),this._fixes.delete(e))}},d=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex^="-"])'].join(","),c=function(e){function i(t,n){o(this,i);var l=s(this,e.call(this));if(!(t instanceof Element))throw new TypeError('OdoDialog requires an element. Got: "'+t+'"');return l.element=t,l.options=Object.assign({},i.Defaults,n),l.id=t.getAttribute("id"),l.backdrop=document.createElement("div"),l.backdrop.className=i.Classes.BACKDROP,l.content=l.getByClass(i.Classes.CONTENT),l._closers=Array.from(l.element.querySelectorAll("[data-odo-dialog-close]")),l._resizeId=null,l._scrollFixId=null,l.isOpen=!1,l.isAnimating=!1,l._hasBodyScrollbar=null,l._originalBodyPadding=-1,l._isFullscreen=null,i.Instances.push(l),1===i.Instances.length&&document.body.addEventListener("click",i._handleTriggerClick),l.element.classList.toggle(i.Classes.NO_AUTO_MARGIN,!i.SUPPORTS_AUTO_MARGINS),l._bindContexts(),l.onResize(),l._addA11yAttributes(),l._ensureBodyChild(),l}return n(i,e),i.prototype.getByClass=function(e){return this.element.getElementsByClassName(e)[0]},i.prototype._bindContexts=function(){this.onKeyPress=this.onKeyPress.bind(this),this.onClick=this.onClick.bind(this),this.close=this.close.bind(this),this.onWindowResize=this.onResize.bind(this,void 0)},i.prototype._addA11yAttributes=function(){this.element.tabIndex=-1,this.element.setAttribute("aria-hidden",!0),this.element.setAttribute("role","dialog"),this.content.setAttribute("role","document")},i.prototype._ensureBodyChild=function(){this.element.parentNode!==document.body&&document.body.appendChild(this.element)},i.prototype._applyScrollFix=function(){if(this.options.scrollableElement){var e=this.element.matches(this.options.scrollableElement)?this.element:this.element.querySelector(this.options.scrollableElement);this._scrollFixId=a.add(e)}},i.prototype._getScrollbarOffset=function(){var e=this.element.scrollHeight>document.documentElement.clientHeight;return this._hasBodyScrollbar&&!e?i.SCROLLBAR_WIDTH:0},i.prototype.onClick=function(e){this.options.dismissable&&e.target===this.element&&this.close()},i.prototype.onKeyPress=function(e){this.options.dismissable&&e.which===i.Keys.ESC&&this.close(),e.which===i.Keys.TAB&&i._trapTabKey(this.element,e)},i.prototype.onResize=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.innerHeight;this.element.style.height=e+"px"},i.prototype.open=function(){var e=this,o=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.isAnimating&&!this.isOpen){var n=window.innerHeight;i.focusedBeforeDialog=document.activeElement,this._hasBodyScrollbar=document.body.clientWidth0&&(this.element.style.paddingRight=e+"px"),this.element.classList.remove(i.Classes.ENTER),this.element.classList.add(i.Classes.ENTERING)},i.prototype._opened=function(){this.element.classList.remove(i.Classes.ENTERING),this.element.classList.add(i.Classes.VISIBLE),this.isAnimating=!1,this.emit(i.EventType.OPENED)},i.prototype.close=function(){var e=this,o=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.isAnimating&&this.isOpen){var n=i._getSiblings(this.element),s=n.map(function(e){return e.getAttribute("data-odo-dialog-original")});n.forEach(function(e,t){s[t]?(e.setAttribute("aria-hidden",s[t]),e.removeAttribute("data-odo-dialog-original")):e.removeAttribute("aria-hidden")}),this.isOpen=!1,this.element.classList.add(i.Classes.LEAVE),this.element.classList.remove(i.Classes.VISIBLE),a.remove(this._scrollFixId),i.focusedBeforeDialog&&"function"==typeof i.focusedBeforeDialog.focus&&i.focusedBeforeDialog.focus(),document.removeEventListener("keydown",this.onKeyPress),window.removeEventListener("resize",this.onWindowResize),this.element.removeEventListener("click",this.onClick),this._closers.forEach(function(t){t.removeEventListener("click",e.close)}),!0===o?(this._closeNext(),this._closed()):i._nextFrame(function(){e._closeNext(),t.animation.onTransitionEnd(e.element,e._closed,e,null,1e3)})}},i.prototype._closeNext=function(){this.isAnimating=!0,this.element.classList.remove(i.Classes.LEAVE),this.element.classList.add(i.Classes.LEAVING)},i.prototype._closed=function(){this.isAnimating=!1,this.element.style.paddingRight="",this.element.setAttribute("aria-hidden",!0),this.element.classList.remove(i.Classes.OPEN),this.element.classList.remove(i.Classes.LEAVING),document.body.style.paddingRight="",document.body.classList.remove(i.Classes.BODY_OPEN),document.body.removeChild(this.backdrop),this.emit(i.EventType.CLOSED)},i.prototype.dispose=function(){this.isOpen&&this.close(!0),this.element=null,this.content=null,this.backdrop=null,this._closers.length=0,t.array.remove(i.Instances,this),0===i.Instances.length&&document.body.removeEventListener("click",i._handleTriggerClick)},i._nextFrame=function(e){window.requestAnimationFrame(window.requestAnimationFrame.bind(null,e))},i._handleTriggerClick=function(e){var t=e.target.closest("[data-odo-dialog-open]");if(null!==t){e.preventDefault();var o=t.getAttribute("data-odo-dialog-open"),n=i.getDialogById(o);n.emit(i.EventType.TRIGGER_CLICKED,t),n.open()}},i._trapTabKey=function(e,t){var o=i._getFocusableChildren(e),n=o.indexOf(document.activeElement);t.shiftKey&&0===n?(o[o.length-1].focus(),t.preventDefault()):t.shiftKey||n!==o.length-1||(o[0].focus(),t.preventDefault())},i._getFocusableChildren=function(e){return Array.from(e.querySelectorAll(d)).filter(i._isVisibleElement)},i._isVisibleElement=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},i._getSiblings=function(e){var t=["script","link","meta"];return Array.from(e.parentNode.children).filter(function(i){return i!==e&&!t.includes(i.nodeName.toLowerCase())})},i._getScrollbarWidth=function(){var e=document.createElement("div");e.style.cssText="width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;",document.body.appendChild(e);var t=e.offsetWidth-e.clientWidth;return document.body.removeChild(e),t},i._autoMarginTest=function(){var e=document.createElement("div"),t=document.createElement("div");e.style.cssText="display:flex;height:50px;width:50px;position:absolute;",t.style.cssText="margin:auto;",t.innerHTML="a",e.appendChild(t),document.body.appendChild(e);var i=t.offsetTop>0;return document.body.removeChild(e),i},i.initializeAll=function(e){return i.disposeAll(),Array.from(document.querySelectorAll("."+i.Classes.BASE)).map(function(t){return new i(t,e)})},i.disposeAll=function(){i.Instances.slice().forEach(function(e){e.dispose()})},i.getDialogById=function(e){return i.Instances.find(function(t){return t.id===e})},i}(e);return c.Classes={BODY_OPEN:"odo-dialog-open",BASE:"odo-dialog",OPEN:"odo-dialog--open",ENTER:"odo-dialog--enter",ENTERING:"odo-dialog--enter-active",LEAVE:"odo-dialog--leave",LEAVING:"odo-dialog--leave-active",VISIBLE:"odo-dialog--visible",FULLSCREEN:"odo-dialog--full",NO_AUTO_MARGIN:"odo-dialog--no-auto-margin",BACKDROP:"odo-dialog-backdrop",CONTENT:"odo-dialog__content"},c.EventType={OPENED:"ododialog:open",CLOSED:"ododialog:closed",TRIGGER_CLICKED:"ododialog:triggerclicked"},c.Keys={ESC:27,TAB:9},c.Defaults={dismissable:!0,scrollableElement:".odo-dialog"},c.Instances=[],c.ScrollFix=a,c.focusedBeforeDialog=null,c.SUPPORTS_AUTO_MARGINS=c._autoMarginTest(),c.SCROLLBAR_WIDTH=c._getScrollbarWidth(),c}); //# sourceMappingURL=odo-dialog.min.js.map diff --git a/docs/odo-dialog/dist/odo-dialog.min.js.map b/docs/odo-dialog/dist/odo-dialog.min.js.map index 867aea5..c9e0ddf 100644 --- a/docs/odo-dialog/dist/odo-dialog.min.js.map +++ b/docs/odo-dialog/dist/odo-dialog.min.js.map @@ -1 +1 @@ -{"version":3,"file":"odo-dialog.min.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\n/**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n *\n * @constructor\n */\nclass Dialog extends TinyEmitter {\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n if (Dialog.focusedBeforeDialog) {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Disposes of global Dialog variables\n * @public\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const elem = evt.target.closest('[data-odo-dialog-open]');\n\n if (elem !== null) {\n evt.preventDefault();\n const id = elem.getAttribute('data-odo-dialog-open');\n Dialog.getDialogById(id).open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Array.}\n * @public\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates\n * @param {Object} options Object of all dialog options. Is optional.\n * @public\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Array} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","this","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","_TinyEmitter","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","_this","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","open","sync","focusedBeforeDialog","activeElement","clientWidth","window","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","style","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_this2","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_this3","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","elem","closest","getDialogById","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","ignore","children","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","slice","find","instance","TinyEmitter"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"odo-dialog.min.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\nclass Dialog extends TinyEmitter {\n /**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n * @param {object} [opts] Instance options.\n * @constructor\n */\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","this","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","_TinyEmitter","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","_this","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_this2","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_this3","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","ignore","children","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file diff --git a/docs/odo-dialog/index.html b/docs/odo-dialog/index.html index 10279d4..a8c9c4d 100644 --- a/docs/odo-dialog/index.html +++ b/docs/odo-dialog/index.html @@ -89,8 +89,6 @@

A shoreline in Vancouver, Canada

-
-
<button data-odo-dialog-open="default">Open the default dialog</button>
 
 <!-- Could be elsewhere in your markup -->
@@ -105,25 +103,45 @@ 

A shoreline in Vancouver, Canada

</div> </div>
-
const defaultDialog = new OdoDialog(document.getElementById('default'));

+
const defaultDialog = new OdoDialog(document.getElementById('default'));

Public Events

-

The opened event is triggered when the content has finished its transition in, while the closed event triggers once the dialog has completely transitioned out.

+

The opened event is triggered when the content has finished its transition in, while the closed event triggers once the dialog has completely transitioned out. In case you need to retrieve some information from the button which triggered the dialog to open, you can subscribe to the TRIGGER_CLICKED event.

defaultDialog.on(OdoDialog.EventType.OPENED, function () {
   console.log('default dialog opened');
 });
 
 defaultDialog.on(OdoDialog.EventType.CLOSED, function () {
   console.log('default dialog closed');
-});
+}); -
+defaultDialog.on(OdoDialog.EventType.TRIGGER_CLICKED, function (triggerElement) { + console.log('dialog about to open because you clicked:', triggerElement); +});

Public Methods

-
defaultDialog.open();
-defaultDialog.close();
-defaultDialog.dispose();
-defaultDialog.getByClass(className);
+
defaultDialog.open() // Open the dialog
+defaultDialog.close() // Close the dialog
+defaultDialog.dispose() // Close the dialog, remove event listeners and element references
+defaultDialog.getByClass(className) // Find a single element by class inside the dialog
+ +

Public Properties

+
defaultDialog.element // Base element
+defaultDialog.options // options object
+defaultDialog.id // Unique id for the dialog. Same as the id attribute of the base element
+defaultDialog.isOpen // Whether the dialog is currently open
+ +

Protected Methods & Properties

+

If you create a subclass of OdoDialog, you will have access to these as well*.

+ +
defaultDialog.onClick(evt) // Delegated click handler on the dialog.
+defaultDialog.onKeyPress(evt) // Listens for the ESC key and traps the TAB key.
+defaultDialog.onResize(viewportHeight) // Sets the height of the dialog on viewport resize.
+defaultDialog.content // Dialog content (role=document)
+defaultDialog.backdrop // The background behind the dialog
+defaultDialog.isAnimating // Whether the dialog is currently animating (opening/closing)
+
+

*Because this is JavaScript, you have access to them already.

Changing Transitions

In addition to the default fade/scale transition, OdoDialog comes with two other transitions: odo-dialog--fade and odo-dialog--zoom-in. The other options in the <select> below are custom transitions added to this page. See the next section for details.

@@ -150,8 +168,6 @@

Cat Ipsum

-
-

Add Custom Transitions

You can add your own transitions too.

OdoDialog follows a similar transition sequence to Vue.js transition effects.

diff --git a/docs/odo-dialog/scripts/demo.js b/docs/odo-dialog/scripts/demo.js index 4b8a59b..751cb75 100644 --- a/docs/odo-dialog/scripts/demo.js +++ b/docs/odo-dialog/scripts/demo.js @@ -61,6 +61,10 @@ defaultDialog.on(OdoDialog.EventType.CLOSED, function () { console.log('default dialog closed'); }); +defaultDialog.on(OdoDialog.EventType.TRIGGER_CLICKED, function (triggerElement) { + console.log('dialog about to open because you clicked:', triggerElement); +}); + var styledDialog = new OdoDialog(document.getElementById('styled')); var animationChanger = new OdoDialog(document.getElementById('animation-changer')); @@ -110,40 +114,56 @@ var ScrollToCloseDialog = function (_OdoDialog) { var _this = possibleConstructorReturn(this, _OdoDialog.call(this, element, options)); - _this.onScroll = _this.onScroll.bind(_this); - _this.on(OdoDialog.EventType.OPENED, _this._onOpened.bind(_this)); - _this.on(OdoDialog.EventType.CLOSED, _this._onClosed.bind(_this)); + _this._onScroll = _this._onScroll.bind(_this); + _this._onOpened = _this._onOpened.bind(_this); + _this._onClosed = _this._onClosed.bind(_this); + _this._saveCloseOffset = _this._saveCloseOffset.bind(_this); + _this.on(OdoDialog.EventType.OPENED, _this._onOpened); + _this.on(OdoDialog.EventType.CLOSED, _this._onClosed); return _this; } - ScrollToCloseDialog.prototype._onOpened = function _onOpened() { + ScrollToCloseDialog.prototype._saveCloseOffset = function _saveCloseOffset() { var viewportHeight = window.innerHeight; // The extra margin is on the inner element, so it's included in the height // of the content element. var contentHeight = this.element.scrollHeight - viewportHeight; - // Require the user to scroll the content + 7/8 of the extra space. - this.closeOffset = contentHeight - Math.round(viewportHeight / 8); + this.closeOffset = contentHeight - Math.round(viewportHeight / ScrollToCloseDialog.VIEWPORT_DIVISOR); + }; - // Listen for scrolls. - this.element.addEventListener('scroll', this.onScroll); + ScrollToCloseDialog.prototype._onOpened = function _onOpened() { + this._saveCloseOffset(); + this.element.addEventListener('scroll', this._onScroll); + window.addEventListener('resize', this._saveCloseOffset); }; ScrollToCloseDialog.prototype._onClosed = function _onClosed() { - this.element.removeEventListener('scroll', this.onScroll); + this.element.removeEventListener('scroll', this._onScroll); + window.removeEventListener('resize', this._saveCloseOffset); }; - ScrollToCloseDialog.prototype.onScroll = function onScroll() { - var scrollTop = this.element.scrollTop; - if (scrollTop > this.closeOffset) { + ScrollToCloseDialog.prototype._onScroll = function _onScroll() { + if (this.element.scrollTop > this.closeOffset) { this.close(); } }; + ScrollToCloseDialog.prototype.dispose = function dispose() { + this.off(OdoDialog.EventType.OPENED, this._onOpened); + this.off(OdoDialog.EventType.CLOSED, this._onClosed); + _OdoDialog.prototype.dispose.call(this); + }; + return ScrollToCloseDialog; }(OdoDialog); +// Require the user to scroll the content + x-1/x of the extra space. + + +ScrollToCloseDialog.VIEWPORT_DIVISOR = 6; + var scrollToClose = new ScrollToCloseDialog(document.getElementById('scroll-to-close')); window.scrollToClose = scrollToClose; diff --git a/packages/odo-dialog/coverage.json b/packages/odo-dialog/coverage.json index e1b2ca3..7fdc8bd 100644 --- a/packages/odo-dialog/coverage.json +++ b/packages/odo-dialog/coverage.json @@ -1 +1 @@ -{"lines":{"total":222,"covered":222},"statements":{"total":225,"covered":225},"functions":{"total":46,"covered":46},"branches":{"total":79,"covered":79},"coverage":100} \ No newline at end of file +{"lines":{"total":229,"covered":229},"statements":{"total":232,"covered":232},"functions":{"total":47,"covered":47},"branches":{"total":80,"covered":80},"coverage":100} \ No newline at end of file diff --git a/packages/odo-dialog/dist/odo-dialog.js b/packages/odo-dialog/dist/odo-dialog.js index 80db773..0d3bd38 100644 --- a/packages/odo-dialog/dist/odo-dialog.js +++ b/packages/odo-dialog/dist/odo-dialog.js @@ -209,16 +209,15 @@ var ScrollFix$1 = { var FOCUSABLE_ELEMENTS = ['a[href]', 'area[href]', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'button:not([disabled])', 'iframe', 'object', 'embed', '[contenteditable]', '[tabindex]:not([tabindex^="-"])'].join(','); -/** - * Dialog that can contain static images, carousels, or videos - * @param {Element} element Main element. - * - * @constructor - */ - var Dialog = function (_TinyEmitter) { inherits(Dialog, _TinyEmitter); + /** + * Dialog that can contain static images, carousels, or videos + * @param {Element} element Main element. + * @param {object} [opts] Instance options. + * @constructor + */ function Dialog(element, opts) { classCallCheck(this, Dialog); @@ -825,8 +824,7 @@ var Dialog = function (_TinyEmitter) { }; /** - * Clear all references to dialogs so there are no duplicates - * @param {Object} options Object of all dialog options. Is optional. + * Clear all references to dialogs so there are no duplicates. */ diff --git a/packages/odo-dialog/dist/odo-dialog.js.map b/packages/odo-dialog/dist/odo-dialog.js.map index 626d521..2bd5e10 100644 --- a/packages/odo-dialog/dist/odo-dialog.js.map +++ b/packages/odo-dialog/dist/odo-dialog.js.map @@ -1 +1 @@ -{"version":3,"file":"odo-dialog.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\n/**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n *\n * @constructor\n */\nclass Dialog extends TinyEmitter {\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates\n * @param {Object} options Object of all dialog options. Is optional.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","children","ignore","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","clone","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"odo-dialog.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\nclass Dialog extends TinyEmitter {\n /**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n * @param {object} [opts] Instance options.\n * @constructor\n */\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","children","ignore","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","clone","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file diff --git a/packages/odo-dialog/dist/odo-dialog.min.js.map b/packages/odo-dialog/dist/odo-dialog.min.js.map index 9227e6d..c9e0ddf 100644 --- a/packages/odo-dialog/dist/odo-dialog.min.js.map +++ b/packages/odo-dialog/dist/odo-dialog.min.js.map @@ -1 +1 @@ -{"version":3,"file":"odo-dialog.min.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\n/**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n *\n * @constructor\n */\nclass Dialog extends TinyEmitter {\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates\n * @param {Object} options Object of all dialog options. Is optional.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","this","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","_TinyEmitter","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","_this","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_this2","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_this3","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","ignore","children","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"odo-dialog.min.js","sources":["../src/scroll-fix.js","../src/dialog.js"],"sourcesContent":["/**\n * @fileoverview Makes an overflowing element scrollable and handles preventing\n * default events and stopping event propagation when the scrollable element is\n * at the top or bottom of the scrollable area.\n *\n * @author Glen Cheney\n */\n\nimport { string } from '@odopod/odo-helpers';\nimport OdoDevice from '@odopod/odo-device';\n\nconst body = document.body;\n\n/**\n * Makes the element scrollable with some smart listeners because iOS\n * behaves unsatisfactory.\n * @param {Element} element Element to use.\n * @param {string} id Unique id.\n * @constructor\n */\nclass ScrollFix {\n constructor(element, id) {\n this.element = element;\n this.id = id;\n this.startY = null;\n this.scrollY = null;\n this._createBoundEvents();\n this._registerEvents();\n }\n\n _createBoundEvents() {\n this._touchStartBound = this._onTouchStart.bind(this);\n this._touchMoveBound = this._onTouchMove.bind(this);\n this._preventDefaultBound = this._preventDefault.bind(this);\n }\n\n /**\n * Add event listeners.\n * @private\n */\n _registerEvents() {\n body.addEventListener('touchstart', this._touchStartBound);\n body.addEventListener('touchmove', this._touchMoveBound);\n document.addEventListener('touchmove', this._preventDefaultBound);\n }\n\n /**\n * Save positions when the touch starts.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchStart(evt) {\n this.startY = evt.changedTouches[0].pageY;\n this.scrollY = this.element.scrollTop;\n }\n\n /**\n * When the touch move and touch start events get to the scrollable element,\n * prevent them from bubbling further.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _onTouchMove(evt) {\n const deltaY = this.startY - evt.changedTouches[0].pageY;\n const scrollTop = this.scrollY + deltaY;\n\n // Prevent default stops all further touches...\n // the user must lift their finger and swipe again before drags in the\n // opposite direction register.\n // However, without this, the same thing occurs, but instead of no\n // scrolling, the page behind the dialog scrolls.\n if (scrollTop < 0 || scrollTop + this.element.offsetHeight >\n this.element.scrollHeight) {\n evt.preventDefault();\n } else {\n evt.stopPropagation();\n }\n }\n\n /**\n * Simply prevent the event's default action.\n * @param {TouchEvent} evt Event object.\n * @private\n */\n _preventDefault(evt) {\n evt.preventDefault();\n }\n\n /**\n * Dispose of this instance by removing handlers and DOM references.\n */\n dispose() {\n body.removeEventListener('touchstart', this._touchStartBound);\n body.removeEventListener('touchmove', this._touchMoveBound);\n document.removeEventListener('touchmove', this._preventDefaultBound);\n\n this.element = null;\n this.id = null;\n }\n}\n\nexport default {\n /**\n * Dictionary of ScrollFix instances.\n * @type {Object.}\n * @private\n */\n _fixes: new Map(),\n\n /**\n * Enable an element to be scrollable.\n * @param {Element} element Element to make scrollable.\n * @return {string} Id which is used to remove it.\n */\n add(element) {\n if (OdoDevice.HAS_TOUCH_EVENTS) {\n const id = string.random();\n this._fixes.set(id, new ScrollFix(element, id));\n return id;\n }\n\n return '';\n },\n\n /**\n * Disable scrolling on an element and remove event listeners. Be aware\n * that this removes the scroll fix class. If your element doesn't have\n * the overflow-scrolling: touch property on it, iOS may flicker the whole\n * container when calling this method.\n * @param {string} id Id returned from enable.\n */\n remove(id) {\n if (this._fixes.has(id)) {\n this._fixes.get(id).dispose();\n this._fixes.delete(id);\n }\n },\n};\n","/**\n * @fileoverview UI Component for universal dialogs.\n * Notes\n * * The transition is on the main `element` so that `scale()` transforms do not\n * cause the calculation of `scrollHeight` to be artificially increased.\n * * The backdrop is a sibling to the dialog so that it does not cover the\n * scrollbar of the dialog and so that it doesn't jitter in iOS.\n *\n * @author Glen Cheney \n */\n\nimport TinyEmitter from 'tiny-emitter';\nimport { animation, array } from '@odopod/odo-helpers';\nimport ScrollFix from './scroll-fix';\n\nconst FOCUSABLE_ELEMENTS = [\n 'a[href]',\n 'area[href]',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'button:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n '[contenteditable]',\n '[tabindex]:not([tabindex^=\"-\"])',\n].join(',');\n\nclass Dialog extends TinyEmitter {\n /**\n * Dialog that can contain static images, carousels, or videos\n * @param {Element} element Main element.\n * @param {object} [opts] Instance options.\n * @constructor\n */\n constructor(element, opts) {\n super();\n\n if (!(element instanceof Element)) {\n throw new TypeError(`OdoDialog requires an element. Got: \"${element}\"`);\n }\n\n /**\n * Base Element.\n * @type {Element}\n */\n this.element = element;\n\n /**\n * Options object.\n * @type {object}\n */\n this.options = Object.assign({}, Dialog.Defaults, opts);\n\n /**\n * Dialog Id.\n * @type {string}\n */\n this.id = element.getAttribute('id');\n\n /**\n * Dialog backdrop\n * @type {Element}\n * @protected\n */\n this.backdrop = document.createElement('div');\n this.backdrop.className = Dialog.Classes.BACKDROP;\n\n /**\n * Dialog content (role=document).\n * @type {Element}\n * @protected\n */\n this.content = this.getByClass(Dialog.Classes.CONTENT);\n\n /**\n * Elements which, when clicked, close the dialog.\n * @type {Element}\n * @private\n */\n this._closers = Array.from(this.element.querySelectorAll('[data-odo-dialog-close]'));\n\n /**\n * Window resize Id\n * @type {string}\n * @private\n */\n this._resizeId = null;\n\n /**\n * ScrollFix id\n * @type {?string}\n * @private\n */\n this._scrollFixId = null;\n\n /**\n * Whether the dialog is open.\n * @type {boolean}\n */\n this.isOpen = false;\n\n /**\n * Is the dialog currently animating.\n * @type {boolean}\n * @protected\n */\n this.isAnimating = false;\n\n /**\n * Whether the body has a scrollbar.\n * @type {?boolean}\n * @private\n */\n this._hasBodyScrollbar = null;\n\n /**\n * Padding on the body.\n * @type {number}\n * @private\n */\n this._originalBodyPadding = -1;\n\n /**\n * Whether this is a fullscreen dialog. Fullscreen dialogs should not have\n * paddingRight applied to them.\n * @type {?boolean}\n * @private\n */\n this._isFullscreen = null;\n\n Dialog.Instances.push(this);\n\n if (Dialog.Instances.length === 1) {\n document.body.addEventListener('click', Dialog._handleTriggerClick);\n }\n\n // If this browser does not support auto margins for flexbox, add a class\n // so that it can be centered differently.\n this.element.classList.toggle(Dialog.Classes.NO_AUTO_MARGIN, !Dialog.SUPPORTS_AUTO_MARGINS);\n\n this._bindContexts();\n this.onResize();\n this._addA11yAttributes();\n this._ensureBodyChild();\n }\n\n /**\n * Find descendent element by class.\n * @param {string} name Name of the class to find.\n * @return {?Element} The element or undefined.\n */\n getByClass(name) {\n return this.element.getElementsByClassName(name)[0];\n }\n\n /**\n * Bind `this` context to event handlers.\n */\n _bindContexts() {\n this.onKeyPress = this.onKeyPress.bind(this);\n this.onClick = this.onClick.bind(this);\n this.close = this.close.bind(this);\n // Bind undefined as the first parameter so that the event object will be\n // the second parameter and the optional viewportHeight parameter will work.\n this.onWindowResize = this.onResize.bind(this, undefined);\n }\n\n /**\n * Add static accessibility attributes so that the implementor can leave them\n * off or in case they forget.\n */\n _addA11yAttributes() {\n this.element.tabIndex = -1;\n this.element.setAttribute('aria-hidden', true);\n this.element.setAttribute('role', 'dialog');\n this.content.setAttribute('role', 'document');\n }\n\n /**\n * If the dialog element is not a direct descendent of the , make it so.\n */\n _ensureBodyChild() {\n if (this.element.parentNode !== document.body) {\n document.body.appendChild(this.element);\n }\n }\n\n /**\n * Determine the correct element to scroll fix and fix it.\n */\n _applyScrollFix() {\n // Allow the scrollable element to be something inside the dialog.\n if (this.options.scrollableElement) {\n const element = this.element.matches(this.options.scrollableElement) ?\n this.element :\n this.element.querySelector(this.options.scrollableElement);\n this._scrollFixId = ScrollFix.add(element);\n }\n }\n\n /**\n * If the page already has a scrollbar, adding overflow: hidden will remove it,\n * shifting the content to the right. To avoid this, there needs to be padding\n * on the body that's the same width as the scrollbar, but only when the dialog\n * will not have a scrollbar to take the page scrollbar's place.\n * @return {number}\n */\n _getScrollbarOffset() {\n const hasDialogScrollbar = this.element.scrollHeight > document.documentElement.clientHeight;\n return this._hasBodyScrollbar && !hasDialogScrollbar ? Dialog.SCROLLBAR_WIDTH : 0;\n }\n\n /**\n * Click handler on the main element. When the dialog is dismissable and the\n * user clicked outside the content (i.e. the backdrop), close it.\n * @param {Event} evt Event object.\n * @protected\n */\n onClick(evt) {\n if (this.options.dismissable && evt.target === this.element) {\n this.close();\n }\n }\n\n /**\n * Keypress event handler\n * @param {Event} evt Event object\n * @protected\n */\n onKeyPress(evt) {\n // If 'ESC' is pressed, close the dialog\n if (this.options.dismissable && evt.which === Dialog.Keys.ESC) {\n this.close();\n }\n\n // If the TAB key is being pressed, make sure the focus stays trapped within\n // the dialog element.\n if (evt.which === Dialog.Keys.TAB) {\n Dialog._trapTabKey(this.element, evt);\n }\n }\n\n /**\n * The dialog has a height of 100vh, which, in mobile safari, is incorrect\n * when the toolbars are visible, not allowing the user to scroll the full\n * height of the content within it.\n * The viewportHeight parameter is optional so that it can be read in the open()\n * method with all the other DOM reads. This avoids read->write->read #perfmatters.\n * @param {number} [viewportHeight=window.innerHeight] Height of the viewport.\n * @protected\n */\n onResize(viewportHeight = window.innerHeight) {\n this.element.style.height = viewportHeight + 'px';\n }\n\n /**\n * Checks to see if a dialog is already open or animating If not, opens dialog.\n * @param {boolean} [sync=false] Whether to open with transitions or not.\n */\n open(sync = false) {\n if (this.isAnimating || this.isOpen) {\n return;\n }\n\n const viewportHeight = window.innerHeight;\n Dialog.focusedBeforeDialog = document.activeElement;\n this._hasBodyScrollbar = document.body.clientWidth < window.innerWidth;\n this._isFullscreen = this.element.classList.contains(Dialog.Classes.FULLSCREEN);\n\n // Add aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('aria-hidden'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('data-odo-dialog-original', originals[i]);\n }\n element.setAttribute('aria-hidden', true);\n });\n\n this.isOpen = true;\n this.onResize(viewportHeight);\n this.element.removeAttribute('aria-hidden');\n this.element.classList.add(Dialog.Classes.OPEN);\n this.element.classList.add(Dialog.Classes.ENTER);\n if (Dialog.SCROLLBAR_WIDTH) {\n document.body.style.paddingRight = Dialog.SCROLLBAR_WIDTH + 'px';\n }\n document.body.classList.add(Dialog.Classes.BODY_OPEN);\n document.body.insertBefore(this.backdrop, this.element.nextSibling);\n this.element.scrollTop = 0;\n\n this._applyScrollFix();\n\n this.element.focus();\n\n document.addEventListener('keydown', this.onKeyPress);\n window.addEventListener('resize', this.onWindowResize);\n this.element.addEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.addEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._openNext();\n this._opened();\n } else {\n Dialog._nextFrame(() => {\n this._openNext();\n animation.onTransitionEnd(this.element, this._opened, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for opening the dialog.\n */\n _openNext() {\n this.isAnimating = true;\n // Now that the dialog is no longer display:none, the scrollHeight can be measured.\n const scrollbarOffset = this._getScrollbarOffset();\n if (!this._isFullscreen && scrollbarOffset > 0) {\n this.element.style.paddingRight = scrollbarOffset + 'px';\n }\n\n this.element.classList.remove(Dialog.Classes.ENTER);\n this.element.classList.add(Dialog.Classes.ENTERING);\n }\n\n /**\n * Handle the end of the open transition. Emits OPENED event.\n */\n _opened() {\n this.element.classList.remove(Dialog.Classes.ENTERING);\n this.element.classList.add(Dialog.Classes.VISIBLE);\n this.isAnimating = false;\n this.emit(Dialog.EventType.OPENED);\n }\n\n /**\n * Hides dialog\n * @param {boolean} [sync=false] Whether to close with transitions or not.\n */\n close(sync = false) {\n if (this.isAnimating || !this.isOpen) {\n return;\n }\n\n // Remove aria-hidden to other top-level things.\n const siblings = Dialog._getSiblings(this.element);\n const originals = siblings.map(element => element.getAttribute('data-odo-dialog-original'));\n siblings.forEach((element, i) => {\n if (originals[i]) {\n element.setAttribute('aria-hidden', originals[i]);\n element.removeAttribute('data-odo-dialog-original');\n } else {\n element.removeAttribute('aria-hidden');\n }\n });\n\n this.isOpen = false;\n this.element.classList.add(Dialog.Classes.LEAVE);\n this.element.classList.remove(Dialog.Classes.VISIBLE);\n\n ScrollFix.remove(this._scrollFixId);\n\n // Support: IE11\n // Clicking on an SVG element inside an will set the `focusedBeforeDialog`\n // to the SVG, but SVG doesn't have a `focus()` method in IE.\n if (Dialog.focusedBeforeDialog && typeof Dialog.focusedBeforeDialog.focus === 'function') {\n Dialog.focusedBeforeDialog.focus();\n }\n\n document.removeEventListener('keydown', this.onKeyPress);\n window.removeEventListener('resize', this.onWindowResize);\n this.element.removeEventListener('click', this.onClick);\n this._closers.forEach((element) => {\n element.removeEventListener('click', this.close);\n });\n\n if (sync === true) {\n this._closeNext();\n this._closed();\n } else {\n Dialog._nextFrame(() => {\n this._closeNext();\n animation.onTransitionEnd(this.element, this._closed, this, null, 1000);\n });\n }\n }\n\n /**\n * Start the transition for closing the dialog.\n */\n _closeNext() {\n this.isAnimating = true;\n this.element.classList.remove(Dialog.Classes.LEAVE);\n this.element.classList.add(Dialog.Classes.LEAVING);\n }\n\n /**\n * Handle the end of the close transition. Emits the CLOSED event.\n */\n _closed() {\n this.isAnimating = false;\n this.element.style.paddingRight = '';\n this.element.setAttribute('aria-hidden', true);\n this.element.classList.remove(Dialog.Classes.OPEN);\n this.element.classList.remove(Dialog.Classes.LEAVING);\n document.body.style.paddingRight = '';\n document.body.classList.remove(Dialog.Classes.BODY_OPEN);\n document.body.removeChild(this.backdrop);\n this.emit(Dialog.EventType.CLOSED);\n }\n\n /**\n * Close the dialog, remove event listeners and element references.\n */\n dispose() {\n if (this.isOpen) {\n this.close(true);\n }\n\n this.element = null;\n this.content = null;\n this.backdrop = null;\n this._closers.length = 0;\n\n array.remove(Dialog.Instances, this);\n\n // If this is the last dialog (being disposed), remove the body listener.\n if (Dialog.Instances.length === 0) {\n document.body.removeEventListener('click', Dialog._handleTriggerClick);\n }\n }\n\n /**\n * Call a function after two animation frames. Using just one is unreliable\n * when using animations to/from display:none elements or ones that are not\n * yet in the DOM.\n * @param {function} fn Function to call on the next frame.\n */\n static _nextFrame(fn) {\n window.requestAnimationFrame(window.requestAnimationFrame.bind(null, fn));\n }\n\n /**\n * Open the correct dialog when an element with `data-odo-dialog-open` attribute\n * is clicked.\n * @param {Event} evt Event object.\n */\n static _handleTriggerClick(evt) {\n const trigger = evt.target.closest('[data-odo-dialog-open]');\n\n if (trigger !== null) {\n evt.preventDefault();\n const id = trigger.getAttribute('data-odo-dialog-open');\n const instance = Dialog.getDialogById(id);\n instance.emit(Dialog.EventType.TRIGGER_CLICKED, trigger);\n instance.open();\n }\n }\n\n /**\n * Trap the focus inside the given element.\n * @param {Element} node\n * @param {Event} evt\n */\n static _trapTabKey(node, evt) {\n const focusableChildren = Dialog._getFocusableChildren(node);\n const focusedItemIndex = focusableChildren.indexOf(document.activeElement);\n\n // If the SHIFT key is being pressed while tabbing (moving backwards) and\n // the currently focused item is the first one, move the focus to the last\n // focusable item from the dialog element\n if (evt.shiftKey && focusedItemIndex === 0) {\n focusableChildren[focusableChildren.length - 1].focus();\n evt.preventDefault();\n // If the SHIFT key is not being pressed (moving forwards) and the currently\n // focused item is the last one, move the focus to the first focusable item\n // from the dialog element\n } else if (!evt.shiftKey && focusedItemIndex === focusableChildren.length - 1) {\n focusableChildren[0].focus();\n evt.preventDefault();\n }\n }\n\n /**\n * Get the focusable children of the given element.\n * @param {Element} element\n * @return {Array.}\n */\n static _getFocusableChildren(element) {\n return Array.from(element.querySelectorAll(FOCUSABLE_ELEMENTS))\n .filter(Dialog._isVisibleElement);\n }\n\n /**\n * Whether an element is visible (and therefore can receive focus). Uses\n * `getClientRects` due to this issue:\n * https://github.com/jquery/jquery/issues/2227\n * http://jsfiddle.net/2tgw2yr3/\n * @param {Element} el Element.\n * @return {boolean}\n */\n static _isVisibleElement(el) {\n return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n }\n\n /**\n * Retrieve the siblings of an element.\n * @param {Element} element Element to get siblings for.\n * @return {Array.}\n */\n static _getSiblings(element) {\n const children = Array.from(element.parentNode.children);\n const ignore = ['script', 'link', 'meta'];\n return children.filter(\n node => node !== element && !ignore.includes(node.nodeName.toLowerCase()));\n }\n\n /**\n * Calculate the width of the scrollbar because when the body has overflow:hidden,\n * the scrollbar disappears.\n * https://davidwalsh.name/detect-scrollbar-width\n * @return {number}\n */\n static _getScrollbarWidth() {\n // Create measurement node.\n const scrollDiv = document.createElement('div');\n scrollDiv.style.cssText = 'width:50px;height:50px;overflow:scroll;position:absolute;top:-9999px;';\n document.body.appendChild(scrollDiv);\n\n // Calculate the scrollbar width.\n const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;\n\n // Remove test element.\n document.body.removeChild(scrollDiv);\n\n return scrollbarWidth;\n }\n\n /**\n * Unfortunately, the auto margins do not work for flex children in IE11 and\n * below because the content element does have an explicit height set on it.\n * @return {boolean}\n */\n static _autoMarginTest() {\n const parent = document.createElement('div');\n const child = document.createElement('div');\n parent.style.cssText = 'display:flex;height:50px;width:50px;position:absolute;';\n child.style.cssText = 'margin:auto;';\n child.innerHTML = 'a';\n parent.appendChild(child);\n document.body.appendChild(parent);\n\n const ret = child.offsetTop > 0;\n document.body.removeChild(parent);\n\n return ret;\n }\n\n /**\n * Instantiates all instances of dialogs with the same settings\n * @param {Object} options Object of all dialog options. Is optional.\n * @return {Dialog[]}\n */\n static initializeAll(options) {\n Dialog.disposeAll();\n\n return Array.from(\n document.querySelectorAll('.' + Dialog.Classes.BASE),\n ).map(dialog => new Dialog(dialog, options));\n }\n\n /**\n * Clear all references to dialogs so there are no duplicates.\n */\n static disposeAll() {\n const clone = Dialog.Instances.slice();\n clone.forEach((dialog) => {\n dialog.dispose();\n });\n }\n\n /**\n * Retrieve a dialog instance by its id.\n * @param {string} id Id of the dialog.\n * @return {?Dialog} The dialog or undefined if there is no dialog with the given id.\n */\n static getDialogById(id) {\n return Dialog.Instances.find(instance => instance.id === id);\n }\n}\n\n/** @enum {string} */\nDialog.Classes = {\n BODY_OPEN: 'odo-dialog-open',\n BASE: 'odo-dialog',\n OPEN: 'odo-dialog--open',\n ENTER: 'odo-dialog--enter',\n ENTERING: 'odo-dialog--enter-active',\n LEAVE: 'odo-dialog--leave',\n LEAVING: 'odo-dialog--leave-active',\n VISIBLE: 'odo-dialog--visible',\n FULLSCREEN: 'odo-dialog--full',\n NO_AUTO_MARGIN: 'odo-dialog--no-auto-margin',\n BACKDROP: 'odo-dialog-backdrop',\n CONTENT: 'odo-dialog__content',\n};\n\n/** @enum {string} */\nDialog.EventType = {\n OPENED: 'ododialog:open',\n CLOSED: 'ododialog:closed',\n TRIGGER_CLICKED: 'ododialog:triggerclicked',\n};\n\n/** @enum {number} */\nDialog.Keys = {\n ESC: 27,\n TAB: 9,\n};\n\n/** @type {!Object} */\nDialog.Defaults = {\n dismissable: true,\n scrollableElement: '.odo-dialog',\n};\n\n/** @enum {Dialog[]} */\nDialog.Instances = [];\n\nDialog.ScrollFix = ScrollFix;\n\n/**\n * Element which had focus before the dialog opened.\n * @type {Element}\n */\nDialog.focusedBeforeDialog = null;\n\nDialog.SUPPORTS_AUTO_MARGINS = Dialog._autoMarginTest();\nDialog.SCROLLBAR_WIDTH = Dialog._getScrollbarWidth();\n\nexport default Dialog;\n"],"names":["body","document","ScrollFix","element","id","startY","scrollY","_createBoundEvents","_registerEvents","_touchStartBound","this","_onTouchStart","bind","_touchMoveBound","_onTouchMove","_preventDefaultBound","_preventDefault","addEventListener","evt","changedTouches","pageY","scrollTop","deltaY","offsetHeight","scrollHeight","preventDefault","stopPropagation","dispose","removeEventListener","Map","OdoDevice","HAS_TOUCH_EVENTS","string","random","_fixes","set","has","get","delete","FOCUSABLE_ELEMENTS","join","Dialog","opts","_TinyEmitter","Element","TypeError","options","Object","assign","Defaults","getAttribute","backdrop","createElement","className","Classes","BACKDROP","content","_this","getByClass","CONTENT","_closers","Array","from","querySelectorAll","_resizeId","_scrollFixId","isOpen","isAnimating","_hasBodyScrollbar","_originalBodyPadding","_isFullscreen","Instances","push","length","_handleTriggerClick","classList","toggle","NO_AUTO_MARGIN","SUPPORTS_AUTO_MARGINS","_bindContexts","onResize","_addA11yAttributes","_ensureBodyChild","name","getElementsByClassName","onKeyPress","onClick","close","onWindowResize","undefined","tabIndex","setAttribute","parentNode","appendChild","_applyScrollFix","scrollableElement","matches","querySelector","add","_getScrollbarOffset","hasDialogScrollbar","documentElement","clientHeight","SCROLLBAR_WIDTH","dismissable","target","which","Keys","ESC","TAB","_trapTabKey","viewportHeight","window","innerHeight","style","height","open","sync","focusedBeforeDialog","activeElement","clientWidth","innerWidth","contains","FULLSCREEN","siblings","_getSiblings","originals","map","forEach","i","removeAttribute","OPEN","ENTER","paddingRight","BODY_OPEN","insertBefore","nextSibling","focus","_this2","_openNext","_opened","_nextFrame","onTransitionEnd","scrollbarOffset","remove","ENTERING","VISIBLE","emit","EventType","OPENED","LEAVE","_this3","_closeNext","_closed","LEAVING","removeChild","CLOSED","fn","requestAnimationFrame","trigger","closest","instance","getDialogById","TRIGGER_CLICKED","node","focusableChildren","_getFocusableChildren","focusedItemIndex","indexOf","shiftKey","filter","_isVisibleElement","el","offsetWidth","getClientRects","ignore","children","includes","nodeName","toLowerCase","_getScrollbarWidth","scrollDiv","cssText","scrollbarWidth","_autoMarginTest","parent","child","innerHTML","ret","offsetTop","initializeAll","disposeAll","BASE","dialog","slice","find","TinyEmitter"],"mappings":"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"} \ No newline at end of file diff --git a/packages/odo-dialog/index.d.ts b/packages/odo-dialog/index.d.ts new file mode 100644 index 0000000..e06d8ed --- /dev/null +++ b/packages/odo-dialog/index.d.ts @@ -0,0 +1,182 @@ +// Type definitions for OdoDialog +// Project: odopod-code-library +// Definitions by: Glen Cheney + +export as namespace OdoDialog; + +export = OdoDialog; + +declare class OdoDialog { + /** + * Create a new dialog. + * @param {Element} element Main element which represents this class. + * @param {OdoDialog.Options} [opts] Instance options. + * @throws {TypeError} Throws when the element is not of type Element. + */ + constructor(element: Element, opts?: OdoDialog.Options); + + /** Main element for this class */ + element: Element; + + options: OdoDialog.Options; + + /** + * Id attribute from the main element. + * @type {string} + */ + id: string; + + /** + * Dialog backdrop element. + * @protected + * @type {Element} + */ + protected backdrop: Element; + + /** + * Dialog content (role=document) + * @protected + * @type {Element} + */ + protected content: Element; + + /** + * Whether the dialog is open. + * @type {boolean} + */ + isOpen: boolean; + + /** + * Whether the dialog is currently animating. + * @protected + * @type {boolean} + */ + protected isAnimating: boolean; + + /*~ + *~ From tiny emitter + *~ https://github.com/scottcorgan/tiny-emitter/blob/master/index.d.ts + */ + on (event: string, callback: Function, ctx?: any): OdoDialog; + once (event: string, callback: Function, ctx?: any): OdoDialog; + emit (event: string, ...args: any[]): OdoDialog; + off (event: string, callback?: Function): OdoDialog; + + /** + * Find descendent element by class. + * @param {string} name Name of the class to find. + * @return {Element} The element or undefined. + */ + getByClass(name: string): Element | undefined; + + /** + * Click handler on the main element. When the dialog is dismissable and the + * user clicked outside the content (i.e. the backdrop), close it. + * @param {Event} evt Event object. + * @protected + */ + protected onClick(evt: MouseEvent): void; + + /** + * Keypress event handler + * @param {Event} evt Event object + * @protected + */ + protected onKeyPress(evt: KeyboardEvent): void; + + /** + * The dialog has a height of 100vh, which, in mobile safari, is incorrect + * when the toolbars are visible, not allowing the user to scroll the full + * height of the content within it. + * The viewportHeight parameter is optional so that it can be read in the open() + * method with all the other DOM reads. This avoids read->write->read #perfmatters. + * @param {number} [viewportHeight=window.innerHeight] Height of the viewport. + * @protected + */ + protected onResize(viewportHeight?: number): void; + + /** + * Checks to see if a dialog is already open or animating If not, opens dialog. + * @param {boolean} [sync=false] Whether to open with transitions or not. + */ + open(sync?: false): void; + + /** + * Hides dialog + * @param {boolean} [sync=false] Whether to close with transitions or not. + */ + close(sync?: false): void; + + /** + * Close the dialog, remove event listeners and element references. + */ + dispose(): void; +} + +declare namespace OdoDialog { + export interface Options { + dismissable?: boolean; + scrollableElement?: string; + } + + /** + * Instantiates all instances of dialogs with the same settings + * @param {OdoDialog.Options} [options] Object of all dialog options. Is optional. + * @return {OdoDialog[]} + */ + static function initializeAll(options?: OdoDialog.Options): OdoDialog[]; + + /** + * Clear all references to dialogs so there are no duplicates. + */ + static function disposeAll(): void; + + /** + * Retrieve a dialog instance by its id. + * @param {string} id Id of the dialog. + * @return {OdoDialog} The dialog or undefined if there is no dialog with the given id. + */ + static function getDialogById(id: string): OdoDialog | undefined; + + /** + * Array of dialog instances. + * @type {OdoDialog[]} + */ + const Instances: OdoDialog[]; + + /** + * HTML class names for elements of the dialog. + */ + const Classes: { + [key: string]: string; + }; + + /** + * Events emitted by dialog instances. + */ + const EventType: { + OPENED: string, + CLOSED: string, + TRIGGER_CLICKED: string, + }; + + const Keys: { + ESC: number, + TAB: number, + }; + + /** + * Default options for each instance. + */ + const Defaults: OdoDialog.Options; + + /** + * Whether auto margins work for flex children in the current browser. + */ + const SUPPORTS_AUTO_MARGINS: boolean; + + /** + * Width of the scrollbar. + */ + const SCROLLBAR_WIDTH: number; +} diff --git a/packages/odo-dialog/package.json b/packages/odo-dialog/package.json index 94ab67d..a918b01 100644 --- a/packages/odo-dialog/package.json +++ b/packages/odo-dialog/package.json @@ -1,7 +1,7 @@ { "name": "@odopod/odo-dialog", "description": "Responsive window dialog component focused on a11y and extendibility", - "version": "1.0.0", + "version": "1.1.0", "main": "dist/odo-dialog.js", "style": "css/odo-dialog.css", "odoModule": "src/dialog.js", @@ -23,7 +23,7 @@ "css", "dist", "src", - "index.js" + "index.d.ts" ], "odoKeywords": [ "ui" diff --git a/packages/odo-dialog/src/dialog.js b/packages/odo-dialog/src/dialog.js index 1275c1a..8919c02 100644 --- a/packages/odo-dialog/src/dialog.js +++ b/packages/odo-dialog/src/dialog.js @@ -27,13 +27,13 @@ const FOCUSABLE_ELEMENTS = [ '[tabindex]:not([tabindex^="-"])', ].join(','); -/** - * Dialog that can contain static images, carousels, or videos - * @param {Element} element Main element. - * - * @constructor - */ class Dialog extends TinyEmitter { + /** + * Dialog that can contain static images, carousels, or videos + * @param {Element} element Main element. + * @param {object} [opts] Instance options. + * @constructor + */ constructor(element, opts) { super(); @@ -575,8 +575,7 @@ class Dialog extends TinyEmitter { } /** - * Clear all references to dialogs so there are no duplicates - * @param {Object} options Object of all dialog options. Is optional. + * Clear all references to dialogs so there are no duplicates. */ static disposeAll() { const clone = Dialog.Instances.slice();