vuetify-dialog-promise
Version:
A Vue plugin with a Promise API for [Vuetify](https://www.npmjs.com/package/vuetify) dialogs and snackbar notifications.
289 lines (268 loc) • 8.17 kB
JavaScript
import { createVNode, ref, render } from 'vue';
import { createI18n } from "vue-i18n";
import dlogI18n from "./i18n";
import SimpleDialog from './components/SimpleDialog';
import SimpleSnackbar from './components/SimpleSnackbar';
const defaults = {
};
/**
* App instance that dialogs & snackbar is supposed to be mounted on
*/
let appInstance;
/**
* Registry of snackbars at different corners so we can display several.
*/
const snackbars = []
/**
* Show a dialog of type "alert", "confirm", or "prompt." Returned promise is resolved with the dialog result when the
* user dismisses or completes it. Message can be a string or an Object with the following properties:
*
* title : {string},
* text : {string},
* acceptText : {string}
* cancelText : {string},
* theme : {string} Vuetify theme, see https://vuetifyjs.com/en/features/theme/
* snackbarParent : {string?} If provided, snackbars will be mounted under the element with this ID, else under the
* node of the caller element.
*
* @param type {string<"alert","confirm","prompt">}
* @param message {string|Object}
* @returns {Promise<any>}
* @private
*/
function _showDialog( type, message )
{
return new Promise( ( resolve, reject ) =>
{
new Promise( _resolve =>
{
if( typeof message === "string" )
{
message = {
title : "",
text : message
};
}
const _message = {};
Object.assign( _message, defaults, message );
const vNode = createVNode(SimpleDialog, {
type: type,
message: _message,
resolve: _resolve
});
vNode.appContext = appInstance._context;
render(vNode, appInstance._container.firstElementChild);
} ).then( result =>
{
setTimeout( () => {
render(null, appInstance._container.firstElementChild)
}, 300 );
if( result === false )
{
reject();
}
else
{
resolve( result );
}
} );
} );
}
/**
* Show a snackbar with the default color and message. If message is an Object, it controls the properties of the
* snackbar:
*
* text : {string}
* color : {string}
* snackbarTimeout {integer}
* snackbarX : {string<"left"|"right">}
* snackbarY : {string<"top"|"bottom">}
*
* @param color
* @param message
* @private
*/
function _showSnackbar( color, message )
{
if( typeof message === "string" )
{
message = {
text : message
}
}
const _message = {
color : color
};
Object.assign( _message, defaults, message );
const pos = ref({
height: 0,
width: 0,
top_left : 0,
top_right : 0,
top_center : 0,
bottom_left : 0,
bottom_right : 0,
bottom_center : 0,
snackbar_x : _message.snackbarX || 'right',
snackbar_y : _message.snackbarY || 'top',
snackbar_href : _message.href
});
snackbars.push( pos.value );
_message.position = pos;
const pNode = defaults.snackbarParent ? document.getElementById( defaults.snackbarParent ) : this.$el;
const mountEl = document.createElement('div');
pNode.appendChild(mountEl);
const vNode = createVNode(SimpleSnackbar, {
..._message,
onClose: () => {
pNode.removeChild(mountEl);
render(null, mountEl);
snackbars.splice( snackbars.indexOf( pos.value ), 1 );
_computeOffsets();
}
});
vNode.appContext = appInstance._context
render(vNode, mountEl);
_addBar( mountEl.firstElementChild, pos.value );
}
/**
* Add a bar and recompute offsets.
*
* @param sbar
* @param pos
* @private
*/
function _addBar( sbarEl, pos )
{
const r = sbarEl.getBoundingClientRect();
pos.height = r.height;
pos.width = r.width;
_computeOffsets();
}
/**
* Recompute position of each bar.
*
* @private
*/
function _computeOffsets()
{
const offsets = {
top_left : 0,
top_right : 0,
top_center : 0,
bottom_left : 0,
bottom_right : 0,
bottom_center : 0
}
for( let i = 0; i < snackbars.length; i++ )
{
const snackbar = snackbars[ i ];
offsets[ `${snackbar.snackbar_y}_${snackbar.snackbar_x}` ] += snackbar.height + 10;
Object.assign( snackbar, offsets );
}
}
/**
* Raise a snackbar with the "info" colour.
*
* @param message
* @private
*/
function _inform( message)
{
_showSnackbar.bind( this, "info" )( message );
}
/**
* Raise a snackbar with the "warn" colour.
*
* @param message
* @private
*/
function _warn( message )
{
_showSnackbar.bind( this, "warning" )( message );
}
/**
* Raise a snackbar with the "error" colour.
*
* @param message
* @private
*/
function _error( message )
{
_showSnackbar.bind( this, "error" )( message );
}
/**
* Plugin which provides the following methods for use in components:
*
* $alert( message ).then( result => handler )
* $confirm( message ).then( result => handler )
* $prompt( message ).then( result => handler )
* $inform( message )
* $warn( message )
* $error( message )
*
* See DialogPromiseDemo in this module for full documentation.
*
* @type {{install(*, *=): void, name: string}}
*/
const DialogPromise = {
name : "DialogPromise",
/**
* Options:
* - locale : {string}, default "en" - Locale identifier
* - acceptText : {string}, default from locale - Dialog accept button label
* - cancelText : {string}, default from locale - Dialog cancel button label
* - closeText : {string}, default from locale - Snackbar close button label
* - snackbarX : {"left"|"right"|"center"} - Snackbar position, default "right"
* - snackbarY : {"top"|"bottom"} - Snackbar vertical position, default "top"
* - snackbarTimeout : {integer} - Snackbar duration in millis, default 3000
* - dialogMaxWidth : {integer} - Dialog max width in pixels, default 500
* - theme : {Object} - Vuetify theme, see https://vuetifyjs.com/en/customization/theme (default is default theme)
*
* @param app {App<Element>}
* @param options {object}
*/
install( app, options )
{
appInstance = app;
options = options || {};
const i18n = createI18n({
locale: options.locale || "en",
fallbackLocale: "en",
messages: dlogI18n
});
Object.assign( defaults, {
acceptText : i18n.global.t( "message.Accept" ),
cancelText : i18n.global.t( "message.Cancel" ),
closeText : i18n.global.t( "message.Close" ),
snackbarX : "right",
snackbarY : "top",
snackbarTimeout : 3000,
dialogMaxWidth : 500,
theme: "light",
}, options );
app.config.globalProperties.$alert = _showDialog.bind( this, "alert" );
app.config.globalProperties.$confirm = _showDialog.bind( this, "confirm" );
app.config.globalProperties.$prompt = _showDialog.bind( this, "prompt" );
app.config.globalProperties.$inform = _inform;
app.config.globalProperties.$warn = _warn;
app.config.globalProperties.$error = _error;
if( document && document.head && !document._vuetify_dialog_promise_styles )
{
const el = document.createElement( "style" );
el.innerText = ".vdp-message {\n" +
" white-space: pre-wrap;\n" +
" font-size: 14px;\n" +
"}\n" +
"\n" +
".vdp-fake-close-icon {\n" +
" font-size: 18px !important;\n" +
" padding-left: 8px;\n" +
" margin-top: -4px;\n" +
"}\n";
document.head.appendChild( el );
document._vuetify_dialog_promise_styles = el;
}
}
};
export default DialogPromise;