@vvedantb/ui 0.17.1 → 0.18.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/README.md +251 -3
- package/package.json +13 -5
- package/shell/src/SidebarResize.tsx +1 -4
- package/src/ai-elements/__snapshots__/queue.test.tsx.snap +11 -0
- package/src/ai-elements/activity-question-utils.ts +26 -0
- package/src/ai-elements/activity-question.tsx +159 -0
- package/src/ai-elements/activity-shared.tsx +459 -0
- package/src/ai-elements/activity-step-detail.tsx +115 -0
- package/src/ai-elements/activity-step-label.ts +981 -0
- package/src/ai-elements/activity-tasks-utils.ts +199 -0
- package/src/ai-elements/activity-tasks.tsx +770 -0
- package/src/ai-elements/artifact.tsx +162 -52
- package/src/ai-elements/attachments.tsx +62 -32
- package/src/ai-elements/code-block.tsx +76 -22
- package/src/ai-elements/context.tsx +370 -0
- package/src/ai-elements/conversation.tsx +186 -62
- package/src/ai-elements/file-chip.tsx +53 -0
- package/src/ai-elements/message.tsx +161 -50
- package/src/ai-elements/model-picker-content.tsx +505 -0
- package/src/ai-elements/model-picker-types.ts +56 -0
- package/src/ai-elements/model-picker.tsx +301 -0
- package/src/ai-elements/model-selector.tsx +102 -21
- package/src/ai-elements/plan.tsx +130 -0
- package/src/ai-elements/prompt-input-settings.tsx +144 -0
- package/src/ai-elements/prompt-input-speech.tsx +189 -0
- package/src/ai-elements/prompt-input-traits.tsx +281 -0
- package/src/ai-elements/prompt-input.tsx +320 -134
- package/src/ai-elements/provider-icon.tsx +133 -0
- package/src/ai-elements/queue.tsx +293 -9
- package/src/ai-elements/shimmer.tsx +72 -1
- package/src/ai-elements/simple-model-ladder.tsx +108 -0
- package/src/ai-elements/test-results.tsx +510 -0
- package/src/ai-elements/trace.tsx +243 -0
- package/src/ai-elements/use-streaming-disclosure.ts +50 -0
- package/src/ai-elements/web-preview.tsx +349 -0
- package/src/composer/input-area/ComposerSlashMenu.tsx +1 -1
- package/src/index.ts +66 -12
- package/src/kibo/avatar-stack.tsx +40 -0
- package/src/kibo/contribution-graph.tsx +539 -0
- package/src/kibo/cursor.tsx +72 -0
- package/src/kibo/gantt-features.tsx +439 -0
- package/src/kibo/gantt-header.tsx +181 -0
- package/src/kibo/gantt-provider.tsx +657 -0
- package/src/kibo/gantt-sidebar.tsx +157 -0
- package/src/kibo/gantt-timeline.tsx +154 -0
- package/src/kibo/index.ts +151 -0
- package/src/kibo/kanban.tsx +338 -0
- package/src/kibo/kanbanDropTarget.ts +25 -0
- package/src/kibo/list.tsx +162 -0
- package/src/kibo/theme-switcher.tsx +109 -0
- package/src/kibo/video-player.tsx +237 -0
- package/src/markdown/Markdown.tsx +112 -0
- package/src/markdown/MarkdownBlockquote.tsx +45 -0
- package/src/markdown/MarkdownCodeBlock.tsx +197 -0
- package/src/markdown/MarkdownInlineCode.tsx +40 -0
- package/src/markdown/MarkdownLink.tsx +92 -0
- package/src/markdown/codeFence.ts +57 -0
- package/src/markdown/codeHighlight.ts +138 -0
- package/src/markdown/incrementalBlocks.ts +182 -0
- package/src/markdown/index.ts +3 -0
- package/src/markdown/markdownClipboard.ts +178 -0
- package/src/markdown/mermaid.ts +155 -0
- package/src/markdown/rehypeGithubAlerts.ts +80 -0
- package/src/motion/presets.ts +24 -0
- package/src/ui/__snapshots__/alt-wrappers.test.tsx.snap +19 -0
- package/src/ui/__snapshots__/sonner-dom.test.tsx.snap +5 -0
- package/src/ui/_menu-parts.tsx +29 -0
- package/src/ui/accordion.alt.tsx +46 -53
- package/src/ui/accordion.tsx +59 -10
- package/src/ui/avatar.tsx +56 -0
- package/src/ui/badge.tsx +45 -21
- package/src/ui/border-beam.tsx +97 -0
- package/src/ui/button-group.tsx +50 -21
- package/src/ui/button.tsx +67 -45
- package/src/ui/calendar.tsx +48 -5
- package/src/ui/card.tsx +77 -8
- package/src/ui/carousel.tsx +60 -0
- package/src/ui/centered-spinner.tsx +30 -0
- package/src/ui/checkbox.tsx +34 -16
- package/src/ui/clear-input.alt.tsx +33 -75
- package/src/ui/clear-input.tsx +140 -15
- package/src/ui/collapsible.tsx +40 -2
- package/src/ui/command.tsx +143 -70
- package/src/ui/context-menu.tsx +159 -93
- package/src/ui/crossfade-icon.tsx +123 -0
- package/src/ui/dialog.tsx +154 -70
- package/src/ui/dropdown-menu.tsx +177 -117
- package/src/ui/hover-card.tsx +59 -17
- package/src/ui/input-group.alt.tsx +83 -83
- package/src/ui/input-group.tsx +129 -58
- package/src/ui/input.tsx +13 -4
- package/src/ui/label.tsx +11 -8
- package/src/ui/list-row.tsx +232 -0
- package/src/ui/loading-state.tsx +215 -0
- package/src/ui/popover.tsx +56 -23
- package/src/ui/progress.tsx +28 -16
- package/src/ui/refresh-spin-icon.tsx +41 -0
- package/src/ui/search-input.tsx +66 -15
- package/src/ui/select.tsx +171 -100
- package/src/ui/separator.tsx +23 -14
- package/src/ui/slider.tsx +21 -7
- package/src/ui/sonner.tsx +68 -30
- package/src/ui/spinner.tsx +229 -11
- package/src/ui/status-dot.tsx +84 -0
- package/src/ui/surface.tsx +49 -0
- package/src/ui/switch.tsx +29 -15
- package/src/ui/table.tsx +38 -16
- package/src/ui/tabs.tsx +123 -35
- package/src/ui/tabsSliding.ts +33 -1
- package/src/ui/textarea.tsx +13 -4
- package/src/ui/tooltip.tsx +50 -14
- package/src/ui/ui-config.tsx +149 -0
- package/src/utils/cat-color.ts +123 -0
- package/src/utils/dayjs.ts +6 -0
- package/src/utils/gesture.ts +59 -0
- package/src/utils/runtimeVisibility.ts +167 -0
- package/src/utils/sharedClock.ts +96 -0
- package/src/utils/surface-radius.ts +16 -0
- package/src/utils/useDragSensors.ts +58 -0
- package/tokens/examples/tokens-moved/Fixture.tsx +283 -0
- package/tokens/examples/tokens-moved/README.md +20 -0
- package/tokens/examples/tokens-moved/index.html +12 -0
- package/tokens/examples/tokens-moved/main.tsx +6 -0
- package/tokens/examples/tokens-moved/package.json +14 -0
- package/tokens/examples/tokens-moved/styles.css +12 -0
- package/tokens/examples/tokens-moved/tsconfig.json +14 -0
- package/tokens/examples/tokens-moved/vite.config.ts +11 -0
- package/tokens/src/css/border-beam.css +116 -0
- package/tokens/src/css/markdown.css +318 -0
|
@@ -5,10 +5,23 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
5
5
|
import { cn } from "../utils/cn";
|
|
6
6
|
import { fieldFocusWithinClass } from "./_field-classes";
|
|
7
7
|
import { Button, type ButtonProps } from "./button";
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
InputGroupAddon,
|
|
10
|
+
InputGroupInput,
|
|
11
|
+
InputGroupText,
|
|
12
|
+
InputGroupTextarea,
|
|
13
|
+
} from "./input-group";
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
// The `Alt` look on the main parts: per-instance `classes` replace the main
|
|
16
|
+
// classes (and any `UiConfig.InputGroup` entry), and `role` / `data-slot`
|
|
17
|
+
// set to `undefined` drop the main attributes, so the markup is the
|
|
18
|
+
// pre-wrapper one. Two parts cannot go through the main ones without changing
|
|
19
|
+
// their markup: the root puts `data-input-group` (the addon's focus hook)
|
|
20
|
+
// before `class`, and the button has no default `type`; they stay a plain
|
|
21
|
+
// `<div>` and a `Button`.
|
|
22
|
+
|
|
23
|
+
/** @deprecated Use `InputGroup`. Kept for existing consumers; removed in a later minor after they move. */
|
|
24
|
+
const InputGroupAlt = React.forwardRef<
|
|
12
25
|
HTMLDivElement,
|
|
13
26
|
React.HTMLAttributes<HTMLDivElement> & { error?: boolean }
|
|
14
27
|
>(({ className, error, ...props }, ref) => (
|
|
@@ -28,10 +41,11 @@ const InputGroup = React.forwardRef<
|
|
|
28
41
|
{...props}
|
|
29
42
|
/>
|
|
30
43
|
));
|
|
31
|
-
|
|
44
|
+
InputGroupAlt.displayName = "InputGroupAlt";
|
|
32
45
|
|
|
33
|
-
const
|
|
34
|
-
|
|
46
|
+
const addonClasses = {
|
|
47
|
+
addon: {
|
|
48
|
+
base: "flex items-center",
|
|
35
49
|
align: {
|
|
36
50
|
"inline-start": "flex-row ps-3",
|
|
37
51
|
"inline-end": "flex-row pe-3",
|
|
@@ -39,35 +53,38 @@ const inputGroupAddonVariants = cva("flex items-center", {
|
|
|
39
53
|
"block-end": "w-full flex-row px-3 py-2",
|
|
40
54
|
},
|
|
41
55
|
},
|
|
42
|
-
|
|
43
|
-
align: "inline-start",
|
|
44
|
-
},
|
|
45
|
-
});
|
|
56
|
+
};
|
|
46
57
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
58
|
+
/** `align={null}` (allowed by the old variant props) adds no align classes. */
|
|
59
|
+
const addonClassesNoAlign = { addon: { base: "flex items-center", align: { "inline-start": "" } } };
|
|
60
|
+
|
|
61
|
+
/** Focuses the group's first textarea or input (the click target of an addon). */
|
|
62
|
+
function focusGroupField(event: React.MouseEvent<HTMLDivElement>) {
|
|
63
|
+
const group = event.currentTarget.closest("[data-input-group]");
|
|
64
|
+
const field = group?.querySelector("textarea, input");
|
|
65
|
+
if (field instanceof HTMLElement) field.focus();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** @deprecated Use `InputGroupAddon`. Kept for existing consumers; removed in a later minor after they move. */
|
|
69
|
+
function InputGroupAddonAlt({
|
|
70
|
+
align,
|
|
71
|
+
onClick,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<"div"> & {
|
|
74
|
+
align?: "inline-start" | "inline-end" | "block-start" | "block-end" | null;
|
|
75
|
+
}) {
|
|
76
|
+
return (
|
|
77
|
+
<InputGroupAddon
|
|
78
|
+
role={undefined}
|
|
79
|
+
data-slot={undefined}
|
|
80
|
+
align={align}
|
|
81
|
+
data-align={align ?? undefined}
|
|
82
|
+
classes={align === null ? addonClassesNoAlign : addonClasses}
|
|
83
|
+
onClick={onClick ?? focusGroupField}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
71
88
|
|
|
72
89
|
const inputGroupButtonVariants = cva("shrink-0 shadow-none", {
|
|
73
90
|
variants: {
|
|
@@ -83,7 +100,8 @@ const inputGroupButtonVariants = cva("shrink-0 shadow-none", {
|
|
|
83
100
|
},
|
|
84
101
|
});
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
/** @deprecated Use `InputGroupButton`. Kept for existing consumers; removed in a later minor after they move. */
|
|
104
|
+
const InputGroupButtonAlt = React.forwardRef<
|
|
87
105
|
HTMLButtonElement,
|
|
88
106
|
ButtonProps & VariantProps<typeof inputGroupButtonVariants>
|
|
89
107
|
>(({ className, size, variant = "ghost", ...props }, ref) => (
|
|
@@ -94,58 +112,40 @@ const InputGroupButton = React.forwardRef<
|
|
|
94
112
|
{...props}
|
|
95
113
|
/>
|
|
96
114
|
));
|
|
97
|
-
|
|
115
|
+
InputGroupButtonAlt.displayName = "InputGroupButtonAlt";
|
|
98
116
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return
|
|
104
|
-
<span
|
|
105
|
-
className={cn(
|
|
106
|
-
"flex items-center text-sm text-muted-foreground/95",
|
|
107
|
-
className,
|
|
108
|
-
)}
|
|
109
|
-
{...props}
|
|
110
|
-
/>
|
|
111
|
-
);
|
|
117
|
+
const textClasses = { text: "flex items-center text-sm text-muted-foreground/95" };
|
|
118
|
+
|
|
119
|
+
/** @deprecated Use `InputGroupText`. Kept for existing consumers; removed in a later minor after they move. */
|
|
120
|
+
function InputGroupTextAlt(props: React.HTMLAttributes<HTMLSpanElement>) {
|
|
121
|
+
return <InputGroupText classes={textClasses} {...props} />;
|
|
112
122
|
}
|
|
113
123
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
<Input
|
|
119
|
-
ref={ref}
|
|
120
|
-
className={cn(
|
|
121
|
-
"h-full flex-1 rounded-none border-0 bg-transparent px-3 py-2 shadow-none focus-visible:border-0 focus-visible:ring-0",
|
|
122
|
-
className,
|
|
123
|
-
)}
|
|
124
|
-
{...props}
|
|
125
|
-
/>
|
|
126
|
-
));
|
|
127
|
-
InputGroupInput.displayName = "InputGroupInput";
|
|
124
|
+
const inputClasses = {
|
|
125
|
+
input:
|
|
126
|
+
"h-full flex-1 rounded-none border-0 bg-transparent px-3 py-2 shadow-none focus-visible:border-0 focus-visible:ring-0",
|
|
127
|
+
};
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
/** @deprecated Use `InputGroupInput`. Kept for existing consumers; removed in a later minor after they move. */
|
|
130
|
+
function InputGroupInputAlt(props: React.ComponentProps<"input">) {
|
|
131
|
+
return <InputGroupInput data-slot={undefined} classes={inputClasses} {...props} />;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const textareaClasses = {
|
|
135
|
+
textarea:
|
|
136
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent px-3 py-2.5 shadow-none focus-visible:border-0 focus-visible:ring-0 field-sizing-content",
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/** @deprecated Use `InputGroupTextarea`. Kept for existing consumers; removed in a later minor after they move. */
|
|
140
|
+
function InputGroupTextareaAlt(props: React.ComponentProps<"textarea">) {
|
|
141
|
+
return <InputGroupTextarea data-slot={undefined} classes={textareaClasses} {...props} />;
|
|
142
|
+
}
|
|
143
143
|
|
|
144
144
|
export {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
InputGroupAlt,
|
|
146
|
+
InputGroupAddonAlt,
|
|
147
|
+
InputGroupButtonAlt,
|
|
148
|
+
InputGroupTextAlt,
|
|
149
|
+
InputGroupInputAlt,
|
|
150
|
+
InputGroupTextareaAlt,
|
|
151
151
|
};
|
package/src/ui/input-group.tsx
CHANGED
|
@@ -3,64 +3,105 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
|
|
6
|
-
import { cn } from "../utils/cn";
|
|
7
6
|
import { Button } from "./button";
|
|
8
7
|
import { Input } from "./input";
|
|
9
8
|
import { Textarea } from "./textarea";
|
|
9
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
10
|
+
|
|
11
|
+
type InputGroupAlign = "inline-start" | "inline-end" | "block-start" | "block-end";
|
|
12
|
+
type InputGroupButtonSize = "xs" | "sm" | "icon-xs" | "icon-sm";
|
|
13
|
+
|
|
14
|
+
type InputGroupClasses = {
|
|
15
|
+
root?: string;
|
|
16
|
+
addon?: { base?: string; align?: Partial<Record<InputGroupAlign, string>> };
|
|
17
|
+
button?: { base?: string; size?: Partial<Record<InputGroupButtonSize, string>> };
|
|
18
|
+
text?: string;
|
|
19
|
+
input?: string;
|
|
20
|
+
textarea?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* `UiConfig.InputGroup`: each given class string replaces the built-in one.
|
|
25
|
+
* `InputGroupAddon`, `InputGroupText`, `InputGroupInput` and
|
|
26
|
+
* `InputGroupTextarea` also take a `classes` prop with their own key, which
|
|
27
|
+
* wins over the config for that instance.
|
|
28
|
+
*/
|
|
29
|
+
export interface InputGroupConfig {
|
|
30
|
+
classes?: InputGroupClasses;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const inputGroupRootClasses = [
|
|
34
|
+
"group/input-group relative flex w-full flex-wrap items-center rounded-field border border-border bg-field-background text-sm transition-[border-color,box-shadow] duration-200 ease-smooth outline-hidden",
|
|
35
|
+
"h-10 min-w-0 has-[>textarea]:h-auto",
|
|
36
|
+
"[&>input]:has-[>[data-align=inline-start]]:pl-2",
|
|
37
|
+
"[&>input]:has-[>[data-align=inline-end]]:pr-2",
|
|
38
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col [&>input]:has-[>[data-align=block-start]]:pb-3",
|
|
39
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col [&>input]:has-[>[data-align=block-end]]:pt-3",
|
|
40
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-focus-border has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot=input-group-control]:focus-visible]:ring-focus-ring",
|
|
41
|
+
"has-[[data-slot][aria-invalid=true]]:border-danger-border has-[[data-slot][aria-invalid=true]]:ring-danger-ring",
|
|
42
|
+
];
|
|
10
43
|
|
|
11
44
|
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
45
|
+
const root = useUiConfig("InputGroup")?.classes?.root;
|
|
46
|
+
const merge = useCn();
|
|
12
47
|
return (
|
|
13
48
|
<div
|
|
14
49
|
data-slot="input-group"
|
|
15
50
|
role="group"
|
|
16
|
-
className={
|
|
17
|
-
"group/input-group relative flex w-full flex-wrap items-center rounded-field border border-border bg-field-background text-sm transition-[border-color,box-shadow] duration-200 ease-smooth outline-hidden",
|
|
18
|
-
"h-10 min-w-0 has-[>textarea]:h-auto",
|
|
19
|
-
"[&>input]:has-[>[data-align=inline-start]]:pl-2",
|
|
20
|
-
"[&>input]:has-[>[data-align=inline-end]]:pr-2",
|
|
21
|
-
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col [&>input]:has-[>[data-align=block-start]]:pb-3",
|
|
22
|
-
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col [&>input]:has-[>[data-align=block-end]]:pt-3",
|
|
23
|
-
"has-[[data-slot=input-group-control]:focus-visible]:border-focus-border has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot=input-group-control]:focus-visible]:ring-focus-ring",
|
|
24
|
-
"has-[[data-slot][aria-invalid=true]]:border-danger-border has-[[data-slot][aria-invalid=true]]:ring-danger-ring",
|
|
25
|
-
className,
|
|
26
|
-
)}
|
|
51
|
+
className={merge(root ?? inputGroupRootClasses, className)}
|
|
27
52
|
{...props}
|
|
28
53
|
/>
|
|
29
54
|
);
|
|
30
55
|
}
|
|
31
56
|
|
|
32
|
-
const
|
|
33
|
-
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-md group-data-[disabled=true]/input-group:opacity-50"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
const inputGroupAddonBaseClasses =
|
|
58
|
+
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-md group-data-[disabled=true]/input-group:opacity-50";
|
|
59
|
+
|
|
60
|
+
const inputGroupAddonAlignClasses = {
|
|
61
|
+
"inline-start":
|
|
62
|
+
"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
63
|
+
"inline-end":
|
|
64
|
+
"order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
65
|
+
"block-start":
|
|
66
|
+
"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
|
67
|
+
"block-end":
|
|
68
|
+
"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const inputGroupAddonVariants = cva(inputGroupAddonBaseClasses, {
|
|
72
|
+
variants: {
|
|
73
|
+
align: inputGroupAddonAlignClasses,
|
|
74
|
+
},
|
|
75
|
+
defaultVariants: {
|
|
76
|
+
align: "inline-start",
|
|
50
77
|
},
|
|
51
|
-
);
|
|
78
|
+
});
|
|
52
79
|
|
|
53
80
|
function InputGroupAddon({
|
|
54
81
|
className,
|
|
55
82
|
align = "inline-start",
|
|
83
|
+
classes: instance,
|
|
56
84
|
...props
|
|
57
|
-
}: React.ComponentProps<"div"> &
|
|
85
|
+
}: React.ComponentProps<"div"> &
|
|
86
|
+
VariantProps<typeof inputGroupAddonVariants> & { classes?: Pick<InputGroupClasses, "addon"> }) {
|
|
87
|
+
const config = useUiConfig("InputGroup")?.classes;
|
|
88
|
+
const classes = instance?.addon ?? config?.addon;
|
|
89
|
+
const merge = useCn();
|
|
90
|
+
const a = align ?? "inline-start";
|
|
58
91
|
return (
|
|
59
92
|
<div
|
|
60
93
|
role="group"
|
|
61
94
|
data-slot="input-group-addon"
|
|
62
95
|
data-align={align}
|
|
63
|
-
className={
|
|
96
|
+
className={
|
|
97
|
+
classes
|
|
98
|
+
? merge(
|
|
99
|
+
classes.base ?? inputGroupAddonBaseClasses,
|
|
100
|
+
classes.align?.[a] ?? inputGroupAddonAlignClasses[a],
|
|
101
|
+
className,
|
|
102
|
+
)
|
|
103
|
+
: merge(inputGroupAddonVariants({ align }), className)
|
|
104
|
+
}
|
|
64
105
|
onClick={(e) => {
|
|
65
106
|
if ((e.target as HTMLElement).closest("button")) {
|
|
66
107
|
return;
|
|
@@ -72,22 +113,23 @@ function InputGroupAddon({
|
|
|
72
113
|
);
|
|
73
114
|
}
|
|
74
115
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
const inputGroupButtonBaseClasses = "text-sm shadow-none flex gap-2 items-center";
|
|
117
|
+
|
|
118
|
+
const inputGroupButtonSizeClasses = {
|
|
119
|
+
xs: "h-7 gap-1 rounded-lg px-2 text-xs [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
|
|
120
|
+
sm: "h-8 rounded-lg px-2.5 gap-1.5 text-xs has-[>svg]:px-2.5",
|
|
121
|
+
"icon-xs": "size-7 rounded-lg p-0 has-[>svg]:p-0",
|
|
122
|
+
"icon-sm": "size-8 rounded-lg p-0 has-[>svg]:p-0",
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const inputGroupButtonVariants = cva(inputGroupButtonBaseClasses, {
|
|
126
|
+
variants: {
|
|
127
|
+
size: inputGroupButtonSizeClasses,
|
|
128
|
+
},
|
|
129
|
+
defaultVariants: {
|
|
130
|
+
size: "xs",
|
|
89
131
|
},
|
|
90
|
-
);
|
|
132
|
+
});
|
|
91
133
|
|
|
92
134
|
function InputGroupButton({
|
|
93
135
|
className,
|
|
@@ -97,22 +139,41 @@ function InputGroupButton({
|
|
|
97
139
|
...props
|
|
98
140
|
}: Omit<React.ComponentProps<typeof Button>, "size"> &
|
|
99
141
|
VariantProps<typeof inputGroupButtonVariants>) {
|
|
142
|
+
const classes = useUiConfig("InputGroup")?.classes?.button;
|
|
143
|
+
const merge = useCn();
|
|
144
|
+
const s = size ?? "xs";
|
|
100
145
|
return (
|
|
101
146
|
<Button
|
|
102
147
|
type={type}
|
|
103
148
|
data-size={size}
|
|
104
149
|
variant={variant}
|
|
105
|
-
className={
|
|
150
|
+
className={
|
|
151
|
+
classes
|
|
152
|
+
? merge(
|
|
153
|
+
classes.base ?? inputGroupButtonBaseClasses,
|
|
154
|
+
classes.size?.[s] ?? inputGroupButtonSizeClasses[s],
|
|
155
|
+
className,
|
|
156
|
+
)
|
|
157
|
+
: merge(inputGroupButtonVariants({ size }), className)
|
|
158
|
+
}
|
|
106
159
|
{...props}
|
|
107
160
|
/>
|
|
108
161
|
);
|
|
109
162
|
}
|
|
110
163
|
|
|
111
|
-
function InputGroupText({
|
|
164
|
+
function InputGroupText({
|
|
165
|
+
className,
|
|
166
|
+
classes,
|
|
167
|
+
...props
|
|
168
|
+
}: React.ComponentProps<"span"> & { classes?: Pick<InputGroupClasses, "text"> }) {
|
|
169
|
+
const config = useUiConfig("InputGroup")?.classes;
|
|
170
|
+
const base = classes?.text ?? config?.text;
|
|
171
|
+
const merge = useCn();
|
|
112
172
|
return (
|
|
113
173
|
<span
|
|
114
|
-
className={
|
|
115
|
-
|
|
174
|
+
className={merge(
|
|
175
|
+
base ??
|
|
176
|
+
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
116
177
|
className,
|
|
117
178
|
)}
|
|
118
179
|
{...props}
|
|
@@ -122,13 +183,18 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
|
122
183
|
|
|
123
184
|
function InputGroupInput({
|
|
124
185
|
className,
|
|
186
|
+
classes,
|
|
125
187
|
...props
|
|
126
|
-
}: React.ComponentProps<"input">) {
|
|
188
|
+
}: React.ComponentProps<"input"> & { classes?: Pick<InputGroupClasses, "input"> }) {
|
|
189
|
+
const config = useUiConfig("InputGroup")?.classes;
|
|
190
|
+
const base = classes?.input ?? config?.input;
|
|
191
|
+
const merge = useCn();
|
|
127
192
|
return (
|
|
128
193
|
<Input
|
|
129
194
|
data-slot="input-group-control"
|
|
130
|
-
className={
|
|
131
|
-
|
|
195
|
+
className={merge(
|
|
196
|
+
base ??
|
|
197
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none focus:outline-hidden focus-visible:ring-0",
|
|
132
198
|
className,
|
|
133
199
|
)}
|
|
134
200
|
{...props}
|
|
@@ -138,13 +204,18 @@ function InputGroupInput({
|
|
|
138
204
|
|
|
139
205
|
function InputGroupTextarea({
|
|
140
206
|
className,
|
|
207
|
+
classes,
|
|
141
208
|
...props
|
|
142
|
-
}: React.ComponentProps<"textarea">) {
|
|
209
|
+
}: React.ComponentProps<"textarea"> & { classes?: Pick<InputGroupClasses, "textarea"> }) {
|
|
210
|
+
const config = useUiConfig("InputGroup")?.classes;
|
|
211
|
+
const base = classes?.textarea ?? config?.textarea;
|
|
212
|
+
const merge = useCn();
|
|
143
213
|
return (
|
|
144
214
|
<Textarea
|
|
145
215
|
data-slot="input-group-control"
|
|
146
|
-
className={
|
|
147
|
-
|
|
216
|
+
className={merge(
|
|
217
|
+
base ??
|
|
218
|
+
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus:outline-hidden focus-visible:ring-0",
|
|
148
219
|
className,
|
|
149
220
|
)}
|
|
150
221
|
{...props}
|
package/src/ui/input.tsx
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { cn } from "../utils/cn";
|
|
3
2
|
import { fieldFocusClass } from "./_field-classes";
|
|
3
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
4
|
+
|
|
5
|
+
/** `UiConfig.Input`: `classes.base` replaces the built-in field classes. */
|
|
6
|
+
export interface InputConfig {
|
|
7
|
+
classes?: { base?: string };
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
11
|
({ className, type, ...props }, ref) => {
|
|
12
|
+
const base = useUiConfig("Input")?.classes?.base;
|
|
13
|
+
const merge = useCn();
|
|
7
14
|
return (
|
|
8
15
|
<input
|
|
9
16
|
type={type}
|
|
10
|
-
className={
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
className={merge(
|
|
18
|
+
base ?? [
|
|
19
|
+
"flex h-10 w-full max-sm:min-w-0 rounded-field border border-border bg-field-background px-3.5 py-2 text-base sm:text-sm transition-[border-color,box-shadow,background-color] duration-200 ease-smooth file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-field-placeholder disabled:cursor-not-allowed disabled:opacity-50",
|
|
20
|
+
fieldFocusClass,
|
|
21
|
+
],
|
|
13
22
|
className,
|
|
14
23
|
)}
|
|
15
24
|
ref={ref}
|
package/src/ui/label.tsx
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
|
-
import {
|
|
3
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
4
|
+
|
|
5
|
+
export interface LabelConfig {
|
|
6
|
+
/** Replaces the built-in classes of a part. */
|
|
7
|
+
classes?: { root?: string };
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
const labelClass =
|
|
6
11
|
"text-sm font-medium leading-none tracking-normal text-foreground/90 peer-disabled:cursor-not-allowed peer-disabled:opacity-70";
|
|
@@ -8,13 +13,11 @@ const labelClass =
|
|
|
8
13
|
const Label = React.forwardRef<
|
|
9
14
|
React.ComponentRef<typeof LabelPrimitive.Root>,
|
|
10
15
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
11
|
-
>(({ className, ...props }, ref) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/>
|
|
17
|
-
));
|
|
16
|
+
>(({ className, ...props }, ref) => {
|
|
17
|
+
const root = useUiConfig("Label")?.classes?.root;
|
|
18
|
+
const merge = useCn();
|
|
19
|
+
return <LabelPrimitive.Root ref={ref} className={merge(root ?? labelClass, className)} {...props} />;
|
|
20
|
+
});
|
|
18
21
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
19
22
|
|
|
20
23
|
export { Label };
|