Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Reland] Change dialog to multi-window mode #1695

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 27 additions & 37 deletions src/components/dialog/GlobalDialog.vue
Original file line number Diff line number Diff line change
@@ -1,61 +1,51 @@
<!-- The main global dialog to show various things -->
<template>
<Dialog
v-model:visible="dialogStore.isVisible"
v-for="(item, index) in dialogStore.dialogStack"
:key="item.key"
v-model:visible="item.visible"
class="global-dialog"
modal
closable
closeOnEscape
dismissableMask
:maximizable="maximizable"
:maximized="maximized"
@hide="dialogStore.closeDialog"
@maximize="onMaximize"
@unmaximize="onUnmaximize"
:aria-labelledby="headerId"
v-bind="item.dialogComponentProps"
:auto-z-index="false"
:pt:mask:style="{ zIndex: baseZIndex + index + 1 }"
:aria-labelledby="item.key"
>
<template #header>
<component
v-if="dialogStore.headerComponent"
:is="dialogStore.headerComponent"
:id="headerId"
v-if="item.headerComponent"
:is="item.headerComponent"
:id="item.key"
/>
<h3 v-else :id="headerId">{{ dialogStore.title || ' ' }}</h3>
<h3 v-else :id="item.key">{{ item.title || ' ' }}</h3>
</template>

<component :is="dialogStore.component" v-bind="contentProps" />
<component
:is="item.component"
v-bind="item.contentProps"
:maximized="item.dialogComponentProps.maximized"
/>
</Dialog>
</template>

<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 Dialog from 'primevue/dialog'

const dialogStore = useDialogStore()
const maximizable = computed(
() => dialogStore.dialogComponentProps.maximizable ?? false
)
const maximized = ref(false)

const onMaximize = () => {
maximized.value = true
}

const onUnmaximize = () => {
maximized.value = false
}
const primevue = usePrimeVue()

const contentProps = computed(() =>
maximizable.value
? {
...dialogStore.props,
maximized: maximized.value
}
: dialogStore.props
)
const baseZIndex = computed(() => {
return primevue?.config?.zIndex?.modal ?? 1100
})

const headerId = `dialog-${Math.random().toString(36).substr(2, 9)}`
onMounted(() => {
const mask = document.createElement('div')
ZIndex.set('model', mask, baseZIndex.value)
})
</script>

<style>
Expand Down
126 changes: 100 additions & 26 deletions src/stores/dialogStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,126 @@
// Currently we need to bridge between legacy app code and Vue app with a Pinia store.

import { defineStore } from 'pinia'
import { ref, shallowRef, type Component, markRaw } from 'vue'
import { ref, type Component, markRaw } from 'vue'

interface DialogComponentProps {
maximizable?: boolean
onClose?: () => void
}

interface DialogInstance {
key: string
visible: boolean
title?: string
headerComponent?: Component
component: Component
contentProps: Record<string, any>
dialogComponentProps: Record<string, any>
}

export const useDialogStore = defineStore('dialog', () => {
const isVisible = ref(false)
const title = ref('')
const headerComponent = shallowRef<Component | null>(null)
const component = shallowRef<Component | null>(null)
const props = ref<Record<string, any>>({})
const dialogComponentProps = ref<DialogComponentProps>({})
const dialogStack = ref<DialogInstance[]>([])

function showDialog(options: {
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
headerComponent?: Component
component: Component
props?: Record<string, any>
dialogComponentProps?: DialogComponentProps
}) {
isVisible.value = true
title.value = options.title ?? ''
headerComponent.value = options.headerComponent
? markRaw(options.headerComponent)
: null
component.value = markRaw(options.component)
props.value = options.props || {}
dialogComponentProps.value = options.dialogComponentProps || {}
const dialog = {
key: options.key,
visible: true,
title: options.title,
headerComponent: options.headerComponent
? markRaw(options.headerComponent)
: undefined,
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() {
if (dialogComponentProps.value.onClose) {
dialogComponentProps.value.onClose()
function showDialog(options: {
key?: string
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 {
isVisible,
title,
headerComponent,
component,
props,
dialogComponentProps,
dialogStack,
riseDialog,
showDialog,
closeDialog
}
Expand Down
Loading