@vvedantb/ui 0.17.0 → 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 +253 -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/compat-shadcn-rgb.css +6 -0
- package/tokens/src/css/markdown.css +318 -0
package/src/ui/table.tsx
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
3
|
+
|
|
4
|
+
export interface TableConfig {
|
|
5
|
+
/** Replaces the built-in classes of a part. A part left out keeps its default. */
|
|
6
|
+
classes?: {
|
|
7
|
+
/** The scroll wrapper `<div>` around the `<table>`. */
|
|
8
|
+
wrapper?: string;
|
|
9
|
+
table?: string;
|
|
10
|
+
header?: string;
|
|
11
|
+
body?: string;
|
|
12
|
+
footer?: string;
|
|
13
|
+
row?: string;
|
|
14
|
+
head?: string;
|
|
15
|
+
cell?: string;
|
|
16
|
+
caption?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The app's class string for `part` (else `fallback`), merged with the caller `className`. */
|
|
21
|
+
function useTableClass(
|
|
22
|
+
part: keyof NonNullable<TableConfig["classes"]>,
|
|
23
|
+
fallback: string,
|
|
24
|
+
className: string | undefined,
|
|
25
|
+
): string {
|
|
26
|
+
const merge = useCn();
|
|
27
|
+
return merge(useUiConfig("Table")?.classes?.[part] ?? fallback, className);
|
|
28
|
+
}
|
|
3
29
|
|
|
4
30
|
const Table = React.forwardRef<
|
|
5
31
|
HTMLTableElement,
|
|
6
32
|
React.HTMLAttributes<HTMLTableElement>
|
|
7
33
|
>(({ className, ...props }, ref) => (
|
|
8
|
-
<div className="relative w-full overflow-auto rounded-lg max-sm:min-w-0 max-sm:max-w-full">
|
|
34
|
+
<div className={useUiConfig("Table")?.classes?.wrapper ?? "relative w-full overflow-auto rounded-lg max-sm:min-w-0 max-sm:max-w-full"}>
|
|
9
35
|
<table
|
|
10
36
|
ref={ref}
|
|
11
|
-
className={
|
|
37
|
+
className={useTableClass("table", "w-full caption-bottom text-sm", className)}
|
|
12
38
|
{...props}
|
|
13
39
|
/>
|
|
14
40
|
</div>
|
|
@@ -21,7 +47,7 @@ const TableHeader = React.forwardRef<
|
|
|
21
47
|
>(({ className, ...props }, ref) => (
|
|
22
48
|
<thead
|
|
23
49
|
ref={ref}
|
|
24
|
-
className={
|
|
50
|
+
className={useTableClass("header", "[&_tr]:border-b [&_tr]:border-separator", className)}
|
|
25
51
|
{...props}
|
|
26
52
|
/>
|
|
27
53
|
));
|
|
@@ -33,7 +59,7 @@ const TableBody = React.forwardRef<
|
|
|
33
59
|
>(({ className, ...props }, ref) => (
|
|
34
60
|
<tbody
|
|
35
61
|
ref={ref}
|
|
36
|
-
className={
|
|
62
|
+
className={useTableClass("body", "[&_tr:last-child]:border-0", className)}
|
|
37
63
|
{...props}
|
|
38
64
|
/>
|
|
39
65
|
));
|
|
@@ -45,10 +71,7 @@ const TableFooter = React.forwardRef<
|
|
|
45
71
|
>(({ className, ...props }, ref) => (
|
|
46
72
|
<tfoot
|
|
47
73
|
ref={ref}
|
|
48
|
-
className={
|
|
49
|
-
"border-t border-separator bg-surface-secondary/30 font-medium [&>tr]:last:border-b-0",
|
|
50
|
-
className,
|
|
51
|
-
)}
|
|
74
|
+
className={useTableClass("footer", "border-t border-separator bg-surface-secondary/30 font-medium [&>tr]:last:border-b-0", className)}
|
|
52
75
|
{...props}
|
|
53
76
|
/>
|
|
54
77
|
));
|
|
@@ -60,7 +83,8 @@ const TableRow = React.forwardRef<
|
|
|
60
83
|
>(({ className, ...props }, ref) => (
|
|
61
84
|
<tr
|
|
62
85
|
ref={ref}
|
|
63
|
-
className={
|
|
86
|
+
className={useTableClass(
|
|
87
|
+
"row",
|
|
64
88
|
"border-b border-separator transition-colors duration-200 ease-smooth hover:bg-surface-tertiary/50 data-[state=selected]:bg-surface-secondary",
|
|
65
89
|
className,
|
|
66
90
|
)}
|
|
@@ -75,7 +99,8 @@ const TableHead = React.forwardRef<
|
|
|
75
99
|
>(({ className, ...props }, ref) => (
|
|
76
100
|
<th
|
|
77
101
|
ref={ref}
|
|
78
|
-
className={
|
|
102
|
+
className={useTableClass(
|
|
103
|
+
"head",
|
|
79
104
|
"h-11 px-4 text-left align-middle text-xs font-semibold uppercase tracking-widest text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
80
105
|
className,
|
|
81
106
|
)}
|
|
@@ -90,10 +115,7 @@ const TableCell = React.forwardRef<
|
|
|
90
115
|
>(({ className, ...props }, ref) => (
|
|
91
116
|
<td
|
|
92
117
|
ref={ref}
|
|
93
|
-
className={
|
|
94
|
-
"p-4 align-middle text-sm [&:has([role=checkbox])]:pr-0",
|
|
95
|
-
className,
|
|
96
|
-
)}
|
|
118
|
+
className={useTableClass("cell", "p-4 align-middle text-sm [&:has([role=checkbox])]:pr-0", className)}
|
|
97
119
|
{...props}
|
|
98
120
|
/>
|
|
99
121
|
));
|
|
@@ -105,7 +127,7 @@ const TableCaption = React.forwardRef<
|
|
|
105
127
|
>(({ className, ...props }, ref) => (
|
|
106
128
|
<caption
|
|
107
129
|
ref={ref}
|
|
108
|
-
className={
|
|
130
|
+
className={useTableClass("caption", "mt-4 text-sm text-muted-foreground", className)}
|
|
109
131
|
{...props}
|
|
110
132
|
/>
|
|
111
133
|
));
|
package/src/ui/tabs.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
|
-
import {
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
import { assignRef } from "../utils/ref";
|
|
5
|
-
import { syncTabsPill } from "./tabsSliding";
|
|
5
|
+
import { syncTabsPill, type TabsPillMotion } from "./tabsSliding";
|
|
6
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
6
7
|
|
|
7
8
|
const Tabs = TabsPrimitive.Root;
|
|
8
9
|
|
|
@@ -11,6 +12,32 @@ const Tabs = TabsPrimitive.Root;
|
|
|
11
12
|
// `soft`: borderless wrapping pills, the active one filled. Uses no `t-tabs`
|
|
12
13
|
// CSS, so triggers can hold an absolutely placed child (e.g. a progress bar).
|
|
13
14
|
type TabsListVariant = "pills" | "segmented" | "soft";
|
|
15
|
+
type TabsListSize = "default" | "sm";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `UiConfig.Tabs`. `classes` replace built-in part classes (`list` replaces
|
|
19
|
+
* the list classes of every variant, `trigger` those of every trigger).
|
|
20
|
+
* `variant` is the default for the `TabsList` prop; `pillMotion` picks how the
|
|
21
|
+
* segmented pill moves: `x` (default) or `xy`, both axes on transform for
|
|
22
|
+
* lists that wrap or run in a column.
|
|
23
|
+
*/
|
|
24
|
+
export interface TabsConfig {
|
|
25
|
+
variant?: TabsListVariant;
|
|
26
|
+
pillMotion?: TabsPillMotion;
|
|
27
|
+
classes?: {
|
|
28
|
+
list?: string;
|
|
29
|
+
size?: Partial<Record<TabsListSize, string>>;
|
|
30
|
+
pill?: string;
|
|
31
|
+
trigger?: string;
|
|
32
|
+
content?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `sm` shrinks the triggers from the list, so callers do not size each one.
|
|
37
|
+
const tabsListSizeClasses: Record<TabsListSize, string> = {
|
|
38
|
+
default: "",
|
|
39
|
+
sm: "[&_.t-tab]:px-2.5 [&_.t-tab]:py-1 [&_.t-tab]:text-xs",
|
|
40
|
+
};
|
|
14
41
|
|
|
15
42
|
const TabsListVariantContext = React.createContext<TabsListVariant>("pills");
|
|
16
43
|
|
|
@@ -18,8 +45,14 @@ const TabsList = React.forwardRef<
|
|
|
18
45
|
React.ComponentRef<typeof TabsPrimitive.List>,
|
|
19
46
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
|
|
20
47
|
variant?: TabsListVariant;
|
|
48
|
+
/** `sm` makes the triggers smaller. No classes for `default`. */
|
|
49
|
+
size?: TabsListSize;
|
|
21
50
|
}
|
|
22
|
-
>(({ className, children, variant
|
|
51
|
+
>(({ className, children, variant: variantProp, size, ...props }, ref) => {
|
|
52
|
+
const config = useUiConfig("Tabs");
|
|
53
|
+
const cn = useCn();
|
|
54
|
+
const variant = variantProp ?? config?.variant ?? "pills";
|
|
55
|
+
const pillMotion = config?.pillMotion ?? "x";
|
|
23
56
|
const listRef = React.useRef<React.ComponentRef<
|
|
24
57
|
typeof TabsPrimitive.List
|
|
25
58
|
> | null>(null);
|
|
@@ -42,11 +75,11 @@ const TabsList = React.forwardRef<
|
|
|
42
75
|
}
|
|
43
76
|
|
|
44
77
|
const snap = () => {
|
|
45
|
-
syncTabsPill(list, pill, false);
|
|
78
|
+
syncTabsPill(list, pill, false, pillMotion);
|
|
46
79
|
};
|
|
47
80
|
|
|
48
81
|
const animate = () => {
|
|
49
|
-
syncTabsPill(list, pill, true);
|
|
82
|
+
syncTabsPill(list, pill, true, pillMotion);
|
|
50
83
|
};
|
|
51
84
|
|
|
52
85
|
snap();
|
|
@@ -83,25 +116,32 @@ const TabsList = React.forwardRef<
|
|
|
83
116
|
resizeObserver.disconnect();
|
|
84
117
|
mutationObserver.disconnect();
|
|
85
118
|
};
|
|
86
|
-
}, [variant]);
|
|
119
|
+
}, [variant, pillMotion]);
|
|
87
120
|
|
|
88
121
|
return (
|
|
89
122
|
<TabsListVariantContext value={variant}>
|
|
90
123
|
<TabsPrimitive.List
|
|
91
124
|
ref={mergedRef}
|
|
92
125
|
className={cn(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
126
|
+
config?.classes?.list ?? [
|
|
127
|
+
variant === "soft"
|
|
128
|
+
? "flex flex-wrap items-center justify-center gap-1.5"
|
|
129
|
+
: "t-tabs max-sm:max-w-full max-sm:justify-center-safe max-sm:overflow-x-auto max-sm:scrollbar-none",
|
|
130
|
+
variant === "segmented" && "t-tabs--segmented",
|
|
131
|
+
variant === "pills" && "t-tabs--pills",
|
|
132
|
+
],
|
|
133
|
+
config?.classes?.size?.[size ?? "default"] ?? tabsListSizeClasses[size ?? "default"],
|
|
98
134
|
className,
|
|
99
135
|
)}
|
|
100
136
|
data-variant={variant}
|
|
101
137
|
{...props}
|
|
102
138
|
>
|
|
103
139
|
{variant === "segmented" ? (
|
|
104
|
-
<span
|
|
140
|
+
<span
|
|
141
|
+
ref={pillRef}
|
|
142
|
+
className={config?.classes?.pill ?? "t-tabs-pill"}
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
/>
|
|
105
145
|
) : null}
|
|
106
146
|
{children}
|
|
107
147
|
</TabsPrimitive.List>
|
|
@@ -113,39 +153,87 @@ TabsList.displayName = TabsPrimitive.List.displayName;
|
|
|
113
153
|
const TabsTrigger = React.forwardRef<
|
|
114
154
|
React.ComponentRef<typeof TabsPrimitive.Trigger>,
|
|
115
155
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
|
116
|
-
>(({ className, ...props }, ref) =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
className
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
156
|
+
>(({ className, ...props }, ref) => {
|
|
157
|
+
const trigger = useUiConfig("Tabs")?.classes?.trigger;
|
|
158
|
+
const cn = useCn();
|
|
159
|
+
const soft = React.use(TabsListVariantContext) === "soft";
|
|
160
|
+
return (
|
|
161
|
+
<TabsPrimitive.Trigger
|
|
162
|
+
ref={ref}
|
|
163
|
+
className={cn(
|
|
164
|
+
trigger ??
|
|
165
|
+
(soft
|
|
166
|
+
? "relative flex items-center gap-2 overflow-hidden rounded-full px-4 py-2 text-sm text-muted-foreground transition-[transform,background-color] hover:bg-surface-secondary active:scale-[0.97] data-[state=active]:bg-surface-secondary data-[state=active]:text-foreground"
|
|
167
|
+
: "t-tab inline-flex items-center justify-center gap-1.5 whitespace-nowrap text-sm font-medium ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"),
|
|
168
|
+
className,
|
|
169
|
+
)}
|
|
170
|
+
{...props}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
});
|
|
128
174
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
129
175
|
|
|
130
176
|
const TabsContent = React.forwardRef<
|
|
131
177
|
React.ComponentRef<typeof TabsPrimitive.Content>,
|
|
132
178
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
|
133
|
-
>(({ className, ...props }, ref) =>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
179
|
+
>(({ className, ...props }, ref) => {
|
|
180
|
+
const content = useUiConfig("Tabs")?.classes?.content;
|
|
181
|
+
const cn = useCn();
|
|
182
|
+
return (
|
|
183
|
+
<TabsPrimitive.Content
|
|
184
|
+
ref={ref}
|
|
185
|
+
className={cn(
|
|
186
|
+
content ??
|
|
187
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2",
|
|
188
|
+
className,
|
|
189
|
+
)}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
});
|
|
143
194
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
144
195
|
|
|
196
|
+
const tabsBarVariants = cva(
|
|
197
|
+
"flex shrink-0 flex-wrap items-center gap-2 border-b border-border",
|
|
198
|
+
{
|
|
199
|
+
variants: {
|
|
200
|
+
size: {
|
|
201
|
+
default: "px-3 py-2",
|
|
202
|
+
sm: "px-2 py-1.5",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
defaultVariants: {
|
|
206
|
+
size: "default",
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Page- or panel-level tab strip: a `TabsList` on the hairline that divides it
|
|
213
|
+
* from the content below, with an optional trailing `actions` slot for toolbar
|
|
214
|
+
* controls. `size="sm"` for nested panels; `className` overrides the padding.
|
|
215
|
+
*/
|
|
216
|
+
const TabsBar = React.forwardRef<
|
|
217
|
+
HTMLDivElement,
|
|
218
|
+
React.ComponentPropsWithoutRef<"div"> &
|
|
219
|
+
VariantProps<typeof tabsBarVariants> & { actions?: React.ReactNode }
|
|
220
|
+
>(({ className, children, actions, size, ...props }, ref) => {
|
|
221
|
+
const cn = useCn();
|
|
222
|
+
return (
|
|
223
|
+
<div ref={ref} className={cn(tabsBarVariants({ size }), className)} {...props}>
|
|
224
|
+
{children}
|
|
225
|
+
{actions ? (
|
|
226
|
+
<div className="ml-auto flex items-center gap-2">{actions}</div>
|
|
227
|
+
) : null}
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
TabsBar.displayName = "TabsBar";
|
|
232
|
+
|
|
145
233
|
// raw radix primitives, use these when you need a tabs layout that the
|
|
146
234
|
// styled `tabs`/`tabslist`/`tabstrigger`/`tabscontent` defaults don't fit
|
|
147
235
|
// (e.g a vertical sidebar of tabs inside a popover) the styled exports
|
|
148
236
|
// above are tuned for horizontal pill, style tab bars
|
|
149
237
|
export { TabsPrimitive };
|
|
150
238
|
|
|
151
|
-
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
239
|
+
export { Tabs, TabsBar, TabsList, TabsTrigger, TabsContent };
|
package/src/ui/tabsSliding.ts
CHANGED
|
@@ -16,6 +16,30 @@ function findActiveTab(list: HTMLElement): HTMLElement | null {
|
|
|
16
16
|
return tabs[0] ?? null;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// `xy`: both axes on transform (top pinned to 0), so a column list slides the
|
|
20
|
+
// same way a row does; `top` is a layout property and does not tween.
|
|
21
|
+
function moveTabsPillXY(
|
|
22
|
+
pill: HTMLElement,
|
|
23
|
+
tab: HTMLElement,
|
|
24
|
+
animate: boolean,
|
|
25
|
+
): void {
|
|
26
|
+
const transform = `translate(${tab.offsetLeft}px, ${tab.offsetTop}px)`;
|
|
27
|
+
const width = `${tab.offsetWidth}px`;
|
|
28
|
+
const height = `${tab.offsetHeight}px`;
|
|
29
|
+
const previousTransition = pill.style.transition;
|
|
30
|
+
if (!animate) {
|
|
31
|
+
pill.style.transition = "none";
|
|
32
|
+
}
|
|
33
|
+
pill.style.top = "0px";
|
|
34
|
+
pill.style.transform = transform;
|
|
35
|
+
pill.style.width = width;
|
|
36
|
+
pill.style.height = height;
|
|
37
|
+
if (!animate) {
|
|
38
|
+
void pill.offsetWidth;
|
|
39
|
+
pill.style.transition = previousTransition;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
19
43
|
function moveTabsPill(
|
|
20
44
|
pill: HTMLElement,
|
|
21
45
|
tab: HTMLElement,
|
|
@@ -44,13 +68,21 @@ function moveTabsPill(
|
|
|
44
68
|
pill.style.height = `${height}px`;
|
|
45
69
|
}
|
|
46
70
|
|
|
71
|
+
export type TabsPillMotion = "x" | "xy";
|
|
72
|
+
|
|
47
73
|
export function syncTabsPill(
|
|
48
74
|
list: HTMLElement,
|
|
49
75
|
pill: HTMLElement,
|
|
50
76
|
animate: boolean,
|
|
77
|
+
motion: TabsPillMotion = "x",
|
|
51
78
|
): void {
|
|
52
79
|
const tab = findActiveTab(list);
|
|
53
|
-
if (tab) {
|
|
80
|
+
if (!tab) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (motion === "xy") {
|
|
84
|
+
moveTabsPillXY(pill, tab, animate);
|
|
85
|
+
} else {
|
|
54
86
|
moveTabsPill(pill, tab, animate);
|
|
55
87
|
}
|
|
56
88
|
}
|
package/src/ui/textarea.tsx
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
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.Textarea`: `classes.base` replaces the built-in field classes. */
|
|
6
|
+
export interface TextareaConfig {
|
|
7
|
+
classes?: { base?: string };
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
const Textarea = React.forwardRef<
|
|
6
11
|
HTMLTextAreaElement,
|
|
7
12
|
React.ComponentProps<"textarea">
|
|
8
13
|
>(({ className, ...props }, ref) => {
|
|
14
|
+
const base = useUiConfig("Textarea")?.classes?.base;
|
|
15
|
+
const merge = useCn();
|
|
9
16
|
return (
|
|
10
17
|
<textarea
|
|
11
|
-
className={
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
className={merge(
|
|
19
|
+
base ?? [
|
|
20
|
+
"flex min-h-20 w-full max-sm:min-w-0 rounded-field border border-border bg-field-background px-3.5 py-2.5 text-base sm:text-sm placeholder:text-field-placeholder disabled:cursor-not-allowed disabled:opacity-50",
|
|
21
|
+
fieldFocusClass,
|
|
22
|
+
],
|
|
14
23
|
className,
|
|
15
24
|
)}
|
|
16
25
|
ref={ref}
|
package/src/ui/tooltip.tsx
CHANGED
|
@@ -1,25 +1,61 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
-
import {
|
|
3
|
+
import { useCn, useUiConfig } from "./ui-config";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/** App defaults for `Tooltip*` (see `UiConfigProvider`). */
|
|
6
|
+
export interface TooltipConfig {
|
|
7
|
+
/** Replaces the built-in classes of a part. */
|
|
8
|
+
classes?: { content?: string };
|
|
9
|
+
/** Default for `TooltipProvider` `delayDuration` (Radix: 700ms). */
|
|
10
|
+
delayDuration?: number;
|
|
11
|
+
/** Default for `TooltipContent` `sideOffset`. */
|
|
12
|
+
sideOffset?: number;
|
|
13
|
+
/** Default for `TooltipContent` `portal`. */
|
|
14
|
+
portal?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function TooltipProvider({
|
|
18
|
+
delayDuration,
|
|
19
|
+
...props
|
|
20
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
21
|
+
const config = useUiConfig("Tooltip");
|
|
22
|
+
return (
|
|
23
|
+
<TooltipPrimitive.Provider
|
|
24
|
+
delayDuration={delayDuration ?? config?.delayDuration}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
6
29
|
const Tooltip = TooltipPrimitive.Root;
|
|
7
30
|
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
8
31
|
|
|
9
32
|
const TooltipContent = React.forwardRef<
|
|
10
33
|
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
|
|
35
|
+
/** Render into `document.body` so a clipping parent cannot cut it off. */
|
|
36
|
+
portal?: boolean;
|
|
37
|
+
}
|
|
38
|
+
>(({ className, sideOffset, portal, ...props }, ref) => {
|
|
39
|
+
const merge = useCn();
|
|
40
|
+
const config = useUiConfig("Tooltip");
|
|
41
|
+
const content = (
|
|
42
|
+
<TooltipPrimitive.Content
|
|
43
|
+
ref={ref}
|
|
44
|
+
sideOffset={sideOffset ?? config?.sideOffset ?? 4}
|
|
45
|
+
className={merge(
|
|
46
|
+
config?.classes?.content ??
|
|
47
|
+
"z-50 max-w-[calc(100vw-2rem)] overflow-hidden rounded-lg bg-overlay px-2.5 py-1.5 text-xs text-overlay-foreground smooth-shadow-ring-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
return (portal ?? config?.portal) ? (
|
|
54
|
+
<TooltipPrimitive.Portal>{content}</TooltipPrimitive.Portal>
|
|
55
|
+
) : (
|
|
56
|
+
content
|
|
57
|
+
);
|
|
58
|
+
});
|
|
23
59
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
24
60
|
|
|
25
61
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ClassValue } from "clsx";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
import type { AccordionConfig } from "./accordion";
|
|
5
|
+
import type { ActivityQuestionCardConfig } from "../ai-elements/activity-question";
|
|
6
|
+
import type { ActivityTasksConfig } from "../ai-elements/activity-shared";
|
|
7
|
+
import type { ArtifactConfig } from "../ai-elements/artifact";
|
|
8
|
+
import type { AttachmentsConfig } from "../ai-elements/attachments";
|
|
9
|
+
import type { BadgeConfig } from "./badge";
|
|
10
|
+
import type { ButtonConfig } from "./button";
|
|
11
|
+
import type { ButtonGroupConfig } from "./button-group";
|
|
12
|
+
import type { CalendarConfig } from "./calendar";
|
|
13
|
+
import type { CardConfig } from "./card";
|
|
14
|
+
import type { CarouselConfig } from "./carousel";
|
|
15
|
+
import type { CheckboxConfig } from "./checkbox";
|
|
16
|
+
import type { SpinnerConfig } from "./spinner";
|
|
17
|
+
import type { ClearInputConfig } from "./clear-input";
|
|
18
|
+
import type { CodeBlockConfig } from "../ai-elements/code-block";
|
|
19
|
+
import type { CollapsibleConfig } from "./collapsible";
|
|
20
|
+
import type { CommandConfig } from "./command";
|
|
21
|
+
import type { ContextMenuConfig } from "./context-menu";
|
|
22
|
+
import type { ConversationConfig } from "../ai-elements/conversation";
|
|
23
|
+
import type { DialogConfig } from "./dialog";
|
|
24
|
+
import type { DropdownMenuConfig } from "./dropdown-menu";
|
|
25
|
+
import type { GanttConfig } from "../kibo/gantt-provider";
|
|
26
|
+
import type { HoverCardConfig } from "./hover-card";
|
|
27
|
+
import type { InputConfig } from "./input";
|
|
28
|
+
import type { InputGroupConfig } from "./input-group";
|
|
29
|
+
import type { KanbanConfig } from "../kibo/kanban";
|
|
30
|
+
import type { LabelConfig } from "./label";
|
|
31
|
+
import type { ListRowConfig } from "./list-row";
|
|
32
|
+
import type { LoadingStateConfig } from "./loading-state";
|
|
33
|
+
import type { MessageConfig } from "../ai-elements/message";
|
|
34
|
+
import type { ModelPickerConfig } from "../ai-elements/model-picker";
|
|
35
|
+
import type { PopoverConfig } from "./popover";
|
|
36
|
+
import type { ProgressConfig } from "./progress";
|
|
37
|
+
import type { PromptInputConfig } from "../ai-elements/prompt-input";
|
|
38
|
+
import type { PromptInputSettingsConfig } from "../ai-elements/prompt-input-settings";
|
|
39
|
+
import type { QueueConfig } from "../ai-elements/queue";
|
|
40
|
+
import type { SearchInputConfig } from "./search-input";
|
|
41
|
+
import type { SelectConfig } from "./select";
|
|
42
|
+
import type { SeparatorConfig } from "./separator";
|
|
43
|
+
import type { ShimmerConfig } from "../ai-elements/shimmer";
|
|
44
|
+
import type { SliderConfig } from "./slider";
|
|
45
|
+
import type { SonnerToasterConfig } from "./sonner";
|
|
46
|
+
import type { StatusDotConfig } from "./status-dot";
|
|
47
|
+
import type { SwitchConfig } from "./switch";
|
|
48
|
+
import type { TableConfig } from "./table";
|
|
49
|
+
import type { TabsConfig } from "./tabs";
|
|
50
|
+
import type { TextareaConfig } from "./textarea";
|
|
51
|
+
import type { ThemeSwitcherConfig } from "../kibo/theme-switcher";
|
|
52
|
+
import type { TooltipConfig } from "./tooltip";
|
|
53
|
+
import type { TraitsPanelConfig } from "../ai-elements/prompt-input-traits";
|
|
54
|
+
import type { VideoPlayerConfig } from "../kibo/video-player";
|
|
55
|
+
|
|
56
|
+
// App-level component defaults. An app wraps its tree in `UiConfigProvider`
|
|
57
|
+
// to give components its own classes and behaviour without passing props at
|
|
58
|
+
// every call site. Without a provider (or with an empty entry) every component
|
|
59
|
+
// renders its built-in default, so apps that do not opt in see no change.
|
|
60
|
+
//
|
|
61
|
+
// Conventions for an entry:
|
|
62
|
+
// - `classes` replaces built-in classes part by part. A part left out keeps
|
|
63
|
+
// its default. A caller `className` still merges after it with `cn`.
|
|
64
|
+
// - Other keys are default prop values. An explicit prop always wins.
|
|
65
|
+
//
|
|
66
|
+
// Each component declares its entry type next to its code and adds it here.
|
|
67
|
+
export interface UiConfig {
|
|
68
|
+
/**
|
|
69
|
+
* Class merger used in place of the built-in `cn` by components that read
|
|
70
|
+
* it. Set it when the app's own tailwind-merge setup differs (for example
|
|
71
|
+
* when it does not treat `rounded-surface` as a radius class), so a caller
|
|
72
|
+
* `className` merges with the app's classes the way it always has.
|
|
73
|
+
*/
|
|
74
|
+
cn?: (...inputs: ClassValue[]) => string;
|
|
75
|
+
Accordion?: AccordionConfig;
|
|
76
|
+
ActivityQuestionCard?: ActivityQuestionCardConfig;
|
|
77
|
+
ActivityTasks?: ActivityTasksConfig;
|
|
78
|
+
Artifact?: ArtifactConfig;
|
|
79
|
+
Attachments?: AttachmentsConfig;
|
|
80
|
+
Badge?: BadgeConfig;
|
|
81
|
+
Button?: ButtonConfig;
|
|
82
|
+
ButtonGroup?: ButtonGroupConfig;
|
|
83
|
+
Calendar?: CalendarConfig;
|
|
84
|
+
Card?: CardConfig;
|
|
85
|
+
Carousel?: CarouselConfig;
|
|
86
|
+
Checkbox?: CheckboxConfig;
|
|
87
|
+
ClearInput?: ClearInputConfig;
|
|
88
|
+
CodeBlock?: CodeBlockConfig;
|
|
89
|
+
Collapsible?: CollapsibleConfig;
|
|
90
|
+
Command?: CommandConfig;
|
|
91
|
+
ContextMenu?: ContextMenuConfig;
|
|
92
|
+
Conversation?: ConversationConfig;
|
|
93
|
+
Dialog?: DialogConfig;
|
|
94
|
+
DropdownMenu?: DropdownMenuConfig;
|
|
95
|
+
Gantt?: GanttConfig;
|
|
96
|
+
HoverCard?: HoverCardConfig;
|
|
97
|
+
Input?: InputConfig;
|
|
98
|
+
InputGroup?: InputGroupConfig;
|
|
99
|
+
Kanban?: KanbanConfig;
|
|
100
|
+
Label?: LabelConfig;
|
|
101
|
+
ListRow?: ListRowConfig;
|
|
102
|
+
LoadingState?: LoadingStateConfig;
|
|
103
|
+
Message?: MessageConfig;
|
|
104
|
+
ModelPicker?: ModelPickerConfig;
|
|
105
|
+
Popover?: PopoverConfig;
|
|
106
|
+
Progress?: ProgressConfig;
|
|
107
|
+
PromptInput?: PromptInputConfig;
|
|
108
|
+
PromptInputSettings?: PromptInputSettingsConfig;
|
|
109
|
+
Queue?: QueueConfig;
|
|
110
|
+
SearchInput?: SearchInputConfig;
|
|
111
|
+
Select?: SelectConfig;
|
|
112
|
+
Separator?: SeparatorConfig;
|
|
113
|
+
Shimmer?: ShimmerConfig;
|
|
114
|
+
Slider?: SliderConfig;
|
|
115
|
+
SonnerToaster?: SonnerToasterConfig;
|
|
116
|
+
Spinner?: SpinnerConfig;
|
|
117
|
+
StatusDot?: StatusDotConfig;
|
|
118
|
+
Switch?: SwitchConfig;
|
|
119
|
+
Table?: TableConfig;
|
|
120
|
+
Tabs?: TabsConfig;
|
|
121
|
+
Textarea?: TextareaConfig;
|
|
122
|
+
ThemeSwitcher?: ThemeSwitcherConfig;
|
|
123
|
+
Tooltip?: TooltipConfig;
|
|
124
|
+
TraitsPanel?: TraitsPanelConfig;
|
|
125
|
+
VideoPlayer?: VideoPlayerConfig;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const UiConfigContext = React.createContext<UiConfig>({});
|
|
129
|
+
|
|
130
|
+
export interface UiConfigProviderProps {
|
|
131
|
+
config: UiConfig;
|
|
132
|
+
children?: React.ReactNode;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function UiConfigProvider({ config, children }: UiConfigProviderProps) {
|
|
136
|
+
return <UiConfigContext.Provider value={config}>{children}</UiConfigContext.Provider>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The app's entry for one component, or `undefined` when the app sets none. */
|
|
140
|
+
function useUiConfig<K extends keyof UiConfig>(name: K): UiConfig[K] | undefined {
|
|
141
|
+
return React.useContext(UiConfigContext)[name];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** The app's class merger (`UiConfig.cn`), or the built-in `cn`. */
|
|
145
|
+
function useCn(): (...inputs: ClassValue[]) => string {
|
|
146
|
+
return React.useContext(UiConfigContext).cn ?? cn;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { UiConfigProvider, useCn, useUiConfig };
|