Change dialog to multi-window mode (#1695)

Fixed Dropdown's z-index being below the dialog
This commit is contained in:
Hayden
2024-11-26 23:11:15 +08:00
committed by GitHub
parent d531bc34c4
commit c977667a15
2 changed files with 128 additions and 64 deletions

View File

@@ -1,61 +1,51 @@
<!-- The main global dialog to show various things --> <!-- The main global dialog to show various things -->
<template> <template>
<Dialog <Dialog
v-model:visible="dialogStore.isVisible" v-for="(item, index) in dialogStore.dialogStack"
:key="item.key"
v-model:visible="item.visible"
class="global-dialog" class="global-dialog"
modal v-bind="item.dialogComponentProps"
closable :auto-z-index="false"
closeOnEscape :pt:mask:style="{ zIndex: baseZIndex + index + 1 }"
dismissableMask :aria-labelledby="item.key"
:maximizable="maximizable"
:maximized="maximized"
@hide="dialogStore.closeDialog"
@maximize="onMaximize"
@unmaximize="onUnmaximize"
:aria-labelledby="headerId"
> >
<template #header> <template #header>
<component <component
v-if="dialogStore.headerComponent" v-if="item.headerComponent"
:is="dialogStore.headerComponent" :is="item.headerComponent"
:id="headerId" :id="item.key"
/> />
<h3 v-else :id="headerId">{{ dialogStore.title || ' ' }}</h3> <h3 v-else :id="item.key">{{ item.title || ' ' }}</h3>
</template> </template>
<component :is="dialogStore.component" v-bind="contentProps" /> <component
:is="item.component"
v-bind="item.contentProps"
:maximized="item.dialogComponentProps.maximized"
/>
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, onMounted } from 'vue'
import { ZIndex } from '@primeuix/utils/zindex'
import { usePrimeVue } from '@primevue/core'
import { useDialogStore } from '@/stores/dialogStore' import { useDialogStore } from '@/stores/dialogStore'
import Dialog from 'primevue/dialog' import Dialog from 'primevue/dialog'
const dialogStore = useDialogStore() const dialogStore = useDialogStore()
const maximizable = computed(
() => dialogStore.dialogComponentProps.maximizable ?? false
)
const maximized = ref(false)
const onMaximize = () => { const primevue = usePrimeVue()
maximized.value = true
}
const onUnmaximize = () => { const baseZIndex = computed(() => {
maximized.value = false return primevue?.config?.zIndex?.modal ?? 1100
} })
const contentProps = computed(() => onMounted(() => {
maximizable.value const mask = document.createElement('div')
? { ZIndex.set('model', mask, baseZIndex.value)
...dialogStore.props, })
maximized: maximized.value
}
: dialogStore.props
)
const headerId = `dialog-${Math.random().toString(36).substr(2, 9)}`
</script> </script>
<style> <style>

View File

@@ -2,52 +2,126 @@
// Currently we need to bridge between legacy app code and Vue app with a Pinia store. // Currently we need to bridge between legacy app code and Vue app with a Pinia store.
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, shallowRef, type Component, markRaw } from 'vue' import { ref, type Component, markRaw } from 'vue'
interface DialogComponentProps { interface DialogComponentProps {
maximizable?: boolean maximizable?: boolean
onClose?: () => void onClose?: () => void
} }
export const useDialogStore = defineStore('dialog', () => { interface DialogInstance {
const isVisible = ref(false) key: string
const title = ref('') visible: boolean
const headerComponent = shallowRef<Component | null>(null) title?: string
const component = shallowRef<Component | null>(null) headerComponent?: Component
const props = ref<Record<string, any>>({}) component: Component
const dialogComponentProps = ref<DialogComponentProps>({}) contentProps: Record<string, any>
dialogComponentProps: Record<string, any>
}
function showDialog(options: { export const useDialogStore = defineStore('dialog', () => {
const dialogStack = ref<DialogInstance[]>([])
const genDialogKey = () => `dialog-${Math.random().toString(36).slice(2, 9)}`
function riseDialog(options: { key: string }) {
const dialogKey = options.key
const index = dialogStack.value.findIndex((d) => d.key === dialogKey)
if (index !== -1) {
const dialogs = dialogStack.value.splice(index, 1)
dialogStack.value.push(...dialogs)
}
}
function closeDialog(options?: { key: string }) {
if (!options) {
dialogStack.value.pop()
return
}
const dialogKey = options.key
const index = dialogStack.value.findIndex((d) => d.key === dialogKey)
if (index === -1) {
return
}
dialogStack.value.splice(index, 1)
}
function createDialog(options: {
key: string
title?: string title?: string
headerComponent?: Component headerComponent?: Component
component: Component component: Component
props?: Record<string, any> props?: Record<string, any>
dialogComponentProps?: DialogComponentProps dialogComponentProps?: DialogComponentProps
}) { }) {
isVisible.value = true const dialog = {
title.value = options.title ?? '' key: options.key,
headerComponent.value = options.headerComponent visible: true,
? markRaw(options.headerComponent) title: options.title,
: null headerComponent: options.headerComponent
component.value = markRaw(options.component) ? markRaw(options.headerComponent)
props.value = options.props || {} : undefined,
dialogComponentProps.value = options.dialogComponentProps || {} component: markRaw(options.component),
contentProps: { ...options.props },
dialogComponentProps: {
maximizable: false,
modal: true,
closable: true,
closeOnEscape: true,
dismissableMask: true,
...options.dialogComponentProps,
maximized: false,
onMaximize: () => {
dialog.dialogComponentProps.maximized = true
},
onUnmaximize: () => {
dialog.dialogComponentProps.maximized = false
},
onAfterHide: () => {
options.dialogComponentProps?.onClose?.()
closeDialog(dialog)
},
pt: {
root: {
onMousedown: () => {
riseDialog(dialog)
}
}
}
}
}
dialogStack.value.push(dialog)
return dialog
} }
function closeDialog() { function showDialog(options: {
if (dialogComponentProps.value.onClose) { key?: string
dialogComponentProps.value.onClose() title?: string
headerComponent?: Component
component: Component
props?: Record<string, any>
dialogComponentProps?: DialogComponentProps
}) {
const dialogKey = options.key || genDialogKey()
let dialog = dialogStack.value.find((d) => d.key === dialogKey)
if (dialog) {
dialog.visible = true
riseDialog(dialog)
} else {
dialog = createDialog({ ...options, key: dialogKey })
} }
isVisible.value = false return dialog
} }
return { return {
isVisible, dialogStack,
title, riseDialog,
headerComponent,
component,
props,
dialogComponentProps,
showDialog, showDialog,
closeDialog closeDialog
} }