@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,43 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A text field: the browser's own `input`, in the kit's look. Use it for single-line text, email,
|
|
7
|
+
* number, date and file entry. All `input` attributes pass through.
|
|
8
|
+
*
|
|
9
|
+
* Keyboard and ARIA: the keys are the browser's own (typing, the arrow keys, Tab). The component
|
|
10
|
+
* adds no role and no `aria-*` attribute; the browser gives the input its role from its `type`.
|
|
11
|
+
* Name the field with a {@link Label} (`htmlFor` pointing at the input's `id`), or with
|
|
12
|
+
* `aria-label` or `aria-labelledby`. `aria-invalid="true"`, which you pass, turns the border and
|
|
13
|
+
* the border destructive and tints the ring.
|
|
14
|
+
*
|
|
15
|
+
* shadcn/ui differences: `class` replaces `className`.
|
|
16
|
+
*
|
|
17
|
+
* @param props The `input` attributes. `class` adds to the field's classes; `type` is the input type.
|
|
18
|
+
* @returns The `input` element.
|
|
19
|
+
* @example
|
|
20
|
+
* import { Input } from "@/components/ui/input";
|
|
21
|
+
*
|
|
22
|
+
* <Input type="email" placeholder="Email" />
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
* @see {@link Label}
|
|
25
|
+
*/
|
|
26
|
+
function Input(props: ComponentProps<"input">): JSX.Element {
|
|
27
|
+
const [local, rest] = splitProps(props, ["class", "type"]);
|
|
28
|
+
return (
|
|
29
|
+
<input
|
|
30
|
+
type={local.type}
|
|
31
|
+
data-slot="input"
|
|
32
|
+
class={cn(
|
|
33
|
+
"flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
34
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
35
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
36
|
+
local.class,
|
|
37
|
+
)}
|
|
38
|
+
{...rest}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { Input };
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const addonVariants = cva(
|
|
7
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)]",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
align: {
|
|
11
|
+
"inline-start": "order-first ps-3 has-[>button]:ms-[-0.45rem] has-[>kbd]:ms-[-0.35rem]",
|
|
12
|
+
"inline-end": "order-last pe-3 has-[>button]:me-[-0.4rem] has-[>kbd]:me-[-0.35rem]",
|
|
13
|
+
"block-start":
|
|
14
|
+
"order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5",
|
|
15
|
+
"block-end":
|
|
16
|
+
"order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { align: "inline-start" },
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A text control with addons (text, icons, buttons) on its sides. The group draws the border, the
|
|
25
|
+
* radius and the focus ring, so the control inside it is borderless. Use it in place of a bare
|
|
26
|
+
* {@link InputGroupInput}, which has no frame of its own.
|
|
27
|
+
*
|
|
28
|
+
* Keyboard and ARIA: the group is a `role="group"` with no keys of its own; the control and the
|
|
29
|
+
* buttons keep theirs. Name the group with `aria-label` or `aria-labelledby` when it needs a name
|
|
30
|
+
* of its own; the control inside still needs one. An `aria-invalid="true"` on the control turns the
|
|
31
|
+
* group's border destructive.
|
|
32
|
+
*
|
|
33
|
+
* @param props The `div` attributes. `class` adds to the group's classes.
|
|
34
|
+
* @returns The group `div` with the addons and the control inside.
|
|
35
|
+
* @example
|
|
36
|
+
* import {
|
|
37
|
+
* InputGroup,
|
|
38
|
+
* InputGroupAddon,
|
|
39
|
+
* InputGroupInput,
|
|
40
|
+
* InputGroupText,
|
|
41
|
+
* } from "@/components/ui/input-group";
|
|
42
|
+
*
|
|
43
|
+
* <InputGroup>
|
|
44
|
+
* <InputGroupAddon>
|
|
45
|
+
* <InputGroupText>https://</InputGroupText>
|
|
46
|
+
* </InputGroupAddon>
|
|
47
|
+
* <InputGroupInput placeholder="example.com" aria-label="Website" />
|
|
48
|
+
* </InputGroup>
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
* @see {@link InputGroupAddon}
|
|
51
|
+
*/
|
|
52
|
+
function InputGroup(props: ComponentProps<"div">): JSX.Element {
|
|
53
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
role="group"
|
|
57
|
+
data-slot="input-group"
|
|
58
|
+
class={cn(
|
|
59
|
+
"group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
|
|
60
|
+
"focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50",
|
|
61
|
+
"has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40",
|
|
62
|
+
"has-[textarea]:h-auto has-[[data-slot=input-group-control]:disabled]:opacity-50",
|
|
63
|
+
// A block addon sits above or below the control, so the parts stack in a column.
|
|
64
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col",
|
|
65
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col",
|
|
66
|
+
local.class,
|
|
67
|
+
)}
|
|
68
|
+
{...rest}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Props of {@link InputGroupAddon}: the `div` attributes plus `align`.
|
|
75
|
+
* @since 1.0.0
|
|
76
|
+
* @see {@link InputGroupAddon}
|
|
77
|
+
*/
|
|
78
|
+
export type InputGroupAddonProps = ComponentProps<"div"> &
|
|
79
|
+
VariantProps<typeof addonVariants> & {
|
|
80
|
+
/**
|
|
81
|
+
* The side the addon sits on. `inline-start` and `inline-end` are beside the control;
|
|
82
|
+
* `block-start` and `block-end` are above and below it, and the group stacks into a column.
|
|
83
|
+
* @default "inline-start"
|
|
84
|
+
*/
|
|
85
|
+
readonly align?: "inline-start" | "inline-end" | "block-start" | "block-end" | undefined;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* An addon on a side of the group: text, an icon or a button. A click on the addon outside its
|
|
90
|
+
* buttons moves focus to the group's control, so the whole strip reads as part of the field. Your
|
|
91
|
+
* own `onClick` runs first; the focus move is the default.
|
|
92
|
+
*
|
|
93
|
+
* Keyboard and ARIA: the addon has no keys and a `role="presentation"`. The buttons and text in it
|
|
94
|
+
* keep their own behavior.
|
|
95
|
+
*
|
|
96
|
+
* @param props The addon's `div` attributes. `align` picks the side (see {@link InputGroupAddonProps});
|
|
97
|
+
* `onClick` is called before the focus move.
|
|
98
|
+
* @returns The addon `div`.
|
|
99
|
+
* @example
|
|
100
|
+
* import {
|
|
101
|
+
* InputGroup,
|
|
102
|
+
* InputGroupAddon,
|
|
103
|
+
* InputGroupButton,
|
|
104
|
+
* InputGroupInput,
|
|
105
|
+
* } from "@/components/ui/input-group";
|
|
106
|
+
*
|
|
107
|
+
* <InputGroup>
|
|
108
|
+
* <InputGroupInput aria-label="Share link" value="https://example.com" readOnly />
|
|
109
|
+
* <InputGroupAddon align="inline-end">
|
|
110
|
+
* <InputGroupButton aria-label="Copy link">Copy</InputGroupButton>
|
|
111
|
+
* </InputGroupAddon>
|
|
112
|
+
* </InputGroup>
|
|
113
|
+
* @since 1.0.0
|
|
114
|
+
* @see {@link InputGroup}
|
|
115
|
+
*/
|
|
116
|
+
function InputGroupAddon(props: InputGroupAddonProps): JSX.Element {
|
|
117
|
+
const [local, rest] = splitProps(props, ["class", "align", "onClick"]);
|
|
118
|
+
// The application's own click handler runs first; the focus move is the default.
|
|
119
|
+
const onClick = (event: MouseEvent): void => {
|
|
120
|
+
(local.onClick as ((event: MouseEvent) => void) | undefined)?.(event);
|
|
121
|
+
const target = event.target;
|
|
122
|
+
if (target instanceof Element && target.closest("button") !== null) return;
|
|
123
|
+
const group = (event.currentTarget as HTMLElement).closest('[data-slot="input-group"]');
|
|
124
|
+
group?.querySelector<HTMLElement>("input,textarea")?.focus();
|
|
125
|
+
};
|
|
126
|
+
return (
|
|
127
|
+
<div
|
|
128
|
+
role="presentation"
|
|
129
|
+
data-slot="input-group-addon"
|
|
130
|
+
data-align={local.align ?? "inline-start"}
|
|
131
|
+
class={cn(addonVariants({ align: local.align }), local.class)}
|
|
132
|
+
onClick={onClick}
|
|
133
|
+
{...rest}
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The bare text control inside a group: no border, no ring, and it fills the free width. Use it
|
|
140
|
+
* only inside an {@link InputGroup}.
|
|
141
|
+
*
|
|
142
|
+
* Keyboard and ARIA: a native `input` with the browser's keys. It has no name of its own: pass
|
|
143
|
+
* `aria-label`, or a {@link Label} whose `htmlFor` points at its `id`. Its `data-slot` is
|
|
144
|
+
* `input-group-control`, which the group's styles check.
|
|
145
|
+
*
|
|
146
|
+
* @param props The `input` attributes. `class` adds to the control's classes.
|
|
147
|
+
* @returns The `input` element.
|
|
148
|
+
* @example
|
|
149
|
+
* import { InputGroup, InputGroupInput } from "@/components/ui/input-group";
|
|
150
|
+
*
|
|
151
|
+
* <InputGroup>
|
|
152
|
+
* <InputGroupInput aria-label="Search" placeholder="Search" />
|
|
153
|
+
* </InputGroup>
|
|
154
|
+
* @since 1.0.0
|
|
155
|
+
* @see {@link InputGroup}
|
|
156
|
+
*/
|
|
157
|
+
function InputGroupInput(props: ComponentProps<"input">): JSX.Element {
|
|
158
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
159
|
+
return (
|
|
160
|
+
<input
|
|
161
|
+
data-slot="input-group-control"
|
|
162
|
+
class={cn(
|
|
163
|
+
"flex h-9 w-full min-w-0 flex-1 rounded-none border-0 bg-transparent px-3 py-1 text-base shadow-none outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed md:text-sm",
|
|
164
|
+
local.class,
|
|
165
|
+
)}
|
|
166
|
+
{...rest}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The bare multi-line control inside a group: no border, no ring, and it fills the free width.
|
|
173
|
+
* Use it only inside an {@link InputGroup}.
|
|
174
|
+
*
|
|
175
|
+
* Keyboard and ARIA: a native `textarea` with the browser's keys. Name it with `aria-label`, or with
|
|
176
|
+
* a {@link Label} whose `htmlFor` points at its `id`.
|
|
177
|
+
*
|
|
178
|
+
* @param props The `textarea` attributes. `class` adds to the control's classes.
|
|
179
|
+
* @returns The `textarea` element.
|
|
180
|
+
* @example
|
|
181
|
+
* import { InputGroup, InputGroupTextarea } from "@/components/ui/input-group";
|
|
182
|
+
*
|
|
183
|
+
* <InputGroup>
|
|
184
|
+
* <InputGroupTextarea aria-label="Message" placeholder="Write a message" />
|
|
185
|
+
* </InputGroup>
|
|
186
|
+
* @since 1.0.0
|
|
187
|
+
* @see {@link InputGroup}
|
|
188
|
+
*/
|
|
189
|
+
function InputGroupTextarea(props: ComponentProps<"textarea">): JSX.Element {
|
|
190
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
191
|
+
return (
|
|
192
|
+
<textarea
|
|
193
|
+
data-slot="input-group-control"
|
|
194
|
+
class={cn(
|
|
195
|
+
"flex min-h-16 w-full flex-1 resize-none rounded-none border-0 bg-transparent px-3 py-3 text-base shadow-none outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed md:text-sm",
|
|
196
|
+
local.class,
|
|
197
|
+
)}
|
|
198
|
+
{...rest}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Text inside an addon: a unit, a protocol or a prefix.
|
|
205
|
+
*
|
|
206
|
+
* Keyboard and ARIA: a `span` with no role and no keys.
|
|
207
|
+
*
|
|
208
|
+
* @param props The `span` attributes. `class` adds to the classes.
|
|
209
|
+
* @returns The `span` element.
|
|
210
|
+
* @example
|
|
211
|
+
* import { InputGroupText } from "@/components/ui/input-group";
|
|
212
|
+
*
|
|
213
|
+
* <InputGroupText>USD</InputGroupText>
|
|
214
|
+
* @since 1.0.0
|
|
215
|
+
* @see {@link InputGroupAddon}
|
|
216
|
+
*/
|
|
217
|
+
function InputGroupText(props: ComponentProps<"span">): JSX.Element {
|
|
218
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
219
|
+
return (
|
|
220
|
+
<span
|
|
221
|
+
class={cn("flex items-center gap-2 text-sm text-muted-foreground", local.class)}
|
|
222
|
+
{...rest}
|
|
223
|
+
/>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const inputGroupButtonVariants = cva(
|
|
228
|
+
"inline-flex shrink-0 items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
229
|
+
{
|
|
230
|
+
variants: {
|
|
231
|
+
variant: {
|
|
232
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
233
|
+
destructive:
|
|
234
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
235
|
+
outline:
|
|
236
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
237
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
238
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
239
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
240
|
+
},
|
|
241
|
+
size: {
|
|
242
|
+
xs: "h-6 gap-1 rounded-sm px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
243
|
+
sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2",
|
|
244
|
+
"icon-xs": "size-6 rounded-sm p-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
245
|
+
"icon-sm": "size-8 rounded-md p-0",
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
defaultVariants: {
|
|
249
|
+
variant: "ghost",
|
|
250
|
+
size: "xs",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Props of {@link InputGroupButton}: the `button` attributes, plus `variant` and `size`.
|
|
257
|
+
* @see {@link InputGroupButton}
|
|
258
|
+
* @since 1.0.0
|
|
259
|
+
*/
|
|
260
|
+
export type InputGroupButtonProps = ComponentProps<"button"> &
|
|
261
|
+
VariantProps<typeof inputGroupButtonVariants>;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* A small button inside an addon, styled to sit in the group. It is a `type="button"` button, so it
|
|
265
|
+
* never submits a surrounding form unless you pass `type="submit"`.
|
|
266
|
+
*
|
|
267
|
+
* Keyboard and ARIA: a native button (Enter and Space press it). Its name is its text; a button with
|
|
268
|
+
* only an icon needs an `aria-label`.
|
|
269
|
+
*
|
|
270
|
+
* shadcn/ui differences: the classes come from this file, not from the Button item, so the group does not
|
|
271
|
+
* depend on the Button item's sizes. `variant` and `size` match shadcn's button sizes for groups: `xs`
|
|
272
|
+
* (the default, 24 px tall), `sm` (32 px), `icon-xs` and `icon-sm` (square). `variant` takes the Button
|
|
273
|
+
* item's variants, and defaults to `ghost`.
|
|
274
|
+
*
|
|
275
|
+
* @param props The `button` attributes, `variant` and `size`: see {@link InputGroupButtonProps}. `class`
|
|
276
|
+
* adds to the classes.
|
|
277
|
+
* @returns The `button` element.
|
|
278
|
+
* @example
|
|
279
|
+
* import { SearchIcon } from "@/components/ui/icons";
|
|
280
|
+
* import { InputGroup, InputGroupButton, InputGroupInput } from "@/components/ui/input-group";
|
|
281
|
+
*
|
|
282
|
+
* <InputGroup>
|
|
283
|
+
* <InputGroupInput aria-label="Search" />
|
|
284
|
+
* <InputGroupButton aria-label="Clear">Clear</InputGroupButton>
|
|
285
|
+
* <InputGroupButton size="icon-xs" variant="secondary" aria-label="Search">
|
|
286
|
+
* <SearchIcon />
|
|
287
|
+
* </InputGroupButton>
|
|
288
|
+
* </InputGroup>
|
|
289
|
+
* @default size "xs", variant "ghost"
|
|
290
|
+
* @since 1.0.0
|
|
291
|
+
* @see {@link InputGroupAddon}
|
|
292
|
+
*/
|
|
293
|
+
function InputGroupButton(props: InputGroupButtonProps): JSX.Element {
|
|
294
|
+
const [local, rest] = splitProps(props, ["class", "variant", "size"]);
|
|
295
|
+
return (
|
|
296
|
+
<button
|
|
297
|
+
type="button"
|
|
298
|
+
data-slot="input-group-button"
|
|
299
|
+
class={cn(
|
|
300
|
+
inputGroupButtonVariants({ variant: local.variant, size: local.size }),
|
|
301
|
+
local.class,
|
|
302
|
+
)}
|
|
303
|
+
{...rest}
|
|
304
|
+
/>
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export {
|
|
309
|
+
InputGroup,
|
|
310
|
+
InputGroupAddon,
|
|
311
|
+
InputGroupButton,
|
|
312
|
+
InputGroupInput,
|
|
313
|
+
InputGroupText,
|
|
314
|
+
InputGroupTextarea,
|
|
315
|
+
};
|