@alpic-ai/ui 0.0.0-dev.85c8341 → 0.0.0-dev.862c47d
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/dist/components/avatar.d.mts +1 -1
- package/dist/components/button.d.mts +3 -1
- package/dist/components/button.mjs +20 -6
- package/dist/components/combobox.mjs +1 -1
- package/dist/components/dialog.mjs +2 -2
- package/dist/components/github-button.d.mts +13 -0
- package/dist/components/github-button.mjs +24 -0
- package/dist/components/page-loader.d.mts +11 -0
- package/dist/components/page-loader.mjs +122 -0
- package/dist/components/select.d.mts +1 -9
- package/dist/components/select.mjs +8 -28
- package/dist/components/shimmer-text.d.mts +12 -0
- package/dist/components/shimmer-text.mjs +22 -0
- package/dist/components/sidebar.mjs +65 -21
- package/dist/components/table.d.mts +10 -1
- package/dist/components/table.mjs +4 -4
- package/dist/components/typography.d.mts +34 -0
- package/dist/components/typography.mjs +29 -0
- package/package.json +15 -15
- package/src/components/button.tsx +13 -9
- package/src/components/combobox.tsx +19 -7
- package/src/components/dialog.tsx +2 -2
- package/src/components/github-button.tsx +34 -0
- package/src/components/page-loader.tsx +59 -0
- package/src/components/select.tsx +2 -35
- package/src/components/shimmer-text.tsx +23 -0
- package/src/components/sidebar.tsx +64 -27
- package/src/components/table.tsx +17 -4
- package/src/components/typography.tsx +19 -0
- package/src/hooks/use-copy-to-clipboard.ts +6 -2
- package/src/stories/button.stories.tsx +23 -1
- package/src/stories/sidebar.stories.tsx +11 -8
- package/src/stories/table.stories.tsx +2 -2
- package/src/styles/tokens.css +217 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as _$react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/components/typography.d.ts
|
|
5
|
+
declare function H1({
|
|
6
|
+
children,
|
|
7
|
+
className
|
|
8
|
+
}: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
className?: string;
|
|
11
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
12
|
+
declare function H2({
|
|
13
|
+
children,
|
|
14
|
+
className
|
|
15
|
+
}: {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
className?: string;
|
|
18
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
19
|
+
declare function H3({
|
|
20
|
+
children,
|
|
21
|
+
className
|
|
22
|
+
}: {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
className?: string;
|
|
25
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
26
|
+
declare function H4({
|
|
27
|
+
children,
|
|
28
|
+
className
|
|
29
|
+
}: {
|
|
30
|
+
children: ReactNode;
|
|
31
|
+
className?: string;
|
|
32
|
+
}): _$react_jsx_runtime0.JSX.Element;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { H1, H2, H3, H4 };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.mjs";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/typography.tsx
|
|
4
|
+
function H1({ children, className }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("h1", {
|
|
6
|
+
className: cn("scroll-m-20 type-display-sm font-bold", className),
|
|
7
|
+
children
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function H2({ children, className }) {
|
|
11
|
+
return /* @__PURE__ */ jsx("h2", {
|
|
12
|
+
className: cn("scroll-m-20 type-display-xs font-semibold", className),
|
|
13
|
+
children
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function H3({ children, className }) {
|
|
17
|
+
return /* @__PURE__ */ jsx("h3", {
|
|
18
|
+
className: cn("scroll-m-20 type-text-lg font-semibold", className),
|
|
19
|
+
children
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function H4({ children, className }) {
|
|
23
|
+
return /* @__PURE__ */ jsx("h4", {
|
|
24
|
+
className: cn("scroll-m-20 type-text-md font-semibold", className),
|
|
25
|
+
children
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { H1, H2, H3, H4 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpic-ai/ui",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.862c47d",
|
|
4
4
|
"description": "Alpic design system — shared UI components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"lucide-react": "^1.
|
|
27
|
-
"react": "^19.
|
|
28
|
-
"react-dom": "^19.
|
|
29
|
-
"react-hook-form": "^7.
|
|
30
|
-
"sonner": "^2.0.
|
|
31
|
-
"tailwindcss": "^4.
|
|
32
|
-
"tw-animate-css": "^1.
|
|
26
|
+
"lucide-react": "^1.14.0",
|
|
27
|
+
"react": "^19.2.6",
|
|
28
|
+
"react-dom": "^19.2.6",
|
|
29
|
+
"react-hook-form": "^7.75.0",
|
|
30
|
+
"sonner": "^2.0.7",
|
|
31
|
+
"tailwindcss": "^4.3.0",
|
|
32
|
+
"tw-animate-css": "^1.4.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -52,21 +52,21 @@
|
|
|
52
52
|
"class-variance-authority": "^0.7.1",
|
|
53
53
|
"clsx": "^2.1.1",
|
|
54
54
|
"cmdk": "^1.1.1",
|
|
55
|
-
"tailwind-merge": "^3.
|
|
55
|
+
"tailwind-merge": "^3.6.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@ladle/react": "^5.1.1",
|
|
59
|
-
"@tailwindcss/postcss": "^4.
|
|
59
|
+
"@tailwindcss/postcss": "^4.3.0",
|
|
60
60
|
"@types/react": "19.2.14",
|
|
61
61
|
"@types/react-dom": "19.2.3",
|
|
62
|
-
"lucide-react": "^1.
|
|
63
|
-
"react-hook-form": "^7.
|
|
62
|
+
"lucide-react": "^1.14.0",
|
|
63
|
+
"react-hook-form": "^7.75.0",
|
|
64
64
|
"shx": "^0.4.0",
|
|
65
65
|
"sonner": "^2.0.7",
|
|
66
|
-
"tailwindcss": "^4.
|
|
67
|
-
"tsdown": "^0.
|
|
66
|
+
"tailwindcss": "^4.3.0",
|
|
67
|
+
"tsdown": "^0.22.0",
|
|
68
68
|
"tw-animate-css": "^1.4.0",
|
|
69
|
-
"typescript": "^6.0.
|
|
69
|
+
"typescript": "^6.0.3"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "shx rm -rf dist && tsdown",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
import { Loader2 } from "lucide-react";
|
|
6
6
|
import type * as React from "react";
|
|
@@ -46,6 +46,13 @@ const buttonVariants = cva(
|
|
|
46
46
|
"bg-destructive text-destructive-foreground",
|
|
47
47
|
"[@media(hover:hover)]:hover:bg-destructive-hover",
|
|
48
48
|
].join(" "),
|
|
49
|
+
cta: [
|
|
50
|
+
"button-cta",
|
|
51
|
+
"h-9 px-4 gap-2 rounded-md",
|
|
52
|
+
"dark:bg-inverted text-foreground",
|
|
53
|
+
"transition-[transform,filter] duration-300 ease-out",
|
|
54
|
+
"active:scale-[0.99]",
|
|
55
|
+
].join(" "),
|
|
49
56
|
},
|
|
50
57
|
size: {
|
|
51
58
|
default: "type-text-sm",
|
|
@@ -65,6 +72,7 @@ interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeo
|
|
|
65
72
|
asChild?: boolean;
|
|
66
73
|
loading?: boolean;
|
|
67
74
|
icon?: React.ReactNode;
|
|
75
|
+
iconTrailing?: React.ReactNode;
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
function Button({
|
|
@@ -75,6 +83,7 @@ function Button({
|
|
|
75
83
|
asChild = false,
|
|
76
84
|
loading = false,
|
|
77
85
|
icon,
|
|
86
|
+
iconTrailing,
|
|
78
87
|
disabled,
|
|
79
88
|
children,
|
|
80
89
|
...props
|
|
@@ -90,14 +99,9 @@ function Button({
|
|
|
90
99
|
aria-busy={loading || undefined}
|
|
91
100
|
{...props}
|
|
92
101
|
>
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<>
|
|
97
|
-
{loading ? <Loader2 className="motion-safe:animate-spin" /> : icon}
|
|
98
|
-
{children}
|
|
99
|
-
</>
|
|
100
|
-
)}
|
|
102
|
+
{loading ? <Loader2 className="motion-safe:animate-spin" /> : icon}
|
|
103
|
+
{asChild ? <Slottable>{children}</Slottable> : children}
|
|
104
|
+
{!loading && iconTrailing ? <span data-cta-icon-trailing>{iconTrailing}</span> : null}
|
|
101
105
|
</Comp>
|
|
102
106
|
);
|
|
103
107
|
}
|
|
@@ -84,7 +84,9 @@ function Combobox(props: ComboboxProps) {
|
|
|
84
84
|
|
|
85
85
|
const onOpenChange = useCallback(
|
|
86
86
|
(newOpen: boolean) => {
|
|
87
|
-
if (!isOpenControlled)
|
|
87
|
+
if (!isOpenControlled) {
|
|
88
|
+
setUncontrolledOpen(newOpen);
|
|
89
|
+
}
|
|
88
90
|
controlledOnOpenChange?.(newOpen);
|
|
89
91
|
},
|
|
90
92
|
[isOpenControlled, controlledOnOpenChange],
|
|
@@ -105,7 +107,9 @@ function Combobox(props: ComboboxProps) {
|
|
|
105
107
|
|
|
106
108
|
const isSelected = useCallback(
|
|
107
109
|
(itemValue: string) => {
|
|
108
|
-
if (multiple)
|
|
110
|
+
if (multiple) {
|
|
111
|
+
return multiValues.includes(itemValue);
|
|
112
|
+
}
|
|
109
113
|
return singleValue === itemValue;
|
|
110
114
|
},
|
|
111
115
|
[multiple, singleValue, multiValues],
|
|
@@ -123,11 +127,15 @@ function Combobox(props: ComboboxProps) {
|
|
|
123
127
|
const next = multiValues.includes(itemValue)
|
|
124
128
|
? multiValues.filter((val) => val !== itemValue)
|
|
125
129
|
: [...multiValues, itemValue];
|
|
126
|
-
if (!isControlledMulti)
|
|
130
|
+
if (!isControlledMulti) {
|
|
131
|
+
setUncontrolledMultiValue(next);
|
|
132
|
+
}
|
|
127
133
|
onValueChangeMulti?.(next);
|
|
128
134
|
// Stay open in multi mode
|
|
129
135
|
} else {
|
|
130
|
-
if (!isControlledSingle)
|
|
136
|
+
if (!isControlledSingle) {
|
|
137
|
+
setUncontrolledSingleValue(itemValue);
|
|
138
|
+
}
|
|
131
139
|
onValueChangeSingle?.(itemValue);
|
|
132
140
|
onOpenChange(false);
|
|
133
141
|
}
|
|
@@ -145,9 +153,13 @@ function Combobox(props: ComboboxProps) {
|
|
|
145
153
|
|
|
146
154
|
const onDeselect = useCallback(
|
|
147
155
|
(itemValue: string) => {
|
|
148
|
-
if (!multiple)
|
|
156
|
+
if (!multiple) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
149
159
|
const next = multiValues.filter((val) => val !== itemValue);
|
|
150
|
-
if (!isControlledMulti)
|
|
160
|
+
if (!isControlledMulti) {
|
|
161
|
+
setUncontrolledMultiValue(next);
|
|
162
|
+
}
|
|
151
163
|
onValueChangeMulti?.(next);
|
|
152
164
|
},
|
|
153
165
|
[multiple, isControlledMulti, onValueChangeMulti, multiValues],
|
|
@@ -349,7 +361,7 @@ function ComboboxItem({ className, children, itemValue, ...props }: ComboboxItem
|
|
|
349
361
|
data-selected-item={selected || undefined}
|
|
350
362
|
onSelect={() => onSelect(itemValue)}
|
|
351
363
|
className={cn(
|
|
352
|
-
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-
|
|
364
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 outline-hidden select-none",
|
|
353
365
|
"type-text-md font-medium text-foreground mx-1.5 my-px",
|
|
354
366
|
"data-[selected=true]:bg-background-hover",
|
|
355
367
|
"data-[selected-item]:bg-accent",
|
|
@@ -37,7 +37,7 @@ function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Dial
|
|
|
37
37
|
const dialogContentVariants = cva(
|
|
38
38
|
[
|
|
39
39
|
"bg-background fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2",
|
|
40
|
-
"
|
|
40
|
+
"max-h-[calc(100vh-4rem)] overflow-hidden rounded-2xl px-6 shadow-lg outline-none",
|
|
41
41
|
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
42
42
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
43
43
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
@@ -93,7 +93,7 @@ function DialogContent({ className, children, size, showCloseButton = true, ...p
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
96
|
-
return <div data-slot="dialog-header" className={cn("flex flex-col gap-0.5
|
|
96
|
+
return <div data-slot="dialog-header" className={cn("flex flex-col gap-0.5 py-6 pr-10", className)} {...props} />;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const dialogFooterVariants = cva("pb-6 pt-6", {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
import { Button } from "./button";
|
|
7
|
+
|
|
8
|
+
const GITHUB_ICON_PATH =
|
|
9
|
+
"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12";
|
|
10
|
+
|
|
11
|
+
function GitHubIcon() {
|
|
12
|
+
return (
|
|
13
|
+
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
14
|
+
<path d={GITHUB_ICON_PATH} />
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type GitHubButtonProps = Omit<ComponentProps<typeof Button>, "variant" | "icon">;
|
|
20
|
+
|
|
21
|
+
function GitHubButton({ className, children, ...props }: GitHubButtonProps) {
|
|
22
|
+
return (
|
|
23
|
+
<Button
|
|
24
|
+
{...props}
|
|
25
|
+
icon={<GitHubIcon />}
|
|
26
|
+
className={cn("bg-foreground text-background [@media(hover:hover)]:hover:bg-foreground/90", className)}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</Button>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type { GitHubButtonProps };
|
|
34
|
+
export { GitHubButton };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
|
|
5
|
+
const CABLE_CAR_SVG = (
|
|
6
|
+
<svg
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
viewBox="0 0 120 130"
|
|
9
|
+
width="240"
|
|
10
|
+
height="260"
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
className="block h-auto w-full"
|
|
13
|
+
>
|
|
14
|
+
<line x1="60" y1="3" x2="60" y2="58" stroke="#333" strokeWidth="4" />
|
|
15
|
+
<circle cx="60" cy="11" r="10" fill="#555" />
|
|
16
|
+
<rect x="5" y="58" width="110" height="64" rx="4" fill="#e90060" />
|
|
17
|
+
<rect x="5" y="58" width="110" height="20" rx="4" fill="#F5F0E8" />
|
|
18
|
+
<rect x="5" y="68" width="110" height="10" fill="#F5F0E8" />
|
|
19
|
+
<rect x="14" y="66" width="26" height="30" rx="2" fill="#5B8EC9" stroke="#C4B9A8" strokeWidth="1.5" />
|
|
20
|
+
<rect x="47" y="66" width="26" height="30" rx="2" fill="#5B8EC9" stroke="#C4B9A8" strokeWidth="1.5" />
|
|
21
|
+
<rect x="80" y="66" width="26" height="30" rx="2" fill="#5B8EC9" stroke="#C4B9A8" strokeWidth="1.5" />
|
|
22
|
+
<rect x="5" y="115" width="110" height="7" rx="3" fill="#9f0042" />
|
|
23
|
+
</svg>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
interface PageLoaderProps {
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function PageLoader({ className }: PageLoaderProps) {
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
className={cn("flex min-h-screen items-center justify-center bg-background", className)}
|
|
34
|
+
role="status"
|
|
35
|
+
aria-label="Loading Alpic…"
|
|
36
|
+
>
|
|
37
|
+
<div className="relative h-[120px] w-[200px]">
|
|
38
|
+
<div
|
|
39
|
+
className="absolute top-[30px] left-0 h-[3px] w-full rounded-sm bg-[#6c6c77]"
|
|
40
|
+
style={{ transform: "rotate(-15deg)", transformOrigin: "left center" }}
|
|
41
|
+
/>
|
|
42
|
+
<div className="absolute top-[33px] -left-[45px] w-[45px] motion-safe:animate-[alpic-ride_4s_linear_infinite]">
|
|
43
|
+
{CABLE_CAR_SVG}
|
|
44
|
+
</div>
|
|
45
|
+
<div
|
|
46
|
+
className="pointer-events-none absolute -top-[40px] -left-[45px] z-10 h-[200px] w-[95px]"
|
|
47
|
+
style={{ background: "linear-gradient(to right, var(--color-background) 47%, transparent)" }}
|
|
48
|
+
/>
|
|
49
|
+
<div
|
|
50
|
+
className="pointer-events-none absolute -top-[40px] left-[142px] z-10 h-[200px] w-[95px]"
|
|
51
|
+
style={{ background: "linear-gradient(to right, transparent, var(--color-background) 53%)" }}
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type { PageLoaderProps };
|
|
59
|
+
export { PageLoader };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
4
4
|
import type { VariantProps } from "class-variance-authority";
|
|
5
|
-
import { CheckIcon, ChevronDownIcon
|
|
5
|
+
import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
|
6
6
|
import type * as React from "react";
|
|
7
7
|
|
|
8
8
|
import { cn } from "../lib/cn";
|
|
@@ -75,7 +75,6 @@ function SelectContent({
|
|
|
75
75
|
position={position}
|
|
76
76
|
{...props}
|
|
77
77
|
>
|
|
78
|
-
<SelectScrollUpButton />
|
|
79
78
|
<SelectPrimitive.Viewport
|
|
80
79
|
className={cn(
|
|
81
80
|
"p-1",
|
|
@@ -85,7 +84,6 @@ function SelectContent({
|
|
|
85
84
|
>
|
|
86
85
|
{children}
|
|
87
86
|
</SelectPrimitive.Viewport>
|
|
88
|
-
<SelectScrollDownButton />
|
|
89
87
|
</SelectPrimitive.Content>
|
|
90
88
|
</SelectPrimitive.Portal>
|
|
91
89
|
);
|
|
@@ -110,7 +108,7 @@ function SelectItem({ className, children, ...props }: React.ComponentProps<type
|
|
|
110
108
|
<SelectPrimitive.Item
|
|
111
109
|
data-slot="select-item"
|
|
112
110
|
className={cn(
|
|
113
|
-
"relative flex
|
|
111
|
+
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pl-2 outline-hidden select-none",
|
|
114
112
|
"type-text-md font-medium text-foreground mx-1 my-px",
|
|
115
113
|
"[@media(hover:hover)]:hover:bg-background-hover",
|
|
116
114
|
"focus:bg-background-hover",
|
|
@@ -142,43 +140,12 @@ function SelectSeparator({ className, ...props }: React.ComponentProps<typeof Se
|
|
|
142
140
|
);
|
|
143
141
|
}
|
|
144
142
|
|
|
145
|
-
/* ── Scroll buttons ──────────────────────────────────────────────────────── */
|
|
146
|
-
|
|
147
|
-
function SelectScrollUpButton({ className, ...props }: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
|
148
|
-
return (
|
|
149
|
-
<SelectPrimitive.ScrollUpButton
|
|
150
|
-
data-slot="select-scroll-up-button"
|
|
151
|
-
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
152
|
-
{...props}
|
|
153
|
-
>
|
|
154
|
-
<ChevronUpIcon className="size-4" />
|
|
155
|
-
</SelectPrimitive.ScrollUpButton>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function SelectScrollDownButton({
|
|
160
|
-
className,
|
|
161
|
-
...props
|
|
162
|
-
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
|
163
|
-
return (
|
|
164
|
-
<SelectPrimitive.ScrollDownButton
|
|
165
|
-
data-slot="select-scroll-down-button"
|
|
166
|
-
className={cn("flex cursor-default items-center justify-center py-1", className)}
|
|
167
|
-
{...props}
|
|
168
|
-
>
|
|
169
|
-
<ChevronDownIcon className="size-4" />
|
|
170
|
-
</SelectPrimitive.ScrollDownButton>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
143
|
export {
|
|
175
144
|
Select,
|
|
176
145
|
SelectContent,
|
|
177
146
|
SelectGroup,
|
|
178
147
|
SelectItem,
|
|
179
148
|
SelectLabel,
|
|
180
|
-
SelectScrollDownButton,
|
|
181
|
-
SelectScrollUpButton,
|
|
182
149
|
SelectSeparator,
|
|
183
150
|
SelectTrigger,
|
|
184
151
|
SelectValue,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
|
|
7
|
+
const shimmerStyle: React.CSSProperties = {
|
|
8
|
+
background: `linear-gradient(90deg, #0000 calc(50% - 60px), var(--color-foreground), #0000 calc(50% + 60px)), linear-gradient(color-mix(in oklab, var(--color-muted-foreground) 70%, transparent), color-mix(in oklab, var(--color-muted-foreground) 70%, transparent))`,
|
|
9
|
+
backgroundSize: "250% 100%, auto",
|
|
10
|
+
backgroundRepeat: "no-repeat, padding-box",
|
|
11
|
+
WebkitBackgroundClip: "text",
|
|
12
|
+
WebkitTextFillColor: "transparent",
|
|
13
|
+
backgroundClip: "text",
|
|
14
|
+
animation: "shimmer-text 2.5s linear infinite",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function ShimmerText({ children, className }: { children: string | number; className?: string }) {
|
|
18
|
+
return (
|
|
19
|
+
<span className={cn(className)} style={shimmerStyle}>
|
|
20
|
+
{children}
|
|
21
|
+
</span>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { Slot } from "@radix-ui/react-slot";
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
-
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
6
5
|
import * as React from "react";
|
|
7
6
|
|
|
8
7
|
import { useIsMobile } from "../hooks/use-mobile";
|
|
@@ -20,6 +19,19 @@ const SIDEBAR_WIDTH_MOBILE = "16rem";
|
|
|
20
19
|
const SIDEBAR_WIDTH_ICON = "3.5rem";
|
|
21
20
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
22
21
|
|
|
22
|
+
const INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR = [
|
|
23
|
+
"a",
|
|
24
|
+
"button",
|
|
25
|
+
"input",
|
|
26
|
+
"select",
|
|
27
|
+
"textarea",
|
|
28
|
+
"[role='button']",
|
|
29
|
+
"[role='link']",
|
|
30
|
+
"[role='menuitem']",
|
|
31
|
+
"[contenteditable='true']",
|
|
32
|
+
"[tabindex]:not([tabindex='-1'])",
|
|
33
|
+
].join(", ");
|
|
34
|
+
|
|
23
35
|
type SidebarContextProps = {
|
|
24
36
|
state: "expanded" | "collapsed";
|
|
25
37
|
open: boolean;
|
|
@@ -143,13 +155,31 @@ function Sidebar({
|
|
|
143
155
|
variant?: "sidebar" | "floating" | "inset";
|
|
144
156
|
collapsible?: "offcanvas" | "icon" | "none";
|
|
145
157
|
}) {
|
|
146
|
-
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
|
|
158
|
+
const { isMobile, state, openMobile, setOpenMobile, open, setOpen } = useSidebar();
|
|
159
|
+
|
|
160
|
+
function handleSurfaceClickCapture(event: React.MouseEvent<HTMLDivElement>) {
|
|
161
|
+
const clickedInteractiveElement =
|
|
162
|
+
event.target instanceof Element && event.target.closest(INTERACTIVE_SIDEBAR_ELEMENT_SELECTOR);
|
|
163
|
+
|
|
164
|
+
if (clickedInteractiveElement) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!open) {
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
setOpen(true);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
setOpen(false);
|
|
176
|
+
}
|
|
147
177
|
|
|
148
178
|
if (collapsible === "none") {
|
|
149
179
|
return (
|
|
150
180
|
<div
|
|
151
181
|
data-slot="sidebar"
|
|
152
|
-
className={cn("bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
|
182
|
+
className={cn("bg-sidebar-surface text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col", className)}
|
|
153
183
|
{...props}
|
|
154
184
|
>
|
|
155
185
|
{children}
|
|
@@ -164,7 +194,7 @@ function Sidebar({
|
|
|
164
194
|
data-sidebar="sidebar"
|
|
165
195
|
data-slot="sidebar"
|
|
166
196
|
data-mobile="true"
|
|
167
|
-
className="bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
197
|
+
className="bg-sidebar-surface text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
|
|
168
198
|
style={
|
|
169
199
|
{
|
|
170
200
|
"--sidebar-width": SIDEBAR_WIDTH_MOBILE,
|
|
@@ -213,7 +243,7 @@ function Sidebar({
|
|
|
213
243
|
// Adjust the padding for floating and inset variants.
|
|
214
244
|
variant === "floating" || variant === "inset"
|
|
215
245
|
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
|
|
216
|
-
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
246
|
+
: "border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
217
247
|
className,
|
|
218
248
|
)}
|
|
219
249
|
{...props}
|
|
@@ -221,7 +251,8 @@ function Sidebar({
|
|
|
221
251
|
<div
|
|
222
252
|
data-sidebar="sidebar"
|
|
223
253
|
data-slot="sidebar-inner"
|
|
224
|
-
className="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
|
254
|
+
className="bg-sidebar-surface group-data-[variant=floating]:border-sidebar-border flex h-full w-full cursor-pointer flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm"
|
|
255
|
+
onClickCapture={handleSurfaceClickCapture}
|
|
225
256
|
>
|
|
226
257
|
{children}
|
|
227
258
|
</div>
|
|
@@ -231,9 +262,7 @@ function Sidebar({
|
|
|
231
262
|
}
|
|
232
263
|
|
|
233
264
|
function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
|
|
234
|
-
const {
|
|
235
|
-
|
|
236
|
-
const isOpen = isMobile ? openMobile : state === "expanded";
|
|
265
|
+
const { toggleSidebar } = useSidebar();
|
|
237
266
|
|
|
238
267
|
return (
|
|
239
268
|
<Button
|
|
@@ -248,12 +277,21 @@ function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<t
|
|
|
248
277
|
}}
|
|
249
278
|
{...props}
|
|
250
279
|
>
|
|
251
|
-
|
|
280
|
+
<SidebarToggleIcon className="size-4.5" />
|
|
252
281
|
<span className="sr-only">Toggle Sidebar</span>
|
|
253
282
|
</Button>
|
|
254
283
|
);
|
|
255
284
|
}
|
|
256
285
|
|
|
286
|
+
function SidebarToggleIcon({ className, ...props }: React.ComponentProps<"svg">) {
|
|
287
|
+
return (
|
|
288
|
+
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className} {...props}>
|
|
289
|
+
<rect x="2.75" y="2.75" width="14.5" height="14.5" rx="4" stroke="currentColor" strokeWidth="1.5" />
|
|
290
|
+
<path d="M10 4.75V15.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
291
|
+
</svg>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
257
295
|
function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
|
258
296
|
const { toggleSidebar } = useSidebar();
|
|
259
297
|
|
|
@@ -266,9 +304,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
|
|
|
266
304
|
onClick={toggleSidebar}
|
|
267
305
|
title="Toggle Sidebar"
|
|
268
306
|
className={cn(
|
|
269
|
-
"
|
|
270
|
-
"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
|
|
271
|
-
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
307
|
+
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex",
|
|
272
308
|
"[@media(hover:hover)]:hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
|
273
309
|
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
274
310
|
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
|
@@ -316,21 +352,22 @@ function SidebarHeader({ className, icon, title, children, ...props }: SidebarHe
|
|
|
316
352
|
<div
|
|
317
353
|
data-slot="sidebar-header"
|
|
318
354
|
data-sidebar="header"
|
|
319
|
-
className={cn("flex flex-col gap-2
|
|
355
|
+
className={cn("flex flex-col gap-2 py-2", className)}
|
|
320
356
|
{...props}
|
|
321
357
|
>
|
|
322
|
-
<div className="flex h-8 items-center gap-2 px-
|
|
323
|
-
<
|
|
324
|
-
<span className="transition-opacity group-data-[collapsible=icon]:group-hover:opacity-0">
|
|
325
|
-
|
|
326
|
-
<SidebarTrigger className="!size-4 !p-0" />
|
|
327
|
-
</div>
|
|
328
|
-
</div>
|
|
329
|
-
{title && (
|
|
330
|
-
<span className="text-foreground text-md min-w-0 truncate font-medium group-data-[collapsible=icon]:hidden">
|
|
331
|
-
{title}
|
|
358
|
+
<div className="flex h-8 items-center gap-2 px-3">
|
|
359
|
+
<span className="relative flex size-8 shrink-0 items-center justify-center">
|
|
360
|
+
<span className="transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-0">
|
|
361
|
+
{icon}
|
|
332
362
|
</span>
|
|
333
|
-
|
|
363
|
+
<SidebarTrigger
|
|
364
|
+
tabIndex={-1}
|
|
365
|
+
className="absolute inset-0 opacity-0 transition-opacity duration-200 group-data-[collapsible=icon]:group-hover:opacity-100"
|
|
366
|
+
/>
|
|
367
|
+
</span>
|
|
368
|
+
<span className="text-foreground text-md min-w-0 truncate font-medium group-data-[collapsible=icon]:hidden">
|
|
369
|
+
{title}
|
|
370
|
+
</span>
|
|
334
371
|
<SidebarTrigger className="ml-auto shrink-0 group-data-[collapsible=icon]:hidden" />
|
|
335
372
|
</div>
|
|
336
373
|
{children}
|
|
@@ -376,7 +413,7 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
376
413
|
<div
|
|
377
414
|
data-slot="sidebar-group"
|
|
378
415
|
data-sidebar="group"
|
|
379
|
-
className={cn("relative flex w-full min-w-0 flex-col
|
|
416
|
+
className={cn("relative flex w-full min-w-0 flex-col", className)}
|
|
380
417
|
{...props}
|
|
381
418
|
/>
|
|
382
419
|
);
|
|
@@ -453,7 +490,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
|
453
490
|
<li
|
|
454
491
|
data-slot="sidebar-menu-item"
|
|
455
492
|
data-sidebar="menu-item"
|
|
456
|
-
className={cn("group/menu-item relative", className)}
|
|
493
|
+
className={cn("group/menu-item relative px-3", className)}
|
|
457
494
|
{...props}
|
|
458
495
|
/>
|
|
459
496
|
);
|