diff options
Diffstat (limited to 'node_modules/materialize-css/js/modal.js')
-rw-r--r-- | node_modules/materialize-css/js/modal.js | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/node_modules/materialize-css/js/modal.js b/node_modules/materialize-css/js/modal.js new file mode 100644 index 0000000000..9f8b2facf8 --- /dev/null +++ b/node_modules/materialize-css/js/modal.js @@ -0,0 +1,382 @@ +(function($, anim) { + 'use strict'; + + let _defaults = { + opacity: 0.5, + inDuration: 250, + outDuration: 250, + onOpenStart: null, + onOpenEnd: null, + onCloseStart: null, + onCloseEnd: null, + preventScrolling: true, + dismissible: true, + startingTop: '4%', + endingTop: '10%' + }; + + /** + * @class + * + */ + class Modal extends Component { + /** + * Construct Modal instance and set up overlay + * @constructor + * @param {Element} el + * @param {Object} options + */ + constructor(el, options) { + super(Modal, el, options); + + this.el.M_Modal = this; + + /** + * Options for the modal + * @member Modal#options + * @prop {Number} [opacity=0.5] - Opacity of the modal overlay + * @prop {Number} [inDuration=250] - Length in ms of enter transition + * @prop {Number} [outDuration=250] - Length in ms of exit transition + * @prop {Function} onOpenStart - Callback function called before modal is opened + * @prop {Function} onOpenEnd - Callback function called after modal is opened + * @prop {Function} onCloseStart - Callback function called before modal is closed + * @prop {Function} onCloseEnd - Callback function called after modal is closed + * @prop {Boolean} [dismissible=true] - Allow modal to be dismissed by keyboard or overlay click + * @prop {String} [startingTop='4%'] - startingTop + * @prop {String} [endingTop='10%'] - endingTop + */ + this.options = $.extend({}, Modal.defaults, options); + + /** + * Describes open/close state of modal + * @type {Boolean} + */ + this.isOpen = false; + + this.id = this.$el.attr('id'); + this._openingTrigger = undefined; + this.$overlay = $('<div class="modal-overlay"></div>'); + this.el.tabIndex = 0; + this._nthModalOpened = 0; + + Modal._count++; + this._setupEventHandlers(); + } + + static get defaults() { + return _defaults; + } + + static init(els, options) { + return super.init(this, els, options); + } + + /** + * Get Instance + */ + static getInstance(el) { + let domElem = !!el.jquery ? el[0] : el; + return domElem.M_Modal; + } + + /** + * Teardown component + */ + destroy() { + Modal._count--; + this._removeEventHandlers(); + this.el.removeAttribute('style'); + this.$overlay.remove(); + this.el.M_Modal = undefined; + } + + /** + * Setup Event Handlers + */ + _setupEventHandlers() { + this._handleOverlayClickBound = this._handleOverlayClick.bind(this); + this._handleModalCloseClickBound = this._handleModalCloseClick.bind(this); + + if (Modal._count === 1) { + document.body.addEventListener('click', this._handleTriggerClick); + } + this.$overlay[0].addEventListener('click', this._handleOverlayClickBound); + this.el.addEventListener('click', this._handleModalCloseClickBound); + } + + /** + * Remove Event Handlers + */ + _removeEventHandlers() { + if (Modal._count === 0) { + document.body.removeEventListener('click', this._handleTriggerClick); + } + this.$overlay[0].removeEventListener('click', this._handleOverlayClickBound); + this.el.removeEventListener('click', this._handleModalCloseClickBound); + } + + /** + * Handle Trigger Click + * @param {Event} e + */ + _handleTriggerClick(e) { + let $trigger = $(e.target).closest('.modal-trigger'); + if ($trigger.length) { + let modalId = M.getIdFromTrigger($trigger[0]); + let modalInstance = document.getElementById(modalId).M_Modal; + if (modalInstance) { + modalInstance.open($trigger); + } + e.preventDefault(); + } + } + + /** + * Handle Overlay Click + */ + _handleOverlayClick() { + if (this.options.dismissible) { + this.close(); + } + } + + /** + * Handle Modal Close Click + * @param {Event} e + */ + _handleModalCloseClick(e) { + let $closeTrigger = $(e.target).closest('.modal-close'); + if ($closeTrigger.length) { + this.close(); + } + } + + /** + * Handle Keydown + * @param {Event} e + */ + _handleKeydown(e) { + // ESC key + if (e.keyCode === 27 && this.options.dismissible) { + this.close(); + } + } + + /** + * Handle Focus + * @param {Event} e + */ + _handleFocus(e) { + // Only trap focus if this modal is the last model opened (prevents loops in nested modals). + if (!this.el.contains(e.target) && this._nthModalOpened === Modal._modalsOpen) { + this.el.focus(); + } + } + + /** + * Animate in modal + */ + _animateIn() { + // Set initial styles + $.extend(this.el.style, { + display: 'block', + opacity: 0 + }); + $.extend(this.$overlay[0].style, { + display: 'block', + opacity: 0 + }); + + // Animate overlay + anim({ + targets: this.$overlay[0], + opacity: this.options.opacity, + duration: this.options.inDuration, + easing: 'easeOutQuad' + }); + + // Define modal animation options + let enterAnimOptions = { + targets: this.el, + duration: this.options.inDuration, + easing: 'easeOutCubic', + // Handle modal onOpenEnd callback + complete: () => { + if (typeof this.options.onOpenEnd === 'function') { + this.options.onOpenEnd.call(this, this.el, this._openingTrigger); + } + } + }; + + // Bottom sheet animation + if (this.el.classList.contains('bottom-sheet')) { + $.extend(enterAnimOptions, { + bottom: 0, + opacity: 1 + }); + anim(enterAnimOptions); + + // Normal modal animation + } else { + $.extend(enterAnimOptions, { + top: [this.options.startingTop, this.options.endingTop], + opacity: 1, + scaleX: [0.8, 1], + scaleY: [0.8, 1] + }); + anim(enterAnimOptions); + } + } + + /** + * Animate out modal + */ + _animateOut() { + // Animate overlay + anim({ + targets: this.$overlay[0], + opacity: 0, + duration: this.options.outDuration, + easing: 'easeOutQuart' + }); + + // Define modal animation options + let exitAnimOptions = { + targets: this.el, + duration: this.options.outDuration, + easing: 'easeOutCubic', + // Handle modal ready callback + complete: () => { + this.el.style.display = 'none'; + this.$overlay.remove(); + + // Call onCloseEnd callback + if (typeof this.options.onCloseEnd === 'function') { + this.options.onCloseEnd.call(this, this.el); + } + } + }; + + // Bottom sheet animation + if (this.el.classList.contains('bottom-sheet')) { + $.extend(exitAnimOptions, { + bottom: '-100%', + opacity: 0 + }); + anim(exitAnimOptions); + + // Normal modal animation + } else { + $.extend(exitAnimOptions, { + top: [this.options.endingTop, this.options.startingTop], + opacity: 0, + scaleX: 0.8, + scaleY: 0.8 + }); + anim(exitAnimOptions); + } + } + + /** + * Open Modal + * @param {cash} [$trigger] + */ + open($trigger) { + if (this.isOpen) { + return; + } + + this.isOpen = true; + Modal._modalsOpen++; + this._nthModalOpened = Modal._modalsOpen; + + // Set Z-Index based on number of currently open modals + this.$overlay[0].style.zIndex = 1000 + Modal._modalsOpen * 2; + this.el.style.zIndex = 1000 + Modal._modalsOpen * 2 + 1; + + // Set opening trigger, undefined indicates modal was opened by javascript + this._openingTrigger = !!$trigger ? $trigger[0] : undefined; + + // onOpenStart callback + if (typeof this.options.onOpenStart === 'function') { + this.options.onOpenStart.call(this, this.el, this._openingTrigger); + } + + if (this.options.preventScrolling) { + document.body.style.overflow = 'hidden'; + } + + this.el.classList.add('open'); + this.el.insertAdjacentElement('afterend', this.$overlay[0]); + + if (this.options.dismissible) { + this._handleKeydownBound = this._handleKeydown.bind(this); + this._handleFocusBound = this._handleFocus.bind(this); + document.addEventListener('keydown', this._handleKeydownBound); + document.addEventListener('focus', this._handleFocusBound, true); + } + + anim.remove(this.el); + anim.remove(this.$overlay[0]); + this._animateIn(); + + // Focus modal + this.el.focus(); + + return this; + } + + /** + * Close Modal + */ + close() { + if (!this.isOpen) { + return; + } + + this.isOpen = false; + Modal._modalsOpen--; + this._nthModalOpened = 0; + + // Call onCloseStart callback + if (typeof this.options.onCloseStart === 'function') { + this.options.onCloseStart.call(this, this.el); + } + + this.el.classList.remove('open'); + + // Enable body scrolling only if there are no more modals open. + if (Modal._modalsOpen === 0) { + document.body.style.overflow = ''; + } + + if (this.options.dismissible) { + document.removeEventListener('keydown', this._handleKeydownBound); + document.removeEventListener('focus', this._handleFocusBound, true); + } + + anim.remove(this.el); + anim.remove(this.$overlay[0]); + this._animateOut(); + return this; + } + } + + /** + * @static + * @memberof Modal + */ + Modal._modalsOpen = 0; + + /** + * @static + * @memberof Modal + */ + Modal._count = 0; + + M.Modal = Modal; + + if (M.jQueryLoaded) { + M.initializeJqueryWrapper(Modal, 'modal', 'M_Modal'); + } +})(cash, M.anime); |