Context
Alertλ Confirmκ³Ό κ°μ΄ μ¬μ©μκ° μνΈμμ©νλ λͺ¨λ¬μ λ§λ€ λ νλ©΄μ λ³λμ μ°½μ λμ°λ μ»΄ν¬λνΈκ° νμνλ€. shadcn/uiμμλ μ΄λ₯Ό Dialogλ₯Ό νμ©ν΄μ λ§λλλ°, μ΄ κΈμμλ μ΄ Dialogμ λν λ΄μ©μ λ€λ£¬λ€.
Agenda
β’
Dialog μ»΄ν¬λνΈμ λ΄μ©μ λΆμνκ³ μ΄ν΄νλ κ²μ λͺ©νλ‘ ν¨
Contents
μ¬μ©μμκ² λ°μ΄ν°λ₯Ό μ
λ ₯νλ λͺ¨λ¬μ μ 곡νκ³ μΆκ±°λ Contextλ₯Ό λΆλ¦¬νμ¬ λ°μ΄ν° μμ
μ νκ³ μΆμ κ²½μ°κ° μ‘΄μ¬νλ€. μ΄ λ λͺ¨λ¬μ νλ©΄μ λμ μ¬μ©μμ κ΄μ¬μ λλ μ ν μμ
μ μννλ€. shadcn/uiμμλ Dialog μ»΄ν¬λνΈλ₯Ό μ¬μ©νμ¬ μ΄λ₯Ό ꡬννλλ° Dialogλ₯Ό ꡬμ±νλ μ»΄ν¬λνΈ λν μ¬λΏ μ‘΄μ¬νλ€.
Dialog
const Dialog = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) => {
return <DialogPrimitive.Root data-slot='dialog' {...props} />;
};
TypeScript
볡μ¬
Dialog μ»΄ν¬λνΈμ΄λ€. radix-uiμ Dialog - Rootλ₯Ό κ°μΈλ wrapper νμμΌλ‘ μ»΄ν¬λνΈκ° ꡬμ±λμ΄ μλ€. νμ λμ¬ Dialogλ₯Ό ꡬμ±νλ μ»΄ν¬λνΈλ€μ κ°μΈλ ννλ‘ μ¬μ©λλ€.
DialogTrigger
const DialogTrigger = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) => {
return <DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />;
};
TypeScript
볡μ¬
DialogTrigger μ»΄ν¬λνΈμ΄λ€. radix-uiμ Dialog - Triggerλ₯Ό κ°μΈλ wrapper ννλ‘ μ»΄ν¬λνΈκ° ꡬνλμ΄ μλ€. Dialogμ μ΄λ¦Ό μνλ₯Ό ν κΈ ν μ μλ κΈ°λ₯μ μ 곡νλ€.
DialogPortal
const DialogPortal = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) => {
return <DialogPrimitive.Portal data-slot='dialog-portal' {...props} />;
};
TypeScript
볡μ¬
DialogPortal μ»΄ν¬λνΈμ΄λ€. radix-uiμ Dialog - Portalμ κ°μΈλ wrapper ννλ‘ μ»΄ν¬λνΈκ° ꡬνλμ΄ μλ€. DialogPortalμ μ»΄ν¬λνΈλ₯Ό λ°°μΉν μμΉμ μκ΄ μμ΄ DOMμ λ€λ₯Έ μμΉμ λ λλ§ νκ² ν΄μ£Όλ κΈ°λ₯μ μννλ€. UIμμ ν΄ν, λλ‘λ€μ΄, λͺ¨λ¬κ°μ κ²½μ°λ λΆλͺ¨ DOM ꡬ쑰μ μν₯μ λ°μΌλ©΄ μ λλ κ²½μ°κ° λ§μλ° λνμ μΌλ‘ z-index μ§μ₯κ³Ό κ°μ΄ λΆλͺ¨ μ»΄ν¬λνΈμ μ€νμΌλ§μ μν₯μ λ°μ μ»΄ν¬λνΈκ° μ¬λ°λ₯΄κ² λ λλ§ λμ§ μλ κ²½μ°μ΄λ€.
DialogClose
const DialogClose = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) => {
return <DialogPrimitive.Close data-slot='dialog-close' {...props} />;
};
TypeScript
볡μ¬
Dialogλ₯Ό λ«μ μ μκ² νλ μ»΄ν¬λνΈμ΄λ€. radix-uiμ Dialog - Close μ»΄ν¬λνΈλ₯Ό κ°μΈλ ννλ‘ κ΅¬ν λμ΄μλ€.
DialogOverlay
const DialogOverlay = ({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) => {
return (
<DialogPrimitive.Overlay
data-slot='dialog-overlay'
className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fase-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
className,
)}
{...props}
/>
);
};
TypeScript
볡μ¬
Dialogμ λ°°κ²½ μ»΄ν¬λνΈμ΄λ€. fixedμ insetμ μ¬μ©νμ¬ νλ©΄ μ 체λ₯Ό κ°μΈκ³ data-state μμ±μ νμ©νμ¬ μ΄λ¦Ό μνμ λ°λΌ μ€νμΌλ§μ μ μ©νλ€.
DialogContent
const DialogContent = ({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean;
}) => {
return (
<DialogPortal data-slot='dialog-portal'>
<DialogOverlay />
<DialogPrimitive.Content
data-slot='dialog-content'
className={cn(
'bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg',
className,
)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close
data-slot='dialog-close'
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span className='sr-only'>Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
);
};
TypeScript
볡μ¬
DialogContent μ»΄ν¬λνΈμ΄λ€. Dialogμ 컨ν
μΈ λ₯Ό νμνλ©° μ΄μ μ μμ±ν μ»΄ν¬λνΈλ€μ μ¬νμ©νμ¬ κ΅¬ννλ€. DialogContentμ νμ λλ 컨ν
μΈ λ€μ childrenμΌλ‘ radix-uiμ Dialog - Content λ΄λΆμ λ λλ§ λλ€.
DialogHeader
const DialogHeader = ({ className, ...props }: React.ComponentProps<'div'>) => {
return (
<div
data-slot='dialog-header'
className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
{...props}
/>
);
};
TypeScript
볡μ¬
Dialogμ μλ¨μ νμ λλ Headerλ₯Ό μν μ»΄ν¬λνΈμ΄λ€. flex displayμ΄κ³ μΈλ‘λ‘ μ»΄ν¬λνΈλ€μ λ°°μΉνλ€.
DialogFooter
const DialogFooter = ({ className, ...props }: React.ComponentProps<'div'>) => {
return (
<div
data-slot='dialog-footer'
className={cn(
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
className,
)}
{...props}
/>
);
};
TypeScript
볡μ¬
Dialog νλ¨μ λ°°μΉ λλ DialogFooter μ»΄ν¬λνΈμ΄λ€. νΉμ΄ν λΆλΆμ΄ μλλ° λ°λ‘ flex-col-reverseμ΄λ€. flexλ‘ λ°°μΉ λλ μμλ₯Ό λ³κ²½νκ±΄λ° μμλ λ€μκ³Ό κ°λ€.
[Save] [Cancel]
Plain Text
볡μ¬
μμ νμμΌλ‘ λ²νΌμ΄ λ°°μΉ λμ΄μλ€κ³ κ°μ νλ€. sm:flex-rowλ λ°μ€ν¬ν± ν¬κΈ°μ νλ©΄μμ μμλ₯Ό κ°λ‘λ‘ λ°°μΉνλ€. κ·Έλ¦¬κ³ λͺ¨λ°μΌ νλ©΄μΌλ‘ κ°λ©΄ μλλ μλμ κ°μ΄ μΈλ‘λ‘ λ°°μΉ λμ΄μΌ νλ€.
[Save]
[Cancel]
Plain Text
볡μ¬
νμ§λ§, flex-col-reverseλ‘ μΈν΄ μλμ κ°μ΄ λ°°μΉ λλ€.
[Cancel]
[Save]
Plain Text
볡μ¬
λ²νΌμ κΈ°λ₯μ μ€μλλ₯Ό λΆμ¬νμ¬ λ²νΌμ μ°μ μμλ₯Ό μκ°ν λ°°μΉλΌκ³ μκ°νλ€.
DialogTitle
const DialogTitle = ({ className, ...props }: React.ComponentProps<'div'>) => {
return (
<DialogPrimitive.Title
data-slot='dialog-title'
className={cn('text-lg leading-none font-semibold', className)}
{...props}
/>
);
};
TypeScript
볡μ¬
DialogTitle λ΄λΆμ λ°°μΉ λλ Title μ»΄ν¬λνΈμ΄λ€.
DialogDescription
const DialogDescription = ({
className,
...props
}: React.ComponentProps<'div'>) => {
return (
<DialogPrimitive.Description
data-slot='dialog-description'
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
};
TypeScript
볡μ¬
μ΄ μ»΄ν¬λνΈλ Dialogμ λν μ€λͺ
μ νμνκΈ° μν μ»΄ν¬λνΈμ΄λ€. μμ λ₯Ό 보면 μ£Όλ‘ DialogHeader μμ DialogTitleκ³Ό νμ depthμ λ°°μΉ λλ€.