@edsis/ui 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/accordion/README.md +195 -0
- package/alert/README.md +177 -0
- package/alert-dialog/README.md +239 -0
- package/aspect-ratio/README.md +112 -0
- package/avatar/README.md +176 -0
- package/badge/README.md +133 -0
- package/breadcrumb/README.md +216 -0
- package/button/README.md +139 -0
- package/button-group/README.md +204 -0
- package/calendar/README.md +132 -0
- package/card/README.md +220 -0
- package/carousel/README.md +276 -0
- package/chart/README.md +249 -0
- package/checkbox/README.md +149 -0
- package/collapsible/README.md +191 -0
- package/combobox/README.md +198 -0
- package/command/README.md +275 -0
- package/composer/README.md +235 -0
- package/context-menu/README.md +267 -0
- package/date-picker/README.md +177 -0
- package/dialog/README.md +237 -0
- package/drawer/README.md +145 -0
- package/dropdown-menu/README.md +311 -0
- package/editor/README.md +136 -0
- package/empty/README.md +183 -0
- package/fesm2022/edsis-ui-accordion.mjs +174 -0
- package/fesm2022/edsis-ui-accordion.mjs.map +1 -0
- package/fesm2022/edsis-ui-alert-dialog.mjs +242 -0
- package/fesm2022/edsis-ui-alert-dialog.mjs.map +1 -0
- package/fesm2022/edsis-ui-alert.mjs +90 -0
- package/fesm2022/edsis-ui-alert.mjs.map +1 -0
- package/fesm2022/edsis-ui-aspect-ratio.mjs +33 -0
- package/fesm2022/edsis-ui-aspect-ratio.mjs.map +1 -0
- package/fesm2022/edsis-ui-avatar.mjs +123 -0
- package/fesm2022/edsis-ui-avatar.mjs.map +1 -0
- package/fesm2022/edsis-ui-badge.mjs +47 -0
- package/fesm2022/edsis-ui-badge.mjs.map +1 -0
- package/fesm2022/edsis-ui-breadcrumb.mjs +186 -0
- package/fesm2022/edsis-ui-breadcrumb.mjs.map +1 -0
- package/fesm2022/edsis-ui-button-group.mjs +95 -0
- package/fesm2022/edsis-ui-button-group.mjs.map +1 -0
- package/fesm2022/edsis-ui-button.mjs +64 -0
- package/fesm2022/edsis-ui-button.mjs.map +1 -0
- package/fesm2022/edsis-ui-calendar.mjs +78 -0
- package/fesm2022/edsis-ui-calendar.mjs.map +1 -0
- package/fesm2022/edsis-ui-card.mjs +137 -0
- package/fesm2022/edsis-ui-card.mjs.map +1 -0
- package/fesm2022/edsis-ui-carousel.mjs +310 -0
- package/fesm2022/edsis-ui-carousel.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-area.mjs +6 -0
- package/fesm2022/edsis-ui-chart-area.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-bar.mjs +6 -0
- package/fesm2022/edsis-ui-chart-bar.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-line.mjs +6 -0
- package/fesm2022/edsis-ui-chart-line.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-pie.mjs +6 -0
- package/fesm2022/edsis-ui-chart-pie.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-radar.mjs +6 -0
- package/fesm2022/edsis-ui-chart-radar.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-radial.mjs +6 -0
- package/fesm2022/edsis-ui-chart-radial.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart-scatter.mjs +6 -0
- package/fesm2022/edsis-ui-chart-scatter.mjs.map +1 -0
- package/fesm2022/edsis-ui-chart.mjs +3714 -0
- package/fesm2022/edsis-ui-chart.mjs.map +1 -0
- package/fesm2022/edsis-ui-checkbox.mjs +104 -0
- package/fesm2022/edsis-ui-checkbox.mjs.map +1 -0
- package/fesm2022/edsis-ui-collapsible.mjs +116 -0
- package/fesm2022/edsis-ui-collapsible.mjs.map +1 -0
- package/fesm2022/edsis-ui-combobox.mjs +263 -0
- package/fesm2022/edsis-ui-combobox.mjs.map +1 -0
- package/fesm2022/edsis-ui-command.mjs +268 -0
- package/fesm2022/edsis-ui-command.mjs.map +1 -0
- package/fesm2022/edsis-ui-composer.mjs +329 -0
- package/fesm2022/edsis-ui-composer.mjs.map +1 -0
- package/fesm2022/edsis-ui-context-menu.mjs +100 -0
- package/fesm2022/edsis-ui-context-menu.mjs.map +1 -0
- package/fesm2022/edsis-ui-date-picker.mjs +155 -0
- package/fesm2022/edsis-ui-date-picker.mjs.map +1 -0
- package/fesm2022/edsis-ui-dialog.mjs +262 -0
- package/fesm2022/edsis-ui-dialog.mjs.map +1 -0
- package/fesm2022/edsis-ui-drawer.mjs +6 -0
- package/fesm2022/edsis-ui-drawer.mjs.map +1 -0
- package/fesm2022/edsis-ui-dropdown-menu.mjs +466 -0
- package/fesm2022/edsis-ui-dropdown-menu.mjs.map +1 -0
- package/fesm2022/edsis-ui-editor.mjs +692 -0
- package/fesm2022/edsis-ui-editor.mjs.map +1 -0
- package/fesm2022/edsis-ui-empty.mjs +132 -0
- package/fesm2022/edsis-ui-empty.mjs.map +1 -0
- package/fesm2022/edsis-ui-form.mjs +334 -0
- package/fesm2022/edsis-ui-form.mjs.map +1 -0
- package/fesm2022/edsis-ui-hover-card.mjs +284 -0
- package/fesm2022/edsis-ui-hover-card.mjs.map +1 -0
- package/fesm2022/edsis-ui-input-group.mjs +164 -0
- package/fesm2022/edsis-ui-input-group.mjs.map +1 -0
- package/fesm2022/edsis-ui-input-otp.mjs +485 -0
- package/fesm2022/edsis-ui-input-otp.mjs.map +1 -0
- package/fesm2022/edsis-ui-input.mjs +43 -0
- package/fesm2022/edsis-ui-input.mjs.map +1 -0
- package/fesm2022/edsis-ui-item.mjs +241 -0
- package/fesm2022/edsis-ui-item.mjs.map +1 -0
- package/fesm2022/edsis-ui-kanban.mjs +289 -0
- package/fesm2022/edsis-ui-kanban.mjs.map +1 -0
- package/fesm2022/edsis-ui-kbd.mjs +51 -0
- package/fesm2022/edsis-ui-kbd.mjs.map +1 -0
- package/fesm2022/edsis-ui-label.mjs +30 -0
- package/fesm2022/edsis-ui-label.mjs.map +1 -0
- package/fesm2022/edsis-ui-menubar.mjs +302 -0
- package/fesm2022/edsis-ui-menubar.mjs.map +1 -0
- package/fesm2022/edsis-ui-native-select.mjs +61 -0
- package/fesm2022/edsis-ui-native-select.mjs.map +1 -0
- package/fesm2022/edsis-ui-navigation-menu.mjs +399 -0
- package/fesm2022/edsis-ui-navigation-menu.mjs.map +1 -0
- package/fesm2022/edsis-ui-pagination.mjs +216 -0
- package/fesm2022/edsis-ui-pagination.mjs.map +1 -0
- package/fesm2022/edsis-ui-pillbox.mjs +777 -0
- package/fesm2022/edsis-ui-pillbox.mjs.map +1 -0
- package/fesm2022/edsis-ui-popover.mjs +163 -0
- package/fesm2022/edsis-ui-popover.mjs.map +1 -0
- package/fesm2022/edsis-ui-progress.mjs +53 -0
- package/fesm2022/edsis-ui-progress.mjs.map +1 -0
- package/fesm2022/edsis-ui-radio.mjs +111 -0
- package/fesm2022/edsis-ui-radio.mjs.map +1 -0
- package/fesm2022/edsis-ui-resizable.mjs +454 -0
- package/fesm2022/edsis-ui-resizable.mjs.map +1 -0
- package/fesm2022/edsis-ui-scroll-area.mjs +48 -0
- package/fesm2022/edsis-ui-scroll-area.mjs.map +1 -0
- package/fesm2022/edsis-ui-select.mjs +164 -0
- package/fesm2022/edsis-ui-select.mjs.map +1 -0
- package/fesm2022/edsis-ui-separator.mjs +33 -0
- package/fesm2022/edsis-ui-separator.mjs.map +1 -0
- package/fesm2022/edsis-ui-sheet.mjs +264 -0
- package/fesm2022/edsis-ui-sheet.mjs.map +1 -0
- package/fesm2022/edsis-ui-skeleton.mjs +29 -0
- package/fesm2022/edsis-ui-skeleton.mjs.map +1 -0
- package/fesm2022/edsis-ui-slider.mjs +405 -0
- package/fesm2022/edsis-ui-slider.mjs.map +1 -0
- package/fesm2022/edsis-ui-spinner.mjs +58 -0
- package/fesm2022/edsis-ui-spinner.mjs.map +1 -0
- package/fesm2022/edsis-ui-switch.mjs +107 -0
- package/fesm2022/edsis-ui-switch.mjs.map +1 -0
- package/fesm2022/edsis-ui-table.mjs +139 -0
- package/fesm2022/edsis-ui-table.mjs.map +1 -0
- package/fesm2022/edsis-ui-tabs.mjs +252 -0
- package/fesm2022/edsis-ui-tabs.mjs.map +1 -0
- package/fesm2022/edsis-ui-textarea.mjs +37 -0
- package/fesm2022/edsis-ui-textarea.mjs.map +1 -0
- package/fesm2022/edsis-ui-timeline.mjs +213 -0
- package/fesm2022/edsis-ui-timeline.mjs.map +1 -0
- package/fesm2022/edsis-ui-toast.mjs +71 -0
- package/fesm2022/edsis-ui-toast.mjs.map +1 -0
- package/fesm2022/edsis-ui-toggle-group.mjs +269 -0
- package/fesm2022/edsis-ui-toggle-group.mjs.map +1 -0
- package/fesm2022/edsis-ui-toggle.mjs +76 -0
- package/fesm2022/edsis-ui-toggle.mjs.map +1 -0
- package/fesm2022/edsis-ui-tooltip.mjs +339 -0
- package/fesm2022/edsis-ui-tooltip.mjs.map +1 -0
- package/fesm2022/edsis-ui-utils.mjs +13 -0
- package/fesm2022/edsis-ui-utils.mjs.map +1 -0
- package/fesm2022/edsis-ui.mjs +11 -0
- package/fesm2022/edsis-ui.mjs.map +1 -0
- package/form/README.md +210 -0
- package/hover-card/README.md +146 -0
- package/input/README.md +159 -0
- package/input-group/README.md +234 -0
- package/input-otp/README.md +273 -0
- package/item/README.md +247 -0
- package/kanban/README.md +81 -0
- package/kbd/README.md +139 -0
- package/label/README.md +136 -0
- package/menubar/README.md +269 -0
- package/native-select/README.md +176 -0
- package/navigation-menu/README.md +160 -0
- package/package.json +310 -0
- package/pagination/README.md +144 -0
- package/pillbox/README.md +67 -0
- package/popover/README.md +43 -0
- package/progress/README.md +160 -0
- package/radio/README.md +209 -0
- package/resizable/README.md +164 -0
- package/scroll-area/README.md +143 -0
- package/select/README.md +174 -0
- package/separator/README.md +170 -0
- package/sheet/README.md +183 -0
- package/skeleton/README.md +158 -0
- package/slider/README.md +207 -0
- package/spinner/README.md +160 -0
- package/switch/README.md +166 -0
- package/table/README.md +291 -0
- package/tabs/README.md +214 -0
- package/textarea/README.md +154 -0
- package/timeline/README.md +94 -0
- package/toast/README.md +321 -0
- package/toggle/README.md +131 -0
- package/toggle-group/README.md +206 -0
- package/tooltip/README.md +211 -0
- package/types/edsis-ui-accordion.d.ts +51 -0
- package/types/edsis-ui-alert-dialog.d.ts +93 -0
- package/types/edsis-ui-alert.d.ts +37 -0
- package/types/edsis-ui-aspect-ratio.d.ts +12 -0
- package/types/edsis-ui-avatar.d.ts +51 -0
- package/types/edsis-ui-badge.d.ts +19 -0
- package/types/edsis-ui-breadcrumb.d.ts +46 -0
- package/types/edsis-ui-button-group.d.ts +26 -0
- package/types/edsis-ui-button.d.ts +22 -0
- package/types/edsis-ui-calendar.d.ts +33 -0
- package/types/edsis-ui-card.d.ts +60 -0
- package/types/edsis-ui-carousel.d.ts +86 -0
- package/types/edsis-ui-chart-area.d.ts +1 -0
- package/types/edsis-ui-chart-bar.d.ts +1 -0
- package/types/edsis-ui-chart-line.d.ts +1 -0
- package/types/edsis-ui-chart-pie.d.ts +1 -0
- package/types/edsis-ui-chart-radar.d.ts +1 -0
- package/types/edsis-ui-chart-radial.d.ts +1 -0
- package/types/edsis-ui-chart-scatter.d.ts +1 -0
- package/types/edsis-ui-chart.d.ts +1094 -0
- package/types/edsis-ui-checkbox.d.ts +35 -0
- package/types/edsis-ui-collapsible.d.ts +42 -0
- package/types/edsis-ui-combobox.d.ts +51 -0
- package/types/edsis-ui-command.d.ts +99 -0
- package/types/edsis-ui-composer.d.ts +90 -0
- package/types/edsis-ui-context-menu.d.ts +35 -0
- package/types/edsis-ui-date-picker.d.ts +41 -0
- package/types/edsis-ui-dialog.d.ts +87 -0
- package/types/edsis-ui-drawer.d.ts +1 -0
- package/types/edsis-ui-dropdown-menu.d.ts +136 -0
- package/types/edsis-ui-editor.d.ts +123 -0
- package/types/edsis-ui-empty.d.ts +50 -0
- package/types/edsis-ui-form.d.ts +141 -0
- package/types/edsis-ui-hover-card.d.ts +74 -0
- package/types/edsis-ui-input-group.d.ts +51 -0
- package/types/edsis-ui-input-otp.d.ts +136 -0
- package/types/edsis-ui-input.d.ts +16 -0
- package/types/edsis-ui-item.d.ts +88 -0
- package/types/edsis-ui-kanban.d.ts +70 -0
- package/types/edsis-ui-kbd.d.ts +16 -0
- package/types/edsis-ui-label.d.ts +11 -0
- package/types/edsis-ui-menubar.d.ts +67 -0
- package/types/edsis-ui-native-select.d.ts +26 -0
- package/types/edsis-ui-navigation-menu.d.ts +96 -0
- package/types/edsis-ui-pagination.d.ts +34 -0
- package/types/edsis-ui-pillbox.d.ts +157 -0
- package/types/edsis-ui-popover.d.ts +43 -0
- package/types/edsis-ui-progress.d.ts +17 -0
- package/types/edsis-ui-radio.d.ts +40 -0
- package/types/edsis-ui-resizable.d.ts +99 -0
- package/types/edsis-ui-scroll-area.d.ts +19 -0
- package/types/edsis-ui-select.d.ts +57 -0
- package/types/edsis-ui-separator.d.ts +14 -0
- package/types/edsis-ui-sheet.d.ts +76 -0
- package/types/edsis-ui-skeleton.d.ts +10 -0
- package/types/edsis-ui-slider.d.ts +74 -0
- package/types/edsis-ui-spinner.d.ts +13 -0
- package/types/edsis-ui-switch.d.ts +40 -0
- package/types/edsis-ui-table.d.ts +52 -0
- package/types/edsis-ui-tabs.d.ts +92 -0
- package/types/edsis-ui-textarea.d.ts +12 -0
- package/types/edsis-ui-timeline.d.ts +63 -0
- package/types/edsis-ui-toast.d.ts +38 -0
- package/types/edsis-ui-toggle-group.d.ts +89 -0
- package/types/edsis-ui-toggle.d.ts +25 -0
- package/types/edsis-ui-tooltip.d.ts +89 -0
- package/types/edsis-ui-utils.d.ts +5 -0
- package/types/edsis-ui.d.ts +2 -0
package/dialog/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+
Displays a modal surface over the current page, traps focus while open, and restores focus to the previously active element when it closes.
|
|
4
|
+
|
|
5
|
+
Use Dialog for confirmation flows, compact forms, link sharing, and long-form review tasks that should keep the user in the current context.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { ButtonComponent } from '@edsis/ui/button';
|
|
11
|
+
import {
|
|
12
|
+
DialogCloseDirective,
|
|
13
|
+
DialogComponent,
|
|
14
|
+
DialogContentComponent,
|
|
15
|
+
DialogDescriptionComponent,
|
|
16
|
+
DialogFooterComponent,
|
|
17
|
+
DialogHeaderComponent,
|
|
18
|
+
DialogTitleComponent,
|
|
19
|
+
} from '@edsis/ui/dialog';
|
|
20
|
+
import { InputComponent } from '@edsis/ui/input';
|
|
21
|
+
import { LabelComponent } from '@edsis/ui/label';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Composition
|
|
25
|
+
|
|
26
|
+
The Angular composition tracks the shadcn structure, with one intentional mapping difference: shadcn `DialogTrigger` becomes any external control that toggles the `open` signal.
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
button[ui-button] (external trigger; sets the open signal)
|
|
30
|
+
ui-dialog
|
|
31
|
+
├── built-in close button (optional)
|
|
32
|
+
├── ui-dialog-header
|
|
33
|
+
│ ├── ui-dialog-title
|
|
34
|
+
│ └── ui-dialog-description
|
|
35
|
+
├── ui-dialog-content (optional body wrapper)
|
|
36
|
+
└── ui-dialog-footer
|
|
37
|
+
└── button[ui-dialog-close] (optional custom close action)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Basic usage
|
|
41
|
+
|
|
42
|
+
Use an external button or link to toggle the `open` signal, then declare the dialog structure inside `ui-dialog`.
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<button type="button" ui-button variant="outline" (click)="open.set(true)">Edit profile</button>
|
|
46
|
+
|
|
47
|
+
<ui-dialog [(open)]="open" aria-labelledby="dialog-title" aria-describedby="dialog-description" class="sm:max-w-sm">
|
|
48
|
+
<ui-dialog-header>
|
|
49
|
+
<ui-dialog-title id="dialog-title">Edit profile</ui-dialog-title>
|
|
50
|
+
<ui-dialog-description id="dialog-description">
|
|
51
|
+
Make changes to your profile here. Click save when you are done.
|
|
52
|
+
</ui-dialog-description>
|
|
53
|
+
</ui-dialog-header>
|
|
54
|
+
|
|
55
|
+
<ui-dialog-content class="grid gap-4 py-2">
|
|
56
|
+
<div class="grid gap-2">
|
|
57
|
+
<label ui-label for="name">Name</label>
|
|
58
|
+
<input ui-input id="name" value="Pedro Duarte" />
|
|
59
|
+
</div>
|
|
60
|
+
<div class="grid gap-2">
|
|
61
|
+
<label ui-label for="username">Username</label>
|
|
62
|
+
<input ui-input id="username" value="@peduarte" />
|
|
63
|
+
</div>
|
|
64
|
+
</ui-dialog-content>
|
|
65
|
+
|
|
66
|
+
<ui-dialog-footer>
|
|
67
|
+
<button type="button" ui-button variant="outline" ui-dialog-close>Cancel</button>
|
|
68
|
+
<button type="button" ui-button (click)="open.set(false)">Save changes</button>
|
|
69
|
+
</ui-dialog-footer>
|
|
70
|
+
</ui-dialog>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Common patterns
|
|
74
|
+
|
|
75
|
+
### Destructive confirmation
|
|
76
|
+
|
|
77
|
+
Use a short title, short body copy, and explicit cancel/confirm actions for destructive flows.
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<button type="button" ui-button variant="destructive" (click)="confirmOpen.set(true)">Delete project</button>
|
|
81
|
+
|
|
82
|
+
<ui-dialog [(open)]="confirmOpen">
|
|
83
|
+
<ui-dialog-header>
|
|
84
|
+
<ui-dialog-title>Delete this project?</ui-dialog-title>
|
|
85
|
+
<ui-dialog-description>
|
|
86
|
+
This action cannot be undone. This permanently deletes your project.
|
|
87
|
+
</ui-dialog-description>
|
|
88
|
+
</ui-dialog-header>
|
|
89
|
+
<ui-dialog-footer>
|
|
90
|
+
<button type="button" ui-button variant="outline" ui-dialog-close>Cancel</button>
|
|
91
|
+
<button type="button" ui-button variant="destructive" (click)="confirmOpen.set(false)">Delete</button>
|
|
92
|
+
</ui-dialog-footer>
|
|
93
|
+
</ui-dialog>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Custom close button
|
|
97
|
+
|
|
98
|
+
Hide the built-in corner close control with `[showCloseButton]="false"` and place `button[ui-dialog-close]` wherever dismissal should live.
|
|
99
|
+
|
|
100
|
+
```html
|
|
101
|
+
<ui-dialog [(open)]="shareOpen" [showCloseButton]="false" class="sm:max-w-md">
|
|
102
|
+
<ui-dialog-header>
|
|
103
|
+
<ui-dialog-title>Share link</ui-dialog-title>
|
|
104
|
+
<ui-dialog-description> Anyone who has this link can view this workspace. </ui-dialog-description>
|
|
105
|
+
</ui-dialog-header>
|
|
106
|
+
<ui-dialog-content class="grid gap-4 py-2">
|
|
107
|
+
<div class="grid gap-2">
|
|
108
|
+
<label ui-label for="share-link">Link</label>
|
|
109
|
+
<input ui-input id="share-link" readonly value="https://ui.shadcn.com/docs/components/radix/dialog" />
|
|
110
|
+
</div>
|
|
111
|
+
</ui-dialog-content>
|
|
112
|
+
<ui-dialog-footer class="sm:justify-start">
|
|
113
|
+
<button type="button" ui-button ui-dialog-close>Close</button>
|
|
114
|
+
</ui-dialog-footer>
|
|
115
|
+
</ui-dialog>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### No close button
|
|
119
|
+
|
|
120
|
+
Use `[showCloseButton]="false"` without an internal close action when Escape and backdrop dismissal are enough.
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<ui-dialog [(open)]="noCloseOpen" [showCloseButton]="false">
|
|
124
|
+
<ui-dialog-header>
|
|
125
|
+
<ui-dialog-title>No Close Button</ui-dialog-title>
|
|
126
|
+
<ui-dialog-description>
|
|
127
|
+
Dismiss this dialog with Escape, the backdrop, or another action you control.
|
|
128
|
+
</ui-dialog-description>
|
|
129
|
+
</ui-dialog-header>
|
|
130
|
+
</ui-dialog>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Sticky footer for long content
|
|
134
|
+
|
|
135
|
+
Move the scroll region into `ui-dialog-content` and keep the footer outside that overflow container so primary actions remain visible.
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<ui-dialog [(open)]="stickyOpen" [showCloseButton]="false" class="max-h-[85vh] overflow-hidden p-0 sm:max-w-lg">
|
|
139
|
+
<ui-dialog-header class="px-6 pt-6">
|
|
140
|
+
<ui-dialog-title>Sticky Footer</ui-dialog-title>
|
|
141
|
+
<ui-dialog-description>Keep actions visible while the content scrolls.</ui-dialog-description>
|
|
142
|
+
</ui-dialog-header>
|
|
143
|
+
<ui-dialog-content class="max-h-[45vh] overflow-y-auto px-6 pb-6">
|
|
144
|
+
<!-- long content -->
|
|
145
|
+
</ui-dialog-content>
|
|
146
|
+
<ui-dialog-footer class="border-t border-border bg-background px-6 py-4 sm:justify-start">
|
|
147
|
+
<button type="button" ui-button variant="outline" ui-dialog-close>Close</button>
|
|
148
|
+
<button type="button" ui-button (click)="stickyOpen.set(false)">Save changes</button>
|
|
149
|
+
</ui-dialog-footer>
|
|
150
|
+
</ui-dialog>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### RTL
|
|
154
|
+
|
|
155
|
+
Wrap the trigger and dialog in a `dir="rtl"` container or set direction globally in the app shell.
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<div dir="rtl" lang="ar" class="text-right">
|
|
159
|
+
<button type="button" ui-button variant="outline" (click)="rtlOpen.set(true)">فتح الحوار</button>
|
|
160
|
+
|
|
161
|
+
<ui-dialog [(open)]="rtlOpen" [showCloseButton]="false" class="sm:max-w-sm">
|
|
162
|
+
<ui-dialog-header>
|
|
163
|
+
<ui-dialog-title>تعديل الملف الشخصي</ui-dialog-title>
|
|
164
|
+
<ui-dialog-description> قم بإجراء تغييرات على ملفك الشخصي هنا. انقر فوق حفظ عند الانتهاء. </ui-dialog-description>
|
|
165
|
+
</ui-dialog-header>
|
|
166
|
+
<ui-dialog-content class="grid gap-4 py-2">
|
|
167
|
+
<div class="grid gap-2">...</div>
|
|
168
|
+
<div class="grid gap-2">...</div>
|
|
169
|
+
</ui-dialog-content>
|
|
170
|
+
<ui-dialog-footer>
|
|
171
|
+
<button type="button" ui-button variant="outline" ui-dialog-close>إلغاء</button>
|
|
172
|
+
<button type="button" ui-button (click)="rtlOpen.set(false)">حفظ التغييرات</button>
|
|
173
|
+
</ui-dialog-footer>
|
|
174
|
+
</ui-dialog>
|
|
175
|
+
</div>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## API reference
|
|
179
|
+
|
|
180
|
+
### `DialogComponent`
|
|
181
|
+
|
|
182
|
+
| Input | Type | Default |
|
|
183
|
+
| ---------------------- | ---------------- | --------- |
|
|
184
|
+
| `open` (model) | `boolean` | `false` |
|
|
185
|
+
| `closeOnEscape` | `boolean` | `true` |
|
|
186
|
+
| `closeOnBackdropClick` | `boolean` | `true` |
|
|
187
|
+
| `showCloseButton` | `boolean` | `true` |
|
|
188
|
+
| `closeButtonLabel` | `string` | `'Close'` |
|
|
189
|
+
| `aria-labelledby` | `string \| null` | `null` |
|
|
190
|
+
| `aria-describedby` | `string \| null` | `null` |
|
|
191
|
+
| `class` | `string` | `''` |
|
|
192
|
+
|
|
193
|
+
Output: `openedChange: boolean`. Method: `close()`.
|
|
194
|
+
|
|
195
|
+
### Parts
|
|
196
|
+
|
|
197
|
+
| Part | Purpose |
|
|
198
|
+
| ------------------------- | -------------------------------------------------------- |
|
|
199
|
+
| `ui-dialog-header` | Title and description wrapper |
|
|
200
|
+
| `ui-dialog-title` | Primary dialog label |
|
|
201
|
+
| `ui-dialog-description` | Supporting copy announced by assistive tech |
|
|
202
|
+
| `ui-dialog-content` | Optional body wrapper for forms, grids, and scroll areas |
|
|
203
|
+
| `ui-dialog-footer` | Action row |
|
|
204
|
+
| `button[ui-dialog-close]` | Custom close action mapped to shadcn `DialogClose` |
|
|
205
|
+
|
|
206
|
+
Lower-level behavior is based on the Radix Dialog pattern: <https://www.radix-ui.com/primitives/docs/components/dialog#api-reference>.
|
|
207
|
+
|
|
208
|
+
## Styling and theming
|
|
209
|
+
|
|
210
|
+
The dialog surface uses the shared theme tokens for background, border, foreground, and ring colors. Pass `class` to `ui-dialog` when you need to adjust width, max height, overflow strategy, or padding for a specific modal pattern.
|
|
211
|
+
|
|
212
|
+
Use `ui-dialog-content` and `ui-dialog-footer` classes to create scroll regions, sticky action rows, or wider content layouts without forking the primitive.
|
|
213
|
+
|
|
214
|
+
## Accessibility
|
|
215
|
+
|
|
216
|
+
- The surface renders with `role="dialog"` and `aria-modal="true"`.
|
|
217
|
+
- Focus is trapped inside with `FocusTrap` from `@angular/cdk/a11y`.
|
|
218
|
+
- Focus returns to the previously active element when the dialog closes.
|
|
219
|
+
- Escape and backdrop dismissal are enabled by default but can be disabled for stricter flows.
|
|
220
|
+
- Keep titles and descriptions concise so screen readers announce useful context immediately.
|
|
221
|
+
|
|
222
|
+
## Keyboard interactions
|
|
223
|
+
|
|
224
|
+
- `Tab` and `Shift+Tab` stay inside the dialog while it is open.
|
|
225
|
+
- `Escape` closes the dialog unless `closeOnEscape` is disabled.
|
|
226
|
+
- Native button activation handles `Enter` and `Space` for dialog actions.
|
|
227
|
+
|
|
228
|
+
## Angular notes
|
|
229
|
+
|
|
230
|
+
- This implementation intentionally maps shadcn `DialogTrigger` to an external control that owns the `open` signal.
|
|
231
|
+
- `button[ui-dialog-close]` is the Angular-friendly equivalent of shadcn `DialogClose` for internal dismissal actions.
|
|
232
|
+
- Seed open state with a signal in the owning component and keep the dialog content declarative.
|
|
233
|
+
- Hide the built-in close affordance with `[showCloseButton]="false"` when the footer or body owns dismissal explicitly.
|
|
234
|
+
|
|
235
|
+
## Source parity
|
|
236
|
+
|
|
237
|
+
This Angular implementation follows the shadcn Dialog information architecture and examples while translating the interaction model to Angular selectors, signal-driven state, and explicit ownership of the trigger outside the modal content.
|
package/drawer/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
Bottom-first modal drawer built on the local Sheet overlay primitive. It mirrors the shadcn Drawer composition while using Angular signals and explicit standalone imports.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { ButtonComponent } from '@edsis/ui/button';
|
|
9
|
+
import {
|
|
10
|
+
DrawerCloseDirective,
|
|
11
|
+
DrawerComponent,
|
|
12
|
+
DrawerContentComponent,
|
|
13
|
+
DrawerDescriptionComponent,
|
|
14
|
+
DrawerFooterComponent,
|
|
15
|
+
DrawerHeaderComponent,
|
|
16
|
+
DrawerTitleComponent,
|
|
17
|
+
} from '@edsis/ui/drawer';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
24
|
+
import { ButtonComponent } from '@edsis/ui/button';
|
|
25
|
+
import {
|
|
26
|
+
DrawerCloseDirective,
|
|
27
|
+
DrawerComponent,
|
|
28
|
+
DrawerDescriptionComponent,
|
|
29
|
+
DrawerFooterComponent,
|
|
30
|
+
DrawerHeaderComponent,
|
|
31
|
+
DrawerTitleComponent,
|
|
32
|
+
} from '@edsis/ui/drawer';
|
|
33
|
+
|
|
34
|
+
@Component({
|
|
35
|
+
selector: 'app-drawer-example',
|
|
36
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
37
|
+
imports: [
|
|
38
|
+
ButtonComponent,
|
|
39
|
+
DrawerCloseDirective,
|
|
40
|
+
DrawerComponent,
|
|
41
|
+
DrawerDescriptionComponent,
|
|
42
|
+
DrawerFooterComponent,
|
|
43
|
+
DrawerHeaderComponent,
|
|
44
|
+
DrawerTitleComponent,
|
|
45
|
+
],
|
|
46
|
+
template: `
|
|
47
|
+
<button type="button" ui-button variant="outline" (click)="open.set(true)">Open Drawer</button>
|
|
48
|
+
|
|
49
|
+
<ui-drawer [(open)]="open" aria-labelledby="drawer-title" aria-describedby="drawer-description">
|
|
50
|
+
<div class="mx-auto w-full max-w-sm">
|
|
51
|
+
<ui-drawer-header>
|
|
52
|
+
<ui-drawer-title id="drawer-title">Move Goal</ui-drawer-title>
|
|
53
|
+
<ui-drawer-description id="drawer-description">Set your daily activity goal.</ui-drawer-description>
|
|
54
|
+
</ui-drawer-header>
|
|
55
|
+
<ui-drawer-footer>
|
|
56
|
+
<button type="button" ui-button>Submit</button>
|
|
57
|
+
<button type="button" ui-button variant="outline" ui-drawer-close>Cancel</button>
|
|
58
|
+
</ui-drawer-footer>
|
|
59
|
+
</div>
|
|
60
|
+
</ui-drawer>
|
|
61
|
+
`,
|
|
62
|
+
})
|
|
63
|
+
export class DrawerExampleComponent {
|
|
64
|
+
protected readonly open = signal(false);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Common Patterns
|
|
69
|
+
|
|
70
|
+
### Sides
|
|
71
|
+
|
|
72
|
+
Drawer defaults to `bottom`, matching the common shadcn and Vaul pattern. Set `side` when the drawer should enter from a different edge.
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<ui-drawer [(open)]="open" side="right" aria-labelledby="drawer-side-title">
|
|
76
|
+
<ui-drawer-header>
|
|
77
|
+
<ui-drawer-title id="drawer-side-title">Scrollable Content</ui-drawer-title>
|
|
78
|
+
</ui-drawer-header>
|
|
79
|
+
<ui-drawer-content class="max-h-[50vh] overflow-y-auto px-1">
|
|
80
|
+
<!-- long content -->
|
|
81
|
+
</ui-drawer-content>
|
|
82
|
+
</ui-drawer>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Responsive Dialog
|
|
86
|
+
|
|
87
|
+
Use a desktop breakpoint in the owning component and render Dialog for larger viewports, Drawer for smaller viewports. Keep both surfaces bound to the same `open` signal so trigger and submit actions stay consistent.
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
@if (isDesktop()) {
|
|
91
|
+
<ui-dialog [(open)]="open">...</ui-dialog>
|
|
92
|
+
} @else {
|
|
93
|
+
<ui-drawer [(open)]="open">...</ui-drawer>
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### DrawerComponent
|
|
100
|
+
|
|
101
|
+
| Input | Type | Default |
|
|
102
|
+
| ---------------------- | ---------------------------------------- | -------------------------- |
|
|
103
|
+
| `open` | `boolean` model | `false` |
|
|
104
|
+
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` for `ui-drawer` |
|
|
105
|
+
| `closeOnEscape` | `boolean` | `true` |
|
|
106
|
+
| `closeOnBackdropClick` | `boolean` | `true` |
|
|
107
|
+
| `aria-labelledby` | `string \| null` | `null` |
|
|
108
|
+
| `aria-describedby` | `string \| null` | `null` |
|
|
109
|
+
| `class` | `string` | empty string |
|
|
110
|
+
|
|
111
|
+
### Parts
|
|
112
|
+
|
|
113
|
+
| Part | Purpose |
|
|
114
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
115
|
+
| `ui-drawer-header` | Title and description wrapper. |
|
|
116
|
+
| `ui-drawer-title` | Accessible title. Provide an `id` and connect it with `aria-labelledby`. |
|
|
117
|
+
| `ui-drawer-description` | Supporting text. Provide an `id` and connect it with `aria-describedby` when useful. |
|
|
118
|
+
| `ui-drawer-content` | Optional scroll/body region inside the surface. |
|
|
119
|
+
| `ui-drawer-footer` | Action row. |
|
|
120
|
+
| `button[ui-drawer-close]`, `a[ui-drawer-close]` | Projected close action equivalent to shadcn `DrawerClose`. |
|
|
121
|
+
|
|
122
|
+
## Styling And Theming
|
|
123
|
+
|
|
124
|
+
The drawer uses the same theme tokens as Sheet: `bg-background`, `border-border`, `text-foreground`, and `text-muted-foreground`. Pass `class` to tune dimensions such as `max-h-[85vh]`, `sm:max-w-md`, or side-specific scroll behavior.
|
|
125
|
+
|
|
126
|
+
## Accessibility
|
|
127
|
+
|
|
128
|
+
- The surface renders with `role="dialog"` and `aria-modal="true"`.
|
|
129
|
+
- Focus is trapped inside the CDK overlay while open and restored to the previously focused element on close.
|
|
130
|
+
- Provide `aria-labelledby` and `aria-describedby` when the drawer contains a title and description.
|
|
131
|
+
- Use native buttons for open, submit, and close actions.
|
|
132
|
+
|
|
133
|
+
## Keyboard Interactions
|
|
134
|
+
|
|
135
|
+
- `Tab` and `Shift+Tab` move within the focus-trapped drawer content.
|
|
136
|
+
- `Escape` closes the drawer when `closeOnEscape` is `true`.
|
|
137
|
+
- Enter and Space activate native buttons, including `button[ui-drawer-close]`.
|
|
138
|
+
|
|
139
|
+
## Angular Notes
|
|
140
|
+
|
|
141
|
+
The Angular Drawer deliberately uses an external trigger button and `[(open)]` signal binding instead of a `DrawerTrigger` component. This keeps ownership of state explicit and avoids hidden context wiring across overlay portals. `DrawerComponent` is exported as an alias over the same implementation as `SheetComponent`, so fixes to focus management, backdrop dismissal, and edge positioning are shared.
|
|
142
|
+
|
|
143
|
+
## Source Parity
|
|
144
|
+
|
|
145
|
+
shadcn Drawer is built on Vaul and includes drag gestures and scale-background options. This Angular version maps the core modal drawer behavior to CDK Overlay and FocusTrap: bottom-first direction, side selection, focus trapping, Escape/backdrop dismissal, close actions, scrollable content, responsive dialog composition, and RTL-friendly content. It does not implement Vaul drag gestures.
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Dropdown Menu
|
|
2
|
+
|
|
3
|
+
Accessible menu rendered in a CDK overlay with keyboard navigation (arrow keys,
|
|
4
|
+
Home/End, type-ahead, Escape).
|
|
5
|
+
|
|
6
|
+
This Angular implementation follows the shadcn Dropdown Menu information architecture while mapping the React root
|
|
7
|
+
component to an explicit trigger directive and projected menu template.
|
|
8
|
+
|
|
9
|
+
## Import
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
MenuCheckboxItemComponent,
|
|
14
|
+
MenuTriggerDirective,
|
|
15
|
+
MenuContentDirective,
|
|
16
|
+
MenuGroupComponent,
|
|
17
|
+
MenuSurfaceComponent,
|
|
18
|
+
MenuItemComponent,
|
|
19
|
+
MenuRadioGroupComponent,
|
|
20
|
+
MenuRadioItemComponent,
|
|
21
|
+
MenuSeparatorComponent,
|
|
22
|
+
MenuLabelComponent,
|
|
23
|
+
MenuShortcutComponent,
|
|
24
|
+
} from '@edsis/ui/dropdown-menu';
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<button
|
|
31
|
+
ui-button
|
|
32
|
+
type="button"
|
|
33
|
+
variant="outline"
|
|
34
|
+
#accountTrigger="uiMenuTrigger"
|
|
35
|
+
[uiMenuTrigger]="menu"
|
|
36
|
+
side="bottom"
|
|
37
|
+
align="end">
|
|
38
|
+
Open
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
<ng-template uiMenuContent #menu="uiMenuContent">
|
|
42
|
+
<ui-menu-surface class="w-56">
|
|
43
|
+
<ui-menu-group>
|
|
44
|
+
<ui-menu-label>My account</ui-menu-label>
|
|
45
|
+
<button ui-menu-item type="button" (selected)="onProfile(); accountTrigger.close()">
|
|
46
|
+
Profile
|
|
47
|
+
<span ui-menu-shortcut>⌘P</span>
|
|
48
|
+
</button>
|
|
49
|
+
<button ui-menu-item type="button" (selected)="onSettings(); accountTrigger.close()">Settings</button>
|
|
50
|
+
</ui-menu-group>
|
|
51
|
+
<ui-menu-separator />
|
|
52
|
+
<ui-menu-group>
|
|
53
|
+
<button ui-menu-item type="button" disabled>Disabled</button>
|
|
54
|
+
<button ui-menu-item type="button" variant="destructive" (selected)="onSignOut(); accountTrigger.close()">
|
|
55
|
+
Sign out
|
|
56
|
+
</button>
|
|
57
|
+
</ui-menu-group>
|
|
58
|
+
</ui-menu-surface>
|
|
59
|
+
</ng-template>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Use the trigger export when a standard command row should dismiss the overlay. Checkbox and radio rows can stay open so
|
|
63
|
+
the user can adjust multiple settings in one pass.
|
|
64
|
+
|
|
65
|
+
## Composition
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
button[ui-button][uiMenuTrigger]
|
|
69
|
+
└── ng-template[uiMenuContent]
|
|
70
|
+
└── ui-menu-surface
|
|
71
|
+
├── ui-menu-group
|
|
72
|
+
│ ├── ui-menu-label
|
|
73
|
+
│ ├── button[ui-menu-item]
|
|
74
|
+
│ └── button[ui-menu-item][uiMenuTrigger]
|
|
75
|
+
│ └── ng-template[uiMenuContent]
|
|
76
|
+
│ └── ui-menu-surface
|
|
77
|
+
├── ui-menu-separator
|
|
78
|
+
├── button[ui-menu-checkbox-item]
|
|
79
|
+
└── ui-menu-radio-group
|
|
80
|
+
└── button[ui-menu-radio-item]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Common patterns
|
|
84
|
+
|
|
85
|
+
### Basic account menu
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<button ui-button type="button" variant="outline" #menuTrigger="uiMenuTrigger" [uiMenuTrigger]="menu">Open</button>
|
|
89
|
+
|
|
90
|
+
<ng-template uiMenuContent #menu="uiMenuContent">
|
|
91
|
+
<ui-menu-surface class="w-56">
|
|
92
|
+
<ui-menu-group>
|
|
93
|
+
<ui-menu-label>My Account</ui-menu-label>
|
|
94
|
+
<button ui-menu-item type="button" (selected)="menuTrigger.close()">Profile</button>
|
|
95
|
+
<button ui-menu-item type="button" (selected)="menuTrigger.close()">Billing</button>
|
|
96
|
+
<button ui-menu-item type="button" (selected)="menuTrigger.close()">Settings</button>
|
|
97
|
+
</ui-menu-group>
|
|
98
|
+
<ui-menu-separator />
|
|
99
|
+
<ui-menu-group>
|
|
100
|
+
<button ui-menu-item type="button" (selected)="menuTrigger.close()">GitHub</button>
|
|
101
|
+
<button ui-menu-item type="button" (selected)="menuTrigger.close()">Support</button>
|
|
102
|
+
<button ui-menu-item type="button" [disabled]="true">API</button>
|
|
103
|
+
</ui-menu-group>
|
|
104
|
+
</ui-menu-surface>
|
|
105
|
+
</ng-template>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Submenu
|
|
109
|
+
|
|
110
|
+
Map shadcn `DropdownMenuSub` to `uiMenuTrigger` on a `button[ui-menu-item]`.
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<ng-template uiMenuContent #inviteUsersMenu="uiMenuContent">
|
|
114
|
+
<ui-menu-surface class="w-48">
|
|
115
|
+
<button ui-menu-item type="button">Email</button>
|
|
116
|
+
<button ui-menu-item type="button">Message</button>
|
|
117
|
+
<button ui-menu-item type="button">Calendar invite</button>
|
|
118
|
+
</ui-menu-surface>
|
|
119
|
+
</ng-template>
|
|
120
|
+
|
|
121
|
+
<ui-menu-surface class="w-56">
|
|
122
|
+
<ui-menu-group>
|
|
123
|
+
<button ui-menu-item type="button">Team</button>
|
|
124
|
+
<button ui-menu-item type="button" [uiMenuTrigger]="inviteUsersMenu" side="right" align="start">
|
|
125
|
+
Invite users
|
|
126
|
+
<span class="ml-auto text-xs text-muted-foreground">›</span>
|
|
127
|
+
</button>
|
|
128
|
+
</ui-menu-group>
|
|
129
|
+
</ui-menu-surface>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Checkboxes
|
|
133
|
+
|
|
134
|
+
Use checkbox rows when the menu exposes independent toggles.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const showStatusBar = signal(true);
|
|
138
|
+
const showActivityBar = signal(false);
|
|
139
|
+
const showPanel = signal(false);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```html
|
|
143
|
+
<ui-menu-surface class="w-44">
|
|
144
|
+
<ui-menu-group>
|
|
145
|
+
<ui-menu-label>Appearance</ui-menu-label>
|
|
146
|
+
<button ui-menu-checkbox-item type="button" [(checked)]="showStatusBar">Status Bar</button>
|
|
147
|
+
<button ui-menu-checkbox-item type="button" [(checked)]="showActivityBar">Activity Bar</button>
|
|
148
|
+
<button ui-menu-checkbox-item type="button" [(checked)]="showPanel">Panel</button>
|
|
149
|
+
</ui-menu-group>
|
|
150
|
+
</ui-menu-surface>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Radio group
|
|
154
|
+
|
|
155
|
+
Use a radio group for one exclusive choice.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const panelPosition = signal<'top' | 'bottom' | 'right'>('bottom');
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```html
|
|
162
|
+
<ui-menu-surface class="w-44">
|
|
163
|
+
<ui-menu-label [inset]="true">Panel Position</ui-menu-label>
|
|
164
|
+
<ui-menu-radio-group [(value)]="panelPosition">
|
|
165
|
+
<button ui-menu-radio-item type="button" value="top">Top</button>
|
|
166
|
+
<button ui-menu-radio-item type="button" value="bottom">Bottom</button>
|
|
167
|
+
<button ui-menu-radio-item type="button" value="right">Right</button>
|
|
168
|
+
</ui-menu-radio-group>
|
|
169
|
+
</ui-menu-surface>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Shortcuts and icons
|
|
173
|
+
|
|
174
|
+
Project shortcut text and inline icons directly into the row content.
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<ui-menu-surface class="w-56">
|
|
178
|
+
<button ui-menu-item type="button">
|
|
179
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
180
|
+
Profile
|
|
181
|
+
<span ui-menu-shortcut>⇧⌘P</span>
|
|
182
|
+
</button>
|
|
183
|
+
<button ui-menu-item type="button">
|
|
184
|
+
<svg aria-hidden="true" class="size-4" viewBox="0 0 24 24">...</svg>
|
|
185
|
+
Billing
|
|
186
|
+
<span ui-menu-shortcut>⌘B</span>
|
|
187
|
+
</button>
|
|
188
|
+
</ui-menu-surface>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Avatar trigger composition
|
|
192
|
+
|
|
193
|
+
Also import the avatar entrypoint when the trigger should be an account avatar.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@edsis/ui/avatar';
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<button
|
|
201
|
+
ui-button
|
|
202
|
+
type="button"
|
|
203
|
+
variant="ghost"
|
|
204
|
+
size="icon"
|
|
205
|
+
class="h-10 w-10 rounded-full p-0"
|
|
206
|
+
[uiMenuTrigger]="accountMenu"
|
|
207
|
+
align="end">
|
|
208
|
+
<ui-avatar>
|
|
209
|
+
<ui-avatar-image src="https://github.com/shadcn.png" alt="Ada Lovelace" />
|
|
210
|
+
<ui-avatar-fallback>AL</ui-avatar-fallback>
|
|
211
|
+
</ui-avatar>
|
|
212
|
+
</button>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### RTL
|
|
216
|
+
|
|
217
|
+
Set `dir="rtl"` and `lang` on the surface, and prefer `side="left"` when nested content should open toward the visual
|
|
218
|
+
start edge.
|
|
219
|
+
|
|
220
|
+
```html
|
|
221
|
+
<ng-template uiMenuContent #rtlAccountMenu="uiMenuContent">
|
|
222
|
+
<ui-menu-surface dir="rtl" lang="ar" class="w-44 text-right">
|
|
223
|
+
<button ui-menu-item type="button">الملف الشخصي</button>
|
|
224
|
+
<button ui-menu-item type="button">الفوترة</button>
|
|
225
|
+
</ui-menu-surface>
|
|
226
|
+
</ng-template>
|
|
227
|
+
|
|
228
|
+
<ui-menu-surface dir="rtl" lang="ar" class="w-56 text-right">
|
|
229
|
+
<button ui-menu-item type="button" [uiMenuTrigger]="rtlAccountMenu" side="left" align="start">
|
|
230
|
+
الحساب
|
|
231
|
+
<span class="mr-auto text-xs text-muted-foreground">‹</span>
|
|
232
|
+
</button>
|
|
233
|
+
</ui-menu-surface>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## API
|
|
237
|
+
|
|
238
|
+
### `[uiMenuTrigger]`
|
|
239
|
+
|
|
240
|
+
| Input | Type | Default |
|
|
241
|
+
| --------------- | ---------------------------------------- | ---------- |
|
|
242
|
+
| `uiMenuTrigger` | `MenuContentDirective` | _required_ |
|
|
243
|
+
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` |
|
|
244
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'start'` |
|
|
245
|
+
| `disabled` | `boolean` | `false` |
|
|
246
|
+
|
|
247
|
+
Output: `openedChange: boolean`. Methods: `open()`, `close()`, `toggle()`.
|
|
248
|
+
|
|
249
|
+
### `ng-template[uiMenuContent]`
|
|
250
|
+
|
|
251
|
+
Projected template wrapper for the menu surface that is consumed by `[uiMenuTrigger]`.
|
|
252
|
+
|
|
253
|
+
### `ui-menu-surface`
|
|
254
|
+
|
|
255
|
+
Container with `role="menu"` and arrow key navigation wired through
|
|
256
|
+
`FocusKeyManager` (wrapping + type-ahead). Emits `closeRequested` on Tab.
|
|
257
|
+
|
|
258
|
+
### `ui-menu-item`
|
|
259
|
+
|
|
260
|
+
`role="menuitem"`. Inputs: `disabled`, `inset`, `class`. Output: `selected`.
|
|
261
|
+
|
|
262
|
+
### `ui-menu-checkbox-item`
|
|
263
|
+
|
|
264
|
+
`role="menuitemcheckbox"`. Inputs: `[(checked)]`, `disabled`, `variant`, `class`.
|
|
265
|
+
|
|
266
|
+
### `ui-menu-radio-group` and `ui-menu-radio-item`
|
|
267
|
+
|
|
268
|
+
Use `[(value)]` on the group and `value` on each radio row. Radio items expose `role="menuitemradio"`.
|
|
269
|
+
|
|
270
|
+
### Auxiliary
|
|
271
|
+
|
|
272
|
+
- `ui-menu-group` → structural wrapper with `role="group"`.
|
|
273
|
+
- `ui-menu-separator` → `role="separator"`.
|
|
274
|
+
- `ui-menu-label` — non-interactive label row.
|
|
275
|
+
- `ui-menu-shortcut` — right-aligned shortcut badge.
|
|
276
|
+
|
|
277
|
+
## Styling and theming
|
|
278
|
+
|
|
279
|
+
Pass `class` to the surface or individual rows to control width, spacing, and emphasis. Borders and separators use the
|
|
280
|
+
shared theme border tokens, destructive rows use the destructive palette, and projected icons inherit the current text
|
|
281
|
+
color.
|
|
282
|
+
|
|
283
|
+
## Accessibility
|
|
284
|
+
|
|
285
|
+
- Trigger: `aria-haspopup="menu"`, `aria-expanded` reflects state.
|
|
286
|
+
- Surface: `role="menu"`, items receive `role="menuitem"`, disabled items get
|
|
287
|
+
`aria-disabled="true"`.
|
|
288
|
+
- Checkbox and radio items expose `aria-checked` semantics through the relevant menuitem roles.
|
|
289
|
+
- Close on outside click, Escape, or Tab. Focus returns to the trigger.
|
|
290
|
+
|
|
291
|
+
## Keyboard interactions
|
|
292
|
+
|
|
293
|
+
- Enter, Space, and ArrowDown on the trigger open the menu.
|
|
294
|
+
- Arrow Up and Arrow Down move between enabled rows; Home and End jump to the first or last row.
|
|
295
|
+
- Typeahead matches the row label text.
|
|
296
|
+
- Enter and Space activate the focused row.
|
|
297
|
+
|
|
298
|
+
## Angular notes
|
|
299
|
+
|
|
300
|
+
- There is no React-style `DropdownMenu` root component; the root behavior is handled by `uiMenuTrigger` and
|
|
301
|
+
`ng-template[uiMenuContent]`.
|
|
302
|
+
- Reuse `uiMenuTrigger` on `button[ui-menu-item]` when you need a submenu.
|
|
303
|
+
- Standard command rows can dismiss the overlay explicitly through a `#menuTrigger="uiMenuTrigger"` template reference,
|
|
304
|
+
while checkbox and radio rows can stay open for multi-step preference changes.
|
|
305
|
+
|
|
306
|
+
## Source parity
|
|
307
|
+
|
|
308
|
+
This Angular entrypoint keeps the main shadcn dropdown-menu patterns: grouped commands, shortcuts, icons, nested menus,
|
|
309
|
+
checkbox items, radio groups, avatar triggers, destructive rows, and RTL guidance. The shadcn page includes additional
|
|
310
|
+
permutations such as checkbox-icons, radio-icons, and a very large complex menu; the local demos cover the same
|
|
311
|
+
primitives through representative compositions instead of duplicating every permutation one-to-one.
|