@tangle-network/ui 11.16.11 → 11.19.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/CHANGELOG.md +29 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.js +14 -1
- package/dist/primitives/alert-dialog.d.ts +34 -0
- package/dist/primitives/alert-dialog.js +68 -0
- package/dist/primitives/badge.d.ts +7 -0
- package/dist/primitives/badge.js +3 -3
- package/dist/primitives/checkbox.d.ts +12 -0
- package/dist/primitives/checkbox.js +34 -0
- package/dist/primitives/chip.d.ts +38 -0
- package/dist/primitives/chip.js +41 -0
- package/dist/primitives/collapsible.d.ts +12 -0
- package/dist/primitives/collapsible.js +13 -0
- package/dist/primitives/context-menu.d.ts +36 -0
- package/dist/primitives/context-menu.js +86 -0
- package/dist/primitives/icon-tile.d.ts +32 -0
- package/dist/primitives/icon-tile.js +49 -0
- package/dist/primitives/popover.d.ts +15 -0
- package/dist/primitives/popover.js +25 -0
- package/dist/primitives/separator.d.ts +11 -0
- package/dist/primitives/separator.js +21 -0
- package/dist/primitives/sheet.d.ts +28 -0
- package/dist/primitives/sheet.js +70 -0
- package/dist/primitives/slider.d.ts +14 -0
- package/dist/primitives/slider.js +37 -0
- package/dist/primitives/tag.d.ts +36 -0
- package/dist/primitives/tag.js +42 -0
- package/dist/primitives/theme-toggle.js +8 -0
- package/dist/primitives/tone.d.ts +40 -0
- package/dist/primitives/tone.js +106 -0
- package/dist/primitives/tooltip.d.ts +16 -0
- package/dist/primitives/tooltip.js +32 -0
- package/dist/primitives.d.ts +14 -1
- package/dist/primitives.js +14 -1
- package/package.json +9 -2
- package/src/primitives/alert-dialog.tsx +139 -0
- package/src/primitives/badge.tsx +17 -4
- package/src/primitives/checkbox-separator-sheet-tooltip.stories.tsx +92 -0
- package/src/primitives/checkbox-separator-sheet-tooltip.test.tsx +107 -0
- package/src/primitives/checkbox.tsx +42 -0
- package/src/primitives/chip.test.tsx +178 -0
- package/src/primitives/chip.tsx +108 -0
- package/src/primitives/collapsible.tsx +16 -0
- package/src/primitives/context-menu.tsx +168 -0
- package/src/primitives/icon-tile.stories.tsx +74 -0
- package/src/primitives/icon-tile.tsx +96 -0
- package/src/primitives/index.ts +69 -0
- package/src/primitives/popover-collapsible-alert-dialog-context-menu-slider.stories.tsx +124 -0
- package/src/primitives/popover-collapsible-alert-dialog-context-menu-slider.test.tsx +165 -0
- package/src/primitives/popover.tsx +45 -0
- package/src/primitives/separator.tsx +30 -0
- package/src/primitives/sheet.tsx +134 -0
- package/src/primitives/slider.tsx +71 -0
- package/src/primitives/tag-chip.stories.tsx +226 -0
- package/src/primitives/tag.tsx +94 -0
- package/src/primitives/theme-toggle.test.tsx +2 -1
- package/src/primitives/theme-toggle.tsx +2 -0
- package/src/primitives/tone.ts +130 -0
- package/src/primitives/tooltip.tsx +54 -0
- package/src/stories/theme-showcase.stories.tsx +2 -0
package/dist/primitives.js
CHANGED
|
@@ -2,6 +2,11 @@ import { Button, buttonVariants } from "./primitives/button.js";
|
|
|
2
2
|
import { Heading } from "./primitives/heading.js";
|
|
3
3
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./primitives/card.js";
|
|
4
4
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./primitives/dialog.js";
|
|
5
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger } from "./primitives/alert-dialog.js";
|
|
6
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./primitives/collapsible.js";
|
|
7
|
+
import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./primitives/context-menu.js";
|
|
8
|
+
import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./primitives/popover.js";
|
|
9
|
+
import { Slider } from "./primitives/slider.js";
|
|
5
10
|
import { Label } from "./primitives/label.js";
|
|
6
11
|
import { Input, Textarea } from "./primitives/input.js";
|
|
7
12
|
import { Badge, badgeVariants } from "./primitives/badge.js";
|
|
@@ -13,6 +18,10 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/tabs.js";
|
|
|
13
18
|
import { SegmentedControl } from "./primitives/segmented-control.js";
|
|
14
19
|
import { Progress } from "./primitives/progress.js";
|
|
15
20
|
import { Switch } from "./primitives/switch.js";
|
|
21
|
+
import { Checkbox } from "./primitives/checkbox.js";
|
|
22
|
+
import { Separator } from "./primitives/separator.js";
|
|
23
|
+
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger } from "./primitives/sheet.js";
|
|
24
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/tooltip.js";
|
|
16
25
|
import { Skeleton, SkeletonCard, SkeletonTable } from "./primitives/skeleton.js";
|
|
17
26
|
import { TextShimmer } from "./primitives/text-shimmer.js";
|
|
18
27
|
import { ToastContainer, ToastProvider, useToast } from "./primitives/toast.js";
|
|
@@ -29,7 +38,11 @@ import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
|
|
|
29
38
|
import { PageShell } from "./primitives/page-shell.js";
|
|
30
39
|
import { PageHeader } from "./primitives/page-header.js";
|
|
31
40
|
import { StatusPill } from "./primitives/status-pill.js";
|
|
41
|
+
import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
|
|
42
|
+
import { Tag } from "./primitives/tag.js";
|
|
43
|
+
import { Chip } from "./primitives/chip.js";
|
|
44
|
+
import { IconTile, initialsOf } from "./primitives/icon-tile.js";
|
|
32
45
|
import { Metric, MetricStrip } from "./primitives/metric-strip.js";
|
|
33
46
|
import { FilterField, Toolbar } from "./primitives/toolbar.js";
|
|
34
47
|
import { Logo, TangleKnot } from "@tangle-network/brand";
|
|
35
|
-
export { ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CodeBlock, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Progress, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, StatCard, StatusPill, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ToastContainer, ToastProvider, Toolbar, UploadProgress, badgeVariants, buttonVariants, useTheme, useToast };
|
|
48
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.19.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -116,16 +116,23 @@
|
|
|
116
116
|
"dependencies": {
|
|
117
117
|
"@pierre/theme": "1.1.0",
|
|
118
118
|
"@pierre/trees": "1.0.0-beta.6",
|
|
119
|
+
"@radix-ui/react-alert-dialog": "^1.1.23",
|
|
119
120
|
"@radix-ui/react-avatar": "^1.2.6",
|
|
121
|
+
"@radix-ui/react-checkbox": "^1.3.11",
|
|
120
122
|
"@radix-ui/react-collapsible": "^1.1.20",
|
|
123
|
+
"@radix-ui/react-context-menu": "^2.3.7",
|
|
121
124
|
"@radix-ui/react-dialog": "^1.1.23",
|
|
122
125
|
"@radix-ui/react-dropdown-menu": "^2.1.24",
|
|
123
126
|
"@radix-ui/react-label": "^2.1.15",
|
|
127
|
+
"@radix-ui/react-popover": "^1.1.23",
|
|
124
128
|
"@radix-ui/react-progress": "^1.1.16",
|
|
125
129
|
"@radix-ui/react-select": "^2.3.7",
|
|
130
|
+
"@radix-ui/react-separator": "^1.1.15",
|
|
131
|
+
"@radix-ui/react-slider": "^1.4.7",
|
|
126
132
|
"@radix-ui/react-slot": "^1.3.3",
|
|
127
133
|
"@radix-ui/react-switch": "^1.3.7",
|
|
128
134
|
"@radix-ui/react-tabs": "^1.1.21",
|
|
135
|
+
"@radix-ui/react-tooltip": "^1.2.16",
|
|
129
136
|
"class-variance-authority": "^0.7.1",
|
|
130
137
|
"clsx": "^2.1.1",
|
|
131
138
|
"github-slugger": "^2.0.0",
|
|
@@ -142,7 +149,7 @@
|
|
|
142
149
|
"peerDependencies": {
|
|
143
150
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
144
151
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
145
|
-
"@tangle-network/brand": "^1.
|
|
152
|
+
"@tangle-network/brand": "^1.13.0",
|
|
146
153
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
147
154
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
148
155
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
import { buttonVariants } from "./button";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A dialog that interrupts for a decision. Unlike `Dialog` it has no close
|
|
10
|
+
* button and ignores outside clicks; Escape and `AlertDialogCancel` dismiss it.
|
|
11
|
+
* Radix focuses `AlertDialogCancel` on open, so a destructive action is never
|
|
12
|
+
* under the Enter key when the dialog appears.
|
|
13
|
+
*/
|
|
14
|
+
const AlertDialog = AlertDialogPrimitive.Root;
|
|
15
|
+
|
|
16
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
17
|
+
|
|
18
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
19
|
+
|
|
20
|
+
const AlertDialogOverlay = React.forwardRef<
|
|
21
|
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
22
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
23
|
+
>(({ className, ...props }, ref) => (
|
|
24
|
+
<AlertDialogPrimitive.Overlay
|
|
25
|
+
ref={ref}
|
|
26
|
+
className={cn(
|
|
27
|
+
"fixed inset-0 z-50 bg-black/40",
|
|
28
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
29
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
));
|
|
35
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
36
|
+
|
|
37
|
+
const AlertDialogContent = React.forwardRef<
|
|
38
|
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
39
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
|
40
|
+
>(({ className, ...props }, ref) => (
|
|
41
|
+
<AlertDialogPortal>
|
|
42
|
+
<AlertDialogOverlay />
|
|
43
|
+
<AlertDialogPrimitive.Content
|
|
44
|
+
ref={ref}
|
|
45
|
+
className={cn(
|
|
46
|
+
"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] sm:max-w-lg",
|
|
47
|
+
"gap-4 rounded-lg border border-border bg-card p-6 text-foreground shadow-lg duration-200 outline-hidden",
|
|
48
|
+
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
49
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
50
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
51
|
+
"motion-reduce:animate-none",
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
</AlertDialogPortal>
|
|
57
|
+
));
|
|
58
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
59
|
+
|
|
60
|
+
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
61
|
+
<div className={cn("flex flex-col gap-2 text-center sm:text-left", className)} {...props} />
|
|
62
|
+
);
|
|
63
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
64
|
+
|
|
65
|
+
const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
66
|
+
<div
|
|
67
|
+
className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
72
|
+
|
|
73
|
+
const AlertDialogTitle = React.forwardRef<
|
|
74
|
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
75
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
76
|
+
>(({ className, ...props }, ref) => (
|
|
77
|
+
<AlertDialogPrimitive.Title
|
|
78
|
+
ref={ref}
|
|
79
|
+
className={cn("font-semibold text-lg leading-snug [overflow-wrap:anywhere]", className)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
));
|
|
83
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
84
|
+
|
|
85
|
+
const AlertDialogDescription = React.forwardRef<
|
|
86
|
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
87
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
88
|
+
>(({ className, ...props }, ref) => (
|
|
89
|
+
<AlertDialogPrimitive.Description
|
|
90
|
+
ref={ref}
|
|
91
|
+
className={cn("text-muted-foreground text-sm [overflow-wrap:anywhere]", className)}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
));
|
|
95
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The confirming action. `tone="destructive"` paints it with the danger fill;
|
|
99
|
+
* the default is the primary button.
|
|
100
|
+
*/
|
|
101
|
+
const AlertDialogAction = React.forwardRef<
|
|
102
|
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
103
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action> & {
|
|
104
|
+
tone?: "default" | "destructive";
|
|
105
|
+
}
|
|
106
|
+
>(({ className, tone = "default", ...props }, ref) => (
|
|
107
|
+
<AlertDialogPrimitive.Action
|
|
108
|
+
ref={ref}
|
|
109
|
+
className={cn(buttonVariants({ variant: tone === "destructive" ? "destructive" : "default" }), className)}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
));
|
|
113
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
114
|
+
|
|
115
|
+
const AlertDialogCancel = React.forwardRef<
|
|
116
|
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
117
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
118
|
+
>(({ className, ...props }, ref) => (
|
|
119
|
+
<AlertDialogPrimitive.Cancel
|
|
120
|
+
ref={ref}
|
|
121
|
+
className={cn(buttonVariants({ variant: "outline" }), className)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
));
|
|
125
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
AlertDialog,
|
|
129
|
+
AlertDialogAction,
|
|
130
|
+
AlertDialogCancel,
|
|
131
|
+
AlertDialogContent,
|
|
132
|
+
AlertDialogDescription,
|
|
133
|
+
AlertDialogFooter,
|
|
134
|
+
AlertDialogHeader,
|
|
135
|
+
AlertDialogOverlay,
|
|
136
|
+
AlertDialogPortal,
|
|
137
|
+
AlertDialogTitle,
|
|
138
|
+
AlertDialogTrigger,
|
|
139
|
+
};
|
package/src/primitives/badge.tsx
CHANGED
|
@@ -22,15 +22,21 @@ const badgeVariants = cva(
|
|
|
22
22
|
"border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
|
|
23
23
|
sandbox:
|
|
24
24
|
"border-border bg-[var(--accent-surface-soft)] text-[var(--accent-text)]",
|
|
25
|
-
/*
|
|
25
|
+
/* Sandbox lifecycle variants: compatibility only, painted with the
|
|
26
|
+
* status triples. A lifecycle state is domain meaning, so its mapping
|
|
27
|
+
* lives in sandbox-ui's lifecycle adapter over `StatusPill`; these
|
|
28
|
+
* stay until consumers move. Running is up and healthy (success);
|
|
29
|
+
* creating is still in progress (info, as StatusPill's `running`);
|
|
30
|
+
* stopped and warm can be resumed (warning); cold is archived (info);
|
|
31
|
+
* deleted is gone (neutral). */
|
|
26
32
|
running:
|
|
27
|
-
"border-[var(--surface-
|
|
33
|
+
"border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
|
|
28
34
|
creating:
|
|
29
|
-
"border-[var(--surface-
|
|
35
|
+
"border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
|
|
30
36
|
stopped:
|
|
31
37
|
"border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
|
|
32
38
|
warm:
|
|
33
|
-
"border-[var(--surface-
|
|
39
|
+
"border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
|
|
34
40
|
cold:
|
|
35
41
|
"border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
|
|
36
42
|
deleted:
|
|
@@ -43,6 +49,13 @@ const badgeVariants = cva(
|
|
|
43
49
|
},
|
|
44
50
|
);
|
|
45
51
|
|
|
52
|
+
/**
|
|
53
|
+
* `success`, `warning`, `error` and `info` paint the same triples as
|
|
54
|
+
* `StatusPill`; for a status, prefer `StatusPill`, which adds the glyph that
|
|
55
|
+
* survives greyscale. The lifecycle variants (`running`, `creating`,
|
|
56
|
+
* `stopped`, `warm`, `cold`, `deleted`) are deprecated: use sandbox-ui's
|
|
57
|
+
* lifecycle adapter.
|
|
58
|
+
*/
|
|
46
59
|
export type BadgeVariant =
|
|
47
60
|
| "default"
|
|
48
61
|
| "secondary"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Info } from 'lucide-react'
|
|
3
|
+
import { Button } from './button'
|
|
4
|
+
import { Checkbox } from './checkbox'
|
|
5
|
+
import { Label } from './label'
|
|
6
|
+
import { Separator } from './separator'
|
|
7
|
+
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from './sheet'
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip'
|
|
9
|
+
|
|
10
|
+
const meta: Meta = {
|
|
11
|
+
title: 'Primitives/Checkbox, Separator, Sheet, Tooltip',
|
|
12
|
+
parameters: { layout: 'centered' },
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default meta
|
|
16
|
+
type Story = StoryObj
|
|
17
|
+
|
|
18
|
+
export const Checkboxes: Story = {
|
|
19
|
+
render: () => (
|
|
20
|
+
<div className="flex flex-col gap-3">
|
|
21
|
+
{[
|
|
22
|
+
{ id: 'a', label: 'Record a video of every run', checked: true },
|
|
23
|
+
{ id: 'b', label: 'Retry failed steps once', checked: false },
|
|
24
|
+
{ id: 'c', label: 'Select every suite in this workspace', checked: 'indeterminate' as const },
|
|
25
|
+
{ id: 'd', label: 'Disabled until billing is set up', checked: false, disabled: true },
|
|
26
|
+
].map((row) => (
|
|
27
|
+
<div key={row.id} className="flex items-center gap-2">
|
|
28
|
+
<Checkbox id={row.id} defaultChecked={row.checked} disabled={row.disabled} />
|
|
29
|
+
<Label htmlFor={row.id}>{row.label}</Label>
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
33
|
+
),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Separators: Story = {
|
|
37
|
+
render: () => (
|
|
38
|
+
<div className="w-72 text-sm">
|
|
39
|
+
<p>Account</p>
|
|
40
|
+
<Separator className="my-3" />
|
|
41
|
+
<div className="flex h-5 items-center gap-3">
|
|
42
|
+
<span>Runs</span>
|
|
43
|
+
<Separator orientation="vertical" />
|
|
44
|
+
<span>Suites</span>
|
|
45
|
+
<Separator orientation="vertical" />
|
|
46
|
+
<span>Settings</span>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const Sheets: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<div className="flex gap-2">
|
|
55
|
+
{(['left', 'right', 'bottom'] as const).map((side) => (
|
|
56
|
+
<Sheet key={side}>
|
|
57
|
+
<SheetTrigger asChild>
|
|
58
|
+
<Button variant="outline">Open {side}</Button>
|
|
59
|
+
</SheetTrigger>
|
|
60
|
+
<SheetContent side={side}>
|
|
61
|
+
<SheetHeader>
|
|
62
|
+
<SheetTitle>Run assistant</SheetTitle>
|
|
63
|
+
<SheetDescription>Ask why a step failed or what to test next.</SheetDescription>
|
|
64
|
+
</SheetHeader>
|
|
65
|
+
<SheetFooter>
|
|
66
|
+
<Button>Send</Button>
|
|
67
|
+
</SheetFooter>
|
|
68
|
+
</SheetContent>
|
|
69
|
+
</Sheet>
|
|
70
|
+
))}
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const Tooltips: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<TooltipProvider>
|
|
78
|
+
<div className="flex gap-2">
|
|
79
|
+
{['Runs', 'Test suites', 'A much longer label that wraps at the maximum tooltip width'].map((label) => (
|
|
80
|
+
<Tooltip key={label}>
|
|
81
|
+
<TooltipTrigger asChild>
|
|
82
|
+
<Button size="icon" variant="ghost" aria-label={label}>
|
|
83
|
+
<Info className="size-4" />
|
|
84
|
+
</Button>
|
|
85
|
+
</TooltipTrigger>
|
|
86
|
+
<TooltipContent>{label}</TooltipContent>
|
|
87
|
+
</Tooltip>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
</TooltipProvider>
|
|
91
|
+
),
|
|
92
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { Checkbox } from "./checkbox";
|
|
5
|
+
import { Label } from "./label";
|
|
6
|
+
import { Separator } from "./separator";
|
|
7
|
+
import { Sheet, SheetContent, SheetDescription, SheetTitle, SheetTrigger } from "./sheet";
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
9
|
+
|
|
10
|
+
describe("Checkbox", () => {
|
|
11
|
+
it("toggles from its label and exposes checkbox state", () => {
|
|
12
|
+
render(
|
|
13
|
+
<>
|
|
14
|
+
<Checkbox id="notify" />
|
|
15
|
+
<Label htmlFor="notify">Email me</Label>
|
|
16
|
+
</>,
|
|
17
|
+
);
|
|
18
|
+
const box = screen.getByRole("checkbox", { name: "Email me" });
|
|
19
|
+
expect(box).toHaveAttribute("aria-checked", "false");
|
|
20
|
+
fireEvent.click(screen.getByText("Email me"));
|
|
21
|
+
expect(box).toHaveAttribute("aria-checked", "true");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("reports the indeterminate state as mixed", () => {
|
|
25
|
+
render(<Checkbox aria-label="Select all" checked="indeterminate" />);
|
|
26
|
+
expect(screen.getByRole("checkbox", { name: "Select all" })).toHaveAttribute(
|
|
27
|
+
"aria-checked",
|
|
28
|
+
"mixed",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("draws the dash for an uncontrolled indeterminate box too", () => {
|
|
33
|
+
const { container } = render(<Checkbox aria-label="Some" defaultChecked="indeterminate" />);
|
|
34
|
+
expect(screen.getByRole("checkbox")).toHaveAttribute("aria-checked", "mixed");
|
|
35
|
+
const dash = container.querySelector("svg.lucide-minus");
|
|
36
|
+
const tick = container.querySelector("svg.lucide-check");
|
|
37
|
+
expect(dash?.getAttribute("class")).toContain("group-data-[state=indeterminate]/checkbox:block");
|
|
38
|
+
expect(tick?.getAttribute("class")).toContain("group-data-[state=indeterminate]/checkbox:hidden");
|
|
39
|
+
expect(screen.getByRole("checkbox")).toHaveAttribute("data-state", "indeterminate");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("paints with tokens, never palette utilities", () => {
|
|
43
|
+
render(<Checkbox aria-label="Agree" defaultChecked />);
|
|
44
|
+
const cls = screen.getByRole("checkbox").className;
|
|
45
|
+
expect(cls).toContain("data-[state=checked]:bg-primary");
|
|
46
|
+
expect(cls).not.toMatch(/\b(?:bg|text|border)-(?:slate|zinc|gray|neutral|blue|green|red|amber|emerald)-\d/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("Separator", () => {
|
|
51
|
+
it("is hidden from assistive technology unless it is semantic", () => {
|
|
52
|
+
const { container, rerender } = render(<Separator />);
|
|
53
|
+
expect(container.firstElementChild).toHaveAttribute("role", "none");
|
|
54
|
+
rerender(<Separator decorative={false} orientation="vertical" />);
|
|
55
|
+
const rule = screen.getByRole("separator");
|
|
56
|
+
expect(rule).toHaveAttribute("aria-orientation", "vertical");
|
|
57
|
+
expect(rule.className).toContain("w-px");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("Sheet", () => {
|
|
62
|
+
it("opens as a labelled dialog and closes from its close button", async () => {
|
|
63
|
+
render(
|
|
64
|
+
<Sheet>
|
|
65
|
+
<SheetTrigger>Open chat</SheetTrigger>
|
|
66
|
+
<SheetContent side="left">
|
|
67
|
+
<SheetTitle>Assistant</SheetTitle>
|
|
68
|
+
<SheetDescription>Ask about this run.</SheetDescription>
|
|
69
|
+
</SheetContent>
|
|
70
|
+
</Sheet>,
|
|
71
|
+
);
|
|
72
|
+
fireEvent.click(screen.getByText("Open chat"));
|
|
73
|
+
const dialog = await screen.findByRole("dialog", { name: "Assistant" });
|
|
74
|
+
expect(dialog.className).toContain("slide-in-from-left");
|
|
75
|
+
fireEvent.click(screen.getByRole("button", { name: "Close" }));
|
|
76
|
+
await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("can hide its close button", async () => {
|
|
80
|
+
render(
|
|
81
|
+
<Sheet defaultOpen>
|
|
82
|
+
<SheetContent hideCloseButton>
|
|
83
|
+
<SheetTitle>Filters</SheetTitle>
|
|
84
|
+
<SheetDescription>Narrow the list.</SheetDescription>
|
|
85
|
+
</SheetContent>
|
|
86
|
+
</Sheet>,
|
|
87
|
+
);
|
|
88
|
+
await screen.findByRole("dialog", { name: "Filters" });
|
|
89
|
+
expect(screen.queryByRole("button", { name: "Close" })).not.toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("Tooltip", () => {
|
|
94
|
+
it("shows its label when the trigger receives keyboard focus", async () => {
|
|
95
|
+
const user = userEvent.setup();
|
|
96
|
+
render(
|
|
97
|
+
<TooltipProvider delayDuration={0}>
|
|
98
|
+
<Tooltip>
|
|
99
|
+
<TooltipTrigger aria-label="Runs">R</TooltipTrigger>
|
|
100
|
+
<TooltipContent>Runs</TooltipContent>
|
|
101
|
+
</Tooltip>
|
|
102
|
+
</TooltipProvider>,
|
|
103
|
+
);
|
|
104
|
+
await user.tab();
|
|
105
|
+
expect(await screen.findByRole("tooltip")).toHaveTextContent("Runs");
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
4
|
+
import { Check, Minus } from "lucide-react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { focusRing } from "../lib/focus";
|
|
7
|
+
import { cn } from "../lib/utils";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A two- or three-state checkbox. An indeterminate state, controlled
|
|
11
|
+
* (`checked`) or uncontrolled (`defaultChecked`), draws a dash.
|
|
12
|
+
* Pair it with a `Label` through `id`/`htmlFor`; `aria-invalid` paints the
|
|
13
|
+
* danger hairline.
|
|
14
|
+
*/
|
|
15
|
+
const Checkbox = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<CheckboxPrimitive.Root
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn(
|
|
22
|
+
"peer group/checkbox inline-grid size-4 shrink-0 cursor-pointer place-content-center rounded-[4px] border border-[var(--border-strong)] bg-transparent transition-[background-color,border-color,box-shadow] duration-150 ease-out",
|
|
23
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
|
+
"aria-invalid:border-[var(--surface-danger-border)]",
|
|
25
|
+
"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
26
|
+
"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
|
|
27
|
+
focusRing,
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
>
|
|
32
|
+
<CheckboxPrimitive.Indicator className="grid place-content-center text-current">
|
|
33
|
+
{/* Both glyphs render and the Root's Radix state picks one, so a
|
|
34
|
+
controlled or uncontrolled indeterminate box draws the same dash. */}
|
|
35
|
+
<Check aria-hidden="true" className="size-3.5 group-data-[state=indeterminate]/checkbox:hidden" strokeWidth={3} />
|
|
36
|
+
<Minus aria-hidden="true" className="hidden size-3.5 group-data-[state=indeterminate]/checkbox:block" strokeWidth={3} />
|
|
37
|
+
</CheckboxPrimitive.Indicator>
|
|
38
|
+
</CheckboxPrimitive.Root>
|
|
39
|
+
));
|
|
40
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
41
|
+
|
|
42
|
+
export { Checkbox };
|