salix/front/core/components/dialog/index.js

148 lines
4.2 KiB
JavaScript

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: {
message: '@?',
onResponse: '&?',
onAccept: '&?'
}
});