diff --git a/docs/content/3.components/modal.md b/docs/content/3.components/modal.md index ca24f85b..e233ae09 100644 --- a/docs/content/3.components/modal.md +++ b/docs/content/3.components/modal.md @@ -276,7 +276,7 @@ This allows you to move the trigger outside of the Modal or remove it entirely. ### Prevent closing -Set the `dismissible` prop to `false` to prevent the Modal from being closed when clicking outside of it or pressing escape. +Set the `dismissible` prop to `false` to prevent the Modal from being closed when clicking outside of it or pressing escape. A `close:prevent` event will be emitted when the user tries to close it. ::component-code --- diff --git a/docs/content/3.components/popover.md b/docs/content/3.components/popover.md index e78c3a6a..5a92c57a 100644 --- a/docs/content/3.components/popover.md +++ b/docs/content/3.components/popover.md @@ -183,7 +183,7 @@ In this example, leveraging [`defineShortcuts`](/composables/define-shortcuts), ### Prevent closing -Set the `dismissible` prop to `false` to prevent the Popover from being closed when clicking outside of it or pressing escape. +Set the `dismissible` prop to `false` to prevent the Popover from being closed when clicking outside of it or pressing escape. A `close:prevent` event will be emitted when the user tries to close it. ::component-example --- diff --git a/docs/content/3.components/slideover.md b/docs/content/3.components/slideover.md index c8023fb4..9f69131e 100644 --- a/docs/content/3.components/slideover.md +++ b/docs/content/3.components/slideover.md @@ -275,7 +275,7 @@ This allows you to move the trigger outside of the Slideover or remove it entire ### Prevent closing -Set the `dismissible` prop to `false` to prevent the Slideover from being closed when clicking outside of it or pressing escape. +Set the `dismissible` prop to `false` to prevent the Slideover from being closed when clicking outside of it or pressing escape. A `close:prevent` event will be emitted when the user tries to close it. ::component-code --- diff --git a/src/runtime/components/Modal.vue b/src/runtime/components/Modal.vue index 3f0025b4..97d19e98 100644 --- a/src/runtime/components/Modal.vue +++ b/src/runtime/components/Modal.vue @@ -55,6 +55,7 @@ export interface ModalProps extends DialogRootProps { export interface ModalEmits extends DialogRootEmits { 'after:leave': [] + 'close:prevent': [] } export interface ModalSlots { @@ -97,20 +98,23 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen' const portalProps = usePortal(toRef(() => props.portal)) const contentProps = toRef(() => props.content) const contentEvents = computed(() => { - const events = { + const defaultEvents = { closeAutoFocus: (e: Event) => e.preventDefault() } if (!props.dismissible) { - return { - pointerDownOutside: (e: Event) => e.preventDefault(), - interactOutside: (e: Event) => e.preventDefault(), - escapeKeyDown: (e: Event) => e.preventDefault(), - ...events - } + const events = ['pointerDownOutside', 'interactOutside', 'escapeKeyDown', 'closeAutoFocus'] as const + type EventType = typeof events[number] + return events.reduce((acc, curr) => { + acc[curr] = (e: Event) => { + e.preventDefault() + emits('close:prevent') + } + return acc + }, {} as Record void>) } - return events + return defaultEvents }) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.modal || {}) })({ diff --git a/src/runtime/components/Popover.vue b/src/runtime/components/Popover.vue index cccd7c32..cc8f31a6 100644 --- a/src/runtime/components/Popover.vue +++ b/src/runtime/components/Popover.vue @@ -36,7 +36,9 @@ export interface PopoverProps extends PopoverRootProps, Pick props.portal)) const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as PopoverContentProps) const contentEvents = computed(() => { if (!props.dismissible) { - return { - pointerDownOutside: (e: Event) => e.preventDefault(), - interactOutside: (e: Event) => e.preventDefault(), - escapeKeyDown: (e: Event) => e.preventDefault() - } + const events = ['pointerDownOutside', 'interactOutside', 'escapeKeyDown'] as const + type EventType = typeof events[number] + return events.reduce((acc, curr) => { + acc[curr] = (e: Event) => { + e.preventDefault() + emits('close:prevent') + } + return acc + }, {} as Record void>) } return {} diff --git a/src/runtime/components/Slideover.vue b/src/runtime/components/Slideover.vue index 04e11aa3..c604f80d 100644 --- a/src/runtime/components/Slideover.vue +++ b/src/runtime/components/Slideover.vue @@ -55,6 +55,7 @@ export interface SlideoverProps extends DialogRootProps { export interface SlideoverEmits extends DialogRootEmits { 'after:leave': [] + 'close:prevent': [] } export interface SlideoverSlots { @@ -98,20 +99,22 @@ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen' const portalProps = usePortal(toRef(() => props.portal)) const contentProps = toRef(() => props.content) const contentEvents = computed(() => { - const events = { + const defaultEvents = { closeAutoFocus: (e: Event) => e.preventDefault() } - if (!props.dismissible) { - return { - pointerDownOutside: (e: Event) => e.preventDefault(), - interactOutside: (e: Event) => e.preventDefault(), - escapeKeyDown: (e: Event) => e.preventDefault(), - ...events - } + const events = ['pointerDownOutside', 'interactOutside', 'escapeKeyDown', 'closeAutoFocus'] as const + type EventType = typeof events[number] + return events.reduce((acc, curr) => { + acc[curr] = (e: Event) => { + e.preventDefault() + emits('close:prevent') + } + return acc + }, {} as Record void>) } - return events + return defaultEvents }) const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.slideover || {}) })({