@shadow-js/ui 0.0.0-stage → 1.0.0
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 +24 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2293 -0
- package/dist/dev/cli/schema.d.ts +73 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +141 -0
- package/dist/dev/locales/ar.js +128 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +141 -0
- package/dist/dev/locales/en.js +128 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +29 -0
- package/dist/dev/registry/utils/utils.js +27 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +187 -0
- package/dist/dev/theme/theme-logic.js +184 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +90 -0
- package/dist/dev/theme/theme-provider.js +150 -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,459 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import { Polymorphic, type PolymorphicProps, type PolymorphicTarget } from "@shadow-js/primitives";
|
|
3
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
6
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The class list of an attachment card for each `size` and `orientation`. Use it to style any element like an attachment.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { attachmentVariants } from "@/components/ui/attachment";
|
|
14
|
+
*
|
|
15
|
+
* export const Chip = <a class={attachmentVariants({ size: "sm" })} href="/files/report.pdf">report.pdf</a>;
|
|
16
|
+
* ```
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @see {@link Attachment}
|
|
19
|
+
*/
|
|
20
|
+
const attachmentVariants = cva(
|
|
21
|
+
"group/attachment relative flex min-w-0 items-center rounded-lg border bg-card text-card-foreground transition-colors has-[[data-slot=attachment-trigger]:hover]:bg-accent/50 motion-reduce:transition-none data-[state=error]:border-destructive/50 data-[state=error]:bg-destructive/5",
|
|
22
|
+
{
|
|
23
|
+
variants: {
|
|
24
|
+
size: {
|
|
25
|
+
default: "gap-3 p-2.5 text-sm",
|
|
26
|
+
sm: "gap-2.5 p-2 text-sm",
|
|
27
|
+
xs: "gap-2 p-1.5 text-xs",
|
|
28
|
+
},
|
|
29
|
+
orientation: {
|
|
30
|
+
horizontal: "flex-row",
|
|
31
|
+
vertical: "flex-col items-stretch",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: { size: "default", orientation: "horizontal" },
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The states an attachment can be in: `idle`, `uploading`, `processing`, `error` or `done`.
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
* @see {@link Attachment}
|
|
42
|
+
*/
|
|
43
|
+
export type AttachmentState = "idle" | "uploading" | "processing" | "error" | "done";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Props of {@link Attachment}: the attributes of a `div` plus `state` (`"idle"`, `"uploading"`,
|
|
47
|
+
* `"processing"`, `"error"` or `"done"`, the default), `size` (`"default"`, `"sm"` or `"xs"`) and
|
|
48
|
+
* `orientation` (`"horizontal"`, the default, or `"vertical"`).
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
* @see {@link Attachment}
|
|
51
|
+
*/
|
|
52
|
+
export type AttachmentProps = ComponentProps<"div"> &
|
|
53
|
+
VariantProps<typeof attachmentVariants> & { readonly state?: AttachmentState | undefined };
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A file or an image in a message, a composer or an upload list: its media, its name and details, its
|
|
57
|
+
* upload state and its actions. `state` drives the look: `uploading` and `processing` shimmer the title, and
|
|
58
|
+
* `error` colors the border. Put the media, the {@link AttachmentContent} and the {@link AttachmentActions} inside; add an
|
|
59
|
+
* {@link AttachmentTrigger} to make the whole card open something while the actions stay pressable.
|
|
60
|
+
*
|
|
61
|
+
* Keyboard and ARIA: the card is a plain `div` with no role. Its trigger and actions are the focusable
|
|
62
|
+
* controls. Give an icon-only action an `aria-label`, and the trigger one too ("Preview report.pdf"). Say why a
|
|
63
|
+
* file failed in the description: do not rely on color alone for `error`.
|
|
64
|
+
*
|
|
65
|
+
* Compared with shadcn/ui: `as` replaces `render` on the trigger, `class` replaces `className`, and every part has a `data-slot`.
|
|
66
|
+
*
|
|
67
|
+
* @param props - The attachment props: `state`, `size`, `orientation` and the `div` attributes.
|
|
68
|
+
* @returns The attachment.
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* import {
|
|
72
|
+
* Attachment,
|
|
73
|
+
* AttachmentContent,
|
|
74
|
+
* AttachmentDescription,
|
|
75
|
+
* AttachmentMedia,
|
|
76
|
+
* AttachmentTitle,
|
|
77
|
+
* } from "@/components/ui/attachment";
|
|
78
|
+
* import { FileTextIcon } from "@/components/ui/icons";
|
|
79
|
+
*
|
|
80
|
+
* export function Report() {
|
|
81
|
+
* return (
|
|
82
|
+
* <Attachment>
|
|
83
|
+
* <AttachmentMedia>
|
|
84
|
+
* <FileTextIcon />
|
|
85
|
+
* </AttachmentMedia>
|
|
86
|
+
* <AttachmentContent>
|
|
87
|
+
* <AttachmentTitle>report.pdf</AttachmentTitle>
|
|
88
|
+
* <AttachmentDescription>PDF, 2.4 MB</AttachmentDescription>
|
|
89
|
+
* </AttachmentContent>
|
|
90
|
+
* </Attachment>
|
|
91
|
+
* );
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
* @since 1.0.0
|
|
95
|
+
* @see {@link AttachmentMedia}
|
|
96
|
+
* @see {@link AttachmentContent}
|
|
97
|
+
* @see {@link AttachmentGroup}
|
|
98
|
+
*/
|
|
99
|
+
function Attachment(props: AttachmentProps): JSX.Element {
|
|
100
|
+
const [local, rest] = splitProps(props, ["class", "state", "size", "orientation"]);
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
data-slot="attachment"
|
|
104
|
+
data-state={local.state ?? "done"}
|
|
105
|
+
data-size={local.size ?? "default"}
|
|
106
|
+
data-orientation={local.orientation ?? "horizontal"}
|
|
107
|
+
class={cn(
|
|
108
|
+
attachmentVariants({ size: local.size, orientation: local.orientation }),
|
|
109
|
+
local.class,
|
|
110
|
+
)}
|
|
111
|
+
{...rest}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const mediaVariants = cva(
|
|
117
|
+
"relative flex shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted text-muted-foreground group-data-[state=error]/attachment:text-destructive [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-5",
|
|
118
|
+
{
|
|
119
|
+
variants: {
|
|
120
|
+
variant: {
|
|
121
|
+
icon: "size-10 group-data-[size=sm]/attachment:size-9 group-data-[size=xs]/attachment:size-7 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-4",
|
|
122
|
+
image:
|
|
123
|
+
"size-10 group-data-[size=sm]/attachment:size-9 group-data-[size=xs]/attachment:size-7 group-data-[orientation=vertical]/attachment:aspect-video group-data-[orientation=vertical]/attachment:h-auto group-data-[orientation=vertical]/attachment:w-full [&_img]:size-full [&_img]:object-cover",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
defaultVariants: { variant: "icon" },
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Props of {@link AttachmentMedia}: the attributes of a `div` plus `variant`, which is `"icon"` (the
|
|
132
|
+
* default, an icon on a tile) or `"image"` (a preview picture that fills it).
|
|
133
|
+
* @since 1.0.0
|
|
134
|
+
* @see {@link AttachmentMedia}
|
|
135
|
+
*/
|
|
136
|
+
export type AttachmentMediaProps = ComponentProps<"div"> & VariantProps<typeof mediaVariants>;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The media slot: an icon on a tile, or an `img` preview. In a vertical attachment an image fills the top
|
|
140
|
+
* of the card. An image needs its own text alternative (`alt=""` when the title already says it).
|
|
141
|
+
*
|
|
142
|
+
* Keyboard and ARIA: a plain `div`, no role. An icon in it is decorative.
|
|
143
|
+
*
|
|
144
|
+
* @param props - The media props: `variant` and the `div` attributes.
|
|
145
|
+
* @returns The media slot.
|
|
146
|
+
* @example
|
|
147
|
+
* ```tsx
|
|
148
|
+
* import { Attachment, AttachmentMedia } from "@/components/ui/attachment";
|
|
149
|
+
*
|
|
150
|
+
* export function Photo() {
|
|
151
|
+
* return (
|
|
152
|
+
* <Attachment orientation="vertical">
|
|
153
|
+
* <AttachmentMedia variant="image">
|
|
154
|
+
* <img src="/photo.jpg" alt="" />
|
|
155
|
+
* </AttachmentMedia>
|
|
156
|
+
* </Attachment>
|
|
157
|
+
* );
|
|
158
|
+
* }
|
|
159
|
+
* ```
|
|
160
|
+
* @since 1.0.0
|
|
161
|
+
* @see {@link Attachment}
|
|
162
|
+
*/
|
|
163
|
+
function AttachmentMedia(props: AttachmentMediaProps): JSX.Element {
|
|
164
|
+
const [local, rest] = splitProps(props, ["class", "variant"]);
|
|
165
|
+
return (
|
|
166
|
+
<div
|
|
167
|
+
data-slot="attachment-media"
|
|
168
|
+
data-variant={local.variant ?? "icon"}
|
|
169
|
+
class={cn(mediaVariants({ variant: local.variant }), local.class)}
|
|
170
|
+
{...rest}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Wraps the title and the description. It takes the space between the media and the actions and truncates its text.
|
|
177
|
+
*
|
|
178
|
+
* Keyboard and ARIA: a plain `div`; no role and no keys.
|
|
179
|
+
*
|
|
180
|
+
* @param props - The `div` attributes and `class`.
|
|
181
|
+
* @returns The content slot.
|
|
182
|
+
* @example
|
|
183
|
+
* ```tsx
|
|
184
|
+
* import { AttachmentContent, AttachmentTitle } from "@/components/ui/attachment";
|
|
185
|
+
*
|
|
186
|
+
* export const Name = (
|
|
187
|
+
* <AttachmentContent>
|
|
188
|
+
* <AttachmentTitle>report.pdf</AttachmentTitle>
|
|
189
|
+
* </AttachmentContent>
|
|
190
|
+
* );
|
|
191
|
+
* ```
|
|
192
|
+
* @since 1.0.0
|
|
193
|
+
* @see {@link Attachment}
|
|
194
|
+
*/
|
|
195
|
+
function AttachmentContent(props: ComponentProps<"div">): JSX.Element {
|
|
196
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
197
|
+
return (
|
|
198
|
+
<div
|
|
199
|
+
data-slot="attachment-content"
|
|
200
|
+
class={cn("flex min-w-0 flex-1 flex-col gap-0.5", local.class)}
|
|
201
|
+
{...rest}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The name of the attachment. It truncates, and it shimmers while the attachment is `uploading` or `processing`.
|
|
208
|
+
*
|
|
209
|
+
* Keyboard and ARIA: a plain `div`; no role and no keys. While it shimmers its text stays real text, so a screen reader reads it.
|
|
210
|
+
*
|
|
211
|
+
* @param props - The `div` attributes and `class`.
|
|
212
|
+
* @returns The title.
|
|
213
|
+
* @example
|
|
214
|
+
* ```tsx
|
|
215
|
+
* import { AttachmentTitle } from "@/components/ui/attachment";
|
|
216
|
+
*
|
|
217
|
+
* export const Title = <AttachmentTitle>report.pdf</AttachmentTitle>;
|
|
218
|
+
* ```
|
|
219
|
+
* @since 1.0.0
|
|
220
|
+
* @see {@link Attachment}
|
|
221
|
+
*/
|
|
222
|
+
function AttachmentTitle(props: ComponentProps<"div">): JSX.Element {
|
|
223
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
224
|
+
return (
|
|
225
|
+
<div
|
|
226
|
+
data-slot="attachment-title"
|
|
227
|
+
class={cn(
|
|
228
|
+
"truncate font-medium group-data-[state=error]/attachment:text-destructive group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer",
|
|
229
|
+
local.class,
|
|
230
|
+
)}
|
|
231
|
+
{...rest}
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Secondary details: the type and size of a file, or the upload status ("Uploading, 40%"). For an
|
|
238
|
+
* `error` state, say what went wrong here.
|
|
239
|
+
*
|
|
240
|
+
* Keyboard and ARIA: a plain `div`; no role and no keys. For an upload that a screen reader user should follow,
|
|
241
|
+
* put the status in a `role="status"` element.
|
|
242
|
+
*
|
|
243
|
+
* @param props - The `div` attributes and `class`.
|
|
244
|
+
* @returns The description.
|
|
245
|
+
* @example
|
|
246
|
+
* ```tsx
|
|
247
|
+
* import { AttachmentDescription } from "@/components/ui/attachment";
|
|
248
|
+
*
|
|
249
|
+
* export const Details = <AttachmentDescription>PDF, 2.4 MB</AttachmentDescription>;
|
|
250
|
+
* ```
|
|
251
|
+
* @since 1.0.0
|
|
252
|
+
* @see {@link Attachment}
|
|
253
|
+
*/
|
|
254
|
+
function AttachmentDescription(props: ComponentProps<"div">): JSX.Element {
|
|
255
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
256
|
+
return (
|
|
257
|
+
<div
|
|
258
|
+
data-slot="attachment-description"
|
|
259
|
+
class={cn(
|
|
260
|
+
"truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive",
|
|
261
|
+
local.class,
|
|
262
|
+
)}
|
|
263
|
+
{...rest}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Holds the actions, at the end of the attachment. It sits above the {@link AttachmentTrigger}, so the actions stay pressable.
|
|
270
|
+
*
|
|
271
|
+
* Keyboard and ARIA: a plain `div`; the actions inside are the focus stops.
|
|
272
|
+
*
|
|
273
|
+
* @param props - The `div` attributes and `class`.
|
|
274
|
+
* @returns The actions container.
|
|
275
|
+
* @example
|
|
276
|
+
* ```tsx
|
|
277
|
+
* import { AttachmentAction, AttachmentActions } from "@/components/ui/attachment";
|
|
278
|
+
* import { XIcon } from "@/components/ui/icons";
|
|
279
|
+
*
|
|
280
|
+
* export const Remove = (
|
|
281
|
+
* <AttachmentActions>
|
|
282
|
+
* <AttachmentAction aria-label="Remove report.pdf">
|
|
283
|
+
* <XIcon />
|
|
284
|
+
* </AttachmentAction>
|
|
285
|
+
* </AttachmentActions>
|
|
286
|
+
* );
|
|
287
|
+
* ```
|
|
288
|
+
* @since 1.0.0
|
|
289
|
+
* @see {@link AttachmentAction}
|
|
290
|
+
*/
|
|
291
|
+
function AttachmentActions(props: ComponentProps<"div">): JSX.Element {
|
|
292
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
293
|
+
return (
|
|
294
|
+
<div
|
|
295
|
+
data-slot="attachment-actions"
|
|
296
|
+
class={cn("relative z-10 ms-auto flex shrink-0 items-center gap-1", local.class)}
|
|
297
|
+
{...rest}
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Props of {@link AttachmentAction}: every prop of {@link Button}. The `size` is `"icon-xs"` unless given
|
|
304
|
+
* and the `variant` is `"ghost"`.
|
|
305
|
+
* @since 1.0.0
|
|
306
|
+
* @see {@link AttachmentAction}
|
|
307
|
+
*/
|
|
308
|
+
export type AttachmentActionProps<T extends PolymorphicTarget = "button"> = ButtonProps<T>;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* An action on an attachment: a {@link Button}, small and quiet by default. An icon-only action needs an `aria-label` that names the file ("Remove report.pdf").
|
|
312
|
+
*
|
|
313
|
+
* Keyboard and ARIA: a native button; Enter and Space press it.
|
|
314
|
+
*
|
|
315
|
+
* @param props - The button props.
|
|
316
|
+
* @returns The action button.
|
|
317
|
+
* @example
|
|
318
|
+
* ```tsx
|
|
319
|
+
* import { AttachmentAction } from "@/components/ui/attachment";
|
|
320
|
+
* import { XIcon } from "@/components/ui/icons";
|
|
321
|
+
*
|
|
322
|
+
* export const Remove = (
|
|
323
|
+
* <AttachmentAction aria-label="Remove report.pdf">
|
|
324
|
+
* <XIcon />
|
|
325
|
+
* </AttachmentAction>
|
|
326
|
+
* );
|
|
327
|
+
* ```
|
|
328
|
+
* @since 1.0.0
|
|
329
|
+
* @see {@link Button}
|
|
330
|
+
*/
|
|
331
|
+
function AttachmentAction<T extends PolymorphicTarget = "button">(
|
|
332
|
+
props: AttachmentActionProps<T>,
|
|
333
|
+
): JSX.Element {
|
|
334
|
+
const [local, rest] = splitProps(
|
|
335
|
+
props as unknown as {
|
|
336
|
+
readonly class?: ClassValue;
|
|
337
|
+
readonly size?: string;
|
|
338
|
+
readonly variant?: string;
|
|
339
|
+
readonly [prop: string]: unknown;
|
|
340
|
+
},
|
|
341
|
+
["class", "size", "variant"],
|
|
342
|
+
);
|
|
343
|
+
return (
|
|
344
|
+
<Button
|
|
345
|
+
data-slot="attachment-action"
|
|
346
|
+
variant={(local.variant as ButtonProps["variant"]) ?? "ghost"}
|
|
347
|
+
size={(local.size as ButtonProps["size"]) ?? "icon-xs"}
|
|
348
|
+
class={cn(local.class)}
|
|
349
|
+
{...(rest as object)}
|
|
350
|
+
/>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** The props as the implementation sees them. */
|
|
355
|
+
type Loose = {
|
|
356
|
+
readonly as?: PolymorphicTarget;
|
|
357
|
+
readonly class?: ClassValue;
|
|
358
|
+
readonly [prop: string]: unknown;
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Props of {@link AttachmentTrigger}: every attribute of the chosen element. The `as` prop picks the element (a `button` by default; use `"a"` for a link).
|
|
363
|
+
* @since 1.0.0
|
|
364
|
+
* @see {@link AttachmentTrigger}
|
|
365
|
+
*/
|
|
366
|
+
export type AttachmentTriggerProps<T extends PolymorphicTarget = "button"> = PolymorphicProps<
|
|
367
|
+
"button",
|
|
368
|
+
object,
|
|
369
|
+
T
|
|
370
|
+
>;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* A full-card overlay that activates the attachment: a link to the file, or the trigger of a dialog that previews it. It sits behind the
|
|
374
|
+
* {@link AttachmentActions}, so both the card and the actions can be focused and pressed. It needs an `aria-label` that names the file.
|
|
375
|
+
*
|
|
376
|
+
* Keyboard and ARIA: a native `button` by default (Enter and Space), or a link with `as="a"` (Enter). It is a tab stop before the actions.
|
|
377
|
+
*
|
|
378
|
+
* @param props - The trigger props: `as` and the attributes of the element.
|
|
379
|
+
* @returns The trigger.
|
|
380
|
+
* @example
|
|
381
|
+
* ```tsx
|
|
382
|
+
* import { Attachment, AttachmentTrigger } from "@/components/ui/attachment";
|
|
383
|
+
*
|
|
384
|
+
* export const Open = (
|
|
385
|
+
* <Attachment>
|
|
386
|
+
* <AttachmentTrigger as="a" href="/report.pdf" aria-label="Open report.pdf" />
|
|
387
|
+
* </Attachment>
|
|
388
|
+
* );
|
|
389
|
+
* ```
|
|
390
|
+
* @since 1.0.0
|
|
391
|
+
* @see {@link Attachment}
|
|
392
|
+
*/
|
|
393
|
+
function AttachmentTrigger<T extends PolymorphicTarget = "button">(
|
|
394
|
+
props: AttachmentTriggerProps<T>,
|
|
395
|
+
): JSX.Element {
|
|
396
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["as", "class"]);
|
|
397
|
+
return (
|
|
398
|
+
<Polymorphic
|
|
399
|
+
as={local.as ?? "button"}
|
|
400
|
+
data-slot="attachment-trigger"
|
|
401
|
+
{...(local.as === undefined || local.as === "button" ? { type: "button" } : {})}
|
|
402
|
+
class={cn(
|
|
403
|
+
"absolute inset-0 z-0 rounded-[inherit] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
404
|
+
local.class,
|
|
405
|
+
)}
|
|
406
|
+
{...rest}
|
|
407
|
+
/>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Lays out attachments in a horizontal row that scrolls and snaps, with a fade at the edges that have more.
|
|
413
|
+
* A group of attachments that are not interactive can still be scrolled by keyboard: give it
|
|
414
|
+
* `tabindex="0"`, `role="group"` and an `aria-label`.
|
|
415
|
+
*
|
|
416
|
+
* Keyboard and ARIA: a plain `div`; the scrolling is native.
|
|
417
|
+
*
|
|
418
|
+
* @param props - The `div` attributes and `class`.
|
|
419
|
+
* @returns The group.
|
|
420
|
+
* @example
|
|
421
|
+
* ```tsx
|
|
422
|
+
* import { Attachment, AttachmentGroup } from "@/components/ui/attachment";
|
|
423
|
+
*
|
|
424
|
+
* export const Files = (
|
|
425
|
+
* <AttachmentGroup role="group" aria-label="Attachments" tabindex="0">
|
|
426
|
+
* <Attachment class="w-56 shrink-0 snap-start" />
|
|
427
|
+
* <Attachment class="w-56 shrink-0 snap-start" />
|
|
428
|
+
* </AttachmentGroup>
|
|
429
|
+
* );
|
|
430
|
+
* ```
|
|
431
|
+
* @since 1.0.0
|
|
432
|
+
* @see {@link Attachment}
|
|
433
|
+
*/
|
|
434
|
+
function AttachmentGroup(props: ComponentProps<"div">): JSX.Element {
|
|
435
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
436
|
+
return (
|
|
437
|
+
<div
|
|
438
|
+
data-slot="attachment-group"
|
|
439
|
+
class={cn(
|
|
440
|
+
"scroll-fade-x flex w-full snap-x snap-mandatory gap-2 overflow-x-auto pb-1 outline-none [scrollbar-width:thin] focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
441
|
+
local.class,
|
|
442
|
+
)}
|
|
443
|
+
{...rest}
|
|
444
|
+
/>
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export {
|
|
449
|
+
Attachment,
|
|
450
|
+
AttachmentAction,
|
|
451
|
+
AttachmentActions,
|
|
452
|
+
AttachmentContent,
|
|
453
|
+
AttachmentDescription,
|
|
454
|
+
AttachmentGroup,
|
|
455
|
+
AttachmentMedia,
|
|
456
|
+
AttachmentTitle,
|
|
457
|
+
AttachmentTrigger,
|
|
458
|
+
attachmentVariants,
|
|
459
|
+
};
|