vuetify3-dialog
Version:
Vue 3 & Vuetify 3 plugin to create dialogs, toasts and bottom-sheets with Promises.
685 lines (670 loc) • 27.1 kB
JavaScript
import { defineComponent, computed, openBlock, createBlock, unref, mergeProps, withCtx, createVNode, createTextVNode, toDisplayString, createElementBlock, Fragment, renderList, ref, watch, isRef, createCommentVNode, h, render, resolveDynamicComponent } from 'vue';
import { VCard, VCardTitle, VIcon, VCardText, VCardActions, VBtn, VBottomSheet, VList, VListItem, VDialog, VLayout, VSnackbar } from 'vuetify/lib/components/index.mjs';
class PluginContext {
static pluginOptions;
static app;
constructor(app, _pluginOptions) {
if (!app)
throw new Error('Error during initialization : app is required');
PluginContext.app = app;
const vuetify = app._context.mixins.find((mixin) => mixin.computed?.$vuetify);
if (!vuetify)
throw new Error('Error during initialization : vuetify is required. Please declare it with Vue.use(Vuetify)');
if (_pluginOptions)
PluginContext.pluginOptions = _pluginOptions;
}
static getPluginOptions() {
return PluginContext.pluginOptions;
}
static getApp() {
return PluginContext.app;
}
}
var script$3 = /*#__PURE__*/ defineComponent({
__name: 'Card',
props: {
title: {
type: String,
required: true
},
text: {
type: String,
required: true
},
buttons: {
type: Array,
},
icon: {
type: String,
default: ''
},
level: {
type: String,
default: 'info'
},
cardOptions: {
type: Object,
default: () => ({})
}
},
emits: ['buttonClicked'],
setup(__props, { emit: __emit }) {
const props = __props;
// ------- EVENTS -------
const emit = __emit;
// ------- COMPUTED -------
const _buttons = computed(() => {
if (props.buttons && props.buttons.length > 0)
return props.buttons;
else
return [
{ key: 'cancel', title: 'Annuler', value: 'cancel', color: 'grey', variant: 'text' },
{ key: 'ok', title: 'OK', value: 'ok', color: props.level, variant: 'tonal' }
];
});
const _icon = computed(() => {
if (props.icon)
return props.icon;
switch (props.level) {
case 'info':
return 'mdi-information';
case 'warning':
return 'mdi-alert';
case 'error':
return 'mdi-alert-circle';
case 'success':
return 'mdi-check-circle';
default:
return 'mdi-information';
}
});
const _color = computed(() => {
return props.level === 'info' ? 'primary' : props.level;
});
// ------- METHODS -------
function close(buttonKey) {
emit('buttonClicked', buttonKey);
}
return (_ctx, _cache) => {
return (openBlock(), createBlock(unref(VCard), mergeProps({ class: "vuetify3-dialog-card" }, __props.cardOptions), {
default: withCtx(() => [
createVNode(unref(VCardTitle), { class: "d-flex align-center" }, {
default: withCtx(() => [
createVNode(unref(VIcon), {
color: _color.value,
class: "mr-2"
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(_icon.value), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["color"]),
createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}),
createVNode(unref(VCardText), null, {
default: withCtx(() => [
createTextVNode(toDisplayString(__props.text), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}),
createVNode(unref(VCardActions), null, {
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(_buttons.value, (button) => {
return (openBlock(), createBlock(unref(VBtn), mergeProps({
key: button.key
}, button, {
color: button.color || _color.value,
onClick: ($event) => (close(button.key))
}), {
default: withCtx(() => [
createTextVNode(toDisplayString(button.title), 1 /* TEXT */)
]),
_: 2 /* DYNAMIC */
}, 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["color", "onClick"]));
}), 128 /* KEYED_FRAGMENT */))
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}, 16 /* FULL_PROPS */));
};
}
});
script$3.__file = "src/components/Card.vue";
var script$2 = /*#__PURE__*/ defineComponent({
__name: 'BottomSheet',
props: {
bottomSheetOptions: {
type: Object,
default: () => ({})
},
dialogOptions: {
type: Object,
required: false
},
items: {
type: Array,
required: false
},
title: {
type: String,
required: false
},
text: {
type: String,
required: false
}
},
emits: ['closeBottomSheet'],
setup(__props, { emit: __emit }) {
const props = __props;
// ------- EVENTS -------
const emit = __emit;
// ------- DATA -------
let showBottomSheet = ref(true);
// ------- COMPUTED -------
const _items = computed(() => {
if (props.items && props.items.length > 0)
return props.items;
else
return [];
});
// ------- WATCH -------
watch(() => showBottomSheet, (val) => {
if (!val)
emit('closeBottomSheet');
});
// ------- METHODS -------
function close(value) {
showBottomSheet.value = false;
emit('closeBottomSheet', value);
}
return (_ctx, _cache) => {
return (openBlock(), createBlock(unref(VBottomSheet), mergeProps({ class: "vuetify3-dialog-bottom-sheet" }, __props.bottomSheetOptions, {
modelValue: unref(showBottomSheet),
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => (isRef(showBottomSheet) ? (showBottomSheet).value = $event : showBottomSheet = $event))
}), {
default: withCtx(() => [
(!__props.dialogOptions)
? (openBlock(), createBlock(unref(VCard), { key: 0 }, {
default: withCtx(() => [
(__props.title)
? (openBlock(), createBlock(unref(VCardTitle), { key: 0 }, {
default: withCtx(() => [
createTextVNode(toDisplayString(__props.title), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}))
: createCommentVNode("v-if", true),
(__props.text)
? (openBlock(), createBlock(unref(VCardText), { key: 1 }, {
default: withCtx(() => [
createTextVNode(toDisplayString(__props.text), 1 /* TEXT */)
]),
_: 1 /* STABLE */
}))
: createCommentVNode("v-if", true),
(__props.items)
? (openBlock(), createBlock(unref(VList), { key: 2 }, {
default: withCtx(() => [
(openBlock(true), createElementBlock(Fragment, null, renderList(_items.value, (item) => {
return (openBlock(), createBlock(unref(VListItem), {
title: item.title,
key: item.value,
onClick: ($event) => (close(item.value))
}, null, 8 /* PROPS */, ["title", "onClick"]));
}), 128 /* KEYED_FRAGMENT */))
]),
_: 1 /* STABLE */
}))
: createCommentVNode("v-if", true)
]),
_: 1 /* STABLE */
}))
: (openBlock(), createBlock(script$3, mergeProps({ key: 1 }, __props.dialogOptions, {
title: __props.dialogOptions.title,
text: __props.dialogOptions.text,
onButtonClicked: close
}), null, 16 /* FULL_PROPS */, ["title", "text"]))
]),
_: 1 /* STABLE */
}, 16 /* FULL_PROPS */, ["modelValue"]));
};
}
});
script$2.__file = "src/components/BottomSheet.vue";
class BottomSheets {
static initContext() {
PluginContext.getApp().config.globalProperties.$bottomSheet = {
create: createBottomSheet,
createList: createBottomSheetList,
};
}
}
function createBottomSheetList(items, options) {
items.forEach((item) => {
if (!isNotEmptyAndNotNull$2(item.title))
throw new Error('title is required for each item');
if (!isNotEmptyAndNotNull$2(item.value))
throw new Error('value is required for each item');
});
return createBottomSheet({
items,
...options,
});
}
function createBottomSheet(options) {
try {
if (options.items && options.dialogOptions) {
throw new Error('You can not use items and dialogOptions together');
}
if (options.dialogOptions) {
if (!isNotEmptyAndNotNull$2(options.dialogOptions.title))
throw new Error('title is required');
if (!isNotEmptyAndNotNull$2(options.dialogOptions.text))
throw new Error('text is required');
}
const div = document.createElement('div');
return new Promise((resolve, reject) => {
const props = {
bottomSheetOptions: options?.bottomSheetOptions ?? PluginContext.getPluginOptions()?.defaults?.bottomSheet,
dialogOptions: options?.dialogOptions,
items: options?.items,
title: options?.title,
text: options?.text,
onCloseBottomSheet: (value) => {
resolve(value);
},
};
const vNode = h(script$2, props);
vNode.appContext = PluginContext.getApp()._context;
render(vNode, div);
});
}
catch (err) {
console.error(`[Vuetify3Dialog] ${err.message} [${err.stack}]`);
}
}
function isNotEmptyAndNotNull$2(value) {
if (value === undefined || value === null)
return false;
return typeof value === 'boolean' ? true : value.trim().length > 0 && value !== '';
}
var script$1 = /*#__PURE__*/ defineComponent({
__name: 'Dialog',
props: {
title: {
type: String,
required: true
},
text: {
type: String,
required: true
},
buttons: {
type: Array,
},
icon: {
type: String,
default: ''
},
level: {
type: String,
default: 'info'
},
cardOptions: {
type: Object,
default: () => ({})
},
dialogOptions: {
type: Object,
default: () => ({})
},
customComponent: {
type: Object,
required: false
}
},
emits: ['closeDialog'],
setup(__props, { emit: __emit }) {
// ------- EVENTS -------
const emit = __emit;
// ------- DATA -------
let showDialog = ref(true);
// ------- METHODS -------
function close(buttonKey) {
showDialog.value = false;
emit('closeDialog', buttonKey);
}
// ------- WATCH ---------
watch((showDialog), (newValue) => {
if (!newValue) {
close(false);
}
});
return (_ctx, _cache) => {
return (openBlock(), createBlock(unref(VDialog), mergeProps({
class: "vuetify3-dialog-popup",
modelValue: unref(showDialog),
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => (isRef(showDialog) ? (showDialog).value = $event : showDialog = $event))
}, __props.dialogOptions), {
default: withCtx(() => [
(__props.customComponent)
? (openBlock(), createBlock(resolveDynamicComponent(__props.customComponent.component), mergeProps({ key: 0 }, __props.customComponent.props, {
onCloseDialog: close,
ref: "custom-component"
}), null, 16 /* FULL_PROPS */))
: (openBlock(), createBlock(script$3, mergeProps({ key: 1 }, __props.cardOptions, {
title: __props.title,
text: __props.text,
buttons: __props.buttons,
icon: __props.icon,
level: __props.level,
onButtonClicked: close
}), null, 16 /* FULL_PROPS */, ["title", "text", "buttons", "icon", "level"]))
]),
_: 1 /* STABLE */
}, 16 /* FULL_PROPS */, ["modelValue"]));
};
}
});
script$1.__file = "src/components/Dialog.vue";
class Dialogs {
static initContext() {
PluginContext.getApp().config.globalProperties.$dialog = {
create: createDialog,
confirm: confirmDialog,
warning: warningDialog,
error: errorDialog,
info: infoDialog,
success: successDialog,
};
}
}
function createDialog(options) {
try {
const div = document.createElement('div');
if (!options.customComponent) {
if (!isNotEmptyAndNotNull$1(options.title))
throw new Error('title is required');
if (!isNotEmptyAndNotNull$1(options.text))
throw new Error('text is required');
}
else {
options.title = options.title ?? '';
options.text = options.text ?? '';
}
if (options.buttons) {
options.buttons.forEach(validateButton);
}
return new Promise((resolve, reject) => {
const props = {
title: options.title,
text: options.text,
buttons: options.buttons,
icon: options.icon,
level: options.level,
customComponent: options.customComponent,
dialogOptions: options.dialogOptions ??
PluginContext.getPluginOptions()?.defaults?.dialog?.component ?? {
width: '400px',
},
cardOptions: options.cardOptions ?? PluginContext.getPluginOptions()?.defaults?.dialog?.card ?? undefined,
onCloseDialog: (value) => {
resolve(value);
},
};
const vNode = h(script$1, props);
vNode.appContext = PluginContext.getApp()._context;
render(vNode, div);
});
}
catch (err) {
console.error(`[Vuetify3Dialog] ${err.message} [${err.stack}]`);
}
}
function warningDialog(options) {
return createDialog({
title: options.title ?? 'Warning',
text: options.text,
icon: options.icon,
buttons: [{ key: 'ok', title: 'OK', color: 'warning', ...options.buttonOptions }],
level: 'warning',
cardOptions: options.cardOptions,
});
}
function errorDialog(options) {
return createDialog({
title: options.title ?? 'Error',
text: options.text,
icon: options.icon,
buttons: [{ key: 'ok', title: 'OK', color: 'error', ...options.buttonOptions }],
level: 'error',
cardOptions: options.cardOptions,
});
}
function infoDialog(options) {
return createDialog({
title: options.title ?? 'Info',
text: options.text,
icon: options.icon,
buttons: [{ key: 'ok', title: 'OK', color: 'info', ...options.buttonOptions }],
level: 'info',
cardOptions: options.cardOptions,
});
}
function successDialog(options) {
return createDialog({
title: options.title ?? 'Success',
text: options.text,
icon: options.icon,
buttons: [{ key: 'ok', title: 'OK', color: 'success', ...options.buttonOptions }],
level: 'success',
cardOptions: options.cardOptions,
});
}
function confirmDialog(options) {
return createDialog({
title: options.title,
text: options.text,
buttons: [
{ key: false, title: options.cancelText ?? 'Cancel', color: 'grey', ...options.cancelButtonOptions },
{
key: true,
title: options.confirmationText ?? 'Confirm',
color: options.level ?? 'primary',
...options.confirmationButtonOptions,
},
],
icon: options.icon,
level: options.level,
cardOptions: options.cardOptions,
});
}
function validateButton(button, index) {
if (!button) {
throw new Error(`button at index ${index} is not defined`);
}
if (!isNotEmptyAndNotNull$1(button.key)) {
throw new Error(`button at index ${index} has no key`);
}
if (!isNotEmptyAndNotNull$1(button.title)) {
throw new Error(`button at index ${index} has no title`);
}
}
function isNotEmptyAndNotNull$1(value) {
if (value === undefined || value === null)
return false;
return typeof value === 'boolean' ? true : value.trim().length > 0 && value !== '';
}
const _hoisted_1 = { key: 0 };
const _hoisted_2 = ["innerHTML"];
var script = /*#__PURE__*/ defineComponent({
__name: 'Snackbar',
props: {
text: {
type: String,
required: false
},
htmlContent: {
type: String,
required: false
},
location: {
type: String,
required: true
},
level: {
type: String,
default: 'info'
},
notifyOptions: {
type: Object,
default: () => ({})
}
},
emits: ['closeSnackbar'],
setup(__props, { emit: __emit }) {
// ------- EVENTS -------
const emit = __emit;
// ------- DATA -------
let showSnackbar = ref(true);
// ------- METHODS -------
function close() {
showSnackbar.value = false;
emit('closeSnackbar');
}
return (_ctx, _cache) => {
return (openBlock(), createBlock(unref(VLayout), null, {
default: withCtx(() => [
createVNode(unref(VSnackbar), mergeProps({ class: "vuetify3-dialog-snackbar" }, __props.notifyOptions, {
modelValue: unref(showSnackbar),
"onUpdate:modelValue": [
_cache[0] || (_cache[0] = ($event) => (isRef(showSnackbar) ? (showSnackbar).value = $event : showSnackbar = $event)),
_cache[1] || (_cache[1] = ($event) => (close()))
],
color: __props.level,
location: __props.location,
"location-strategy": "static",
dark: __props.level === 'warning' || __props.level === 'error'
}), {
default: withCtx(() => [
(__props.text)
? (openBlock(), createElementBlock("span", _hoisted_1, toDisplayString(__props.text), 1 /* TEXT */))
: createCommentVNode("v-if", true),
(__props.htmlContent)
? (openBlock(), createElementBlock("div", {
key: 1,
innerHTML: __props.htmlContent
}, null, 8 /* PROPS */, _hoisted_2))
: createCommentVNode("v-if", true)
]),
_: 1 /* STABLE */
}, 16 /* FULL_PROPS */, ["modelValue", "color", "location", "dark"])
]),
_: 1 /* STABLE */
}));
};
}
});
script.__file = "src/components/Snackbar.vue";
class SnackBar {
static initContext() {
PluginContext.getApp().config.globalProperties.$notify = {
create: createNotification,
warning: notifyWarning,
error: notifyError,
info: notifyInfo,
success: notifySuccess,
};
}
}
function notifyWarning(text, notifyOptions) {
return createNotification({ text, level: 'warning', notifyOptions });
}
function notifyError(text, notifyOptions) {
return createNotification({ text, level: 'error', notifyOptions });
}
function notifyInfo(text, notifyOptions) {
return createNotification({ text, level: 'info', notifyOptions });
}
function notifySuccess(text, notifyOptions) {
return createNotification({ text, level: 'success', notifyOptions });
}
function createNotification(options) {
try {
const potentialLocation = options.location ??
options.notifyOptions?.location ??
PluginContext.getPluginOptions()?.defaults?.notify?.location ??
'top right';
let locationY = potentialLocation.split(' ')[0] ?? 'top';
let locationX = potentialLocation.split(' ')[1] ?? 'right';
let div = document.createElement('div');
if (!isNotEmptyAndNotNull(options.text) && !isNotEmptyAndNotNull(options.htmlContent))
throw new Error('text or htmlContent is required');
return new Promise((resolve, reject) => {
const props = {
text: options.text,
htmlContent: options.htmlContent,
level: options.level,
location: potentialLocation,
notifyOptions: options.notifyOptions ?? PluginContext.getPluginOptions()?.defaults?.notify,
onCloseSnackbar: () => {
resolve(true);
},
};
const vNode = h(script, props);
vNode.appContext = PluginContext.getApp()._context;
render(vNode, div);
const vuetifyDivOverlay = document.querySelector('.v-overlay-container');
let margin = 0;
if (vuetifyDivOverlay?.childElementCount > 1) {
for (let child of vuetifyDivOverlay.children) {
if (child === vuetifyDivOverlay.lastElementChild ||
!(child.classList.contains('v-snackbar--' + locationX) &&
child.classList.contains('v-snackbar--' + locationY)))
continue;
if (child.lastElementChild) {
margin += child.lastElementChild.offsetHeight + 12;
}
}
}
if (margin > 0) {
switch (locationY) {
case 'top':
(vuetifyDivOverlay?.lastElementChild).style.marginTop = `${margin + 12}px`;
break;
case 'bottom':
(vuetifyDivOverlay?.lastElementChild).style.marginBottom = `${margin + 12}px`;
break;
default:
throw new Error('location must be top or bottom');
}
}
});
}
catch (err) {
console.error(`[Vuetify3Dialog] ${err.message} [${err.stack}]`);
}
}
function isNotEmptyAndNotNull(value) {
return value !== undefined && value !== null && value.trim().length > 0 && value !== '';
}
const Vuetify3Dialog = {
install(app, options) {
try {
new PluginContext(app, options);
Dialogs.initContext();
SnackBar.initContext();
BottomSheets.initContext();
}
catch (err) {
console.error(`[Vuetify3Dialog] ${err.message} [${err.stack}]`);
}
},
};
export { Vuetify3Dialog, confirmDialog, createBottomSheet, createBottomSheetList, createDialog, createNotification, errorDialog, infoDialog, notifyError, notifyInfo, notifySuccess, notifyWarning, successDialog, warningDialog };
//# sourceMappingURL=index.js.map