@preset.nz/ux-kit 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/README.md +31 -42
- package/THIRD_PARTY_NOTICES.md +253 -0
- package/package.json +13 -13
- package/src/chat/index.ts +9 -0
- package/src/chat/message.tsx +80 -0
- package/src/chat/prompt-input.tsx +121 -0
- package/src/chat/reasoning.tsx +182 -0
- package/src/chat/scroller.tsx +147 -0
- package/src/chat/suggestion.tsx +65 -0
- package/src/chat/tool.tsx +143 -0
- package/src/chat/types.ts +8 -0
- package/src/components/alert-shell.tsx +66 -0
- package/src/components/avatar.tsx +1 -1
- package/src/components/badge.tsx +6 -6
- package/src/components/breadcrumb.tsx +8 -10
- package/src/components/button.tsx +1 -1
- package/src/components/card.tsx +4 -21
- package/src/components/checkbox.tsx +3 -5
- package/src/components/color-field.tsx +20 -7
- package/src/components/color-swatch.tsx +3 -3
- package/src/components/confirm-dialog.tsx +93 -0
- package/src/components/context-menu.tsx +16 -13
- package/src/components/dialog.tsx +8 -9
- package/src/components/download-prompt.tsx +108 -0
- package/src/components/dropdown-menu.tsx +16 -13
- package/src/components/empty-state.tsx +6 -3
- package/src/components/input.tsx +2 -2
- package/src/components/label.tsx +3 -2
- package/src/components/list.tsx +205 -0
- package/src/components/navigation-menu.tsx +17 -10
- package/src/components/number-field.tsx +30 -26
- package/src/components/popover.tsx +6 -10
- package/src/components/progress.tsx +77 -0
- package/src/components/select.tsx +16 -28
- package/src/components/separator.tsx +2 -6
- package/src/components/sheet.tsx +16 -8
- package/src/components/side-panel.tsx +31 -8
- package/src/components/sidebar.tsx +19 -52
- package/src/components/skeleton.tsx +1 -1
- package/src/components/slider.tsx +4 -8
- package/src/components/snackbar.tsx +39 -36
- package/src/components/status-bar.tsx +10 -3
- package/src/components/tabs.tsx +5 -6
- package/src/components/textarea.tsx +2 -2
- package/src/components/toggle-group.tsx +4 -4
- package/src/components/toggle.tsx +3 -1
- package/src/components/toolbar.tsx +8 -11
- package/src/components/tooltip.tsx +1 -1
- package/src/graphics/adsr.ts +21 -13
- package/src/graphics/curve-editor.tsx +56 -14
- package/src/graphics/curve-edits.ts +20 -3
- package/src/graphics/index.ts +6 -6
- package/src/graphics/scale.ts +4 -1
- package/src/graphics/use-canvas.ts +1 -1
- package/src/graphics/use-tokens.ts +4 -1
- package/src/index.css +9 -1
- package/src/index.ts +7 -5
- package/src/lib/use-mobile.ts +1 -1
- package/src/lib/utils.ts +1 -1
- package/src/shadcn-tailwind.css +665 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils"
|
|
4
|
+
|
|
5
|
+
type ProgressProps = {
|
|
6
|
+
/** `null` is indeterminate: a slow moving segment, still under `prefers-reduced-motion`. */
|
|
7
|
+
value: number | null
|
|
8
|
+
max?: number
|
|
9
|
+
/** The accessible name; shown above the bar when `showLabel`. */
|
|
10
|
+
label?: string
|
|
11
|
+
showLabel?: boolean
|
|
12
|
+
/** Right-aligned text the caller builds ("12.4 / 44.7 MB", "63%"). The kit does not format it. */
|
|
13
|
+
detail?: string
|
|
14
|
+
className?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// A 6 px rounded bar that fills its container: `--primary` on `--muted`. No per-state colour:
|
|
18
|
+
// a failure is the caller's message, not a red bar.
|
|
19
|
+
function Progress({
|
|
20
|
+
value,
|
|
21
|
+
max = 100,
|
|
22
|
+
label,
|
|
23
|
+
showLabel = false,
|
|
24
|
+
detail,
|
|
25
|
+
className,
|
|
26
|
+
}: ProgressProps) {
|
|
27
|
+
const labelShown = showLabel && label !== undefined
|
|
28
|
+
return (
|
|
29
|
+
<ProgressPrimitive.Root
|
|
30
|
+
data-slot="progress"
|
|
31
|
+
value={value}
|
|
32
|
+
max={max}
|
|
33
|
+
aria-label={labelShown ? undefined : label}
|
|
34
|
+
className={cn("flex w-full flex-col gap-1.5", className)}
|
|
35
|
+
>
|
|
36
|
+
{(labelShown || detail !== undefined) && (
|
|
37
|
+
<div className="flex items-baseline justify-between gap-3 text-xs">
|
|
38
|
+
{labelShown ? (
|
|
39
|
+
<ProgressPrimitive.Label data-slot="progress-label" className="font-medium">
|
|
40
|
+
{label}
|
|
41
|
+
</ProgressPrimitive.Label>
|
|
42
|
+
) : (
|
|
43
|
+
<span />
|
|
44
|
+
)}
|
|
45
|
+
{detail !== undefined && (
|
|
46
|
+
<span data-slot="progress-detail" className="text-muted-foreground tabular-nums">
|
|
47
|
+
{detail}
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
<ProgressPrimitive.Track
|
|
53
|
+
data-slot="progress-track"
|
|
54
|
+
className="relative h-1.5 w-full overflow-hidden rounded-full bg-muted"
|
|
55
|
+
>
|
|
56
|
+
<ProgressPrimitive.Indicator
|
|
57
|
+
data-slot="progress-indicator"
|
|
58
|
+
className="h-full rounded-full bg-primary transition-[width] data-indeterminate:w-2/5 data-indeterminate:animate-[progress-slide_2s_ease-in-out_infinite] data-indeterminate:transition-none motion-reduce:data-indeterminate:animate-none"
|
|
59
|
+
/>
|
|
60
|
+
</ProgressPrimitive.Track>
|
|
61
|
+
</ProgressPrimitive.Root>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Decimal sizes, one place: bytes under a kilobyte, then kilobytes, megabytes and gigabytes.
|
|
66
|
+
// 46_900_000 is "46.9 MB", 1_200_000_000 is "1.2 GB", 2_048 is "2.0 KB", 0 is "0 B".
|
|
67
|
+
function formatBytes(n: number): string {
|
|
68
|
+
if (!Number.isFinite(n) || n <= 0) return "0 B"
|
|
69
|
+
if (n < 1_000) return `${Math.round(n)} B`
|
|
70
|
+
// Each threshold sits where one place rounds up to 1000.0 of the smaller unit.
|
|
71
|
+
if (n < 999_950) return `${(n / 1_000).toFixed(1)} KB`
|
|
72
|
+
if (n < 999_950_000) return `${(n / 1_000_000).toFixed(1)} MB`
|
|
73
|
+
return `${(n / 1_000_000_000).toFixed(1)} GB`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type { ProgressProps }
|
|
77
|
+
export { formatBytes, Progress }
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import * as React from "react"
|
|
4
3
|
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
|
5
|
-
|
|
4
|
+
import { CaretDownIcon, CaretUpIcon, CheckIcon } from "@phosphor-icons/react"
|
|
5
|
+
import type * as React from "react"
|
|
6
6
|
import { cn } from "../lib/utils"
|
|
7
|
-
import { CaretDownIcon, CheckIcon, CaretUpIcon } from "@phosphor-icons/react"
|
|
8
7
|
|
|
9
8
|
const Select = SelectPrimitive.Root
|
|
10
9
|
|
|
@@ -42,15 +41,13 @@ function SelectTrigger({
|
|
|
42
41
|
data-size={size}
|
|
43
42
|
className={cn(
|
|
44
43
|
"flex w-fit items-center justify-between gap-1.5 rounded-none border border-input bg-transparent py-2 pr-2 pl-2.5 text-xs whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
45
|
-
className
|
|
44
|
+
className,
|
|
46
45
|
)}
|
|
47
46
|
{...props}
|
|
48
47
|
>
|
|
49
48
|
{children}
|
|
50
49
|
<SelectPrimitive.Icon
|
|
51
|
-
render={
|
|
52
|
-
<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
53
|
-
}
|
|
50
|
+
render={<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />}
|
|
54
51
|
/>
|
|
55
52
|
</SelectPrimitive.Trigger>
|
|
56
53
|
)
|
|
@@ -83,7 +80,10 @@ function SelectContent({
|
|
|
83
80
|
<SelectPrimitive.Popup
|
|
84
81
|
data-slot="select-content"
|
|
85
82
|
data-align-trigger={alignItemWithTrigger}
|
|
86
|
-
className={cn(
|
|
83
|
+
className={cn(
|
|
84
|
+
"relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
87
|
{...props}
|
|
88
88
|
>
|
|
89
89
|
<SelectScrollUpButton />
|
|
@@ -95,10 +95,7 @@ function SelectContent({
|
|
|
95
95
|
)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
function SelectLabel({
|
|
99
|
-
className,
|
|
100
|
-
...props
|
|
101
|
-
}: SelectPrimitive.GroupLabel.Props) {
|
|
98
|
+
function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) {
|
|
102
99
|
return (
|
|
103
100
|
<SelectPrimitive.GroupLabel
|
|
104
101
|
data-slot="select-label"
|
|
@@ -108,17 +105,13 @@ function SelectLabel({
|
|
|
108
105
|
)
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
function SelectItem({
|
|
112
|
-
className,
|
|
113
|
-
children,
|
|
114
|
-
...props
|
|
115
|
-
}: SelectPrimitive.Item.Props) {
|
|
108
|
+
function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Props) {
|
|
116
109
|
return (
|
|
117
110
|
<SelectPrimitive.Item
|
|
118
111
|
data-slot="select-item"
|
|
119
112
|
className={cn(
|
|
120
113
|
"relative flex w-full cursor-default items-center gap-2 rounded-none py-2 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
121
|
-
className
|
|
114
|
+
className,
|
|
122
115
|
)}
|
|
123
116
|
{...props}
|
|
124
117
|
>
|
|
@@ -136,10 +129,7 @@ function SelectItem({
|
|
|
136
129
|
)
|
|
137
130
|
}
|
|
138
131
|
|
|
139
|
-
function SelectSeparator({
|
|
140
|
-
className,
|
|
141
|
-
...props
|
|
142
|
-
}: SelectPrimitive.Separator.Props) {
|
|
132
|
+
function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) {
|
|
143
133
|
return (
|
|
144
134
|
<SelectPrimitive.Separator
|
|
145
135
|
data-slot="select-separator"
|
|
@@ -158,12 +148,11 @@ function SelectScrollUpButton({
|
|
|
158
148
|
data-slot="select-scroll-up-button"
|
|
159
149
|
className={cn(
|
|
160
150
|
"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
161
|
-
className
|
|
151
|
+
className,
|
|
162
152
|
)}
|
|
163
153
|
{...props}
|
|
164
154
|
>
|
|
165
|
-
<CaretUpIcon
|
|
166
|
-
/>
|
|
155
|
+
<CaretUpIcon />
|
|
167
156
|
</SelectPrimitive.ScrollUpArrow>
|
|
168
157
|
)
|
|
169
158
|
}
|
|
@@ -177,12 +166,11 @@ function SelectScrollDownButton({
|
|
|
177
166
|
data-slot="select-scroll-down-button"
|
|
178
167
|
className={cn(
|
|
179
168
|
"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
180
|
-
className
|
|
169
|
+
className,
|
|
181
170
|
)}
|
|
182
171
|
{...props}
|
|
183
172
|
>
|
|
184
|
-
<CaretDownIcon
|
|
185
|
-
/>
|
|
173
|
+
<CaretDownIcon />
|
|
186
174
|
</SelectPrimitive.ScrollDownArrow>
|
|
187
175
|
)
|
|
188
176
|
}
|
|
@@ -4,18 +4,14 @@ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
|
|
|
4
4
|
|
|
5
5
|
import { cn } from "../lib/utils"
|
|
6
6
|
|
|
7
|
-
function Separator({
|
|
8
|
-
className,
|
|
9
|
-
orientation = "horizontal",
|
|
10
|
-
...props
|
|
11
|
-
}: SeparatorPrimitive.Props) {
|
|
7
|
+
function Separator({ className, orientation = "horizontal", ...props }: SeparatorPrimitive.Props) {
|
|
12
8
|
return (
|
|
13
9
|
<SeparatorPrimitive
|
|
14
10
|
data-slot="separator"
|
|
15
11
|
orientation={orientation}
|
|
16
12
|
className={cn(
|
|
17
13
|
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
|
|
18
|
-
className
|
|
14
|
+
className,
|
|
19
15
|
)}
|
|
20
16
|
{...props}
|
|
21
17
|
/>
|
package/src/components/sheet.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
1
|
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
|
|
3
2
|
import { XIcon } from "@phosphor-icons/react"
|
|
3
|
+
import type * as React from "react"
|
|
4
4
|
|
|
5
5
|
import { cn } from "../lib/utils"
|
|
6
6
|
import { Button } from "./button"
|
|
@@ -28,7 +28,7 @@ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
|
|
|
28
28
|
data-slot="sheet-overlay"
|
|
29
29
|
className={cn(
|
|
30
30
|
"fixed inset-0 z-50 bg-foreground/40 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
31
|
-
className
|
|
31
|
+
className,
|
|
32
32
|
)}
|
|
33
33
|
{...props}
|
|
34
34
|
/>
|
|
@@ -61,7 +61,7 @@ function SheetContent({
|
|
|
61
61
|
"inset-x-0 top-0 h-auto border-b data-closed:slide-out-to-top data-open:slide-in-from-top",
|
|
62
62
|
side === "bottom" &&
|
|
63
63
|
"inset-x-0 bottom-0 h-auto border-t data-closed:slide-out-to-bottom data-open:slide-in-from-bottom",
|
|
64
|
-
className
|
|
64
|
+
className,
|
|
65
65
|
)}
|
|
66
66
|
{...props}
|
|
67
67
|
>
|
|
@@ -82,13 +82,21 @@ function SheetContent({
|
|
|
82
82
|
|
|
83
83
|
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
84
84
|
return (
|
|
85
|
-
<div
|
|
85
|
+
<div
|
|
86
|
+
data-slot="sheet-header"
|
|
87
|
+
className={cn("flex flex-col gap-1.5 p-4", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
86
90
|
)
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
90
94
|
return (
|
|
91
|
-
<div
|
|
95
|
+
<div
|
|
96
|
+
data-slot="sheet-footer"
|
|
97
|
+
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
92
100
|
)
|
|
93
101
|
}
|
|
94
102
|
|
|
@@ -114,11 +122,11 @@ function SheetDescription({ className, ...props }: SheetPrimitive.Description.Pr
|
|
|
114
122
|
|
|
115
123
|
export {
|
|
116
124
|
Sheet,
|
|
117
|
-
SheetTrigger,
|
|
118
125
|
SheetClose,
|
|
119
126
|
SheetContent,
|
|
120
|
-
|
|
127
|
+
SheetDescription,
|
|
121
128
|
SheetFooter,
|
|
129
|
+
SheetHeader,
|
|
122
130
|
SheetTitle,
|
|
123
|
-
|
|
131
|
+
SheetTrigger,
|
|
124
132
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import type * as React from "react"
|
|
2
2
|
|
|
3
3
|
import { cn } from "../lib/utils"
|
|
4
4
|
import { buttonVariants } from "./button"
|
|
@@ -58,7 +58,12 @@ function SidePanel({
|
|
|
58
58
|
data-slot="side-panel"
|
|
59
59
|
data-state="closed"
|
|
60
60
|
aria-label={title}
|
|
61
|
-
className={cn(
|
|
61
|
+
className={cn(
|
|
62
|
+
"flex w-10 shrink-0 flex-col items-center bg-sidebar py-2 text-sidebar-foreground",
|
|
63
|
+
border,
|
|
64
|
+
"border-sidebar-border",
|
|
65
|
+
className,
|
|
66
|
+
)}
|
|
62
67
|
{...props}
|
|
63
68
|
>
|
|
64
69
|
<Tooltip>
|
|
@@ -77,7 +82,10 @@ function SidePanel({
|
|
|
77
82
|
<TooltipContent side={side === "left" ? "right" : "left"}>
|
|
78
83
|
<span>Show {title}</span>
|
|
79
84
|
{shortcut ? (
|
|
80
|
-
<kbd
|
|
85
|
+
<kbd
|
|
86
|
+
data-slot="kbd"
|
|
87
|
+
className="rounded-sm bg-background/20 px-1 font-sans text-[11px]"
|
|
88
|
+
>
|
|
81
89
|
{shortcut}
|
|
82
90
|
</kbd>
|
|
83
91
|
) : null}
|
|
@@ -121,10 +129,16 @@ function SidePanel({
|
|
|
121
129
|
data-state="open"
|
|
122
130
|
aria-label={title}
|
|
123
131
|
style={{ width: w }}
|
|
124
|
-
className={cn(
|
|
132
|
+
className={cn(
|
|
133
|
+
"relative flex shrink-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground",
|
|
134
|
+
border,
|
|
135
|
+
"border-sidebar-border",
|
|
136
|
+
className,
|
|
137
|
+
)}
|
|
125
138
|
{...props}
|
|
126
139
|
>
|
|
127
140
|
{children}
|
|
141
|
+
{/* biome-ignore lint/a11y/useSemanticElements: a focusable, keyboard-operable splitter (WAI-ARIA window splitter); <hr> is not focusable */}
|
|
128
142
|
<div
|
|
129
143
|
role="separator"
|
|
130
144
|
aria-orientation="vertical"
|
|
@@ -137,7 +151,7 @@ function SidePanel({
|
|
|
137
151
|
onKeyDown={onKey}
|
|
138
152
|
className={cn(
|
|
139
153
|
"absolute inset-y-0 z-10 w-1 cursor-col-resize touch-none hover:bg-primary/30 focus-visible:bg-primary/40 focus-visible:outline-none",
|
|
140
|
-
side === "left" ? "right-0" : "left-0"
|
|
154
|
+
side === "left" ? "right-0" : "left-0",
|
|
141
155
|
)}
|
|
142
156
|
/>
|
|
143
157
|
</aside>
|
|
@@ -149,7 +163,10 @@ function SidePanelHeader({ className, children, ...props }: React.ComponentProps
|
|
|
149
163
|
return (
|
|
150
164
|
<div
|
|
151
165
|
data-slot="side-panel-header"
|
|
152
|
-
className={cn(
|
|
166
|
+
className={cn(
|
|
167
|
+
"flex h-9 shrink-0 items-center gap-2 border-b border-sidebar-border px-3 text-xs font-medium uppercase tracking-wide text-muted-foreground",
|
|
168
|
+
className,
|
|
169
|
+
)}
|
|
153
170
|
{...props}
|
|
154
171
|
>
|
|
155
172
|
{children}
|
|
@@ -159,8 +176,14 @@ function SidePanelHeader({ className, children, ...props }: React.ComponentProps
|
|
|
159
176
|
|
|
160
177
|
/** The scrolling body under the header. */
|
|
161
178
|
function SidePanelContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
162
|
-
return
|
|
179
|
+
return (
|
|
180
|
+
<div
|
|
181
|
+
data-slot="side-panel-content"
|
|
182
|
+
className={cn("min-h-0 flex-1 overflow-auto", className)}
|
|
183
|
+
{...props}
|
|
184
|
+
/>
|
|
185
|
+
)
|
|
163
186
|
}
|
|
164
187
|
|
|
165
|
-
export { SidePanel, SidePanelContent, SidePanelHeader, clampWidth }
|
|
166
188
|
export type { SidePanelProps }
|
|
189
|
+
export { clampWidth, SidePanel, SidePanelContent, SidePanelHeader }
|
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import * as React from "react"
|
|
4
3
|
import { useRender } from "@base-ui/react/use-render"
|
|
5
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
4
|
import { SidebarSimpleIcon } from "@phosphor-icons/react"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
import * as React from "react"
|
|
7
7
|
|
|
8
8
|
import { useIsMobile } from "../lib/use-mobile"
|
|
9
9
|
import { cn } from "../lib/utils"
|
|
10
10
|
import { Button } from "./button"
|
|
11
11
|
import { Input } from "./input"
|
|
12
12
|
import { Separator } from "./separator"
|
|
13
|
-
import {
|
|
14
|
-
Sheet,
|
|
15
|
-
SheetContent,
|
|
16
|
-
SheetDescription,
|
|
17
|
-
SheetHeader,
|
|
18
|
-
SheetTitle,
|
|
19
|
-
} from "./sheet"
|
|
13
|
+
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./sheet"
|
|
20
14
|
import { Skeleton } from "./skeleton"
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
} from "./tooltip"
|
|
15
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"
|
|
16
|
+
|
|
17
|
+
// Sidebar: shadcn's sidebar with `asChild` replaced by `render`, and `useIsMobile` kept in
|
|
18
|
+
// the kit. Only the `collapsible="none"` form is exercised in the playground; the offcanvas
|
|
19
|
+
// and icon forms are untested here.
|
|
27
20
|
|
|
28
21
|
// Base UI's replacement for Radix's asChild: pass `render={<a href="..." />}`
|
|
29
22
|
// to swap the element, props and ref are merged onto it.
|
|
@@ -93,6 +86,7 @@ function SidebarProvider({
|
|
|
93
86
|
}
|
|
94
87
|
|
|
95
88
|
// This sets the cookie to keep the sidebar state.
|
|
89
|
+
// biome-ignore lint/suspicious/noDocumentCookie: the Cookie Store API is not in every webview, and the cookie is written for hosts that read it server-side
|
|
96
90
|
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
|
|
97
91
|
},
|
|
98
92
|
[setOpenProp, open],
|
|
@@ -101,15 +95,12 @@ function SidebarProvider({
|
|
|
101
95
|
// Helper to toggle the sidebar.
|
|
102
96
|
const toggleSidebar = React.useCallback(() => {
|
|
103
97
|
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
|
|
104
|
-
}, [isMobile, setOpen
|
|
98
|
+
}, [isMobile, setOpen])
|
|
105
99
|
|
|
106
100
|
// Adds a keyboard shortcut to toggle the sidebar.
|
|
107
101
|
React.useEffect(() => {
|
|
108
102
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
109
|
-
if (
|
|
110
|
-
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
|
|
111
|
-
(event.metaKey || event.ctrlKey)
|
|
112
|
-
) {
|
|
103
|
+
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
|
|
113
104
|
event.preventDefault()
|
|
114
105
|
toggleSidebar()
|
|
115
106
|
}
|
|
@@ -133,7 +124,7 @@ function SidebarProvider({
|
|
|
133
124
|
setOpenMobile,
|
|
134
125
|
toggleSidebar,
|
|
135
126
|
}),
|
|
136
|
-
[state, open, setOpen, isMobile, openMobile,
|
|
127
|
+
[state, open, setOpen, isMobile, openMobile, toggleSidebar],
|
|
137
128
|
)
|
|
138
129
|
|
|
139
130
|
return (
|
|
@@ -263,11 +254,7 @@ function Sidebar({
|
|
|
263
254
|
)
|
|
264
255
|
}
|
|
265
256
|
|
|
266
|
-
function SidebarTrigger({
|
|
267
|
-
className,
|
|
268
|
-
onClick,
|
|
269
|
-
...props
|
|
270
|
-
}: React.ComponentProps<typeof Button>) {
|
|
257
|
+
function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
|
|
271
258
|
const { toggleSidebar } = useSidebar()
|
|
272
259
|
|
|
273
260
|
return (
|
|
@@ -328,10 +315,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
|
|
|
328
315
|
)
|
|
329
316
|
}
|
|
330
317
|
|
|
331
|
-
function SidebarInput({
|
|
332
|
-
className,
|
|
333
|
-
...props
|
|
334
|
-
}: React.ComponentProps<typeof Input>) {
|
|
318
|
+
function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
|
|
335
319
|
return (
|
|
336
320
|
<Input
|
|
337
321
|
data-slot="sidebar-input"
|
|
@@ -364,10 +348,7 @@ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
364
348
|
)
|
|
365
349
|
}
|
|
366
350
|
|
|
367
|
-
function SidebarSeparator({
|
|
368
|
-
className,
|
|
369
|
-
...props
|
|
370
|
-
}: React.ComponentProps<typeof Separator>) {
|
|
351
|
+
function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
|
|
371
352
|
return (
|
|
372
353
|
<Separator
|
|
373
354
|
data-slot="sidebar-separator"
|
|
@@ -447,10 +428,7 @@ function SidebarGroupAction({
|
|
|
447
428
|
)
|
|
448
429
|
}
|
|
449
430
|
|
|
450
|
-
function SidebarGroupContent({
|
|
451
|
-
className,
|
|
452
|
-
...props
|
|
453
|
-
}: React.ComponentProps<"div">) {
|
|
431
|
+
function SidebarGroupContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
454
432
|
return (
|
|
455
433
|
<div
|
|
456
434
|
data-slot="sidebar-group-content"
|
|
@@ -588,10 +566,7 @@ function SidebarMenuAction({
|
|
|
588
566
|
)
|
|
589
567
|
}
|
|
590
568
|
|
|
591
|
-
function SidebarMenuBadge({
|
|
592
|
-
className,
|
|
593
|
-
...props
|
|
594
|
-
}: React.ComponentProps<"div">) {
|
|
569
|
+
function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">) {
|
|
595
570
|
return (
|
|
596
571
|
<div
|
|
597
572
|
data-slot="sidebar-menu-badge"
|
|
@@ -627,12 +602,7 @@ function SidebarMenuSkeleton({
|
|
|
627
602
|
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
|
628
603
|
{...props}
|
|
629
604
|
>
|
|
630
|
-
{showIcon &&
|
|
631
|
-
<Skeleton
|
|
632
|
-
className="size-4 rounded-md"
|
|
633
|
-
data-sidebar="menu-skeleton-icon"
|
|
634
|
-
/>
|
|
635
|
-
)}
|
|
605
|
+
{showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
|
|
636
606
|
<Skeleton
|
|
637
607
|
className="h-4 max-w-(--skeleton-width) flex-1"
|
|
638
608
|
data-sidebar="menu-skeleton-text"
|
|
@@ -661,10 +631,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
|
|
|
661
631
|
)
|
|
662
632
|
}
|
|
663
633
|
|
|
664
|
-
function SidebarMenuSubItem({
|
|
665
|
-
className,
|
|
666
|
-
...props
|
|
667
|
-
}: React.ComponentProps<"li">) {
|
|
634
|
+
function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
668
635
|
return (
|
|
669
636
|
<li
|
|
670
637
|
data-slot="sidebar-menu-sub-item"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
1
|
import { Slider as SliderPrimitive } from "@base-ui/react/slider"
|
|
2
|
+
import * as React from "react"
|
|
3
3
|
|
|
4
4
|
import { cn } from "../lib/utils"
|
|
5
5
|
|
|
@@ -21,13 +21,8 @@ function Slider({
|
|
|
21
21
|
const [live, setLive] = React.useState<typeof valueProp | null>(null)
|
|
22
22
|
const value = live ?? valueProp
|
|
23
23
|
const _values = React.useMemo(
|
|
24
|
-
() =>
|
|
25
|
-
|
|
26
|
-
? value
|
|
27
|
-
: Array.isArray(defaultValue)
|
|
28
|
-
? defaultValue
|
|
29
|
-
: [min],
|
|
30
|
-
[value, defaultValue, min]
|
|
24
|
+
() => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min]),
|
|
25
|
+
[value, defaultValue, min],
|
|
31
26
|
)
|
|
32
27
|
|
|
33
28
|
return (
|
|
@@ -62,6 +57,7 @@ function Slider({
|
|
|
62
57
|
{Array.from({ length: _values.length }, (_, index) => (
|
|
63
58
|
<SliderPrimitive.Thumb
|
|
64
59
|
data-slot="slider-thumb"
|
|
60
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional; the count is fixed by the value array and they have no identity
|
|
65
61
|
key={index}
|
|
66
62
|
index={index}
|
|
67
63
|
className="block h-4 w-2 shrink-0 rounded-[1px] border border-muted-foreground bg-background ring-ring/50 transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden has-[:focus-visible]:ring-4 data-disabled:pointer-events-none data-disabled:opacity-50"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
1
|
import { Toast } from "@base-ui/react/toast"
|
|
3
2
|
import { CheckCircleIcon, WarningIcon, XCircleIcon, XIcon } from "@phosphor-icons/react"
|
|
3
|
+
import type * as React from "react"
|
|
4
4
|
|
|
5
5
|
import { cn } from "../lib/utils"
|
|
6
6
|
|
|
7
|
-
// The kit's one toast: Strata's snackbar, on Base UI Toast.
|
|
8
|
-
// One manager for the module, so `notify()` works outside React. Mount one
|
|
7
|
+
// The kit's one toast: Strata's snackbar, on Base UI Toast. Kinds: plain, success, warning, error.
|
|
8
|
+
// One manager for the module, so `notify()` works outside React. Mount one `SnackbarProvider` per
|
|
9
|
+
// window, with a `SnackbarViewport` inside it.
|
|
9
10
|
const manager = Toast.createToastManager()
|
|
10
11
|
|
|
11
12
|
export function SnackbarProvider({ children }: { children: React.ReactNode }) {
|
|
@@ -17,7 +18,7 @@ export function SnackbarViewport({ className }: { className?: string }) {
|
|
|
17
18
|
<Toast.Viewport
|
|
18
19
|
className={cn(
|
|
19
20
|
"pointer-events-none absolute bottom-3 left-3 z-50 flex w-72 flex-col gap-2 outline-none",
|
|
20
|
-
className
|
|
21
|
+
className,
|
|
21
22
|
)}
|
|
22
23
|
>
|
|
23
24
|
<SnackbarList />
|
|
@@ -52,40 +53,40 @@ function SnackbarList() {
|
|
|
52
53
|
{toasts.map((t) => {
|
|
53
54
|
const kind = KIND[(t.type as SnackbarKind | undefined) ?? "plain"] ?? KIND.plain
|
|
54
55
|
return (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
className={cn(
|
|
59
|
-
"pointer-events-auto flex items-start gap-2 rounded-sm border border-border bg-popover px-3 py-2 text-xs text-popover-foreground shadow-md outline-none",
|
|
60
|
-
"transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0",
|
|
61
|
-
kind.root
|
|
62
|
-
)}
|
|
63
|
-
>
|
|
64
|
-
{kind.icon}
|
|
65
|
-
<div className="min-w-0 flex-1">
|
|
66
|
-
<Toast.Title className="font-medium">{t.title}</Toast.Title>
|
|
67
|
-
{t.description && (
|
|
68
|
-
<Toast.Description className="mt-0.5 opacity-80">{t.description}</Toast.Description>
|
|
69
|
-
)}
|
|
70
|
-
</div>
|
|
71
|
-
<Toast.Action
|
|
72
|
-
className={cn(
|
|
73
|
-
"inline-flex items-center rounded-sm px-2 py-0.5 text-xs font-medium",
|
|
74
|
-
"text-current/80 hover:bg-foreground/10 hover:text-current",
|
|
75
|
-
"focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none"
|
|
76
|
-
)}
|
|
77
|
-
/>
|
|
78
|
-
<Toast.Close
|
|
79
|
-
aria-label="Dismiss"
|
|
56
|
+
<Toast.Root
|
|
57
|
+
key={t.id}
|
|
58
|
+
toast={t}
|
|
80
59
|
className={cn(
|
|
81
|
-
"
|
|
82
|
-
"opacity-
|
|
83
|
-
|
|
60
|
+
"pointer-events-auto flex items-start gap-2 rounded-sm border border-border bg-popover px-3 py-2 text-xs text-popover-foreground shadow-md outline-none",
|
|
61
|
+
"transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0",
|
|
62
|
+
kind.root,
|
|
84
63
|
)}
|
|
85
64
|
>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
65
|
+
{kind.icon}
|
|
66
|
+
<div className="min-w-0 flex-1">
|
|
67
|
+
<Toast.Title className="font-medium">{t.title}</Toast.Title>
|
|
68
|
+
{t.description && (
|
|
69
|
+
<Toast.Description className="mt-0.5 opacity-80">{t.description}</Toast.Description>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
<Toast.Action
|
|
73
|
+
className={cn(
|
|
74
|
+
"inline-flex items-center rounded-sm px-2 py-0.5 text-xs font-medium",
|
|
75
|
+
"text-current/80 hover:bg-foreground/10 hover:text-current",
|
|
76
|
+
"focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
|
|
77
|
+
)}
|
|
78
|
+
/>
|
|
79
|
+
<Toast.Close
|
|
80
|
+
aria-label="Dismiss"
|
|
81
|
+
className={cn(
|
|
82
|
+
"inline-flex size-4 items-center justify-center rounded-sm",
|
|
83
|
+
"opacity-60 hover:bg-foreground/10 hover:opacity-100",
|
|
84
|
+
"focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
<XIcon weight="bold" className="size-3" />
|
|
88
|
+
</Toast.Close>
|
|
89
|
+
</Toast.Root>
|
|
89
90
|
)
|
|
90
91
|
})}
|
|
91
92
|
</>
|
|
@@ -113,7 +114,9 @@ const show = (m: Manager, opts: SnackbarOptions) =>
|
|
|
113
114
|
description: opts.description,
|
|
114
115
|
type: opts.kind ?? "plain",
|
|
115
116
|
timeout: opts.persistent ? 0 : (opts.timeout ?? 5000),
|
|
116
|
-
actionProps: opts.action
|
|
117
|
+
actionProps: opts.action
|
|
118
|
+
? { children: opts.action.label, onClick: opts.action.onClick }
|
|
119
|
+
: undefined,
|
|
117
120
|
})
|
|
118
121
|
|
|
119
122
|
// Must be called under a SnackbarProvider.
|