@shadow-js/ui 0.0.0-stage → 1.0.1
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/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2281 -0
- package/dist/dev/cli/schema.d.ts +71 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +139 -0
- package/dist/dev/locales/ar.js +127 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +139 -0
- package/dist/dev/locales/en.js +127 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +27 -0
- package/dist/dev/registry/utils/utils.js +26 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +185 -0
- package/dist/dev/theme/theme-logic.js +183 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +88 -0
- package/dist/dev/theme/theme-provider.js +149 -0
- package/dist/dev/theme/theme-provider.js.map +1 -0
- package/dist/prod/index.js +1 -0
- package/dist/prod/locales/ar.js +2 -0
- package/dist/prod/locales/ar.js.map +1 -0
- package/dist/prod/locales/en.js +2 -0
- package/dist/prod/locales/en.js.map +1 -0
- package/dist/prod/registry/utils/utils.js +2 -0
- package/dist/prod/registry/utils/utils.js.map +1 -0
- package/dist/prod/theme/theme-logic.js +2 -0
- package/dist/prod/theme/theme-logic.js.map +1 -0
- package/dist/prod/theme/theme-provider.js +2 -0
- package/dist/prod/theme/theme-provider.js.map +1 -0
- package/package.json +102 -4
- package/registry/accordion/accordion.tsx +210 -0
- package/registry/accordion/item.json +4 -0
- package/registry/alert/alert.tsx +200 -0
- package/registry/alert/item.json +4 -0
- package/registry/alert-dialog/alert-dialog.tsx +530 -0
- package/registry/alert-dialog/item.json +4 -0
- package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
- package/registry/aspect-ratio/item.json +4 -0
- package/registry/attachment/attachment.tsx +459 -0
- package/registry/attachment/item.json +4 -0
- package/registry/avatar/avatar.tsx +385 -0
- package/registry/avatar/item.json +4 -0
- package/registry/badge/badge.tsx +108 -0
- package/registry/badge/item.json +4 -0
- package/registry/breadcrumb/breadcrumb.tsx +354 -0
- package/registry/breadcrumb/item.json +4 -0
- package/registry/bubble/bubble.tsx +260 -0
- package/registry/bubble/item.json +4 -0
- package/registry/button/button.tsx +125 -0
- package/registry/button/item.json +4 -0
- package/registry/button-group/button-group.tsx +189 -0
- package/registry/button-group/item.json +4 -0
- package/registry/calendar/calendar.tsx +348 -0
- package/registry/calendar/item.json +4 -0
- package/registry/card/card.tsx +311 -0
- package/registry/card/item.json +4 -0
- package/registry/carousel/carousel.tsx +851 -0
- package/registry/carousel/item.json +4 -0
- package/registry/chart/chart.tsx +1956 -0
- package/registry/chart/item.json +4 -0
- package/registry/chart-scale/chart-scale.ts +670 -0
- package/registry/chart-scale/item.json +5 -0
- package/registry/checkbox/checkbox.tsx +79 -0
- package/registry/checkbox/item.json +4 -0
- package/registry/code-block/code-block.tsx +470 -0
- package/registry/code-block/item.json +4 -0
- package/registry/collapsible/collapsible.tsx +117 -0
- package/registry/collapsible/item.json +4 -0
- package/registry/combobox/combobox.tsx +977 -0
- package/registry/combobox/item.json +4 -0
- package/registry/command/command.tsx +757 -0
- package/registry/command/item.json +4 -0
- package/registry/context-menu/context-menu.tsx +697 -0
- package/registry/context-menu/item.json +4 -0
- package/registry/data-table/data-table.tsx +437 -0
- package/registry/data-table/item.json +4 -0
- package/registry/date-picker/date-picker.tsx +349 -0
- package/registry/date-picker/item.json +4 -0
- package/registry/dialog/dialog.tsx +412 -0
- package/registry/dialog/item.json +4 -0
- package/registry/direction/direction.tsx +88 -0
- package/registry/direction/item.json +4 -0
- package/registry/drawer/drawer.tsx +787 -0
- package/registry/drawer/item.json +4 -0
- package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
- package/registry/dropdown-menu/item.json +4 -0
- package/registry/empty/empty.tsx +240 -0
- package/registry/empty/item.json +4 -0
- package/registry/field/field.tsx +624 -0
- package/registry/field/item.json +4 -0
- package/registry/form/form.tsx +1112 -0
- package/registry/form/item.json +4 -0
- package/registry/hover-card/hover-card.tsx +144 -0
- package/registry/hover-card/item.json +4 -0
- package/registry/icons/icons.tsx +1691 -0
- package/registry/icons/item.json +4 -0
- package/registry/input/input.tsx +43 -0
- package/registry/input/item.json +4 -0
- package/registry/input-group/input-group.tsx +315 -0
- package/registry/input-group/item.json +4 -0
- package/registry/input-otp/input-otp.tsx +329 -0
- package/registry/input-otp/item.json +4 -0
- package/registry/item/item.json +4 -0
- package/registry/item/item.tsx +439 -0
- package/registry/kbd/item.json +4 -0
- package/registry/kbd/kbd.tsx +65 -0
- package/registry/label/item.json +4 -0
- package/registry/label/label.tsx +60 -0
- package/registry/marker/item.json +4 -0
- package/registry/marker/marker.tsx +175 -0
- package/registry/menubar/item.json +4 -0
- package/registry/menubar/menubar.tsx +694 -0
- package/registry/message/item.json +4 -0
- package/registry/message/message.tsx +280 -0
- package/registry/message-scroller/item.json +4 -0
- package/registry/message-scroller/message-scroller.tsx +262 -0
- package/registry/native-select/item.json +4 -0
- package/registry/native-select/native-select.tsx +113 -0
- package/registry/navigation-menu/item.json +4 -0
- package/registry/navigation-menu/navigation-menu.tsx +362 -0
- package/registry/pagination/item.json +4 -0
- package/registry/pagination/pagination.tsx +460 -0
- package/registry/password-input/item.json +5 -0
- package/registry/password-input/password-input.tsx +494 -0
- package/registry/popover/item.json +4 -0
- package/registry/popover/popover.tsx +284 -0
- package/registry/progress/item.json +4 -0
- package/registry/progress/progress.tsx +190 -0
- package/registry/questionnaire/item.json +4 -0
- package/registry/questionnaire/questionnaire.tsx +569 -0
- package/registry/radio-group/item.json +4 -0
- package/registry/radio-group/radio-group.tsx +101 -0
- package/registry/resizable/item.json +4 -0
- package/registry/resizable/resizable.tsx +455 -0
- package/registry/scroll-area/item.json +4 -0
- package/registry/scroll-area/scroll-area.tsx +149 -0
- package/registry/select/item.json +4 -0
- package/registry/select/select.tsx +571 -0
- package/registry/separator/item.json +4 -0
- package/registry/separator/separator.tsx +52 -0
- package/registry/sheet/item.json +4 -0
- package/registry/sheet/sheet.tsx +476 -0
- package/registry/sidebar/item.json +4 -0
- package/registry/sidebar/sidebar.tsx +1870 -0
- package/registry/skeleton/item.json +4 -0
- package/registry/skeleton/skeleton.tsx +47 -0
- package/registry/slider/item.json +4 -0
- package/registry/slider/slider.tsx +149 -0
- package/registry/spinner/item.json +4 -0
- package/registry/spinner/spinner.tsx +51 -0
- package/registry/switch/item.json +4 -0
- package/registry/switch/switch.tsx +93 -0
- package/registry/table/item.json +4 -0
- package/registry/table/table.tsx +373 -0
- package/registry/tabs/item.json +4 -0
- package/registry/tabs/tabs.tsx +209 -0
- package/registry/textarea/item.json +4 -0
- package/registry/textarea/textarea.tsx +51 -0
- package/registry/toast/item.json +4 -0
- package/registry/toast/toast.tsx +949 -0
- package/registry/toggle/item.json +4 -0
- package/registry/toggle/toggle.tsx +110 -0
- package/registry/toggle-group/item.json +4 -0
- package/registry/toggle-group/toggle-group.tsx +189 -0
- package/registry/tooltip/item.json +4 -0
- package/registry/tooltip/tooltip.tsx +169 -0
- package/registry/trace/item.json +4 -0
- package/registry/trace/trace.tsx +356 -0
- package/registry/ui-strings/item.json +5 -0
- package/registry/ui-strings/ui-strings.tsx +454 -0
- package/registry/use-highlighted-code/item.json +5 -0
- package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
- package/registry/use-mobile/item.json +5 -0
- package/registry/use-mobile/use-mobile.ts +46 -0
- package/registry/utils/item.json +5 -0
- package/registry/utils/utils.ts +38 -0
- package/registry.json +3274 -0
- package/styles/animations.css +321 -0
- package/styles/theme-core.css +55 -0
- package/styles/theme.css +12 -0
- package/styles/themes/gray.css +69 -0
- package/styles/themes/neutral.css +69 -0
- package/styles/themes/slate.css +69 -0
- package/styles/themes/stone.css +69 -0
- package/styles/themes/zinc.css +69 -0
- package/styles/typeset.css +251 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type AlertDialogActionProps,
|
|
4
|
+
type AlertDialogCancelProps,
|
|
5
|
+
type AlertDialogContentProps,
|
|
6
|
+
AlertDialog as AlertDialogPrimitive,
|
|
7
|
+
type AlertDialogProps,
|
|
8
|
+
type DialogDescriptionProps,
|
|
9
|
+
type DialogOverlayProps,
|
|
10
|
+
type DialogPortalProps,
|
|
11
|
+
type DialogTitleProps,
|
|
12
|
+
type DialogTriggerProps,
|
|
13
|
+
type PolymorphicTarget,
|
|
14
|
+
} from "@shadow-js/primitives";
|
|
15
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
16
|
+
import { buttonVariants } from "@/components/ui/button";
|
|
17
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
18
|
+
|
|
19
|
+
/** The props as the implementation sees them: the typed own props plus whatever the target takes. */
|
|
20
|
+
type Loose = {
|
|
21
|
+
readonly as?: PolymorphicTarget;
|
|
22
|
+
readonly class?: ClassValue;
|
|
23
|
+
readonly [prop: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A modal dialog that interrupts the user with a message that needs a response, such as a
|
|
28
|
+
* confirmation before a delete. Pressing outside never dismisses it. Focus starts on
|
|
29
|
+
* {@link AlertDialogCancel}, and Escape closes it. Control it with `open` and `onOpenChange`, or
|
|
30
|
+
* start uncontrolled with `defaultOpen`.
|
|
31
|
+
*
|
|
32
|
+
* Keyboard and ARIA: Enter and Space activate the trigger. Tab and Shift+Tab move between the
|
|
33
|
+
* buttons inside the dialog and do not leave it while it is open. Escape closes it and returns focus
|
|
34
|
+
* to the trigger. The content has `role="alertdialog"` and `aria-modal="true"`. It is named by the
|
|
35
|
+
* {@link AlertDialogTitle} and described by the {@link AlertDialogDescription}. The trigger has
|
|
36
|
+
* `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`.
|
|
37
|
+
*
|
|
38
|
+
* Compared with shadcn/ui: `as` replaces `asChild` on {@link AlertDialogTrigger}, `class` replaces
|
|
39
|
+
* `className`, and every part carries `data-slot`.
|
|
40
|
+
*
|
|
41
|
+
* @param props - The root props: `open`, `onOpenChange`, `defaultOpen` and the children.
|
|
42
|
+
* @returns The alert dialog root.
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* import { Button } from "@/components/ui/button";
|
|
46
|
+
* import {
|
|
47
|
+
* AlertDialog,
|
|
48
|
+
* AlertDialogAction,
|
|
49
|
+
* AlertDialogCancel,
|
|
50
|
+
* AlertDialogContent,
|
|
51
|
+
* AlertDialogDescription,
|
|
52
|
+
* AlertDialogFooter,
|
|
53
|
+
* AlertDialogHeader,
|
|
54
|
+
* AlertDialogTitle,
|
|
55
|
+
* AlertDialogTrigger,
|
|
56
|
+
* } from "@/components/ui/alert-dialog";
|
|
57
|
+
*
|
|
58
|
+
* export function DeleteAccount() {
|
|
59
|
+
* const deleteAccount = (): void => {
|
|
60
|
+
* console.log("account deleted");
|
|
61
|
+
* };
|
|
62
|
+
* return (
|
|
63
|
+
* <AlertDialog>
|
|
64
|
+
* <AlertDialogTrigger as={Button} variant="outline">
|
|
65
|
+
* Delete account
|
|
66
|
+
* </AlertDialogTrigger>
|
|
67
|
+
* <AlertDialogContent>
|
|
68
|
+
* <AlertDialogHeader>
|
|
69
|
+
* <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
|
70
|
+
* <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
71
|
+
* </AlertDialogHeader>
|
|
72
|
+
* <AlertDialogFooter>
|
|
73
|
+
* <AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
74
|
+
* <AlertDialogAction onClick={deleteAccount}>Continue</AlertDialogAction>
|
|
75
|
+
* </AlertDialogFooter>
|
|
76
|
+
* </AlertDialogContent>
|
|
77
|
+
* </AlertDialog>
|
|
78
|
+
* );
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
* @since 1.0.0
|
|
82
|
+
* @see {@link AlertDialogTrigger}
|
|
83
|
+
* @see {@link AlertDialogContent}
|
|
84
|
+
* @see {@link AlertDialogCancel}
|
|
85
|
+
* @see {@link AlertDialogAction}
|
|
86
|
+
*/
|
|
87
|
+
function AlertDialog(props: AlertDialogProps): JSX.Element {
|
|
88
|
+
return <AlertDialogPrimitive {...props} />;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The button that opens the alert dialog. It renders a native `button` by default. Use `as` to
|
|
93
|
+
* render your own button component, such as {@link Button}.
|
|
94
|
+
*
|
|
95
|
+
* Keyboard and ARIA: Enter and Space activate it. It carries `aria-haspopup="dialog"`,
|
|
96
|
+
* `aria-expanded` and `aria-controls`, which name the open dialog. Give it visible text, because
|
|
97
|
+
* that text is its accessible name.
|
|
98
|
+
*
|
|
99
|
+
* @param props - The trigger props: `as`, `children`, and the attributes of the element.
|
|
100
|
+
* @returns The trigger element.
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* import { Button } from "@/components/ui/button";
|
|
104
|
+
* import {
|
|
105
|
+
* AlertDialog,
|
|
106
|
+
* AlertDialogContent,
|
|
107
|
+
* AlertDialogTrigger,
|
|
108
|
+
* } from "@/components/ui/alert-dialog";
|
|
109
|
+
*
|
|
110
|
+
* export function OpenDialog() {
|
|
111
|
+
* return (
|
|
112
|
+
* <AlertDialog>
|
|
113
|
+
* <AlertDialogTrigger as={Button} variant="destructive">
|
|
114
|
+
* Delete
|
|
115
|
+
* </AlertDialogTrigger>
|
|
116
|
+
* <AlertDialogContent>Are you sure?</AlertDialogContent>
|
|
117
|
+
* </AlertDialog>
|
|
118
|
+
* );
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
* @since 1.0.0
|
|
122
|
+
* @see {@link AlertDialog}
|
|
123
|
+
*/
|
|
124
|
+
function AlertDialogTrigger<T extends PolymorphicTarget = "button">(
|
|
125
|
+
props: DialogTriggerProps<T>,
|
|
126
|
+
): JSX.Element {
|
|
127
|
+
return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Renders its children into `document.body` while the alert dialog is open, so the dialog sits
|
|
132
|
+
* above the page. It is server-rendered while open, and it keeps the content mounted until its exit
|
|
133
|
+
* animation ends.
|
|
134
|
+
*
|
|
135
|
+
* @param props - The portal props: `container` (default `document.body`) and the children.
|
|
136
|
+
* @returns The portal, or nothing while the dialog is closed.
|
|
137
|
+
* @example
|
|
138
|
+
* ```tsx
|
|
139
|
+
* import { AlertDialogOverlay, AlertDialogPortal } from "@/components/ui/alert-dialog";
|
|
140
|
+
*
|
|
141
|
+
* export function Layer() {
|
|
142
|
+
* return (
|
|
143
|
+
* <AlertDialogPortal>
|
|
144
|
+
* <AlertDialogOverlay />
|
|
145
|
+
* </AlertDialogPortal>
|
|
146
|
+
* );
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
* @since 1.0.0
|
|
150
|
+
* @see {@link AlertDialogContent}
|
|
151
|
+
*/
|
|
152
|
+
function AlertDialogPortal(props: DialogPortalProps): JSX.Element {
|
|
153
|
+
return <AlertDialogPrimitive.Portal {...props} />;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The dimmed backdrop behind the alert dialog. {@link AlertDialogContent} renders it for you. A
|
|
158
|
+
* click on it does not dismiss the alert dialog.
|
|
159
|
+
*
|
|
160
|
+
* @param props - The overlay props: `class` and the `div` attributes.
|
|
161
|
+
* @returns The overlay element.
|
|
162
|
+
* @example
|
|
163
|
+
* ```tsx
|
|
164
|
+
* import { AlertDialogOverlay } from "@/components/ui/alert-dialog";
|
|
165
|
+
*
|
|
166
|
+
* export function DarkBackdrop() {
|
|
167
|
+
* return <AlertDialogOverlay class="bg-black/70" />;
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
* @since 1.0.0
|
|
171
|
+
* @see {@link AlertDialogContent}
|
|
172
|
+
*/
|
|
173
|
+
function AlertDialogOverlay(props: DialogOverlayProps): JSX.Element {
|
|
174
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
175
|
+
return (
|
|
176
|
+
<AlertDialogPrimitive.Overlay
|
|
177
|
+
data-slot="alert-dialog-overlay"
|
|
178
|
+
class={cn(
|
|
179
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
180
|
+
local.class,
|
|
181
|
+
)}
|
|
182
|
+
{...rest}
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The alert dialog panel. It renders the portal and the overlay itself, then the centered panel
|
|
189
|
+
* with `role="alertdialog"`. Put the {@link AlertDialogHeader}, the {@link AlertDialogFooter} and
|
|
190
|
+
* your body text inside it.
|
|
191
|
+
*
|
|
192
|
+
* Keyboard and ARIA: focus starts on {@link AlertDialogCancel}. Escape closes the dialog, and a
|
|
193
|
+
* press outside does not. The panel has `role="alertdialog"` and `aria-modal="true"`, and it is
|
|
194
|
+
* named by the title and described by the description.
|
|
195
|
+
*
|
|
196
|
+
* @param props - The content props: `class`, `children`, and the attributes of the panel.
|
|
197
|
+
* @returns The portal with the overlay and the panel.
|
|
198
|
+
* @example
|
|
199
|
+
* ```tsx
|
|
200
|
+
* import {
|
|
201
|
+
* AlertDialog,
|
|
202
|
+
* AlertDialogCancel,
|
|
203
|
+
* AlertDialogContent,
|
|
204
|
+
* AlertDialogFooter,
|
|
205
|
+
* AlertDialogHeader,
|
|
206
|
+
* AlertDialogTitle,
|
|
207
|
+
* AlertDialogTrigger,
|
|
208
|
+
* } from "@/components/ui/alert-dialog";
|
|
209
|
+
*
|
|
210
|
+
* export function Confirm() {
|
|
211
|
+
* return (
|
|
212
|
+
* <AlertDialog>
|
|
213
|
+
* <AlertDialogTrigger>Leave</AlertDialogTrigger>
|
|
214
|
+
* <AlertDialogContent>
|
|
215
|
+
* <AlertDialogHeader>
|
|
216
|
+
* <AlertDialogTitle>Discard changes?</AlertDialogTitle>
|
|
217
|
+
* </AlertDialogHeader>
|
|
218
|
+
* <AlertDialogFooter>
|
|
219
|
+
* <AlertDialogCancel>Keep editing</AlertDialogCancel>
|
|
220
|
+
* </AlertDialogFooter>
|
|
221
|
+
* </AlertDialogContent>
|
|
222
|
+
* </AlertDialog>
|
|
223
|
+
* );
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
* @since 1.0.0
|
|
227
|
+
* @see {@link AlertDialog}
|
|
228
|
+
* @see {@link AlertDialogOverlay}
|
|
229
|
+
*/
|
|
230
|
+
function AlertDialogContent(
|
|
231
|
+
props: AlertDialogContentProps & { readonly size?: "default" | "sm" | undefined },
|
|
232
|
+
): JSX.Element {
|
|
233
|
+
const [local, rest] = splitProps(props, ["class", "size"]);
|
|
234
|
+
return (
|
|
235
|
+
<AlertDialogPortal>
|
|
236
|
+
<AlertDialogOverlay />
|
|
237
|
+
<AlertDialogPrimitive.Content
|
|
238
|
+
data-slot="alert-dialog-content"
|
|
239
|
+
data-size={local.size ?? "default"}
|
|
240
|
+
class={cn(
|
|
241
|
+
"fixed start-1/2 top-1/2 z-50 grid max-h-[calc(100%-2rem)] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg data-[size=sm]:max-w-xs rtl:translate-x-1/2",
|
|
242
|
+
local.class,
|
|
243
|
+
)}
|
|
244
|
+
{...rest}
|
|
245
|
+
/>
|
|
246
|
+
</AlertDialogPortal>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* An icon or picture at the top of an alert dialog, on a muted tile. Put it first in the
|
|
252
|
+
* {@link AlertDialogHeader}.
|
|
253
|
+
*
|
|
254
|
+
* Keyboard and ARIA: it is decorative and has no role; name the dialog with its title.
|
|
255
|
+
*
|
|
256
|
+
* @param props - The media props: `children` and the `div` attributes.
|
|
257
|
+
* @returns The media element.
|
|
258
|
+
* @example
|
|
259
|
+
* ```tsx
|
|
260
|
+
* import { AlertDialogHeader, AlertDialogMedia, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
|
261
|
+
* import { TriangleAlertIcon } from "@/components/ui/icons";
|
|
262
|
+
*
|
|
263
|
+
* export function Header() {
|
|
264
|
+
* return (
|
|
265
|
+
* <AlertDialogHeader>
|
|
266
|
+
* <AlertDialogMedia>
|
|
267
|
+
* <TriangleAlertIcon />
|
|
268
|
+
* </AlertDialogMedia>
|
|
269
|
+
* <AlertDialogTitle>Delete?</AlertDialogTitle>
|
|
270
|
+
* </AlertDialogHeader>
|
|
271
|
+
* );
|
|
272
|
+
* }
|
|
273
|
+
* ```
|
|
274
|
+
* @since 1.0.0
|
|
275
|
+
* @see {@link AlertDialogContent}
|
|
276
|
+
*/
|
|
277
|
+
function AlertDialogMedia(props: ComponentProps<"div">): JSX.Element {
|
|
278
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
279
|
+
return (
|
|
280
|
+
<div
|
|
281
|
+
data-slot="alert-dialog-media"
|
|
282
|
+
class={cn(
|
|
283
|
+
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted [&_svg:not([class*='size-'])]:size-6",
|
|
284
|
+
local.class,
|
|
285
|
+
)}
|
|
286
|
+
{...rest}
|
|
287
|
+
>
|
|
288
|
+
{local.children}
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The layout of the title and description at the top of the alert dialog. Text is centered on
|
|
295
|
+
* small screens and aligned to the start from the `sm` breakpoint.
|
|
296
|
+
*
|
|
297
|
+
* @param props - The header props: `class` and the `div` attributes.
|
|
298
|
+
* @returns The header element.
|
|
299
|
+
* @example
|
|
300
|
+
* ```tsx
|
|
301
|
+
* import {
|
|
302
|
+
* AlertDialogDescription,
|
|
303
|
+
* AlertDialogHeader,
|
|
304
|
+
* AlertDialogTitle,
|
|
305
|
+
* } from "@/components/ui/alert-dialog";
|
|
306
|
+
*
|
|
307
|
+
* export function Heading() {
|
|
308
|
+
* return (
|
|
309
|
+
* <AlertDialogHeader>
|
|
310
|
+
* <AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
311
|
+
* <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
312
|
+
* </AlertDialogHeader>
|
|
313
|
+
* );
|
|
314
|
+
* }
|
|
315
|
+
* ```
|
|
316
|
+
* @since 1.0.0
|
|
317
|
+
* @see {@link AlertDialogContent}
|
|
318
|
+
*/
|
|
319
|
+
function AlertDialogHeader(props: ComponentProps<"div">): JSX.Element {
|
|
320
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
321
|
+
return (
|
|
322
|
+
<div
|
|
323
|
+
data-slot="alert-dialog-header"
|
|
324
|
+
class={cn("flex flex-col gap-2 text-center sm:text-start", local.class)}
|
|
325
|
+
{...rest}
|
|
326
|
+
/>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The layout of the cancel and action buttons at the bottom of the alert dialog. The buttons stack
|
|
332
|
+
* on small screens and sit in a row, aligned to the end, from the `sm` breakpoint.
|
|
333
|
+
*
|
|
334
|
+
* @param props - The footer props: `class` and the `div` attributes.
|
|
335
|
+
* @returns The footer element.
|
|
336
|
+
* @example
|
|
337
|
+
* ```tsx
|
|
338
|
+
* import {
|
|
339
|
+
* AlertDialogAction,
|
|
340
|
+
* AlertDialogCancel,
|
|
341
|
+
* AlertDialogFooter,
|
|
342
|
+
* } from "@/components/ui/alert-dialog";
|
|
343
|
+
*
|
|
344
|
+
* export function Buttons() {
|
|
345
|
+
* return (
|
|
346
|
+
* <AlertDialogFooter>
|
|
347
|
+
* <AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
348
|
+
* <AlertDialogAction>Continue</AlertDialogAction>
|
|
349
|
+
* </AlertDialogFooter>
|
|
350
|
+
* );
|
|
351
|
+
* }
|
|
352
|
+
* ```
|
|
353
|
+
* @since 1.0.0
|
|
354
|
+
* @see {@link AlertDialogContent}
|
|
355
|
+
*/
|
|
356
|
+
function AlertDialogFooter(props: ComponentProps<"div">): JSX.Element {
|
|
357
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
358
|
+
return (
|
|
359
|
+
<div
|
|
360
|
+
data-slot="alert-dialog-footer"
|
|
361
|
+
class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", local.class)}
|
|
362
|
+
{...rest}
|
|
363
|
+
/>
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The title of the alert dialog. It renders an `h2`, and the dialog is named by it.
|
|
369
|
+
*
|
|
370
|
+
* Keyboard and ARIA: no keys. The dialog's `aria-labelledby` points at this heading, so the
|
|
371
|
+
* screen reader announces the dialog by this text.
|
|
372
|
+
*
|
|
373
|
+
* @param props - The title props: `class`, `children`, and the `h2` attributes.
|
|
374
|
+
* @returns The title element.
|
|
375
|
+
* @example
|
|
376
|
+
* ```tsx
|
|
377
|
+
* import { AlertDialogTitle } from "@/components/ui/alert-dialog";
|
|
378
|
+
*
|
|
379
|
+
* export function Title() {
|
|
380
|
+
* return <AlertDialogTitle>Are you sure?</AlertDialogTitle>;
|
|
381
|
+
* }
|
|
382
|
+
* ```
|
|
383
|
+
* @since 1.0.0
|
|
384
|
+
* @see {@link AlertDialogDescription}
|
|
385
|
+
*/
|
|
386
|
+
function AlertDialogTitle(props: DialogTitleProps): JSX.Element {
|
|
387
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
388
|
+
return (
|
|
389
|
+
<AlertDialogPrimitive.Title
|
|
390
|
+
data-slot="alert-dialog-title"
|
|
391
|
+
class={cn("text-lg font-semibold", local.class)}
|
|
392
|
+
{...rest}
|
|
393
|
+
/>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* The description of the alert dialog. It renders a `p`, and the dialog is described by it.
|
|
399
|
+
*
|
|
400
|
+
* Keyboard and ARIA: no keys. The dialog's `aria-describedby` points at this paragraph.
|
|
401
|
+
*
|
|
402
|
+
* @param props - The description props: `class`, `children`, and the `p` attributes.
|
|
403
|
+
* @returns The description element.
|
|
404
|
+
* @example
|
|
405
|
+
* ```tsx
|
|
406
|
+
* import { AlertDialogDescription } from "@/components/ui/alert-dialog";
|
|
407
|
+
*
|
|
408
|
+
* export function Description() {
|
|
409
|
+
* return <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>;
|
|
410
|
+
* }
|
|
411
|
+
* ```
|
|
412
|
+
* @since 1.0.0
|
|
413
|
+
* @see {@link AlertDialogTitle}
|
|
414
|
+
*/
|
|
415
|
+
function AlertDialogDescription(props: DialogDescriptionProps): JSX.Element {
|
|
416
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
417
|
+
return (
|
|
418
|
+
<AlertDialogPrimitive.Description
|
|
419
|
+
data-slot="alert-dialog-description"
|
|
420
|
+
class={cn("text-sm text-muted-foreground", local.class)}
|
|
421
|
+
{...rest}
|
|
422
|
+
/>
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The confirming action. It is styled like a default button. Pressing it closes the dialog, so run
|
|
428
|
+
* the action itself from `onClick`.
|
|
429
|
+
*
|
|
430
|
+
* Keyboard and ARIA: a native button. Enter and Space activate it.
|
|
431
|
+
*
|
|
432
|
+
* @param props - The action props: `onClick`, `children`, and the attributes of the button.
|
|
433
|
+
* @returns The action element.
|
|
434
|
+
* @example
|
|
435
|
+
* ```tsx
|
|
436
|
+
* import {
|
|
437
|
+
* AlertDialog,
|
|
438
|
+
* AlertDialogAction,
|
|
439
|
+
* AlertDialogContent,
|
|
440
|
+
* AlertDialogTrigger,
|
|
441
|
+
* } from "@/components/ui/alert-dialog";
|
|
442
|
+
*
|
|
443
|
+
* export function Remove() {
|
|
444
|
+
* const remove = (): void => {
|
|
445
|
+
* console.log("removed");
|
|
446
|
+
* };
|
|
447
|
+
* return (
|
|
448
|
+
* <AlertDialog>
|
|
449
|
+
* <AlertDialogTrigger>Remove</AlertDialogTrigger>
|
|
450
|
+
* <AlertDialogContent>
|
|
451
|
+
* <AlertDialogAction onClick={remove}>Delete</AlertDialogAction>
|
|
452
|
+
* </AlertDialogContent>
|
|
453
|
+
* </AlertDialog>
|
|
454
|
+
* );
|
|
455
|
+
* }
|
|
456
|
+
* ```
|
|
457
|
+
* @since 1.0.0
|
|
458
|
+
* @see {@link AlertDialogCancel}
|
|
459
|
+
*/
|
|
460
|
+
function AlertDialogAction<T extends PolymorphicTarget = "button">(
|
|
461
|
+
props: AlertDialogActionProps<T>,
|
|
462
|
+
): JSX.Element {
|
|
463
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
|
|
464
|
+
return (
|
|
465
|
+
<AlertDialogPrimitive.Action
|
|
466
|
+
data-slot="alert-dialog-action"
|
|
467
|
+
class={cn(buttonVariants(), local.class)}
|
|
468
|
+
{...rest}
|
|
469
|
+
/>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Closes the dialog without taking the action. It is styled like an outline button, and it receives
|
|
475
|
+
* the initial focus, so Enter does not confirm a destructive action by accident.
|
|
476
|
+
*
|
|
477
|
+
* Keyboard and ARIA: a native button. Enter and Space activate it.
|
|
478
|
+
*
|
|
479
|
+
* @param props - The cancel props: `children`, `class`, and the attributes of the button.
|
|
480
|
+
* @returns The cancel element.
|
|
481
|
+
* @example
|
|
482
|
+
* ```tsx
|
|
483
|
+
* import {
|
|
484
|
+
* AlertDialog,
|
|
485
|
+
* AlertDialogCancel,
|
|
486
|
+
* AlertDialogContent,
|
|
487
|
+
* AlertDialogTrigger,
|
|
488
|
+
* } from "@/components/ui/alert-dialog";
|
|
489
|
+
*
|
|
490
|
+
* export function KeepAccount() {
|
|
491
|
+
* return (
|
|
492
|
+
* <AlertDialog>
|
|
493
|
+
* <AlertDialogTrigger>Delete</AlertDialogTrigger>
|
|
494
|
+
* <AlertDialogContent>
|
|
495
|
+
* <AlertDialogCancel>Keep my account</AlertDialogCancel>
|
|
496
|
+
* </AlertDialogContent>
|
|
497
|
+
* </AlertDialog>
|
|
498
|
+
* );
|
|
499
|
+
* }
|
|
500
|
+
* ```
|
|
501
|
+
* @since 1.0.0
|
|
502
|
+
* @see {@link AlertDialogAction}
|
|
503
|
+
*/
|
|
504
|
+
function AlertDialogCancel<T extends PolymorphicTarget = "button">(
|
|
505
|
+
props: AlertDialogCancelProps<T>,
|
|
506
|
+
): JSX.Element {
|
|
507
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["class"]);
|
|
508
|
+
return (
|
|
509
|
+
<AlertDialogPrimitive.Cancel
|
|
510
|
+
data-slot="alert-dialog-cancel"
|
|
511
|
+
class={cn(buttonVariants({ variant: "outline" }), local.class)}
|
|
512
|
+
{...rest}
|
|
513
|
+
/>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export {
|
|
518
|
+
AlertDialog,
|
|
519
|
+
AlertDialogAction,
|
|
520
|
+
AlertDialogCancel,
|
|
521
|
+
AlertDialogContent,
|
|
522
|
+
AlertDialogDescription,
|
|
523
|
+
AlertDialogFooter,
|
|
524
|
+
AlertDialogHeader,
|
|
525
|
+
AlertDialogMedia,
|
|
526
|
+
AlertDialogOverlay,
|
|
527
|
+
AlertDialogPortal,
|
|
528
|
+
AlertDialogTitle,
|
|
529
|
+
AlertDialogTrigger,
|
|
530
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import { mergeElementProps } from "@shadow-js/primitives";
|
|
3
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Own props of {@link AspectRatio}. The rest of its props are the `div` attributes.
|
|
8
|
+
* @since 1.0.0
|
|
9
|
+
* @see {@link AspectRatio}
|
|
10
|
+
*/
|
|
11
|
+
export interface AspectRatioExtraProps {
|
|
12
|
+
/**
|
|
13
|
+
* Width divided by height, for example `16 / 9`. It is written to the CSS `aspect-ratio`
|
|
14
|
+
* property.
|
|
15
|
+
* @default 1
|
|
16
|
+
*/
|
|
17
|
+
readonly ratio?: number | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Props of {@link AspectRatio}: the attributes of a `div` plus `ratio`.
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @see {@link AspectRatio}
|
|
24
|
+
*/
|
|
25
|
+
export type AspectRatioProps = ComponentProps<"div"> & AspectRatioExtraProps;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Keeps its content at a fixed width-to-height ratio, using the CSS `aspect-ratio` property. Make
|
|
29
|
+
* the child fill the box, for example with `class="size-full object-cover"` on an image.
|
|
30
|
+
*
|
|
31
|
+
* Keyboard and ARIA: the box is not interactive and has no role. It adds no keys and no ARIA
|
|
32
|
+
* attributes. Give an image inside it an `alt` text.
|
|
33
|
+
*
|
|
34
|
+
* Compared with shadcn/ui: `class` replaces `className`, and the element carries `data-slot`. A
|
|
35
|
+
* `style` you pass is merged with the ratio, not replaced.
|
|
36
|
+
*
|
|
37
|
+
* @param props - The box props: `ratio`, `children`, and the `div` attributes.
|
|
38
|
+
* @returns The box element.
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* import { AspectRatio } from "@/components/ui/aspect-ratio";
|
|
42
|
+
*
|
|
43
|
+
* export function Cover() {
|
|
44
|
+
* return (
|
|
45
|
+
* <AspectRatio ratio={16 / 9}>
|
|
46
|
+
* <img src="/cover.jpg" alt="Cover" class="size-full object-cover" />
|
|
47
|
+
* </AspectRatio>
|
|
48
|
+
* );
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @see {@link AspectRatioProps}
|
|
53
|
+
*/
|
|
54
|
+
function AspectRatio(props: AspectRatioProps): JSX.Element {
|
|
55
|
+
const [local, rest] = splitProps(props, ["class", "ratio", "children"]);
|
|
56
|
+
const merged = mergeElementProps(
|
|
57
|
+
{
|
|
58
|
+
get style() {
|
|
59
|
+
return { "aspect-ratio": String(local.ratio ?? 1) };
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
rest,
|
|
63
|
+
);
|
|
64
|
+
return (
|
|
65
|
+
<div data-slot="aspect-ratio" class={cn("relative w-full", local.class)} {...merged}>
|
|
66
|
+
{local.children}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { AspectRatio };
|