Fixed #2653 - New Component: DynamicDialog
parent
cdf42edd14
commit
6e608144b9
|
@ -0,0 +1,15 @@
|
|||
const DynamicDialogProps = [];
|
||||
|
||||
const DynamicDialogEvents = [];
|
||||
|
||||
const DynamicDialogSlots = [];
|
||||
|
||||
module.exports = {
|
||||
dynamicdialog: {
|
||||
name: "DynamicDialog",
|
||||
description: "Dialogs can be created dynamically with any component as the content using a DialogService.",
|
||||
props: DynamicDialogProps,
|
||||
events: DynamicDialogEvents,
|
||||
slots: DynamicDialogSlots
|
||||
}
|
||||
};
|
|
@ -56,7 +56,7 @@
|
|||
"gulp-uglify": "^3.0.2",
|
||||
"gulp-uglifycss": "^1.0.6",
|
||||
"sass": "^1.45.0",
|
||||
"primeflex": "3.1.2",
|
||||
"primeflex": "3.2.1",
|
||||
"primeicons": "5.0.0",
|
||||
"prismjs": "^1.15.0",
|
||||
"quill": "^1.3.7",
|
||||
|
|
|
@ -20,9 +20,11 @@ let coreDependencies = {
|
|||
'primevue/confirmationeventbus': 'primevue.confirmationeventbus',
|
||||
'primevue/toasteventbus': 'primevue.toasteventbus',
|
||||
'primevue/overlayeventbus': 'primevue.overlayeventbus',
|
||||
'primevue/dynamicdialogeventbus': 'primevue.dynamicdialogeventbus',
|
||||
'primevue/terminalservice': 'primevue.terminalservice',
|
||||
'primevue/useconfirm': 'primevue.useconfirm',
|
||||
'primevue/usetoast': 'primevue.usetoast',
|
||||
'primevue/usedialog': 'primevue.usedialog',
|
||||
'primevue/button': 'primevue.button',
|
||||
'primevue/inputtext': 'primevue.inputtext',
|
||||
'primevue/inputnumber': 'primevue.inputnumber',
|
||||
|
@ -182,6 +184,9 @@ function addServices() {
|
|||
addEntry('overlayeventbus', 'OverlayEventBus.js', 'overlayeventbus');
|
||||
addEntry('usetoast', 'UseToast.js', 'usetoast');
|
||||
addEntry('terminalservice', 'TerminalService.js', 'terminalservice');
|
||||
addEntry('usedialog', 'UseDialog.js', 'usedialog');
|
||||
addEntry('dialogservice', 'DialogService.js', 'dialogservice');
|
||||
addEntry('dynamicdialogeventbus', 'DynamicDialogEventBus.js', 'dynamicdialogeventbus');
|
||||
}
|
||||
|
||||
addUtils();
|
||||
|
|
|
@ -580,6 +580,10 @@
|
|||
"name": "Dialog",
|
||||
"to": "/dialog"
|
||||
},
|
||||
{
|
||||
"name": "DynamicDialog",
|
||||
"to": "/dynamicdialog"
|
||||
},
|
||||
{
|
||||
"name": "OverlayPanel",
|
||||
"to": "/overlaypanel"
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import { Plugin, VNode } from 'vue';
|
||||
import { DynamicDialogOptions } from '../dynamicdialogoptions';
|
||||
|
||||
declare const plugin: Plugin;
|
||||
export default plugin;
|
||||
|
||||
export interface DialogInstance {
|
||||
/**
|
||||
* Dynamic component for content template
|
||||
*/
|
||||
content: VNode | undefined;
|
||||
/**
|
||||
* Instance options
|
||||
* @see DynamicDialogOptions
|
||||
*/
|
||||
options: DynamicDialogOptions;
|
||||
/**
|
||||
* Custom data object
|
||||
*/
|
||||
data: any;
|
||||
/**
|
||||
* Hides the dialog.
|
||||
* @param {*} params - Parameters sent by the user to the root instance
|
||||
*/
|
||||
hide: (params?: any) => void;
|
||||
}
|
||||
|
||||
export interface DialogServiceMethods {
|
||||
/**
|
||||
* Displays the dialog using the dynamic dialog object options.
|
||||
* @param {VNode} content - Dynamic component for content template
|
||||
* @param {DynamicDialogOptions} options - DynamicDialog Object
|
||||
*/
|
||||
open(content: VNode, options?: DynamicDialogOptions): DialogInstance;
|
||||
}
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$dialog: DialogServiceMethods;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$dialog: DialogServiceMethods;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { markRaw } from 'vue';
|
||||
import { PrimeVueDialogSymbol } from 'primevue/usedialog';
|
||||
import DynamicDialogEventBus from 'primevue/dynamicdialogeventbus';
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
const DialogService = {
|
||||
open: (content, options) => {
|
||||
const instance = {
|
||||
content: content && markRaw(content),
|
||||
options: options || {},
|
||||
data: options && options.data,
|
||||
hide: (params) => {
|
||||
DynamicDialogEventBus.emit('close', { instance, params });
|
||||
}
|
||||
}
|
||||
|
||||
DynamicDialogEventBus.emit('open', { instance });
|
||||
|
||||
return instance;
|
||||
}
|
||||
};
|
||||
|
||||
app.config.unwrapInjectedRef = true; // Remove it after Vue 3.3. Details: https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity
|
||||
app.config.globalProperties.$dialog = DialogService;
|
||||
app.provide(PrimeVueDialogSymbol, DialogService);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"main": "./dialogservice.cjs.js",
|
||||
"module": "./dialogservice.esm.js",
|
||||
"unpkg": "./dialogservice.min.js",
|
||||
"types": "./DialogService.d.ts"
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
|
||||
|
||||
export interface DynamicDialogProps {}
|
||||
|
||||
export declare type DynamicDialogEmits = {}
|
||||
|
||||
export interface DynamicDialogSlots {}
|
||||
|
||||
declare class DynamicDialog extends ClassComponent<DynamicDialogProps, DynamicDialogSlots, DynamicDialogEmits> { }
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface GlobalComponents {
|
||||
DynamicDialog: GlobalComponentConstructor<DynamicDialog>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* DynamicDialogs can be created dynamically with any component as the content using a DialogService.
|
||||
*
|
||||
* Demos:
|
||||
*
|
||||
* - [DynamicDialog](https://www.primefaces.org/primevue/showcase/#/dynamicdialog)
|
||||
*
|
||||
*/
|
||||
export default DynamicDialog;
|
|
@ -0,0 +1,76 @@
|
|||
<template>
|
||||
<template v-for="(instance, key) in instanceMap" :key="key">
|
||||
<DDialog :_instance="instance" v-bind="instance.options.props" v-model:visible="instance.visible" @hide="onDialogHide(instance)" @after-hide="onDialogAfterHide">
|
||||
<template #header v-if="instance.options.templates && instance.options.templates.header">
|
||||
<component :is="header" v-for="(header, index) in getTemplateItems(instance.options.templates.header)" :key="index + '_header'"></component>
|
||||
</template>
|
||||
<component :is="instance.content"></component>
|
||||
<template #footer v-if="instance.options.templates && instance.options.templates.footer">
|
||||
<component :is="footer" v-for="(footer, index) in getTemplateItems(instance.options.templates.footer)" :key="index + '_footer'"></component>
|
||||
</template>
|
||||
</DDialog>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ObjectUtils, UniqueComponentId } from 'primevue/utils';
|
||||
import DynamicDialogEventBus from 'primevue/dynamicdialogeventbus';
|
||||
import Dialog from 'primevue/dialog';
|
||||
|
||||
export default {
|
||||
name: 'DynamicDialog',
|
||||
inheritAttrs: false,
|
||||
data() {
|
||||
return {
|
||||
instanceMap: {}
|
||||
}
|
||||
},
|
||||
openListener: null,
|
||||
closeListener: null,
|
||||
currentInstance: null,
|
||||
mounted() {
|
||||
this.openListener = ({ instance }) => {
|
||||
const key = UniqueComponentId() + '_dynamic_dialog';
|
||||
|
||||
instance.visible = true;
|
||||
instance.key = key;
|
||||
this.instanceMap[key] = instance;
|
||||
};
|
||||
|
||||
this.closeListener = ({ instance, params }) => {
|
||||
const key = instance.key;
|
||||
const currentInstance = this.instanceMap[key];
|
||||
|
||||
if (currentInstance) {
|
||||
currentInstance.visible = false;
|
||||
currentInstance.options.onHide && currentInstance.options.onHide({ data: params, type: 'config-close' });
|
||||
|
||||
this.currentInstance = currentInstance;
|
||||
}
|
||||
};
|
||||
|
||||
DynamicDialogEventBus.on('open', this.openListener);
|
||||
DynamicDialogEventBus.on('close', this.closeListener);
|
||||
},
|
||||
beforeUnmount() {
|
||||
DynamicDialogEventBus.off('open', this.openListener);
|
||||
DynamicDialogEventBus.off('close', this.closeListener);
|
||||
},
|
||||
methods: {
|
||||
onDialogHide(instance) {
|
||||
!this.currentInstance && instance.options.onHide && instance.options.onHide({ type: 'dialog-close' });
|
||||
},
|
||||
onDialogAfterHide() {
|
||||
this.currentInstance && (delete this.currentInstance);
|
||||
this.currentInstance = null;
|
||||
},
|
||||
getTemplateItems(template) {
|
||||
const items = ObjectUtils.getItemValue(template);
|
||||
return Array.isArray(items) ? items : [items];
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'DDialog': Dialog
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"main": "./dynamicdialog.cjs.js",
|
||||
"module": "./dynamicdialog.esm.js",
|
||||
"unpkg": "./dynamicdialog.min.js",
|
||||
"types": "./DynamicDialog.d.ts",
|
||||
"browser": {
|
||||
"./sfc": "./DynamicDialog.vue"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { EventBus } from 'primevue/utils';
|
||||
|
||||
export default EventBus();
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"main": "./dynamicdialogeventbus.cjs.js",
|
||||
"module": "./dynamicdialogeventbus.esm.js",
|
||||
"unpkg": "./dynamicdialogeventbus.min.js"
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { VNode } from 'vue';
|
||||
import { DialogProps } from '../dialog';
|
||||
|
||||
export type DynamicDialogHideType = 'config-close' | 'dialog-close' | undefined;
|
||||
|
||||
export interface DynamicDialogTemplates {
|
||||
/**
|
||||
* Custom header template.
|
||||
*/
|
||||
header: () => VNode[];
|
||||
/**
|
||||
* Custom footer template.
|
||||
*/
|
||||
footer: () => VNode[];
|
||||
}
|
||||
|
||||
export interface DynamicDialogHideOptions {
|
||||
/**
|
||||
* Custom data object
|
||||
*/
|
||||
data?: any;
|
||||
/**
|
||||
* Hide type
|
||||
* @see DynamicDialogHideType
|
||||
*/
|
||||
type: DynamicDialogHideType;
|
||||
}
|
||||
|
||||
export interface DynamicDialogOptions {
|
||||
/**
|
||||
* Dialog Props
|
||||
* @see DialogProps
|
||||
*/
|
||||
props?: DialogProps;
|
||||
/**
|
||||
* Dialog Slots
|
||||
* @see DynamicDialogTemplates
|
||||
*/
|
||||
templates?: DynamicDialogTemplates;
|
||||
/**
|
||||
* Custom data object
|
||||
*/
|
||||
data?: any;
|
||||
/**
|
||||
* Hides the dialog.
|
||||
* @see DynamicDialogHideOptions
|
||||
*/
|
||||
onHide?: (options?: DynamicDialogHideOptions) => void;
|
||||
/**
|
||||
* Optional
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"types": "./DynamicDialogOptions.d.ts"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
//import { DynamicDialogOptions } from '../dynamicdialogoptions';
|
||||
|
||||
export declare function useDialog(): {
|
||||
open: (dialog: any) => void;
|
||||
close: () => void;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { inject } from 'vue';
|
||||
|
||||
export const PrimeVueDialogSymbol = Symbol();
|
||||
|
||||
export function useDialog() {
|
||||
const PrimeVueDialog = inject(PrimeVueDialogSymbol);
|
||||
if (!PrimeVueDialog) {
|
||||
throw new Error('No PrimeVue Dialog provided!');
|
||||
}
|
||||
|
||||
return PrimeVueDialog;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"main": "./usedialog.cjs.js",
|
||||
"module": "./usedialog.esm.js",
|
||||
"unpkg": "./usedialog.min.js",
|
||||
"types": "./UseDialog.d.ts"
|
||||
}
|
|
@ -86,6 +86,10 @@ export default {
|
|||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
},
|
||||
|
||||
getItemValue(obj, ...params) {
|
||||
return this.isFunction(obj) ? obj(...params) : obj;
|
||||
},
|
||||
|
||||
filter(value, fields, filterValue) {
|
||||
var filteredItems = [];
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ import DataView from './components/dataview/DataView';
|
|||
import DataViewLayoutOptions from './components/dataviewlayoutoptions/DataViewLayoutOptions';
|
||||
import DeferredContent from './components/deferredcontent/DeferredContent';
|
||||
import Dialog from './components/dialog/Dialog';
|
||||
import DynamicDialog from './components/dynamicdialog/DynamicDialog';
|
||||
import DialogService from './components/dialogservice/DialogService';
|
||||
import Divider from './components/divider/Divider';
|
||||
import Dock from './components/dock/Dock';
|
||||
import Dropdown from './components/dropdown/Dropdown';
|
||||
|
@ -128,6 +130,7 @@ app.config.globalProperties.$appState = reactive({theme: 'lara-light-blue', dark
|
|||
app.use(PrimeVue, {ripple: true});
|
||||
app.use(ToastService);
|
||||
app.use(ConfirmationService);
|
||||
app.use(DialogService);
|
||||
app.use(router);
|
||||
|
||||
app.directive('badge', BadgeDirective);
|
||||
|
@ -163,6 +166,7 @@ app.component('DataView', DataView);
|
|||
app.component('DataViewLayoutOptions', DataViewLayoutOptions);
|
||||
app.component('DeferredContent', DeferredContent);
|
||||
app.component('Dialog', Dialog);
|
||||
app.component('DynamicDialog', DynamicDialog);
|
||||
app.component('Divider', Divider);
|
||||
app.component('Dock', Dock);
|
||||
app.component('Dropdown', Dropdown);
|
||||
|
|
|
@ -349,6 +349,11 @@ const routes = [
|
|||
name: 'dropdown',
|
||||
component: () => import('../views/dropdown/DropdownDemo.vue')
|
||||
},
|
||||
{
|
||||
path: '/dynamicdialog',
|
||||
name: 'dynamicdialog',
|
||||
component: () => import('../views/dynamicdialog/DynamicDialogDemo.vue')
|
||||
},
|
||||
{
|
||||
path: '/editor',
|
||||
name: 'editor',
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="content-section introduction">
|
||||
<div class="feature-intro">
|
||||
<h1>Dynamic Dialog</h1>
|
||||
<p>Dialogs can be created dynamically with any component as the content using a DialogService.</p>
|
||||
</div>
|
||||
<AppDemoActions />
|
||||
</div>
|
||||
|
||||
<div class="content-section implementation">
|
||||
<div class="card">
|
||||
<Button label="Show" @click="onShow" />
|
||||
|
||||
<DynamicDialog />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProductListDemo from './ProductListDemo';
|
||||
import Button from 'primevue/button';
|
||||
import { h } from 'vue';
|
||||
|
||||
export default {
|
||||
methods:{
|
||||
onShow() {
|
||||
const dialogRef = this.$dialog.open(ProductListDemo, {
|
||||
props: {
|
||||
header: 'Product List',
|
||||
style: {
|
||||
width: '50%',
|
||||
height: '550px',
|
||||
},
|
||||
modal: true
|
||||
},
|
||||
templates: {
|
||||
footer: () => {
|
||||
return [
|
||||
h(Button, { label: "No", icon: "pi pi-times", onClick: () => dialogRef.hide({ buttonType: 'No' }), class: "p-button-text" }),
|
||||
h(Button, { label: "Yes", icon: "pi pi-check", onClick: () => dialogRef.hide({ buttonType: 'Yes' }), autofocus: true })
|
||||
]
|
||||
}
|
||||
},
|
||||
onHide: (options) => {
|
||||
const data = options.data;
|
||||
if (data) {
|
||||
const buttonType = data.buttonType;
|
||||
const summary_and_detail = buttonType ? { summary: 'No Product Selected', detail: `Pressed '${buttonType}' button` } : { summary: 'Product Selected', detail: data.name };
|
||||
|
||||
this.$toast.add({ severity:'info', ...summary_and_detail, life: 3000 });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div class="flex flex-column align-items-center justify-content-center row-gap-3">
|
||||
<p>There are <strong>{{totalProducts}}</strong> products in total in this list.</p>
|
||||
<Button type="button" label="Close" @click="closeDialog"></Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['dialogRef'],
|
||||
data() {
|
||||
return {
|
||||
totalProducts: 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.totalProducts = this.dialogRef.data.totalProducts;
|
||||
},
|
||||
methods: {
|
||||
closeDialog() {
|
||||
this.dialogRef.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<div>
|
||||
<Button label="Info" icon="pi pi-info-circle" class="p-button-sm p-button-outlined p-button-info my-2" @click="onInfoShow" />
|
||||
<DataTable :value="products" responsiveLayout="scroll">
|
||||
<Column field="code" header="Code"></Column>
|
||||
<Column field="name" header="Name"></Column>
|
||||
<Column field="category" header="Category"></Column>
|
||||
<Column field="quantity" header="Quantity"></Column>
|
||||
<Column>
|
||||
<template #body="slotProps">
|
||||
<Button type="button" icon="pi pi-cog" @click="closeDialog(slotProps.data)"></Button>
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ProductService from '../../service/ProductService';
|
||||
import InfoDemo from './InfoDemo.vue';
|
||||
|
||||
export default {
|
||||
inject: ['dialogRef'],
|
||||
data() {
|
||||
return {
|
||||
products: null
|
||||
}
|
||||
},
|
||||
productService: null,
|
||||
created() {
|
||||
this.productService = new ProductService();
|
||||
},
|
||||
mounted() {
|
||||
this.productService.getProductsSmall().then(data => this.products = data);
|
||||
},
|
||||
methods: {
|
||||
closeDialog(data) {
|
||||
this.dialogRef.hide(data);
|
||||
},
|
||||
onInfoShow() {
|
||||
this.$dialog.open(InfoDemo, {
|
||||
props: {
|
||||
header: 'Information',
|
||||
modal: true,
|
||||
dismissableMask: true
|
||||
},
|
||||
data: {
|
||||
totalProducts: this.products ? this.products.length : 0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -10,6 +10,7 @@ module.exports = {
|
|||
'primevue/tooltip': path.resolve(__dirname, 'src/components/tooltip/Tooltip.js'),
|
||||
'primevue/useconfirm': path.resolve(__dirname, 'src/components/useconfirm/UseConfirm.js'),
|
||||
'primevue/usetoast': path.resolve(__dirname, 'src/components/usetoast/UseToast.js'),
|
||||
'primevue/usedialog': path.resolve(__dirname, 'src/components/usedialog/UseDialog.js'),
|
||||
'primevue/utils': path.resolve(__dirname, 'src/components/utils/Utils.js'),
|
||||
'primevue/api': path.resolve(__dirname, 'src/components/api/Api.js'),
|
||||
'primevue/portal': path.resolve(__dirname, 'src/components/portal/Portal.vue'),
|
||||
|
@ -28,6 +29,7 @@ module.exports = {
|
|||
'primevue/toasteventbus': path.resolve(__dirname, 'src/components/toasteventbus/ToastEventBus.js'),
|
||||
'primevue/overlayeventbus': path.resolve(__dirname, 'src/components/overlayeventbus/OverlayEventBus.js'),
|
||||
'primevue/terminalservice': path.resolve(__dirname, 'src/components/terminalservice/TerminalService.js'),
|
||||
'primevue/dynamicdialogeventbus': path.resolve(__dirname, 'src/components/dynamicdialogeventbus/DynamicDialogEventBus.js'),
|
||||
'primevue/virtualscroller': path.resolve(__dirname, 'src/components/virtualscroller/VirtualScroller.vue')
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue