Skip to content

Commit 27646bd

Browse files
committed
feature symfony#3173 [Toolkit][Shadcn] Add Dialog component (seb-jean, Kocal)
This PR was merged into the 2.x branch. Discussion ---------- [Toolkit][Shadcn] Add `Dialog` component | Q | A | -------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md --> | Documentation? | yes<!-- required for new features, or documentation updates --> | Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT This pull request introduces the new `Dialog` component to the Shadcn UI kit in the Symfony UX Toolkit, including its templates for all subcomponents (Close, Content, Description, Footer, Header, Title, Trigger), JavaScript controller, documentation, and manifest. I encountered a display issue with the `Label` component. So I updated this component to reflect [Shadcn](https://ui.shadcn.com/docs/components/label). Below is a demonstration of the `Dialog` component: https://github.com/user-attachments/assets/184036ed-1280-40fe-b126-06a288b257dc However, I am having a problem with the `lucid-x` icon. In the middle of it, there is what looks like a white line. <img width="28" height="28" alt="lucide-x" src="https://github.com/user-attachments/assets/6c95c993-ec49-4c53-9664-bb342627eb78" /> The second problem I see is that when using the Tab key while the dialog is open, elements outside the dialog can also be focused. This is unacceptable. What do you think? (In any case, this kit system is great, and it's only the beginning.) Commits ------- 9f433f9 [Toolkit][Shadcn] Minor tweaks on alert-dialog and dialog cmoponents cedd007 [Toolkit][Shadcn] Add Dialog recipe
2 parents c756147 + 9f433f9 commit 27646bd

File tree

38 files changed

+415
-137
lines changed

38 files changed

+415
-137
lines changed

biome.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"rules": {
2222
"suspicious": {
2323
"noExplicitAny": "off",
24-
"noEmptyBlockStatements": "off"
24+
"noEmptyBlockStatements": "off",
25+
"noPrototypeBuiltins": "off"
2526
},
2627
"complexity": {
2728
"noStaticOnlyClass": "off",

src/Toolkit/kits/shadcn/alert-dialog/EXAMPLES.md

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,3 @@
2222
</twig:AlertDialog:Content>
2323
</twig:AlertDialog>
2424
```
25-
26-
## Opened by default
27-
28-
```twig {"preview":true,"height":"500px"}
29-
<twig:AlertDialog id="delete_account" open>
30-
<twig:AlertDialog:Trigger>
31-
<twig:Button {{ ...trigger_attrs }}>Open</twig:Button>
32-
</twig:AlertDialog:Trigger>
33-
<twig:AlertDialog:Content>
34-
<twig:AlertDialog:Header>
35-
<twig:AlertDialog:Title>Are you absolutely sure?</twig:AlertDialog:Title>
36-
<twig:AlertDialog:Description>
37-
This action cannot be undone. This will permanently delete your account
38-
and remove your data from our servers.
39-
</twig:AlertDialog:Description>
40-
</twig:AlertDialog:Header>
41-
<twig:AlertDialog:Footer>
42-
<twig:AlertDialog:Cancel>Cancel</twig:AlertDialog:Cancel>
43-
<twig:AlertDialog:Action>Continue</twig:AlertDialog:Action>
44-
</twig:AlertDialog:Footer>
45-
</twig:AlertDialog:Content>
46-
</twig:AlertDialog>
47-
```
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,34 @@
11
import { Controller } from '@hotwired/stimulus';
2-
import { enter, leave } from 'el-transition';
32

43
export default class extends Controller {
54

6-
static targets = ['trigger', 'overlay', 'dialog', 'content'];
5+
static targets = ['trigger', 'dialog'];
76

87
async open() {
98
this.dialogTarget.showModal();
109

11-
await Promise.all([enter(this.overlayTarget), enter(this.contentTarget)]);
12-
1310
if (this.hasTriggerTarget) {
14-
this.triggerTarget.setAttribute('aria-expanded', 'true');
11+
if (this.dialogTarget.getAnimations().length > 0) {
12+
this.dialogTarget.addEventListener('transitionend', () => {
13+
this.triggerTarget.setAttribute('aria-expanded', 'true');
14+
})
15+
} else {
16+
this.triggerTarget.setAttribute('aria-expanded', 'true');
17+
}
1518
}
1619
}
1720

1821
async close() {
19-
await Promise.all([leave(this.overlayTarget), leave(this.contentTarget)]);
20-
2122
this.dialogTarget.close();
2223

2324
if (this.hasTriggerTarget) {
24-
this.triggerTarget.setAttribute('aria-expanded', 'false');
25+
if (this.dialogTarget.getAnimations().length > 0) {
26+
this.dialogTarget.addEventListener('transitionend', () => {
27+
this.triggerTarget.setAttribute('aria-expanded', 'false');
28+
})
29+
} else {
30+
this.triggerTarget.setAttribute('aria-expanded', 'false');
31+
}
2532
}
2633
}
2734
}

src/Toolkit/kits/shadcn/alert-dialog/manifest.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
},
1010
"dependencies": {
1111
"recipe": ["button"],
12-
"composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra"],
13-
"npm": ["el-transition"],
14-
"importmap": ["el-transition"]
12+
"composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra"]
1513
}
1614
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
{# @prop open boolean Open (or not) the AlertDialog at initial rendering, default to `false` #}
1+
{# @prop id string Unique suffix identifier for generating AlertDialog internal IDs #}
22
{# @block content The default block #}
3-
{%- props open = false, id -%}
3+
{%- props id -%}
44

5-
{%- set _alert_dialog_open = open %}
65
{%- set _alert_dialog_id = 'alert-dialog-' ~ id -%}
76
{%- set _alert_dialog_title_id = _alert_dialog_id ~ '-title' -%}
87
{%- set _alert_dialog_description_id = _alert_dialog_id ~ '-description' -%}
Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,10 @@
11
{# @block content The default block #}
22
<dialog
33
id="{{ _alert_dialog_id }}"
4-
{{ _alert_dialog_open ? 'open' }}
5-
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 }}"
4+
class="{{ 'bg-background fixed top-[50%] left-[50%] z-50 max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] scale-95 gap-4 rounded-lg border p-6 opacity-0 shadow-lg transition-all transition-discrete duration-200 backdrop:transition-discrete backdrop:duration-150 open:grid open:scale-100 open:opacity-100 open:backdrop:bg-black/50 sm:max-w-lg starting:open:scale-95 starting:open:opacity-0 ' ~ attributes.render('class')|tailwind_merge }}"
65
data-alert-dialog-target="dialog"
76
data-action="keydown.esc->alert-dialog#close:prevent"
87
{{ attributes.without('id') }}
98
>
10-
<div
11-
data-alert-dialog-target="overlay"
12-
data-transition-enter="transition ease-out duration-100"
13-
data-transition-enter-start="transform opacity-0"
14-
data-transition-enter-end="transform opacity-100"
15-
data-transition-leave="transition ease-in duration-75"
16-
data-transition-leave-start="transform opacity-100"
17-
data-transition-leave-end="transform opacity-0"
18-
class="{{ _alert_dialog_open ? '' : 'hidden' }} fixed inset-0 z-50 bg-black/50"
19-
></div>
20-
21-
<section
22-
tabindex="0"
23-
class="flex min-h-full items-end justify-center p-4 text-center focus:outline-none sm:items-center sm:p-0"
24-
>
25-
<div
26-
data-transition-enter="transition ease-out duration-200"
27-
data-transition-enter-start="transform opacity-0 scale-95"
28-
data-transition-enter-end="transform opacity-100 scale-100"
29-
data-transition-leave="transition ease-in duration-75"
30-
data-transition-leave-start="transform opacity-100 scale-100"
31-
data-transition-leave-end="transform opacity-0 scale-95"
32-
class="{{ _alert_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"
33-
data-alert-dialog-target="content"
34-
>
35-
{%- block content %}{% endblock -%}
36-
</div>
37-
</section>
9+
{%- block content %}{% endblock -%}
3810
</dialog>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
'data-action': 'click->alert-dialog#open',
44
'data-alert-dialog-target': 'trigger',
55
'aria-haspopup': 'dialog',
6-
'aria-expanded': _alert_dialog_open ? 'true' : 'false',
6+
'aria-expanded': 'false',
77
} -%}
88
{%- block content %}{% endblock -%}
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
export default class extends Controller {
4+
5+
static targets = ['trigger', 'dialog'];
6+
7+
open() {
8+
this.dialogTarget.showModal();
9+
10+
if (this.hasTriggerTarget) {
11+
if (this.dialogTarget.getAnimations().length > 0) {
12+
this.dialogTarget.addEventListener('transitionend', () => {
13+
this.triggerTarget.setAttribute('aria-expanded', 'true');
14+
})
15+
} else {
16+
this.triggerTarget.setAttribute('aria-expanded', 'true');
17+
}
18+
}
19+
}
20+
21+
closeOnClickOutside({ target }) {
22+
if (target === this.dialogTarget) {
23+
this.close()
24+
}
25+
}
26+
27+
close() {
28+
this.dialogTarget.close();
29+
30+
if (this.hasTriggerTarget) {
31+
if (this.dialogTarget.getAnimations().length > 0) {
32+
this.dialogTarget.addEventListener('transitionend', () => {
33+
this.triggerTarget.setAttribute('aria-expanded', 'false');
34+
})
35+
} else {
36+
this.triggerTarget.setAttribute('aria-expanded', 'false');
37+
}
38+
}
39+
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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": [
12+
"symfony/ux-icons",
13+
"twig/extra-bundle",
14+
"twig/html-extra:^3.12.0",
15+
"tales-from-a-dev/twig-tailwind-extra"
16+
]
17+
}
18+
}

0 commit comments

Comments
 (0)