@elizaos/ui 2.0.0-alpha.73 → 2.0.0-alpha.76
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/components/ui/badge.d.ts +10 -0
- package/components/ui/badge.d.ts.map +1 -0
- package/components/ui/badge.js +20 -0
- package/components/ui/banner.d.ts +18 -0
- package/components/ui/banner.d.ts.map +1 -0
- package/components/ui/banner.js +27 -0
- package/components/ui/button.d.ts +12 -0
- package/components/ui/button.d.ts.map +1 -0
- package/components/ui/button.js +37 -0
- package/components/ui/card.d.ts +9 -0
- package/components/ui/card.d.ts.map +1 -0
- package/components/ui/card.js +16 -0
- package/components/ui/chat-atoms.d.ts +26 -0
- package/components/ui/chat-atoms.d.ts.map +1 -0
- package/components/ui/chat-atoms.js +17 -0
- package/components/ui/checkbox.d.ts +5 -0
- package/components/ui/checkbox.d.ts.map +1 -0
- package/components/ui/checkbox.js +8 -0
- package/components/ui/confirm-delete.d.ts +12 -0
- package/components/ui/confirm-delete.d.ts.map +1 -0
- package/components/ui/confirm-delete.js +13 -0
- package/components/ui/confirm-dialog.d.ts +24 -0
- package/components/ui/confirm-dialog.d.ts.map +1 -0
- package/components/ui/confirm-dialog.js +55 -0
- package/components/ui/connection-status.d.ts +9 -0
- package/components/ui/connection-status.d.ts.map +1 -0
- package/components/ui/connection-status.js +25 -0
- package/components/ui/copy-button.d.ts +13 -0
- package/components/ui/copy-button.d.ts.map +1 -0
- package/components/ui/copy-button.js +14 -0
- package/components/ui/dialog.d.ts +20 -0
- package/components/ui/dialog.d.ts.map +1 -0
- package/components/ui/dialog.js +22 -0
- package/components/ui/dropdown-menu.d.ts +28 -0
- package/components/ui/dropdown-menu.d.ts.map +1 -0
- package/components/ui/dropdown-menu.js +35 -0
- package/components/ui/empty-state.d.ts +13 -0
- package/components/ui/empty-state.d.ts.map +1 -0
- package/components/ui/empty-state.js +5 -0
- package/components/ui/error-boundary.d.ts +18 -0
- package/components/ui/error-boundary.d.ts.map +1 -0
- package/components/ui/error-boundary.js +27 -0
- package/components/ui/grid.d.ts +11 -0
- package/components/ui/grid.d.ts.map +1 -0
- package/components/ui/grid.js +30 -0
- package/components/ui/input.d.ts +6 -0
- package/components/ui/input.d.ts.map +1 -0
- package/components/ui/input.js +8 -0
- package/components/ui/label.d.ts +6 -0
- package/components/ui/label.d.ts.map +1 -0
- package/components/ui/label.js +9 -0
- package/components/ui/popover.d.ts +7 -0
- package/components/ui/popover.d.ts.map +1 -0
- package/components/ui/popover.js +9 -0
- package/components/ui/save-footer.d.ts +13 -0
- package/components/ui/save-footer.d.ts.map +1 -0
- package/components/ui/save-footer.js +9 -0
- package/components/ui/search-bar.d.ts +17 -0
- package/components/ui/search-bar.d.ts.map +1 -0
- package/components/ui/search-bar.js +19 -0
- package/components/ui/search-input.d.ts +11 -0
- package/components/ui/search-input.d.ts.map +1 -0
- package/components/ui/search-input.js +9 -0
- package/components/ui/section-card.d.ts +15 -0
- package/components/ui/section-card.d.ts.map +1 -0
- package/components/ui/section-card.js +9 -0
- package/components/ui/select.d.ts +14 -0
- package/components/ui/select.d.ts.map +1 -0
- package/components/ui/select.js +26 -0
- package/components/ui/separator.d.ts +5 -0
- package/components/ui/separator.d.ts.map +1 -0
- package/components/ui/separator.js +7 -0
- package/components/ui/skeleton.d.ts +17 -0
- package/components/ui/skeleton.d.ts.map +1 -0
- package/components/ui/skeleton.js +25 -0
- package/components/ui/slider.d.ts +5 -0
- package/components/ui/slider.d.ts.map +1 -0
- package/components/ui/slider.js +7 -0
- package/components/ui/sonner.d.ts +5 -0
- package/components/ui/sonner.d.ts.map +1 -0
- package/components/ui/sonner.js +15 -0
- package/components/ui/spinner.d.ts +6 -0
- package/components/ui/spinner.d.ts.map +1 -0
- package/components/ui/spinner.js +8 -0
- package/components/ui/stack.d.ts +13 -0
- package/components/ui/stack.d.ts.map +1 -0
- package/components/ui/stack.js +39 -0
- package/components/ui/status-badge.d.ts +20 -0
- package/components/ui/status-badge.d.ts.map +1 -0
- package/components/ui/status-badge.js +41 -0
- package/components/ui/switch.d.ts +5 -0
- package/components/ui/switch.d.ts.map +1 -0
- package/components/ui/switch.js +7 -0
- package/components/ui/tabs.d.ts +8 -0
- package/components/ui/tabs.d.ts.map +1 -0
- package/components/ui/tabs.js +12 -0
- package/components/ui/tag-editor.d.ts +24 -0
- package/components/ui/tag-editor.d.ts.map +1 -0
- package/components/ui/tag-editor.js +32 -0
- package/components/ui/tag-input.d.ts +19 -0
- package/components/ui/tag-input.d.ts.map +1 -0
- package/components/ui/tag-input.js +28 -0
- package/components/ui/textarea.d.ts +6 -0
- package/components/ui/textarea.d.ts.map +1 -0
- package/components/ui/textarea.js +8 -0
- package/components/ui/themed-select.d.ts +27 -0
- package/components/ui/themed-select.d.ts.map +1 -0
- package/components/ui/themed-select.js +57 -0
- package/components/ui/tooltip-extended.d.ts +76 -0
- package/components/ui/tooltip-extended.d.ts.map +1 -0
- package/components/ui/tooltip-extended.js +128 -0
- package/components/ui/tooltip.d.ts +8 -0
- package/components/ui/tooltip.d.ts.map +1 -0
- package/components/ui/tooltip.js +10 -0
- package/components/ui/typography.d.ts +17 -0
- package/components/ui/typography.d.ts.map +1 -0
- package/components/ui/typography.js +44 -0
- package/index.d.ts +42 -0
- package/index.d.ts.map +1 -0
- package/{src/index.ts → index.js} +0 -1
- package/lib/button-styles.d.ts +12 -0
- package/lib/button-styles.d.ts.map +1 -0
- package/lib/button-styles.js +11 -0
- package/lib/utils.d.ts +6 -0
- package/lib/utils.d.ts.map +1 -0
- package/{src/lib/utils.ts → lib/utils.js} +3 -4
- package/package.json +16 -18
- package/README.md +0 -6
- package/src/components/ui/badge.tsx +0 -35
- package/src/components/ui/banner.tsx +0 -81
- package/src/components/ui/button.tsx +0 -56
- package/src/components/ui/card.tsx +0 -79
- package/src/components/ui/chat-atoms.tsx +0 -129
- package/src/components/ui/checkbox.tsx +0 -28
- package/src/components/ui/confirm-delete.tsx +0 -67
- package/src/components/ui/confirm-dialog.tsx +0 -142
- package/src/components/ui/connection-status.tsx +0 -54
- package/src/components/ui/copy-button.tsx +0 -60
- package/src/components/ui/dialog.tsx +0 -120
- package/src/components/ui/dropdown-menu.tsx +0 -198
- package/src/components/ui/empty-state.tsx +0 -42
- package/src/components/ui/error-boundary.tsx +0 -63
- package/src/components/ui/grid.tsx +0 -43
- package/src/components/ui/input.tsx +0 -25
- package/src/components/ui/label.tsx +0 -24
- package/src/components/ui/popover.tsx +0 -29
- package/src/components/ui/save-footer.tsx +0 -58
- package/src/components/ui/search-bar.tsx +0 -61
- package/src/components/ui/search-input.tsx +0 -56
- package/src/components/ui/section-card.tsx +0 -85
- package/src/components/ui/select.tsx +0 -158
- package/src/components/ui/separator.tsx +0 -29
- package/src/components/ui/skeleton.tsx +0 -115
- package/src/components/ui/slider.tsx +0 -26
- package/src/components/ui/sonner.tsx +0 -28
- package/src/components/ui/spinner.tsx +0 -21
- package/src/components/ui/stack.tsx +0 -55
- package/src/components/ui/status-badge.tsx +0 -128
- package/src/components/ui/switch.tsx +0 -27
- package/src/components/ui/tabs.tsx +0 -53
- package/src/components/ui/tag-editor.tsx +0 -111
- package/src/components/ui/tag-input.tsx +0 -108
- package/src/components/ui/textarea.tsx +0 -24
- package/src/components/ui/themed-select.tsx +0 -141
- package/src/components/ui/tooltip-extended.tsx +0 -340
- package/src/components/ui/tooltip.tsx +0 -28
- package/src/components/ui/typography.tsx +0 -74
- package/src/lib/button-styles.ts +0 -19
- package/tsconfig.build.json +0 -18
- package/tsconfig.json +0 -32
- /package/{src/styles → styles}/theme.css +0 -0
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ThemedSelect — custom dropdown with grouped options.
|
|
3
|
-
*
|
|
4
|
-
* A generic, fully-controlled select component that supports grouped option
|
|
5
|
-
* lists and optional hint text per item. Has no dependency on any app-level
|
|
6
|
-
* context; all state is passed in as props.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { useEffect, useRef, useState } from "react";
|
|
10
|
-
import { Button } from "./button";
|
|
11
|
-
|
|
12
|
-
export interface ThemedSelectGroup<T extends string = string> {
|
|
13
|
-
label: string;
|
|
14
|
-
items: { id: T; text: string; hint?: string }[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ThemedSelectProps<T extends string = string> {
|
|
18
|
-
value: T | null;
|
|
19
|
-
groups: ThemedSelectGroup<T>[];
|
|
20
|
-
onChange: (id: T) => void;
|
|
21
|
-
placeholder?: string;
|
|
22
|
-
menuPlacement?: "top" | "bottom";
|
|
23
|
-
className?: string;
|
|
24
|
-
triggerClassName?: string;
|
|
25
|
-
menuClassName?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function ThemedSelect<T extends string>({
|
|
29
|
-
value,
|
|
30
|
-
groups,
|
|
31
|
-
onChange,
|
|
32
|
-
placeholder = "select...",
|
|
33
|
-
menuPlacement = "bottom",
|
|
34
|
-
className = "",
|
|
35
|
-
triggerClassName = "",
|
|
36
|
-
menuClassName = "",
|
|
37
|
-
}: ThemedSelectProps<T>) {
|
|
38
|
-
const [open, setOpen] = useState(false);
|
|
39
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
40
|
-
|
|
41
|
-
// Close on outside click
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (!open) return;
|
|
44
|
-
const handler = (e: MouseEvent) => {
|
|
45
|
-
if (ref.current && !ref.current.contains(e.target as Node))
|
|
46
|
-
setOpen(false);
|
|
47
|
-
};
|
|
48
|
-
document.addEventListener("mousedown", handler);
|
|
49
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
50
|
-
}, [open]);
|
|
51
|
-
|
|
52
|
-
// Close on Escape
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (!open) return;
|
|
55
|
-
const handler = (e: KeyboardEvent) => {
|
|
56
|
-
if (e.key === "Escape") setOpen(false);
|
|
57
|
-
};
|
|
58
|
-
document.addEventListener("keydown", handler);
|
|
59
|
-
return () => document.removeEventListener("keydown", handler);
|
|
60
|
-
}, [open]);
|
|
61
|
-
|
|
62
|
-
// Find current label
|
|
63
|
-
let currentLabel = placeholder;
|
|
64
|
-
for (const g of groups) {
|
|
65
|
-
const found = g.items.find((i) => i.id === value);
|
|
66
|
-
if (found) {
|
|
67
|
-
currentLabel = found.hint ? `${found.text} — ${found.hint}` : found.text;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const menuStyle =
|
|
73
|
-
menuPlacement === "top"
|
|
74
|
-
? { bottom: "calc(100% + 0.125rem)" }
|
|
75
|
-
: { top: "calc(100% + 0.125rem)" };
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<div
|
|
79
|
-
ref={ref}
|
|
80
|
-
className={`relative min-w-0 w-full ${open ? "z-[100]" : ""} ${className}`}
|
|
81
|
-
>
|
|
82
|
-
<Button
|
|
83
|
-
type="button"
|
|
84
|
-
variant="outline"
|
|
85
|
-
size="sm"
|
|
86
|
-
className={`flex h-12 w-full items-center justify-between border-border bg-card px-2.5 py-1.5 text-left text-xs shadow-sm hover:border-accent focus-visible:ring-1 focus-visible:ring-accent ${triggerClassName}`}
|
|
87
|
-
onClick={() => setOpen(!open)}
|
|
88
|
-
>
|
|
89
|
-
<span className="truncate">{currentLabel}</span>
|
|
90
|
-
<span
|
|
91
|
-
className={`ml-2 text-[10px] text-muted transition-transform ${open ? "rotate-180" : ""}`}
|
|
92
|
-
>
|
|
93
|
-
▼
|
|
94
|
-
</span>
|
|
95
|
-
</Button>
|
|
96
|
-
|
|
97
|
-
{open && (
|
|
98
|
-
<div
|
|
99
|
-
className={`absolute left-0 right-0 z-50 max-h-[280px] overflow-y-auto rounded-md border border-border bg-card shadow-lg ${menuClassName}`}
|
|
100
|
-
style={menuStyle}
|
|
101
|
-
>
|
|
102
|
-
{groups.map((g) => (
|
|
103
|
-
<div key={g.label}>
|
|
104
|
-
<div className="px-2.5 py-1 text-[10px] font-semibold text-muted bg-bg-muted sticky top-0">
|
|
105
|
-
{g.label}
|
|
106
|
-
</div>
|
|
107
|
-
{g.items.map((item) => {
|
|
108
|
-
const active = item.id === value;
|
|
109
|
-
return (
|
|
110
|
-
<Button
|
|
111
|
-
key={item.id}
|
|
112
|
-
variant="ghost"
|
|
113
|
-
size="sm"
|
|
114
|
-
className={`w-full justify-start text-left px-2.5 py-1.5 h-auto text-xs rounded-none ${
|
|
115
|
-
active
|
|
116
|
-
? "bg-accent text-accent-foreground"
|
|
117
|
-
: "text-txt hover:bg-bg-muted"
|
|
118
|
-
}`}
|
|
119
|
-
onClick={() => {
|
|
120
|
-
onChange(item.id);
|
|
121
|
-
setOpen(false);
|
|
122
|
-
}}
|
|
123
|
-
>
|
|
124
|
-
<span className="font-semibold">{item.text}</span>
|
|
125
|
-
{item.hint && (
|
|
126
|
-
<span
|
|
127
|
-
className={`ml-1.5 ${active ? "opacity-70" : "text-muted"}`}
|
|
128
|
-
>
|
|
129
|
-
{item.hint}
|
|
130
|
-
</span>
|
|
131
|
-
)}
|
|
132
|
-
</Button>
|
|
133
|
-
);
|
|
134
|
-
})}
|
|
135
|
-
</div>
|
|
136
|
-
))}
|
|
137
|
-
</div>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extended tooltip components for contextual help, icon buttons, guided
|
|
3
|
-
* tours, and spotlight onboarding overlays.
|
|
4
|
-
*
|
|
5
|
-
* All exports here are framework-agnostic (no app context dependency).
|
|
6
|
-
*
|
|
7
|
-
* Note: The Radix-based `Tooltip` is exported from `./tooltip`. This file
|
|
8
|
-
* exports a custom CSS-only hover tooltip (`HoverTooltip`), `IconTooltip`,
|
|
9
|
-
* `Spotlight`, and `useGuidedTour`.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { X } from "lucide-react";
|
|
13
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
14
|
-
|
|
15
|
-
/* ── HoverTooltip ────────────────────────────────────────────────────── */
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* CSS-only hover tooltip that wraps any element in a `<button>` and shows
|
|
19
|
-
* a floating content panel on hover/focus. Supports controlled `visible`
|
|
20
|
-
* mode and an optional dismiss button.
|
|
21
|
-
*
|
|
22
|
-
* Use `IconTooltip` for a div-based (non-button) variant that is safe to
|
|
23
|
-
* wrap interactive children.
|
|
24
|
-
*/
|
|
25
|
-
export interface HoverTooltipProps {
|
|
26
|
-
children: React.ReactNode;
|
|
27
|
-
content: React.ReactNode;
|
|
28
|
-
position?: "top" | "bottom" | "left" | "right";
|
|
29
|
-
delay?: number;
|
|
30
|
-
showArrow?: boolean;
|
|
31
|
-
className?: string;
|
|
32
|
-
visible?: boolean;
|
|
33
|
-
onDismiss?: () => void;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function HoverTooltip({
|
|
37
|
-
children,
|
|
38
|
-
content,
|
|
39
|
-
position = "top",
|
|
40
|
-
delay = 300,
|
|
41
|
-
showArrow = true,
|
|
42
|
-
className = "",
|
|
43
|
-
visible: controlledVisible,
|
|
44
|
-
onDismiss,
|
|
45
|
-
}: HoverTooltipProps) {
|
|
46
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
47
|
-
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
48
|
-
const containerRef = useRef<HTMLButtonElement>(null);
|
|
49
|
-
const isVisibleState =
|
|
50
|
-
controlledVisible !== undefined ? controlledVisible : isVisible;
|
|
51
|
-
|
|
52
|
-
const show = useCallback(() => {
|
|
53
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
54
|
-
timeoutRef.current = setTimeout(() => setIsVisible(true), delay);
|
|
55
|
-
}, [delay]);
|
|
56
|
-
|
|
57
|
-
const hide = useCallback(() => {
|
|
58
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
59
|
-
timeoutRef.current = setTimeout(() => setIsVisible(false), 150);
|
|
60
|
-
}, []);
|
|
61
|
-
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
return () => {
|
|
64
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
65
|
-
};
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
68
|
-
const positionClasses = {
|
|
69
|
-
top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
70
|
-
bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
|
|
71
|
-
left: "right-full top-1/2 -translate-y-1/2 mr-2",
|
|
72
|
-
right: "left-full top-1/2 -translate-y-1/2 ml-2",
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const arrowClasses = {
|
|
76
|
-
top: "top-full left-1/2 -translate-x-1/2 border-t-border border-l-transparent border-r-transparent border-b-transparent",
|
|
77
|
-
bottom:
|
|
78
|
-
"bottom-full left-1/2 -translate-x-1/2 border-b-border border-l-transparent border-r-transparent border-t-transparent",
|
|
79
|
-
left: "left-full top-1/2 -translate-y-1/2 border-l-border border-t-transparent border-b-transparent border-r-transparent",
|
|
80
|
-
right:
|
|
81
|
-
"right-full top-1/2 -translate-y-1/2 border-r-border border-t-transparent border-b-transparent border-l-transparent",
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<button
|
|
86
|
-
type="button"
|
|
87
|
-
ref={containerRef}
|
|
88
|
-
className="relative inline-flex bg-transparent border-0 p-0 cursor-default"
|
|
89
|
-
onMouseEnter={show}
|
|
90
|
-
onMouseLeave={hide}
|
|
91
|
-
onFocus={show}
|
|
92
|
-
onBlur={hide}
|
|
93
|
-
>
|
|
94
|
-
{children}
|
|
95
|
-
|
|
96
|
-
{isVisibleState && (
|
|
97
|
-
<div
|
|
98
|
-
className={`absolute z-50 ${positionClasses[position]} ${className}`}
|
|
99
|
-
>
|
|
100
|
-
<div className="relative bg-bg-elevated border border-border rounded-lg shadow-xl p-3 max-w-xs">
|
|
101
|
-
{onDismiss && (
|
|
102
|
-
<button
|
|
103
|
-
type="button"
|
|
104
|
-
onClick={onDismiss}
|
|
105
|
-
className="absolute top-1 right-1 p-1 text-muted hover:text-txt rounded"
|
|
106
|
-
aria-label="Dismiss tooltip"
|
|
107
|
-
>
|
|
108
|
-
<X className="w-3 h-3" />
|
|
109
|
-
</button>
|
|
110
|
-
)}
|
|
111
|
-
{content}
|
|
112
|
-
|
|
113
|
-
{showArrow && (
|
|
114
|
-
<div
|
|
115
|
-
className={`absolute w-0 h-0 border-4 ${arrowClasses[position]}`}
|
|
116
|
-
/>
|
|
117
|
-
)}
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
)}
|
|
121
|
-
</button>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/* ── IconTooltip ─────────────────────────────────────────────────────── */
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Lightweight tooltip for icon buttons.
|
|
129
|
-
*
|
|
130
|
-
* Uses a `<div>` wrapper (not `<button>`) so it can safely wrap interactive
|
|
131
|
-
* elements like icon buttons without nesting buttons.
|
|
132
|
-
*/
|
|
133
|
-
export function IconTooltip({
|
|
134
|
-
children,
|
|
135
|
-
label,
|
|
136
|
-
shortcut,
|
|
137
|
-
position = "top",
|
|
138
|
-
}: {
|
|
139
|
-
children: React.ReactNode;
|
|
140
|
-
label: string;
|
|
141
|
-
shortcut?: string;
|
|
142
|
-
position?: "top" | "bottom";
|
|
143
|
-
}) {
|
|
144
|
-
const posClass =
|
|
145
|
-
position === "top"
|
|
146
|
-
? "bottom-full left-1/2 -translate-x-1/2 mb-2"
|
|
147
|
-
: "top-full left-1/2 -translate-x-1/2 mt-2";
|
|
148
|
-
const arrowClass =
|
|
149
|
-
position === "top"
|
|
150
|
-
? "top-full left-1/2 -translate-x-1/2 -mt-1 border-4 border-transparent border-t-bg-elevated"
|
|
151
|
-
: "bottom-full left-1/2 -translate-x-1/2 -mb-1 border-4 border-transparent border-b-bg-elevated";
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<div className="relative group">
|
|
155
|
-
{children}
|
|
156
|
-
<div
|
|
157
|
-
className={`absolute ${posClass} px-2 py-1 bg-bg-elevated border border-border text-[11px] text-txt-strong rounded-md whitespace-nowrap opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50 shadow-lg pointer-events-none`}
|
|
158
|
-
role="tooltip"
|
|
159
|
-
>
|
|
160
|
-
<div className="font-medium">{label}</div>
|
|
161
|
-
{shortcut && <div className="text-muted mt-0.5">{shortcut}</div>}
|
|
162
|
-
<div className={`absolute ${arrowClass}`} />
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* ── Spotlight ───────────────────────────────────────────────────────── */
|
|
169
|
-
|
|
170
|
-
export interface SpotlightProps {
|
|
171
|
-
target: string;
|
|
172
|
-
title: string;
|
|
173
|
-
description: string;
|
|
174
|
-
step: number;
|
|
175
|
-
totalSteps: number;
|
|
176
|
-
onNext: () => void;
|
|
177
|
-
onPrev: () => void;
|
|
178
|
-
onSkip: () => void;
|
|
179
|
-
/** Labels to override defaults. */
|
|
180
|
-
labels?: {
|
|
181
|
-
stepOf?: string;
|
|
182
|
-
skipTour?: string;
|
|
183
|
-
previous?: string;
|
|
184
|
-
finish?: string;
|
|
185
|
-
next?: string;
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function Spotlight({
|
|
190
|
-
target,
|
|
191
|
-
title,
|
|
192
|
-
description,
|
|
193
|
-
step,
|
|
194
|
-
totalSteps,
|
|
195
|
-
onNext,
|
|
196
|
-
onPrev,
|
|
197
|
-
onSkip,
|
|
198
|
-
labels = {},
|
|
199
|
-
}: SpotlightProps) {
|
|
200
|
-
const [targetRect, setTargetRect] = useState<DOMRect | null>(null);
|
|
201
|
-
|
|
202
|
-
useEffect(() => {
|
|
203
|
-
const element = document.querySelector(target);
|
|
204
|
-
if (element) {
|
|
205
|
-
const rect = element.getBoundingClientRect();
|
|
206
|
-
setTargetRect(rect);
|
|
207
|
-
}
|
|
208
|
-
}, [target]);
|
|
209
|
-
|
|
210
|
-
if (!targetRect) return null;
|
|
211
|
-
|
|
212
|
-
const padding = 8;
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<div className="fixed inset-0 z-[300] pointer-events-none">
|
|
216
|
-
{/* Backdrop with cutout */}
|
|
217
|
-
<div
|
|
218
|
-
className="absolute inset-0 bg-black/60 pointer-events-auto"
|
|
219
|
-
style={{
|
|
220
|
-
clipPath: `polygon(
|
|
221
|
-
0% 0%,
|
|
222
|
-
0% 100%,
|
|
223
|
-
${targetRect.left - padding}px 100%,
|
|
224
|
-
${targetRect.left - padding}px ${targetRect.top - padding}px,
|
|
225
|
-
${targetRect.right + padding}px ${targetRect.top - padding}px,
|
|
226
|
-
${targetRect.right + padding}px ${targetRect.bottom + padding}px,
|
|
227
|
-
${targetRect.left - padding}px ${targetRect.bottom + padding}px,
|
|
228
|
-
${targetRect.left - padding}px 100%,
|
|
229
|
-
100% 100%,
|
|
230
|
-
100% 0%
|
|
231
|
-
)`,
|
|
232
|
-
}}
|
|
233
|
-
/>
|
|
234
|
-
|
|
235
|
-
{/* Tooltip card */}
|
|
236
|
-
<div
|
|
237
|
-
className="absolute bg-card border border-border rounded-xl shadow-2xl p-5 max-w-sm pointer-events-auto"
|
|
238
|
-
style={{
|
|
239
|
-
top: targetRect.bottom + padding + 16,
|
|
240
|
-
left: Math.min(targetRect.left, window.innerWidth - 340),
|
|
241
|
-
}}
|
|
242
|
-
>
|
|
243
|
-
<div className="flex items-center justify-between mb-3">
|
|
244
|
-
<span className="text-xs text-muted font-medium">
|
|
245
|
-
{labels.stepOf ?? "Step"} {step} of {totalSteps}
|
|
246
|
-
</span>
|
|
247
|
-
<button
|
|
248
|
-
type="button"
|
|
249
|
-
onClick={onSkip}
|
|
250
|
-
className="text-xs text-muted hover:text-txt"
|
|
251
|
-
>
|
|
252
|
-
{labels.skipTour ?? "Skip Tour"}
|
|
253
|
-
</button>
|
|
254
|
-
</div>
|
|
255
|
-
|
|
256
|
-
<h3 className="text-lg font-bold text-txt-strong mb-2">{title}</h3>
|
|
257
|
-
<p className="text-sm text-muted mb-4">{description}</p>
|
|
258
|
-
|
|
259
|
-
<div className="flex items-center justify-between">
|
|
260
|
-
<button
|
|
261
|
-
type="button"
|
|
262
|
-
onClick={onPrev}
|
|
263
|
-
disabled={step === 1}
|
|
264
|
-
className="px-4 py-2 text-sm border border-border rounded-lg disabled:opacity-40 disabled:cursor-not-allowed hover:bg-bg-hover transition-colors"
|
|
265
|
-
>
|
|
266
|
-
{labels.previous ?? "Previous"}
|
|
267
|
-
</button>
|
|
268
|
-
|
|
269
|
-
<div className="flex gap-1">
|
|
270
|
-
{Array.from({ length: totalSteps }, (_, idx) => idx).map(
|
|
271
|
-
(dotIndex) => (
|
|
272
|
-
<div
|
|
273
|
-
key={`step-dot-${dotIndex}`}
|
|
274
|
-
className={`w-2 h-2 rounded-full transition-colors ${
|
|
275
|
-
dotIndex + 1 === step ? "bg-accent" : "bg-border"
|
|
276
|
-
}`}
|
|
277
|
-
/>
|
|
278
|
-
),
|
|
279
|
-
)}
|
|
280
|
-
</div>
|
|
281
|
-
|
|
282
|
-
<button
|
|
283
|
-
type="button"
|
|
284
|
-
onClick={onNext}
|
|
285
|
-
className="px-4 py-2 text-sm bg-accent text-accent-fg rounded-lg hover:opacity-90 transition-opacity"
|
|
286
|
-
>
|
|
287
|
-
{step === totalSteps
|
|
288
|
-
? (labels.finish ?? "Finish")
|
|
289
|
-
: (labels.next ?? "Next")}
|
|
290
|
-
</button>
|
|
291
|
-
</div>
|
|
292
|
-
</div>
|
|
293
|
-
</div>
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/* ── useGuidedTour ───────────────────────────────────────────────────── */
|
|
298
|
-
|
|
299
|
-
export interface TourStep {
|
|
300
|
-
target: string;
|
|
301
|
-
title: string;
|
|
302
|
-
description: string;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export function useGuidedTour(steps: TourStep[]) {
|
|
306
|
-
const [isActive, setIsActive] = useState(false);
|
|
307
|
-
const [currentStep, setCurrentStep] = useState(0);
|
|
308
|
-
|
|
309
|
-
const start = useCallback(() => {
|
|
310
|
-
setIsActive(true);
|
|
311
|
-
setCurrentStep(0);
|
|
312
|
-
}, []);
|
|
313
|
-
|
|
314
|
-
const next = useCallback(() => {
|
|
315
|
-
if (currentStep < steps.length - 1) {
|
|
316
|
-
setCurrentStep((prev) => prev + 1);
|
|
317
|
-
} else {
|
|
318
|
-
setIsActive(false);
|
|
319
|
-
}
|
|
320
|
-
}, [currentStep, steps.length]);
|
|
321
|
-
|
|
322
|
-
const prev = useCallback(() => {
|
|
323
|
-
setCurrentStep((prev) => Math.max(0, prev - 1));
|
|
324
|
-
}, []);
|
|
325
|
-
|
|
326
|
-
const skip = useCallback(() => {
|
|
327
|
-
setIsActive(false);
|
|
328
|
-
}, []);
|
|
329
|
-
|
|
330
|
-
return {
|
|
331
|
-
isActive,
|
|
332
|
-
currentStep,
|
|
333
|
-
step: steps[currentStep],
|
|
334
|
-
start,
|
|
335
|
-
next,
|
|
336
|
-
prev,
|
|
337
|
-
skip,
|
|
338
|
-
totalSteps: steps.length,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
|
|
6
|
-
const TooltipProvider = TooltipPrimitive.Provider;
|
|
7
|
-
|
|
8
|
-
const Tooltip = TooltipPrimitive.Root;
|
|
9
|
-
|
|
10
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
11
|
-
|
|
12
|
-
const TooltipContent = React.forwardRef<
|
|
13
|
-
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
14
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
15
|
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
16
|
-
<TooltipPrimitive.Content
|
|
17
|
-
ref={ref}
|
|
18
|
-
sideOffset={sideOffset}
|
|
19
|
-
className={cn(
|
|
20
|
-
"z-50 overflow-hidden rounded-md border border-border bg-card px-3 py-1.5 text-sm text-txt shadow-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",
|
|
21
|
-
className,
|
|
22
|
-
)}
|
|
23
|
-
{...props}
|
|
24
|
-
/>
|
|
25
|
-
));
|
|
26
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
27
|
-
|
|
28
|
-
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
|
|
6
|
-
const textVariants = cva("text-txt", {
|
|
7
|
-
variants: {
|
|
8
|
-
variant: {
|
|
9
|
-
default: "text-base",
|
|
10
|
-
medium: "text-sm",
|
|
11
|
-
small: "text-xs",
|
|
12
|
-
muted: "text-sm text-muted",
|
|
13
|
-
lead: "text-xl text-muted",
|
|
14
|
-
large: "text-lg font-semibold",
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
defaultVariants: {
|
|
18
|
-
variant: "default",
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export interface TextProps
|
|
23
|
-
extends React.HTMLAttributes<HTMLParagraphElement>,
|
|
24
|
-
VariantProps<typeof textVariants> {
|
|
25
|
-
asChild?: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const Text = React.forwardRef<HTMLParagraphElement, TextProps>(
|
|
29
|
-
({ className, variant, asChild = false, ...props }, ref) => {
|
|
30
|
-
const Comp = asChild ? "span" : "p";
|
|
31
|
-
return (
|
|
32
|
-
<Comp
|
|
33
|
-
ref={ref}
|
|
34
|
-
className={cn(textVariants({ variant }), className)}
|
|
35
|
-
{...props}
|
|
36
|
-
/>
|
|
37
|
-
);
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
Text.displayName = "Text";
|
|
41
|
-
|
|
42
|
-
const headingVariants = cva("text-txt font-semibold tracking-tight", {
|
|
43
|
-
variants: {
|
|
44
|
-
level: {
|
|
45
|
-
h1: "text-4xl font-extrabold lg:text-5xl",
|
|
46
|
-
h2: "text-3xl",
|
|
47
|
-
h3: "text-2xl",
|
|
48
|
-
h4: "text-xl",
|
|
49
|
-
h5: "text-lg",
|
|
50
|
-
h6: "text-base",
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
defaultVariants: {
|
|
54
|
-
level: "h1",
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
export interface HeadingProps
|
|
59
|
-
extends React.HTMLAttributes<HTMLHeadingElement>,
|
|
60
|
-
VariantProps<typeof headingVariants> {}
|
|
61
|
-
|
|
62
|
-
export const Heading = React.forwardRef<HTMLHeadingElement, HeadingProps>(
|
|
63
|
-
({ className, level = "h1", ...props }, ref) => {
|
|
64
|
-
const Comp = level || "h1";
|
|
65
|
-
return (
|
|
66
|
-
<Comp
|
|
67
|
-
ref={ref}
|
|
68
|
-
className={cn(headingVariants({ level }), className)}
|
|
69
|
-
{...props}
|
|
70
|
-
/>
|
|
71
|
-
);
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
Heading.displayName = "Heading";
|
package/src/lib/button-styles.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared button class strings for consistent styling across views.
|
|
3
|
-
*
|
|
4
|
-
* These CSS class strings complement the `Button` component for cases where
|
|
5
|
-
* a plain HTML `button` element is used or Tailwind class composition is
|
|
6
|
-
* preferred over the variant prop.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const btnPrimary =
|
|
10
|
-
"px-4 py-2 text-sm font-medium bg-[var(--accent)] text-[var(--accent-foreground,#1a1f26)] border border-[var(--accent)] cursor-pointer hover:opacity-90 transition-opacity disabled:opacity-40 disabled:cursor-default rounded-lg";
|
|
11
|
-
|
|
12
|
-
export const btnGhost =
|
|
13
|
-
"px-3 py-1.5 text-xs bg-transparent text-[var(--muted)] border border-[var(--border)] cursor-pointer hover:text-[var(--txt)] hover:border-[var(--txt)] transition-colors disabled:opacity-40 disabled:cursor-default rounded-lg";
|
|
14
|
-
|
|
15
|
-
export const btnDanger =
|
|
16
|
-
"px-3 py-1.5 text-xs bg-transparent text-[var(--danger,#e74c3c)] border border-[var(--danger,#e74c3c)] cursor-pointer hover:bg-[var(--danger,#e74c3c)] hover:text-white transition-colors disabled:opacity-40 disabled:cursor-default rounded-lg";
|
|
17
|
-
|
|
18
|
-
export const inputCls =
|
|
19
|
-
"flex-1 px-3 py-2 text-sm bg-[var(--card)] border border-[var(--border)] text-[var(--txt)] focus:border-[var(--accent)] focus:outline-none transition-colors rounded-lg";
|
package/tsconfig.build.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"declaration": true,
|
|
5
|
-
"declarationMap": true,
|
|
6
|
-
"noEmit": false,
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"tsBuildInfoFile": "./dist/.tsbuildinfo"
|
|
10
|
-
},
|
|
11
|
-
"exclude": [
|
|
12
|
-
"src/**/*.stories.ts",
|
|
13
|
-
"src/**/*.stories.tsx",
|
|
14
|
-
"src/**/*.test.ts",
|
|
15
|
-
"src/**/*.test.tsx"
|
|
16
|
-
],
|
|
17
|
-
"include": ["src"]
|
|
18
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"useDefineForClassFields": true,
|
|
5
|
-
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
6
|
-
"allowJs": false,
|
|
7
|
-
"skipLibCheck": true,
|
|
8
|
-
"esModuleInterop": false,
|
|
9
|
-
"allowSyntheticDefaultImports": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"module": "ESNext",
|
|
13
|
-
"moduleResolution": "bundler",
|
|
14
|
-
"resolveJsonModule": true,
|
|
15
|
-
"isolatedModules": true,
|
|
16
|
-
"noEmit": true,
|
|
17
|
-
"jsx": "react-jsx",
|
|
18
|
-
"types": ["node"],
|
|
19
|
-
"baseUrl": ".",
|
|
20
|
-
"paths": {
|
|
21
|
-
"@ui/*": ["./src/*"]
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
"exclude": [
|
|
25
|
-
"src/**/*.stories.ts",
|
|
26
|
-
"src/**/*.stories.tsx",
|
|
27
|
-
"src/**/*.test.ts",
|
|
28
|
-
"src/**/*.test.tsx"
|
|
29
|
-
],
|
|
30
|
-
"include": ["src"],
|
|
31
|
-
"references": []
|
|
32
|
-
}
|
|
File without changes
|