Skip to content

Commit a4a31fe

Browse files
committed
[Toolkit][Shadcn] Add Dialog recipe
1 parent 9045a17 commit a4a31fe

File tree

30 files changed

+437
-15
lines changed

30 files changed

+437
-15
lines changed

src/Toolkit/kits/shadcn/alert-dialog/templates/components/AlertDialog.html.twig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{# @prop open boolean Open (or not) the AlertDialog at initial rendering, default to `false` #}
2+
{# @prop id string Unique suffix identifier for generating AlertDialog internal IDs #}
23
{# @block content The default block #}
34
{%- props open = false, id -%}
45

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Examples
2+
3+
## Default
4+
5+
```twig {"preview":true,"height":"500px"}
6+
<twig:Dialog id="delete_account">
7+
<twig:Dialog:Trigger>
8+
<twig:Button {{ ...trigger_attrs }}>Open</twig:Button>
9+
</twig:Dialog:Trigger>
10+
<twig:Dialog:Content>
11+
<twig:Dialog:Header>
12+
<twig:Dialog:Title>Are you absolutely sure?</twig:Dialog:Title>
13+
<twig:Dialog:Description>
14+
This action cannot be undone. This will permanently delete your account
15+
and remove your data from our servers.
16+
</twig:Dialog:Description>
17+
</twig:Dialog:Header>
18+
</twig:Dialog:Content>
19+
</twig:Dialog>
20+
```
21+
22+
## Custom close button
23+
24+
```twig {"preview":true,"height":"500px"}
25+
<twig:Dialog id="share_link">
26+
<twig:Dialog:Trigger>
27+
<twig:Button variant="outline" {{ ...trigger_attrs }}>Share</twig:Button>
28+
</twig:Dialog:Trigger>
29+
<twig:Dialog:Content class="sm:max-w-md">
30+
<twig:Dialog:Header>
31+
<twig:Dialog:Title>Share link</twig:Dialog:Title>
32+
<twig:Dialog:Description>
33+
Anyone who has this link will be able to view this.
34+
</twig:Dialog:Description>
35+
</twig:Dialog:Header>
36+
<div class="flex items-center gap-2">
37+
<div class="grid flex-1 gap-2">
38+
<twig:Label for="link" class="sr-only">Link</twig:Label>
39+
<twig:Input id="link" value="https://ui.shadcn.com/docs/installation" readonly />
40+
</div>
41+
</div>
42+
<twig:Dialog:Footer class="sm:justify-start">
43+
<twig:Dialog:Close>
44+
<twig:Button type="button" variant="secondary" {{ ...close_attrs }}>
45+
Close
46+
</twig:Button>
47+
</twig:Dialog:Close>
48+
</twig:Dialog:Footer>
49+
</twig:Dialog:Content>
50+
</twig:Dialog>
51+
```
52+
53+
## With form
54+
55+
```twig {"preview":true,"height":"500px"}
56+
<twig:Dialog id="edit_profile">
57+
<twig:Dialog:Trigger>
58+
<twig:Button {{ ...trigger_attrs }} variant="outline">Open Dialog</twig:Button>
59+
</twig:Dialog:Trigger>
60+
<twig:Dialog:Content class="sm:max-w-[425px]">
61+
<twig:Dialog:Header>
62+
<twig:Dialog:Title>Edit profile</twig:Dialog:Title>
63+
<twig:Dialog:Description>
64+
Make changes to your profile here. Click save when you&apos;re done.
65+
</twig:Dialog:Description>
66+
</twig:Dialog:Header>
67+
<div class="grid gap-4">
68+
<div class="grid gap-3">
69+
<twig:Label for="name">Name</twig:Label>
70+
<twig:Input id="name" name="name" value="Pedro Duarte" />
71+
</div>
72+
<div class="grid gap-3">
73+
<twig:Label for="username">Username</twig:Label>
74+
<twig:Input id="username" name="username" value="@peduarte" />
75+
</div>
76+
</div>
77+
<twig:Dialog:Footer>
78+
<twig:Dialog:Close>
79+
<twig:Button variant="outline" {{ ...close_attrs }}>Cancel</twig:Button>
80+
</twig:Dialog:Close>
81+
<twig:Button type="submit">Save changes</twig:Button>
82+
</twig:Dialog:Footer>
83+
</twig:Dialog:Content>
84+
</twig:Dialog>
85+
```
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
import { enter, leave } from 'el-transition';
3+
4+
export default class extends Controller {
5+
6+
static targets = ['trigger', 'overlay', 'dialog', 'content'];
7+
8+
async open() {
9+
this.dialogTarget.showModal();
10+
11+
await Promise.all([enter(this.overlayTarget), enter(this.contentTarget)]);
12+
13+
if (this.hasTriggerTarget) {
14+
this.triggerTarget.setAttribute('aria-expanded', 'true');
15+
}
16+
}
17+
18+
async closeOnClickOutside({ target }) {
19+
if (target === this.overlayTarget) {
20+
await this.close()
21+
}
22+
}
23+
24+
async close() {
25+
await Promise.all([leave(this.overlayTarget), leave(this.contentTarget)]);
26+
27+
this.dialogTarget.close();
28+
29+
if (this.hasTriggerTarget) {
30+
this.triggerTarget.setAttribute('aria-expanded', 'false');
31+
}
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "../../../schema-kit-recipe-v1.json",
3+
"type": "component",
4+
"name": "Dialog",
5+
"description": "A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.",
6+
"copy-files": {
7+
"assets/": "assets/",
8+
"templates/": "templates/"
9+
},
10+
"dependencies": {
11+
"composer": ["symfony/ux-icons", "twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra"],
12+
"npm": ["el-transition"],
13+
"importmap": ["el-transition"]
14+
}
15+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{# @prop open boolean Open (or not) the Dialog at initial rendering, default to `false` #}
2+
{# @prop id string Unique suffix identifier for generating Dialog internal IDs #}
3+
{# @block content The default block #}
4+
{%- props open = false, id -%}
5+
6+
{%- set _dialog_open = open %}
7+
{%- set _dialog_id = 'dialog-' ~ id -%}
8+
{%- set _dialog_title_id = _dialog_id ~ '-title' -%}
9+
{%- set _dialog_description_id = _dialog_id ~ '-description' -%}
10+
<div {{ attributes.defaults({
11+
'data-controller': 'dialog',
12+
'aria-labelledby': _dialog_title_id,
13+
'aria-describedby': _dialog_description_id,
14+
}) }}>
15+
{% block content %}{% endblock %}
16+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{# @block content The default block #}
2+
{%- set close_attrs = {
3+
'data-action': 'click->dialog#close',
4+
} -%}
5+
{%- block content %}{% endblock -%}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{# @prop showCloseButton boolean Whether the close button is shown, default to `true` #}
2+
{# @block content The default block #}
3+
{%- props showCloseButton = true -%}
4+
5+
<dialog
6+
id="{{ _dialog_id }}"
7+
{{ _dialog_open ? 'open' }}
8+
class="{{ 'fixed inset-0 size-auto max-h-none max-w-none overflow-y-auto bg-transparent backdrop:bg-transparent ' ~ attributes.render('class')|tailwind_merge }}"
9+
data-dialog-target="dialog"
10+
data-action="keydown.esc->dialog#close:prevent click->dialog#closeOnClickOutside"
11+
{{ attributes.without('id') }}
12+
>
13+
<div
14+
data-dialog-target="overlay"
15+
data-transition-enter="transition ease-out duration-100"
16+
data-transition-enter-start="transform opacity-0"
17+
data-transition-enter-end="transform opacity-100"
18+
data-transition-leave="transition ease-in duration-75"
19+
data-transition-leave-start="transform opacity-100"
20+
data-transition-leave-end="transform opacity-0"
21+
class="{{ _dialog_open ? '' : 'hidden' }} fixed inset-0 z-50 bg-black/50"
22+
></div>
23+
24+
<section
25+
tabindex="0"
26+
class="flex min-h-full items-end justify-center p-4 text-center focus:outline-none sm:items-center sm:p-0"
27+
>
28+
29+
<div
30+
data-transition-enter="transition ease-out duration-200"
31+
data-transition-enter-start="transform opacity-0 scale-95"
32+
data-transition-enter-end="transform opacity-100 scale-100"
33+
data-transition-leave="transition ease-in duration-75"
34+
data-transition-leave-start="transform opacity-100 scale-100"
35+
data-transition-leave-end="transform opacity-0 scale-95"
36+
class="{{ _dialog_open ? '' : 'hidden' }} bg-background 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 sm:max-w-lg"
37+
data-dialog-target="content"
38+
>
39+
{%- block content %}{% endblock -%}
40+
{% if showCloseButton %}
41+
<button type="button" class="ring-offset-background focus:ring-ring 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" data-action="click->dialog#close">
42+
<twig:ux:icon name="lucide:x" />
43+
<span class="sr-only">Close</span>
44+
</button>
45+
{% endif %}
46+
</div>
47+
</section>
48+
</dialog>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{# @block content The default block #}
2+
<p
3+
id="{{ _dialog_description_id }}"
4+
class="{{ 'text-muted-foreground text-sm ' ~ attributes.render('class')|tailwind_merge }}"
5+
{{ attributes.without('id') }}
6+
>
7+
{%- block content %}{% endblock -%}
8+
</p>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{# @block content The default block #}
2+
<footer
3+
class="{{ 'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end ' ~ attributes.render('class')|tailwind_merge }}"
4+
{{ attributes }}
5+
>
6+
{%- block content %}{% endblock -%}
7+
</footer>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{# @block content The default block #}
2+
<header
3+
class="{{ 'flex flex-col gap-2 text-center sm:text-left ' ~ attributes.render('class')|tailwind_merge }}"
4+
{{ attributes }}
5+
>
6+
{%- block content %}{% endblock -%}
7+
</header>

0 commit comments

Comments
 (0)