mirror of
https://github.com/VTECRM/vtenext.git
synced 2026-02-26 16:18:47 +00:00
375 lines
9.2 KiB
JavaScript
375 lines
9.2 KiB
JavaScript
/*************************************
|
|
* SPDX-FileCopyrightText: 2009-2020 Vtenext S.r.l. <info@vtenext.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
************************************/
|
|
(function($, Vel) {
|
|
'use strict';
|
|
|
|
let _defaults = {
|
|
opacity: 0.5,
|
|
inDuration: 250,
|
|
outDuration: 250,
|
|
ready: undefined,
|
|
complete: undefined,
|
|
dismissible: true,
|
|
startingTop: '4%',
|
|
endingTop: '10%'
|
|
};
|
|
|
|
|
|
/**
|
|
* @class
|
|
*
|
|
*/
|
|
class Modal {
|
|
/**
|
|
* Construct Modal instance and set up overlay
|
|
* @constructor
|
|
* @param {jQuery} $el
|
|
* @param {Object} options
|
|
*/
|
|
constructor($el, options) {
|
|
|
|
// If exists, destroy and reinitialize
|
|
if (!!$el[0].M_Modal) {
|
|
$el[0].M_Modal.destroy();
|
|
}
|
|
|
|
/**
|
|
* The jQuery element
|
|
* @type {jQuery}
|
|
*/
|
|
this.$el = $el;
|
|
|
|
/**
|
|
* 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} ready - Callback function called when modal is finished entering
|
|
* @prop {Function} complete - Callback function called when modal is finished exiting
|
|
* @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.$el[0].M_Modal = this;
|
|
this.id = $el.attr('id');
|
|
this.openingTrigger = undefined;
|
|
this.$overlay = $('<div class="modal-overlay"></div>');
|
|
|
|
Modal._increment++;
|
|
Modal._count++;
|
|
this.$overlay[0].style.zIndex = 1000 + Modal._increment * 2;
|
|
this.$el[0].style.zIndex = 1000 + Modal._increment * 2 + 1;
|
|
this.setupEventHandlers();
|
|
}
|
|
|
|
static get defaults() {
|
|
return _defaults;
|
|
}
|
|
|
|
static init($els, options) {
|
|
let arr = [];
|
|
$els.each(function() {
|
|
arr.push(new Modal($(this), options));
|
|
});
|
|
return arr;
|
|
}
|
|
|
|
/**
|
|
* Get Instance
|
|
*/
|
|
getInstance() {
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Teardown component
|
|
*/
|
|
destroy() {
|
|
this.removeEventHandlers();
|
|
this.$el[0].removeAttribute('style')
|
|
if (!!this.$overlay[0].parentNode) {
|
|
this.$overlay[0].parentNode.removeChild(this.$overlay[0]);
|
|
}
|
|
this.$el[0].M_Modal = undefined;
|
|
Modal._count--;
|
|
}
|
|
|
|
/**
|
|
* 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[0].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[0].removeEventListener('click', this.handleModalCloseClickBound);
|
|
}
|
|
|
|
/**
|
|
* Handle Trigger Click
|
|
* @param {Event} e
|
|
*/
|
|
handleTriggerClick(e) {
|
|
let $trigger = $(e.target).closest('.modal-trigger');
|
|
if (e.target && $trigger.length) {
|
|
let modalId = $trigger[0].getAttribute('href');
|
|
if (modalId) {
|
|
modalId = modalId.slice(1);
|
|
} else {
|
|
modalId = $trigger[0].getAttribute('data-target');
|
|
}
|
|
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 (e.target && $closeTrigger.length) {
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle Keydown
|
|
* @param {Event} e
|
|
*/
|
|
handleKeydown(e) {
|
|
// ESC key
|
|
if (e.keyCode === 27 && this.options.dismissible) {
|
|
this.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Animate in modal
|
|
*/
|
|
animateIn() {
|
|
// Set initial styles
|
|
$.extend(this.$el[0].style, {
|
|
display: 'block',
|
|
opacity: 0
|
|
});
|
|
$.extend(this.$overlay[0].style, {
|
|
display: 'block',
|
|
opacity: 0
|
|
});
|
|
|
|
// Animate overlay
|
|
Vel(
|
|
this.$overlay[0],
|
|
{opacity: this.options.opacity},
|
|
{duration: this.options.inDuration, queue: false, ease: 'easeOutCubic'}
|
|
);
|
|
|
|
|
|
// Define modal animation options
|
|
let enterVelocityOptions = {
|
|
duration: this.options.inDuration,
|
|
queue: false,
|
|
ease: 'easeOutCubic',
|
|
// Handle modal ready callback
|
|
complete: () => {
|
|
if (typeof(this.options.ready) === 'function') {
|
|
this.options.ready.call(this, this.$el, this.openingTrigger);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Bottom sheet animation
|
|
if (this.$el[0].classList.contains('bottom-sheet')) {
|
|
Vel(
|
|
this.$el[0],
|
|
{bottom: 0, opacity: 1},
|
|
enterVelocityOptions);
|
|
|
|
// Normal modal animation
|
|
} else {
|
|
Vel.hook(this.$el[0], 'scaleX', 0.7);
|
|
this.$el[0].style.top = this.options.startingTop;
|
|
Vel(
|
|
this.$el[0],
|
|
{top: this.options.endingTop, opacity: 1, scaleX: 1},
|
|
enterVelocityOptions
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Animate out modal
|
|
*/
|
|
animateOut() {
|
|
// Animate overlay
|
|
Vel(
|
|
this.$overlay[0],
|
|
{ opacity: 0},
|
|
{duration: this.options.outDuration, queue: false, ease: 'easeOutQuart'}
|
|
);
|
|
|
|
// Define modal animation options
|
|
var exitVelocityOptions = {
|
|
duration: this.options.outDuration,
|
|
queue: false,
|
|
ease: 'easeOutCubic',
|
|
// Handle modal ready callback
|
|
complete: () => {
|
|
this.$el[0].style.display = 'none';
|
|
// Call complete callback
|
|
if (typeof(this.options.complete) === 'function') {
|
|
this.options.complete.call(this, this.$el);
|
|
}
|
|
this.$overlay[0].parentNode.removeChild(this.$overlay[0]);
|
|
}
|
|
};
|
|
|
|
// Bottom sheet animation
|
|
if (this.$el[0].classList.contains('bottom-sheet')) {
|
|
Vel(
|
|
this.$el[0],
|
|
{bottom: '-100%', opacity: 0},
|
|
exitVelocityOptions
|
|
);
|
|
|
|
// Normal modal animation
|
|
} else {
|
|
Vel(
|
|
this.$el[0],
|
|
{top: this.options.startingTop, opacity: 0, scaleX: 0.7},
|
|
exitVelocityOptions
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Open Modal
|
|
* @param {jQuery} [$trigger]
|
|
*/
|
|
open($trigger) {
|
|
if (this.isOpen) {
|
|
return;
|
|
}
|
|
|
|
this.isOpen = true;
|
|
let body = document.body;
|
|
body.style.overflow = 'hidden';
|
|
this.$el[0].classList.add('open');
|
|
body.appendChild(this.$overlay[0]);
|
|
|
|
// Set opening trigger, undefined indicates modal was opened by javascript
|
|
this.openingTrigger = !!$trigger ? $trigger : undefined;
|
|
|
|
|
|
if (this.options.dismissible) {
|
|
this.handleKeydownBound = this.handleKeydown.bind(this);
|
|
document.addEventListener('keydown', this.handleKeydownBound);
|
|
}
|
|
|
|
this.animateIn();
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Close Modal
|
|
*/
|
|
close() {
|
|
if (!this.isOpen) {
|
|
return;
|
|
}
|
|
|
|
this.isOpen = false;
|
|
this.$el[0].classList.remove('open');
|
|
document.body.style.overflow = '';
|
|
|
|
if (this.options.dismissible) {
|
|
document.removeEventListener('keydown', this.handleKeydownBound);
|
|
}
|
|
|
|
this.animateOut();
|
|
|
|
return this;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @static
|
|
* @memberof Modal
|
|
*/
|
|
Modal._increment = 0;
|
|
|
|
/**
|
|
* @static
|
|
* @memberof Modal
|
|
*/
|
|
Modal._count = 0;
|
|
|
|
Materialize.Modal = Modal;
|
|
|
|
$.fn.modal = function(methodOrOptions) {
|
|
// Call plugin method if valid method name is passed in
|
|
if (Modal.prototype[methodOrOptions]) {
|
|
// Getter methods
|
|
if (methodOrOptions.slice(0,3) === 'get') {
|
|
return this.first()[0].M_Modal[methodOrOptions]();
|
|
|
|
// Void methods
|
|
} else {
|
|
return this.each(function() {
|
|
this.M_Modal[methodOrOptions]();
|
|
});
|
|
}
|
|
|
|
// Initialize plugin if options or no argument is passed in
|
|
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
|
|
Modal.init(this, arguments[0]);
|
|
return this;
|
|
|
|
// Return error if an unrecognized method name is passed in
|
|
} else {
|
|
$.error(`Method ${methodOrOptions} does not exist on jQuery.modal`);
|
|
}
|
|
};
|
|
|
|
})(jQuery, Materialize.Vel); |