src/stores/dialogv2.ts
Rama main · réplica de lectura del repositorio original.
import { acceptHMRUpdate, defineStore } from 'pinia'
import { ref, watch } from 'vue'
export interface DialogV2Button {
text: string
id?: string
href?: string
target?: '_self' | '_blank' | '_parent' | '_top'
rel?: string
handler?: () => void | boolean | Promise<void | boolean>
role?: 'primary' | 'secondary' | 'danger' | 'cancel'
/** 'start' renders the button as a quiet text action on the left of the footer (e.g. "Don't show again"). */
placement?: 'start' | 'end'
preventClose?: boolean
disabled?: boolean
skipNavigation?: boolean
}
export interface DialogV2Options {
id?: string
title?: string
description?: string
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'
buttons?: DialogV2Button[]
preventAccidentalClose?: boolean
}
export const useDialogV2Store = defineStore('dialogv2', () => {
const showDialog = ref(false)
const dialogOptions = ref<DialogV2Options>({})
const dialogCanceled = ref(false)
const lastButtonRole = ref('')
const pendingDialogAction = ref(false)
const assignDialogButtonsDisabled = (disabled: boolean) => {
const current = dialogOptions.value
if (!current.buttons?.length)
return
dialogOptions.value = {
...current,
buttons: current.buttons.map(button => ({ ...button, disabled })),
}
}
const openDialog = (options: DialogV2Options) => {
pendingDialogAction.value = false
dialogOptions.value = options
showDialog.value = true
dialogCanceled.value = false
lastButtonRole.value = ''
}
const openButtonHref = (button: DialogV2Button) => {
if (!button.href)
return
if (typeof window === 'undefined')
return
if (button.target === '_blank') {
const relTokens = button.rel
? button.rel.split(/[\s,]+/).map(token => token.toLowerCase())
: []
const relSet = new Set<string>()
relSet.add('noopener')
if (relTokens.includes('noreferrer'))
relSet.add('noreferrer')
const relFeatures = Array.from(relSet).join(',')
window.open(button.href, button.target, relFeatures)
return
}
if (button.target && button.target !== '_self')
window.open(button.href, button.target)
else
window.location.assign(button.href)
}
const closeDialog = async (button?: DialogV2Button) => {
if (!button) {
// Modal dismissed without a button action (overlay, escape, close icon)
dialogCanceled.value = true
lastButtonRole.value = ''
showDialog.value = false
pendingDialogAction.value = false
return
}
if (button.disabled || pendingDialogAction.value)
return
pendingDialogAction.value = true
lastButtonRole.value = button.id ?? button.role ?? ''
dialogCanceled.value = button.role === 'cancel'
assignDialogButtonsDisabled(true)
try {
if (button.handler) {
const result = await button.handler()
// If handler returns false, don't close the dialog
if (result === false) {
if (showDialog.value)
assignDialogButtonsDisabled(false)
return
}
}
if (!button.preventClose) {
showDialog.value = false
if (button.href && !button.skipNavigation)
openButtonHref(button)
return
}
if (showDialog.value)
assignDialogButtonsDisabled(false)
}
catch (error) {
if (showDialog.value)
assignDialogButtonsDisabled(false)
throw error
}
finally {
pendingDialogAction.value = false
}
}
const onDialogDismiss = (): Promise<boolean> => {
return new Promise((resolve) => {
const unwatch = watch(showDialog, (val) => {
if (!val) {
resolve(dialogCanceled.value)
unwatch()
}
})
})
}
return {
showDialog,
dialogOptions,
dialogCanceled,
lastButtonRole,
openDialog,
closeDialog,
onDialogDismiss,
}
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useDialogV2Store, import.meta.hot))