import ngModule from '../../module'; import Popup from '../popup'; import './style.scss'; /** * Dialog component that allows to register function handlers for responses. If * any of the handlers returns false or a promise that resolves to false, * the dialog closing is cancelled. Also, if promise is returned, the dialog * will wait until it is resolved by locking itself and displaying a loading * animation. * * @property {Function} onResponse Handler for dialog response * @property {Function} onAccept Shortcut handler for accept response * @slot body The dialog HTML body * @slot buttons The dialog HTML buttons */ export default class Dialog extends Popup { /** * Shows the dialog and optionally registers a handler for the response. * * @param {*} data Optional user data to pass to response handler * @param {Function} responseHandler An optional response handler * @return {Promise} A promise that will be resolved with response when dialog is closed */ show(data, responseHandler) { if (this.shown) return this.$q.reject(new Error('Dialog already shown')); super.show(); if (typeof data == 'function') { responseHandler = data; data = null; } this.data = data; this.showHandler = responseHandler; return this.$q(resolve => { this.resolve = resolve; }); } /** * Hides the dialog resolving the promise returned by show(). * * @param {String} response The response */ hide(response) { if (!this.shown) return; super.hide(); this.showHandler = null; if (this.resolve) this.resolve(response); } /** * Calls the response handlers. * * @param {String} response The response code * @return {Boolean} The response handler return */ respond(response) { if (!this.shown) return this.$q.resolve(); return this.responseHandler(response); } /** * The default response handler, it can be overriden by child classes to * add custom logic. * * @param {String} response The response code * @return {Boolean} The response handler return */ responseHandler(response) { let handlerArgs = { $response: response, $data: this.data }; let cancellers = []; if (this.onResponse) cancellers.push(this.onResponse(handlerArgs)); if (response == 'accept' && this.onAccept) cancellers.push(this.onAccept(handlerArgs)); if (this.showHandler) cancellers.push(this.showHandler(response, this.data)); let promises = []; let resolvedCancellers = []; for (let canceller of cancellers) { if (canceller instanceof Object && canceller.then) promises.push(canceller); else resolvedCancellers.push(canceller); } let close = () => { if (resolvedCancellers.indexOf(false) == -1) this.hide(response); else return false; }; if (promises.length) { this.loading = true; return this.$q.all(promises) .then(res => { resolvedCancellers = resolvedCancellers.concat(res); return close(); }) .finally(() => { this.loading = false; }); } else return this.$q.resolve(close()); } onButtonClick(event) { let buttons = this.popup.querySelector('.buttons'); let tplButtons = buttons.querySelector('tpl-buttons'); let node = event.target; while (node.parentNode != tplButtons) { if (node == buttons) return; node = node.parentNode; } this.respond(node.getAttribute('response')); } } ngModule.vnComponent('vnDialog', { slotTemplate: require('./index.html'), controller: Dialog, transclude: { title: '?tplTitle', body: '?tplBody', buttons: '?tplButtons' }, bindings: { onResponse: '&?', onAccept: '&?' } });