laif-ds
Version:
Design System di Laif con componenti React basati su principi di Atomic Design
272 lines (226 loc) • 8.7 kB
Markdown
# AppDialog
## Overview
Enhanced modal dialog component built on top of the base Dialog component. Provides a simplified API with built-in header, body, and footer sections, multiple size variants, and support for both controlled and uncontrolled usage patterns.
---
## Props
### AppDialogProps
| Prop | Type | Default | Description |
| ------------------ | --------------------------------------------- | ------------ | --------------------------------------------- |
| `trigger` | `React.ReactNode` | `undefined` | Element that triggers the dialog on click. |
| `title` | `string \| React.ReactNode` | **required** | Dialog title displayed in the header. |
| `description` | `string \| React.ReactNode` | `undefined` | Dialog description displayed below the title. |
| `footer` | `React.ReactNode` | `undefined` | Footer content (typically action buttons). |
| `size` | `"sm" \| "default" \| "lg" \| "xl" \| "full"` | `"default"` | Size variant of the dialog. |
| `open` | `boolean` | `undefined` | Controlled open state. |
| `defaultOpen` | `boolean` | `undefined` | Default open state for uncontrolled usage. |
| `onOpenChange` | `(open: boolean) => void` | `undefined` | Callback when open state changes. |
| `disabled` | `boolean` | `false` | Whether to disable the trigger. |
| `asChild` | `boolean` | `false` | Whether to use asChild pattern for trigger. |
| `preventClose` | `"overlay" \| "all"` | `undefined` | Prevent closing via overlay or all methods. |
| `triggerClassName` | `string` | `undefined` | Additional className for the trigger wrapper. |
| `contentClassName` | `string` | `undefined` | Additional className for the dialog content. |
| `headerClassName` | `string` | `undefined` | Additional className for the dialog header. |
| `bodyClassName` | `string` | `undefined` | Additional className for the dialog body. |
| `footerClassName` | `string` | `undefined` | Additional className for the dialog footer. |
### Size Variants
| Size | Dimensions |
| --------- | ---------------------------------- |
| `sm` | 5/12 width and height |
| `default` | 7/12 width and height |
| `lg` | 9/12 width and height |
| `xl` | 11/12 width and height |
| `full` | Full viewport (no rounded corners) |
---
## Exports
- **AppDialog**: Main dialog component.
- **AppDialogClose**: Re-export of DialogClose for closing the dialog programmatically.
---
## Behavior
- **Responsive**: On small screens (`max-sm`), the dialog takes full viewport.
- **Scrollable body**: Content in the body section scrolls if it exceeds available space.
- **Close button**: Built-in close button in the header (top-right).
- **Border separators**: Automatic borders between header/body and body/footer when content exists.
- **Focus management**: Inherits Radix Dialog focus trapping behavior.
---
## Examples
### Basic Usage
```tsx
import { AppDialog, Button } from "laif-ds";
export function BasicAppDialog() {
return (
<AppDialog
title="Dialog Title"
description="Optional description text."
trigger={<Button>Open Dialog</Button>}
asChild
>
<p>Dialog content goes here.</p>
</AppDialog>
);
}
```
### With Footer Actions
```tsx
import { AppDialog, AppDialogClose, Button } from "laif-ds";
export function DialogWithFooter() {
return (
<AppDialog
title="Confirm Action"
description="Are you sure you want to proceed?"
trigger={<Button>Open Dialog</Button>}
footer={
<>
<AppDialogClose asChild>
<Button variant="outline">Cancel</Button>
</AppDialogClose>
<AppDialogClose asChild>
<Button>Confirm</Button>
</AppDialogClose>
</>
}
asChild
>
<p>This action cannot be undone.</p>
</AppDialog>
);
}
```
### Controlled Dialog
```tsx
import { useState } from "react";
import { AppDialog, Button } from "laif-ds";
export function ControlledDialog() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Open Dialog</Button>
<AppDialog
open={open}
onOpenChange={setOpen}
title="Controlled Dialog"
description="This dialog is controlled externally."
footer={<Button onClick={() => setOpen(false)}>Close</Button>}
>
<p>The dialog state is managed by the parent component.</p>
</AppDialog>
</>
);
}
```
### Size Variants
```tsx
import { AppDialog, Button } from "laif-ds";
export function SizeVariants() {
return (
<div className="flex flex-wrap gap-4">
<AppDialog
title="Small Dialog"
size="sm"
trigger={<Button variant="outline">Small</Button>}
asChild
>
<p>Small dialog content.</p>
</AppDialog>
<AppDialog
title="Default Dialog"
size="default"
trigger={<Button variant="outline">Default</Button>}
asChild
>
<p>Default dialog content.</p>
</AppDialog>
<AppDialog
title="Large Dialog"
size="lg"
trigger={<Button variant="outline">Large</Button>}
asChild
>
<p>Large dialog content.</p>
</AppDialog>
<AppDialog
title="Extra Large Dialog"
size="xl"
trigger={<Button variant="outline">Extra Large</Button>}
asChild
>
<p>Extra large dialog content.</p>
</AppDialog>
<AppDialog
title="Full Dialog"
size="full"
trigger={<Button variant="outline">Full</Button>}
asChild
>
<p>Full screen dialog content.</p>
</AppDialog>
</div>
);
}
```
### With Form
```tsx
import { useState } from "react";
import { AppDialog, AppDialogClose, Button, Input, Label } from "laif-ds";
export function DialogWithForm() {
const [open, setOpen] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Handle form submission
setOpen(false);
};
return (
<AppDialog
open={open}
onOpenChange={setOpen}
title="Edit Profile"
description="Update your profile information."
trigger={<Button>Edit Profile</Button>}
footer={
<>
<AppDialogClose asChild>
<Button variant="outline">Cancel</Button>
</AppDialogClose>
<Button type="submit" onClick={handleSubmit}>
Save
</Button>
</>
}
asChild
>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Your name" />
</div>
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="Your email" />
</div>
</form>
</AppDialog>
);
}
```
### Disabled Trigger
```tsx
import { AppDialog, Button } from "laif-ds";
export function DisabledDialog() {
return (
<AppDialog
title="Disabled Dialog"
trigger={<Button disabled>Cannot Open</Button>}
disabled
asChild
>
<p>This content won't be accessible.</p>
</AppDialog>
);
}
```
---
## Notes
- **Use `asChild`**: When passing a Button or other interactive element as trigger, set `asChild={true}` to avoid nesting buttons.
- **AppDialogClose**: Use this component to create buttons that close the dialog. Wrap with `asChild` when using custom elements.
- **Scrolling content**: The body section automatically handles overflow with scrolling.
- **Accessibility**: Title is rendered as an `h5` heading; description uses caption typography.
- **vs Dialog**: Use `AppDialog` for simpler use cases with consistent header/body/footer layout. Use base `Dialog` for more complex custom layouts.