diff --git a/apps/www/src/lib/registry/default/examples/DialogDemo.vue b/apps/www/src/lib/registry/default/examples/DialogDemo.vue index 1e1aee03..8cc76685 100644 --- a/apps/www/src/lib/registry/default/examples/DialogDemo.vue +++ b/apps/www/src/lib/registry/default/examples/DialogDemo.vue @@ -20,7 +20,7 @@ import { Label } from '@/registry/default/ui/label' Edit Profile - + Edit profile diff --git a/apps/www/src/lib/registry/default/ui/alert-dialog.ts b/apps/www/src/lib/registry/default/ui/alert-dialog.ts index ca50c386..a8c4b63d 100644 --- a/apps/www/src/lib/registry/default/ui/alert-dialog.ts +++ b/apps/www/src/lib/registry/default/ui/alert-dialog.ts @@ -2,14 +2,16 @@ import { defineComponent, h } from 'vue' import { AlertDialogAction as AlertDialogActionPrimitive, type AlertDialogActionProps, AlertDialogCancel as AlertDialogCancelPrimitive, type AlertDialogCancelProps, - AlertDialogContent as AlertDialogContentPrimitive, type AlertDialogContentProps, + type AlertDialogContentEmits, AlertDialogContent as AlertDialogContentPrimitive, type AlertDialogContentProps, AlertDialogDescription as AlertDialogDescriptionPrimitive, type AlertDialogDescriptionProps, AlertDialogOverlay as AlertDialogOverlayPrimitive, type AlertDialogOverlayProps, AlertDialogPortal as AlertDialogPortalPrimitive, AlertDialogRoot, AlertDialogTitle as AlertDialogTitlePrimitive, type AlertDialogTitleProps, AlertDialogTrigger as AlertDialogTriggerPrimitive, + } from 'radix-vue' -import { cn } from '@/utils' +import type { ParseEmits } from '@/utils' +import { cn, useEmitAsProps } from '@/utils' import { buttonVariants } from '@/registry/default/ui/button' export const AlertDialog = AlertDialogRoot @@ -25,16 +27,19 @@ export const AlertDialogOverlay = defineComponent( }, { name: 'AlertDialogOverlay' }, ) -export const AlertDialogContent = defineComponent( - (props, { attrs, slots }) => { +export const AlertDialogContent = defineComponent>( + (props, { emit, attrs, slots }) => { + const emitsAsProps = useEmitAsProps(emit) + return () => h(AlertDialogPortal, () => [ h(AlertDialogOverlay), h(AlertDialogContentPrimitive, { class: cn('fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', attrs.class ?? ''), ...props, + ...emitsAsProps, }, slots), ]) - }, { name: 'AlertDialogContent' }, + }, { name: 'AlertDialogContent', emits: AlertDialogContentPrimitive.emits }, ) export const AlertDialogHeader = defineComponent( diff --git a/apps/www/src/lib/registry/default/ui/dialog.ts b/apps/www/src/lib/registry/default/ui/dialog.ts index 5388f061..35ee5ad3 100644 --- a/apps/www/src/lib/registry/default/ui/dialog.ts +++ b/apps/www/src/lib/registry/default/ui/dialog.ts @@ -1,6 +1,6 @@ import { defineComponent, h } from 'vue' import { - DialogClose, DialogContent as DialogContentPrimitive, type DialogContentProps, + DialogClose, type DialogContentEmits, DialogContent as DialogContentPrimitive, type DialogContentProps, DialogDescription as DialogDescriptionPrimitive, type DialogDescriptionProps, DialogOverlay as DialogOverlayPrimitive, type DialogOverlayProps, DialogPortal as DialogPortalPrimitive, DialogRoot, @@ -8,7 +8,8 @@ import { DialogTrigger as DialogTriggerPrimitive, } from 'radix-vue' import { X } from 'lucide-vue-next' -import { cn } from '@/utils' +import type { ParseEmits } from '@/utils' +import { cn, useEmitAsProps } from '@/utils' export const Dialog = DialogRoot export const DialogTrigger = DialogTriggerPrimitive @@ -23,13 +24,16 @@ export const DialogOverlay = defineComponent( }, { name: 'DialogOverlay' }, ) -export const DialogContent = defineComponent( - (props, { attrs, slots }) => { +export const DialogContent = defineComponent>( + (props, { emit, attrs, slots }) => { + const emitsAsProps = useEmitAsProps(emit) + return () => h(DialogPortal, () => [ h(DialogOverlay), h(DialogContentPrimitive, { class: cn('fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full', attrs.class ?? ''), ...props, + ...emitsAsProps, }, () => [ slots.default(), h(DialogClose, @@ -38,7 +42,7 @@ export const DialogContent = defineComponent( ), ]), ]) - }, { name: 'DialogContent' }, + }, { name: 'DialogContent', emits: DialogContentPrimitive.emits }, ) export const DialogHeader = defineComponent( diff --git a/apps/www/src/lib/utils.ts b/apps/www/src/lib/utils.ts index 9b6b5045..e1d860eb 100644 --- a/apps/www/src/lib/utils.ts +++ b/apps/www/src/lib/utils.ts @@ -1,6 +1,7 @@ import type { ClassValue } from 'clsx' import { clsx } from 'clsx' import { twMerge } from 'tailwind-merge' +import { camelize, getCurrentInstance, toHandlerKey } from 'vue' export type ParseEmits> = { [K in keyof T]: (...args: T[K]) => void; @@ -9,3 +10,24 @@ export type ParseEmits> = { export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +// Vue doesn't have emits forwarding, in order to bind the emits we have to convert events into `onXXX` handlers +// issue: https://github.com/vuejs/core/issues/5917 +export function useEmitAsProps( + emit: (name: Name, ...args: any[]) => void, +) { + const vm = getCurrentInstance() + + const events = vm?.type.emits as Name[] + const result: Record = {} + if (!events?.length) { + console.warn( + `No emitted event found. Please check component: ${vm?.type.__name}`, + ) + } + + events?.forEach((ev) => { + result[toHandlerKey(camelize(ev))] = (...arg: any) => emit(ev, ...arg) + }) + return result +}