@vvedantb/ui 0.1.0 → 0.1.2
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/LICENSE +21 -21
- package/package.json +3 -2
- package/src/ai-elements/artifact.tsx +99 -99
- package/src/ai-elements/attachments.tsx +366 -366
- package/src/ai-elements/chain-of-thought.tsx +212 -212
- package/src/ai-elements/code-block.tsx +478 -478
- package/src/ai-elements/conversation.tsx +157 -157
- package/src/ai-elements/image.tsx +25 -25
- package/src/ai-elements/inline-citation.tsx +252 -252
- package/src/ai-elements/message.tsx +269 -269
- package/src/ai-elements/model-selector.tsx +211 -211
- package/src/ai-elements/prompt-input.tsx +1018 -1018
- package/src/ai-elements/queue.tsx +251 -251
- package/src/ai-elements/reasoning.tsx +178 -178
- package/src/ai-elements/shimmer.tsx +56 -56
- package/src/ai-elements/sources.tsx +77 -77
- package/src/ai-elements/speech-input.tsx +290 -290
- package/src/ai-elements/suggestion.tsx +191 -191
- package/src/css.d.ts +2 -2
- package/src/index.ts +96 -96
- package/src/motion/presets.ts +35 -35
- package/src/motion/presets.vibot.ts +105 -105
- package/src/ui/_menu-classes.ts +30 -30
- package/src/ui/_menu-classes.vibot.ts +33 -33
- package/src/ui/accordion.tsx +81 -81
- package/src/ui/accordion.verve.tsx +56 -56
- package/src/ui/badge.tsx +34 -34
- package/src/ui/breadcrumb.tsx +83 -83
- package/src/ui/button-group.tsx +84 -84
- package/src/ui/button.tsx +57 -57
- package/src/ui/card.tsx +27 -27
- package/src/ui/carousel.tsx +211 -211
- package/src/ui/checkbox.tsx +27 -27
- package/src/ui/clear-input.tsx +170 -170
- package/src/ui/clear-input.vibot.tsx +76 -76
- package/src/ui/clearInputDissolve.ts +214 -214
- package/src/ui/collapsible.tsx +7 -7
- package/src/ui/command.tsx +148 -148
- package/src/ui/context-menu.tsx +173 -173
- package/src/ui/dialog.tsx +149 -149
- package/src/ui/dropdown-menu.tsx +178 -178
- package/src/ui/hover-card.tsx +31 -31
- package/src/ui/input-group.tsx +162 -162
- package/src/ui/input-group.verve.tsx +150 -150
- package/src/ui/input.tsx +21 -21
- package/src/ui/kbd.tsx +18 -18
- package/src/ui/label.tsx +20 -20
- package/src/ui/labeled-switch-row.tsx +53 -53
- package/src/ui/modalTransition.ts +85 -85
- package/src/ui/pagination.tsx +47 -47
- package/src/ui/popover.tsx +33 -33
- package/src/ui/progress.tsx +25 -25
- package/src/ui/radix-select.tsx +202 -202
- package/src/ui/scroll-area.tsx +55 -55
- package/src/ui/scroll-area.verve.tsx +43 -43
- package/src/ui/search-input.tsx +29 -29
- package/src/ui/select.tsx +163 -163
- package/src/ui/separator.tsx +28 -28
- package/src/ui/sheet.tsx +87 -87
- package/src/ui/skeleton.tsx +18 -18
- package/src/ui/sonner.css +16 -16
- package/src/ui/sonner.tsx +47 -47
- package/src/ui/spinner.tsx +27 -27
- package/src/ui/surface-classes.ts +55 -55
- package/src/ui/switch.tsx +26 -26
- package/src/ui/table.tsx +123 -123
- package/src/ui/tabs.tsx +140 -140
- package/src/ui/tabsSliding.ts +56 -56
- package/src/ui/textarea.tsx +21 -21
- package/src/ui/time-picker.tsx +170 -170
- package/src/ui/tooltip.tsx +25 -25
- package/src/ui/visually-hidden.tsx +3 -3
- package/src/utils/cn.ts +16 -16
- package/src/utils/ref.ts +10 -10
package/src/ui/time-picker.tsx
CHANGED
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { IconClock } from "@tabler/icons-react";
|
|
3
|
-
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
|
|
4
|
-
import { cn } from "../utils/cn";
|
|
5
|
-
|
|
6
|
-
// custom 24, hour "hh:mm" time picker replaces `<input type="time">` because the native picker varies wildly
|
|
7
|
-
// across browsers and platforms (chrome/firefox/safari each render a
|
|
8
|
-
// different control, and the spinner buttons feel laggy on windows) ux:, the trigger is styled like an input, same height/border/radius, so it slots into forms without bespoke alignment work, clicking the trigger (or its clock icon) opens a popover with two scrollable columns: hours 00, 23 on the left, minutes 00, 59 on the right click an item to select, the popover stays open so users can set both halves in one trip, selected items use the same `bg, surface, tertiary` treatment used elsewhere for active states, no rings or borders, matching the design system the component is fully controlled, `value` is always "hh:mm" 24h and
|
|
9
|
-
// the consumer is responsible for any local↔utc conversion that keeps
|
|
10
|
-
// the picker free of timezone logic and matches how the rest of the
|
|
11
|
-
// codebase (e.g dream mode) shapes scheduling values
|
|
12
|
-
interface TimePickerProps {
|
|
13
|
-
value: string;
|
|
14
|
-
onChange: (value: string) => void;
|
|
15
|
-
className?: string;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
ariaLabel?: string;
|
|
18
|
-
// width of the trigger button defaults to a compact 110px
|
|
19
|
-
triggerClassName?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
23
|
-
const MINUTES = Array.from({ length: 60 }, (_, i) =>
|
|
24
|
-
String(i).padStart(2, "0"),
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
function parseValue(value: string): { hour: string; minute: string } {
|
|
28
|
-
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
29
|
-
if (match) {
|
|
30
|
-
const hour = match[1];
|
|
31
|
-
const minute = match[2];
|
|
32
|
-
if (hour !== undefined && minute !== undefined) {
|
|
33
|
-
return { hour, minute };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { hour: "00", minute: "00" };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// auto, scroll a column to its selected item when the popover opens using
|
|
40
|
-
// `scrollintoview({ block: "centre" })` is good enough, the popover only
|
|
41
|
-
// has 2 columns and a fixed height, so we don't need a custom scroll calc
|
|
42
|
-
function useScrollSelectedIntoView(
|
|
43
|
-
ref: React.RefObject<HTMLDivElement | null>,
|
|
44
|
-
selector: string,
|
|
45
|
-
open: boolean,
|
|
46
|
-
): void {
|
|
47
|
-
React.useEffect(() => {
|
|
48
|
-
if (!open) return;
|
|
49
|
-
const root = ref.current;
|
|
50
|
-
if (!root) return;
|
|
51
|
-
const target = root.querySelector(selector);
|
|
52
|
-
if (target instanceof HTMLElement) {
|
|
53
|
-
target.scrollIntoView({ block: "center", behavior: "auto" });
|
|
54
|
-
}
|
|
55
|
-
}, [open, ref, selector]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const TimePicker = React.forwardRef<HTMLButtonElement, TimePickerProps>(
|
|
59
|
-
(
|
|
60
|
-
{ value, onChange, className, disabled, ariaLabel, triggerClassName },
|
|
61
|
-
ref,
|
|
62
|
-
) => {
|
|
63
|
-
const [open, setOpen] = React.useState(false);
|
|
64
|
-
const { hour, minute } = parseValue(value);
|
|
65
|
-
const hoursRef = React.useRef<HTMLDivElement | null>(null);
|
|
66
|
-
const minutesRef = React.useRef<HTMLDivElement | null>(null);
|
|
67
|
-
|
|
68
|
-
useScrollSelectedIntoView(hoursRef, `[data-time-value="${hour}"]`, open);
|
|
69
|
-
useScrollSelectedIntoView(
|
|
70
|
-
minutesRef,
|
|
71
|
-
`[data-time-value="${minute}"]`,
|
|
72
|
-
open,
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const handleHourSelect = (h: string) => {
|
|
76
|
-
onChange(`${h}:${minute}`);
|
|
77
|
-
};
|
|
78
|
-
const handleMinuteSelect = (m: string) => {
|
|
79
|
-
onChange(`${hour}:${m}`);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<Popover open={open} onOpenChange={setOpen}>
|
|
84
|
-
<PopoverTrigger asChild>
|
|
85
|
-
<button
|
|
86
|
-
ref={ref}
|
|
87
|
-
type="button"
|
|
88
|
-
disabled={disabled}
|
|
89
|
-
aria-label={ariaLabel}
|
|
90
|
-
className={cn(
|
|
91
|
-
"inline-flex h-8 items-center justify-between gap-2 rounded-field border border-border bg-field-background px-2.5 text-xs font-medium tabular-nums transition-[border-color,box-shadow,background-color] duration-200 ease-smooth hover:bg-field-background/90 focus-visible:border-focus-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
92
|
-
triggerClassName ?? "w-[110px]",
|
|
93
|
-
className,
|
|
94
|
-
)}
|
|
95
|
-
>
|
|
96
|
-
<span>{`${hour}:${minute}`}</span>
|
|
97
|
-
<IconClock className="h-3.5 w-3.5 text-muted" />
|
|
98
|
-
</button>
|
|
99
|
-
</PopoverTrigger>
|
|
100
|
-
<PopoverContent
|
|
101
|
-
align="end"
|
|
102
|
-
sideOffset={4}
|
|
103
|
-
className="flex w-auto gap-1 p-2"
|
|
104
|
-
>
|
|
105
|
-
<TimeColumn
|
|
106
|
-
ref={hoursRef}
|
|
107
|
-
label="Hours"
|
|
108
|
-
values={HOURS}
|
|
109
|
-
selected={hour}
|
|
110
|
-
onSelect={handleHourSelect}
|
|
111
|
-
/>
|
|
112
|
-
<TimeColumn
|
|
113
|
-
ref={minutesRef}
|
|
114
|
-
label="Minutes"
|
|
115
|
-
values={MINUTES}
|
|
116
|
-
selected={minute}
|
|
117
|
-
onSelect={handleMinuteSelect}
|
|
118
|
-
/>
|
|
119
|
-
</PopoverContent>
|
|
120
|
-
</Popover>
|
|
121
|
-
);
|
|
122
|
-
},
|
|
123
|
-
);
|
|
124
|
-
TimePicker.displayName = "TimePicker";
|
|
125
|
-
|
|
126
|
-
interface TimeColumnProps {
|
|
127
|
-
label: string;
|
|
128
|
-
values: string[];
|
|
129
|
-
selected: string;
|
|
130
|
-
onSelect: (value: string) => void;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const TimeColumn = React.forwardRef<HTMLDivElement, TimeColumnProps>(
|
|
134
|
-
({ label, values, selected, onSelect }, ref) => {
|
|
135
|
-
return (
|
|
136
|
-
<div className="flex w-14 flex-col">
|
|
137
|
-
<div className="px-2 pb-1 text-[10px] uppercase tracking-wider text-muted">
|
|
138
|
-
{label}
|
|
139
|
-
</div>
|
|
140
|
-
<div
|
|
141
|
-
ref={ref}
|
|
142
|
-
className="h-48 overflow-y-auto rounded-lg bg-segment py-1"
|
|
143
|
-
>
|
|
144
|
-
{values.map((v) => {
|
|
145
|
-
const isSelected = v === selected;
|
|
146
|
-
return (
|
|
147
|
-
<button
|
|
148
|
-
key={v}
|
|
149
|
-
type="button"
|
|
150
|
-
data-time-value={v}
|
|
151
|
-
onClick={() => onSelect(v)}
|
|
152
|
-
className={cn(
|
|
153
|
-
"flex h-7 w-full items-center justify-center text-xs tabular-nums transition-colors",
|
|
154
|
-
isSelected
|
|
155
|
-
? "bg-accent text-accent-foreground"
|
|
156
|
-
: "text-foreground/80 hover:bg-surface-tertiary/50",
|
|
157
|
-
)}
|
|
158
|
-
>
|
|
159
|
-
{v}
|
|
160
|
-
</button>
|
|
161
|
-
);
|
|
162
|
-
})}
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
);
|
|
166
|
-
},
|
|
167
|
-
);
|
|
168
|
-
TimeColumn.displayName = "TimeColumn";
|
|
169
|
-
|
|
170
|
-
export { TimePicker };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { IconClock } from "@tabler/icons-react";
|
|
3
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
|
|
4
|
+
import { cn } from "../utils/cn";
|
|
5
|
+
|
|
6
|
+
// custom 24, hour "hh:mm" time picker replaces `<input type="time">` because the native picker varies wildly
|
|
7
|
+
// across browsers and platforms (chrome/firefox/safari each render a
|
|
8
|
+
// different control, and the spinner buttons feel laggy on windows) ux:, the trigger is styled like an input, same height/border/radius, so it slots into forms without bespoke alignment work, clicking the trigger (or its clock icon) opens a popover with two scrollable columns: hours 00, 23 on the left, minutes 00, 59 on the right click an item to select, the popover stays open so users can set both halves in one trip, selected items use the same `bg, surface, tertiary` treatment used elsewhere for active states, no rings or borders, matching the design system the component is fully controlled, `value` is always "hh:mm" 24h and
|
|
9
|
+
// the consumer is responsible for any local↔utc conversion that keeps
|
|
10
|
+
// the picker free of timezone logic and matches how the rest of the
|
|
11
|
+
// codebase (e.g dream mode) shapes scheduling values
|
|
12
|
+
interface TimePickerProps {
|
|
13
|
+
value: string;
|
|
14
|
+
onChange: (value: string) => void;
|
|
15
|
+
className?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
ariaLabel?: string;
|
|
18
|
+
// width of the trigger button defaults to a compact 110px
|
|
19
|
+
triggerClassName?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
|
|
23
|
+
const MINUTES = Array.from({ length: 60 }, (_, i) =>
|
|
24
|
+
String(i).padStart(2, "0"),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
function parseValue(value: string): { hour: string; minute: string } {
|
|
28
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
29
|
+
if (match) {
|
|
30
|
+
const hour = match[1];
|
|
31
|
+
const minute = match[2];
|
|
32
|
+
if (hour !== undefined && minute !== undefined) {
|
|
33
|
+
return { hour, minute };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { hour: "00", minute: "00" };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// auto, scroll a column to its selected item when the popover opens using
|
|
40
|
+
// `scrollintoview({ block: "centre" })` is good enough, the popover only
|
|
41
|
+
// has 2 columns and a fixed height, so we don't need a custom scroll calc
|
|
42
|
+
function useScrollSelectedIntoView(
|
|
43
|
+
ref: React.RefObject<HTMLDivElement | null>,
|
|
44
|
+
selector: string,
|
|
45
|
+
open: boolean,
|
|
46
|
+
): void {
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (!open) return;
|
|
49
|
+
const root = ref.current;
|
|
50
|
+
if (!root) return;
|
|
51
|
+
const target = root.querySelector(selector);
|
|
52
|
+
if (target instanceof HTMLElement) {
|
|
53
|
+
target.scrollIntoView({ block: "center", behavior: "auto" });
|
|
54
|
+
}
|
|
55
|
+
}, [open, ref, selector]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const TimePicker = React.forwardRef<HTMLButtonElement, TimePickerProps>(
|
|
59
|
+
(
|
|
60
|
+
{ value, onChange, className, disabled, ariaLabel, triggerClassName },
|
|
61
|
+
ref,
|
|
62
|
+
) => {
|
|
63
|
+
const [open, setOpen] = React.useState(false);
|
|
64
|
+
const { hour, minute } = parseValue(value);
|
|
65
|
+
const hoursRef = React.useRef<HTMLDivElement | null>(null);
|
|
66
|
+
const minutesRef = React.useRef<HTMLDivElement | null>(null);
|
|
67
|
+
|
|
68
|
+
useScrollSelectedIntoView(hoursRef, `[data-time-value="${hour}"]`, open);
|
|
69
|
+
useScrollSelectedIntoView(
|
|
70
|
+
minutesRef,
|
|
71
|
+
`[data-time-value="${minute}"]`,
|
|
72
|
+
open,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const handleHourSelect = (h: string) => {
|
|
76
|
+
onChange(`${h}:${minute}`);
|
|
77
|
+
};
|
|
78
|
+
const handleMinuteSelect = (m: string) => {
|
|
79
|
+
onChange(`${hour}:${m}`);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
84
|
+
<PopoverTrigger asChild>
|
|
85
|
+
<button
|
|
86
|
+
ref={ref}
|
|
87
|
+
type="button"
|
|
88
|
+
disabled={disabled}
|
|
89
|
+
aria-label={ariaLabel}
|
|
90
|
+
className={cn(
|
|
91
|
+
"inline-flex h-8 items-center justify-between gap-2 rounded-field border border-border bg-field-background px-2.5 text-xs font-medium tabular-nums transition-[border-color,box-shadow,background-color] duration-200 ease-smooth hover:bg-field-background/90 focus-visible:border-focus-border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
92
|
+
triggerClassName ?? "w-[110px]",
|
|
93
|
+
className,
|
|
94
|
+
)}
|
|
95
|
+
>
|
|
96
|
+
<span>{`${hour}:${minute}`}</span>
|
|
97
|
+
<IconClock className="h-3.5 w-3.5 text-muted" />
|
|
98
|
+
</button>
|
|
99
|
+
</PopoverTrigger>
|
|
100
|
+
<PopoverContent
|
|
101
|
+
align="end"
|
|
102
|
+
sideOffset={4}
|
|
103
|
+
className="flex w-auto gap-1 p-2"
|
|
104
|
+
>
|
|
105
|
+
<TimeColumn
|
|
106
|
+
ref={hoursRef}
|
|
107
|
+
label="Hours"
|
|
108
|
+
values={HOURS}
|
|
109
|
+
selected={hour}
|
|
110
|
+
onSelect={handleHourSelect}
|
|
111
|
+
/>
|
|
112
|
+
<TimeColumn
|
|
113
|
+
ref={minutesRef}
|
|
114
|
+
label="Minutes"
|
|
115
|
+
values={MINUTES}
|
|
116
|
+
selected={minute}
|
|
117
|
+
onSelect={handleMinuteSelect}
|
|
118
|
+
/>
|
|
119
|
+
</PopoverContent>
|
|
120
|
+
</Popover>
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
TimePicker.displayName = "TimePicker";
|
|
125
|
+
|
|
126
|
+
interface TimeColumnProps {
|
|
127
|
+
label: string;
|
|
128
|
+
values: string[];
|
|
129
|
+
selected: string;
|
|
130
|
+
onSelect: (value: string) => void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const TimeColumn = React.forwardRef<HTMLDivElement, TimeColumnProps>(
|
|
134
|
+
({ label, values, selected, onSelect }, ref) => {
|
|
135
|
+
return (
|
|
136
|
+
<div className="flex w-14 flex-col">
|
|
137
|
+
<div className="px-2 pb-1 text-[10px] uppercase tracking-wider text-muted">
|
|
138
|
+
{label}
|
|
139
|
+
</div>
|
|
140
|
+
<div
|
|
141
|
+
ref={ref}
|
|
142
|
+
className="h-48 overflow-y-auto rounded-lg bg-segment py-1"
|
|
143
|
+
>
|
|
144
|
+
{values.map((v) => {
|
|
145
|
+
const isSelected = v === selected;
|
|
146
|
+
return (
|
|
147
|
+
<button
|
|
148
|
+
key={v}
|
|
149
|
+
type="button"
|
|
150
|
+
data-time-value={v}
|
|
151
|
+
onClick={() => onSelect(v)}
|
|
152
|
+
className={cn(
|
|
153
|
+
"flex h-7 w-full items-center justify-center text-xs tabular-nums transition-colors",
|
|
154
|
+
isSelected
|
|
155
|
+
? "bg-accent text-accent-foreground"
|
|
156
|
+
: "text-foreground/80 hover:bg-surface-tertiary/50",
|
|
157
|
+
)}
|
|
158
|
+
>
|
|
159
|
+
{v}
|
|
160
|
+
</button>
|
|
161
|
+
);
|
|
162
|
+
})}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
TimeColumn.displayName = "TimeColumn";
|
|
169
|
+
|
|
170
|
+
export { TimePicker };
|
package/src/ui/tooltip.tsx
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
-
import { cn } from "../utils/cn";
|
|
4
|
-
|
|
5
|
-
const TooltipProvider = TooltipPrimitive.Provider;
|
|
6
|
-
const Tooltip = TooltipPrimitive.Root;
|
|
7
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
8
|
-
|
|
9
|
-
const TooltipContent = React.forwardRef<
|
|
10
|
-
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
12
|
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
13
|
-
<TooltipPrimitive.Content
|
|
14
|
-
ref={ref}
|
|
15
|
-
sideOffset={sideOffset}
|
|
16
|
-
className={cn(
|
|
17
|
-
"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",
|
|
18
|
-
className,
|
|
19
|
-
)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
));
|
|
23
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
24
|
-
|
|
25
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
|
|
5
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
6
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
7
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
8
|
+
|
|
9
|
+
const TooltipContent = React.forwardRef<
|
|
10
|
+
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
|
11
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
12
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
13
|
+
<TooltipPrimitive.Content
|
|
14
|
+
ref={ref}
|
|
15
|
+
sideOffset={sideOffset}
|
|
16
|
+
className={cn(
|
|
17
|
+
"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",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
));
|
|
23
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
24
|
+
|
|
25
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
export { Root as VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { Root as VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
package/src/utils/cn.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { type ClassValue, clsx } from "clsx";
|
|
2
|
-
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
-
|
|
4
|
-
// `rounded-field` is a custom radius token — teach twMerge it conflicts with
|
|
5
|
-
// `rounded-none` / `rounded-md` / etc. so Input overrides actually win.
|
|
6
|
-
const twMerge = extendTailwindMerge({
|
|
7
|
-
extend: {
|
|
8
|
-
theme: {
|
|
9
|
-
borderRadius: ["field"],
|
|
10
|
-
},
|
|
11
|
-
},
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export function cn(...inputs: ClassValue[]) {
|
|
15
|
-
return twMerge(clsx(inputs));
|
|
16
|
-
}
|
|
1
|
+
import { type ClassValue, clsx } from "clsx";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
// `rounded-field` is a custom radius token — teach twMerge it conflicts with
|
|
5
|
+
// `rounded-none` / `rounded-md` / etc. so Input overrides actually win.
|
|
6
|
+
const twMerge = extendTailwindMerge({
|
|
7
|
+
extend: {
|
|
8
|
+
theme: {
|
|
9
|
+
borderRadius: ["field"],
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export function cn(...inputs: ClassValue[]) {
|
|
15
|
+
return twMerge(clsx(inputs));
|
|
16
|
+
}
|
package/src/utils/ref.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { Ref } from "react";
|
|
2
|
-
|
|
3
|
-
// forward a node to a callback or object ref (the two shapes forwardRef hands us)
|
|
4
|
-
export function assignRef<T>(ref: Ref<T> | undefined, node: T): void {
|
|
5
|
-
if (typeof ref === "function") {
|
|
6
|
-
ref(node);
|
|
7
|
-
} else if (ref) {
|
|
8
|
-
ref.current = node;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
import type { Ref } from "react";
|
|
2
|
+
|
|
3
|
+
// forward a node to a callback or object ref (the two shapes forwardRef hands us)
|
|
4
|
+
export function assignRef<T>(ref: Ref<T> | undefined, node: T): void {
|
|
5
|
+
if (typeof ref === "function") {
|
|
6
|
+
ref(node);
|
|
7
|
+
} else if (ref) {
|
|
8
|
+
ref.current = node;
|
|
9
|
+
}
|
|
10
|
+
}
|