@inklu/docs 0.2.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 +7 -0
- package/components.json +25 -0
- package/dist/index.d.mts +271 -0
- package/dist/index.d.ts +271 -0
- package/dist/index.js +2455 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2368 -0
- package/dist/index.mjs.map +1 -0
- package/dist/shiki.d.mts +10 -0
- package/dist/shiki.d.ts +10 -0
- package/dist/shiki.js +47 -0
- package/dist/shiki.js.map +1 -0
- package/dist/shiki.mjs +22 -0
- package/dist/shiki.mjs.map +1 -0
- package/dist/styles.css +2556 -0
- package/package.json +55 -0
- package/src/components/content/changelog-content.tsx +119 -0
- package/src/components/content/code-block.tsx +111 -0
- package/src/components/content/docs-content.tsx +113 -0
- package/src/components/layout/docs-layout.tsx +192 -0
- package/src/components/layout/site-header.tsx +152 -0
- package/src/components/layout/site-layout.tsx +28 -0
- package/src/components/layout/site-provider.tsx +23 -0
- package/src/components/layout/site-template.tsx +20 -0
- package/src/components/layout/theme-provider.tsx +10 -0
- package/src/components/layout/theme-switcher.tsx +42 -0
- package/src/components/motion-primitives/morph-icon.tsx +97 -0
- package/src/components/motion-primitives/progressive-blur.tsx +64 -0
- package/src/components/motion-primitives/sliding-number.tsx +131 -0
- package/src/components/motion-primitives/text-effect.tsx +290 -0
- package/src/components/motion-primitives/text-morph.tsx +77 -0
- package/src/components/motion-primitives/text-scramble.tsx +87 -0
- package/src/components/shared/command-block.tsx +193 -0
- package/src/components/shared/scroll-fade.tsx +22 -0
- package/src/components/ui/badge.tsx +50 -0
- package/src/components/ui/brand-logo.tsx +68 -0
- package/src/components/ui/button-group.tsx +87 -0
- package/src/components/ui/button.tsx +104 -0
- package/src/components/ui/card.tsx +97 -0
- package/src/components/ui/direction.tsx +6 -0
- package/src/components/ui/hover-card.tsx +51 -0
- package/src/components/ui/item.tsx +201 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/toast.tsx +322 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-copy-to-clipboard.ts +54 -0
- package/src/index.ts +30 -0
- package/src/lib/constants.ts +63 -0
- package/src/lib/motions.ts +21 -0
- package/src/lib/utils/audio.ts +56 -0
- package/src/lib/utils.ts +14 -0
- package/src/shiki.ts +25 -0
- package/src/styles.css +112 -0
- package/src/typeset.css +513 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +11 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { SmoothCorners } from "@lisse/react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
const badgeVariants = cva(
|
|
9
|
+
"inline-flex h-4 items-center justify-center px-1.5 text-[10px] font-mono uppercase tracking-wide transition-all whitespace-nowrap",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground",
|
|
14
|
+
secondary: "bg-secondary text-muted-foreground hover:text-foreground",
|
|
15
|
+
outline: "border border-border text-foreground",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
variant: "default",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
function Badge({
|
|
25
|
+
className,
|
|
26
|
+
variant = "default",
|
|
27
|
+
render,
|
|
28
|
+
...props
|
|
29
|
+
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
30
|
+
return (
|
|
31
|
+
<SmoothCorners asChild corners={{ radius: 4, smoothing: 1 }}>
|
|
32
|
+
{useRender({
|
|
33
|
+
defaultTagName: "span",
|
|
34
|
+
props: mergeProps<"span">(
|
|
35
|
+
{
|
|
36
|
+
className: cn(badgeVariants({ variant }), className),
|
|
37
|
+
},
|
|
38
|
+
props,
|
|
39
|
+
),
|
|
40
|
+
render,
|
|
41
|
+
state: {
|
|
42
|
+
slot: "badge",
|
|
43
|
+
variant,
|
|
44
|
+
},
|
|
45
|
+
})}
|
|
46
|
+
</SmoothCorners>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { AnimatePresence, type HTMLMotionProps, motion } from "motion/react";
|
|
4
|
+
import { forwardRef, useState } from "react";
|
|
5
|
+
|
|
6
|
+
interface BrandLogoProps extends HTMLMotionProps<"div"> {}
|
|
7
|
+
|
|
8
|
+
export const BrandLogo = forwardRef<HTMLDivElement, BrandLogoProps>(
|
|
9
|
+
({ className = "", ...props }, ref) => {
|
|
10
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
11
|
+
|
|
12
|
+
// Apple's approach to spring physics
|
|
13
|
+
const springConfig = {
|
|
14
|
+
type: "spring" as const,
|
|
15
|
+
duration: 0.5,
|
|
16
|
+
bounce: 0.2,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Strong ease-out curve for the morphing crossfade
|
|
20
|
+
const easeOut = [0.23, 1, 0.32, 1] as const;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<motion.div
|
|
24
|
+
ref={ref}
|
|
25
|
+
// No background here. The container is completely transparent.
|
|
26
|
+
className={`relative flex items-center justify-center overflow-hidden transition-shadow ${className}`}
|
|
27
|
+
onHoverStart={() => setIsHovered(true)}
|
|
28
|
+
onHoverEnd={() => setIsHovered(false)}
|
|
29
|
+
whileTap={{ scale: 0.97 }}
|
|
30
|
+
// 1. Container width expands/shrinks naturally using layout
|
|
31
|
+
layout
|
|
32
|
+
transition={springConfig}
|
|
33
|
+
style={{
|
|
34
|
+
height: 24, // Keeps vertical rhythm stable
|
|
35
|
+
cursor: "pointer",
|
|
36
|
+
}}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<AnimatePresence mode="popLayout" initial={false}>
|
|
40
|
+
{isHovered ? (
|
|
41
|
+
<motion.span
|
|
42
|
+
key="text"
|
|
43
|
+
// 2. The blur creates a glowing "morph" between the shapes
|
|
44
|
+
initial={{ opacity: 0, scale: 0.95, filter: "blur(6px)" }}
|
|
45
|
+
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
|
|
46
|
+
exit={{ opacity: 0, scale: 0.95, filter: "blur(6px)" }}
|
|
47
|
+
transition={{ duration: 0.2, ease: easeOut }}
|
|
48
|
+
className="whitespace-nowrap font-sans text-[16px] font-extrabold lowercase leading-none tracking-tight text-slate-900 dark:text-white"
|
|
49
|
+
>
|
|
50
|
+
inklu
|
|
51
|
+
</motion.span>
|
|
52
|
+
) : (
|
|
53
|
+
<motion.div
|
|
54
|
+
key="icon"
|
|
55
|
+
initial={{ opacity: 0, scale: 0.95, filter: "blur(6px)" }}
|
|
56
|
+
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
|
|
57
|
+
exit={{ opacity: 0, scale: 0.95, filter: "blur(6px)" }}
|
|
58
|
+
transition={{ duration: 0.2, ease: easeOut }}
|
|
59
|
+
className="h-4 w-4 rounded-sm bg-slate-900 dark:bg-white"
|
|
60
|
+
/>
|
|
61
|
+
)}
|
|
62
|
+
</AnimatePresence>
|
|
63
|
+
</motion.div>
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
BrandLogo.displayName = "BrandLogo";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { Separator } from "@/components/ui/separator";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const buttonGroupVariants = cva(
|
|
8
|
+
"flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-e-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
orientation: {
|
|
12
|
+
horizontal:
|
|
13
|
+
"*:data-slot:rounded-e-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-e-md! [&>[data-slot]~[data-slot]]:rounded-s-none [&>[data-slot]~[data-slot]]:border-s-0",
|
|
14
|
+
vertical:
|
|
15
|
+
"flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
orientation: "horizontal",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
function ButtonGroup({
|
|
25
|
+
className,
|
|
26
|
+
orientation,
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
|
|
29
|
+
return (
|
|
30
|
+
// biome-ignore lint/a11y/useSemanticElements: Button group pattern
|
|
31
|
+
<div
|
|
32
|
+
role="group"
|
|
33
|
+
data-slot="button-group"
|
|
34
|
+
data-orientation={orientation}
|
|
35
|
+
className={cn(buttonGroupVariants({ orientation }), className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ButtonGroupText({
|
|
42
|
+
className,
|
|
43
|
+
render,
|
|
44
|
+
...props
|
|
45
|
+
}: useRender.ComponentProps<"div">) {
|
|
46
|
+
return useRender({
|
|
47
|
+
defaultTagName: "div",
|
|
48
|
+
props: mergeProps<"div">(
|
|
49
|
+
{
|
|
50
|
+
className: cn(
|
|
51
|
+
"flex items-center gap-2 rounded-md border bg-muted px-2.5 text-xs/relaxed font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
52
|
+
className,
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
props,
|
|
56
|
+
),
|
|
57
|
+
render,
|
|
58
|
+
state: {
|
|
59
|
+
slot: "button-group-text",
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ButtonGroupSeparator({
|
|
65
|
+
className,
|
|
66
|
+
orientation = "vertical",
|
|
67
|
+
...props
|
|
68
|
+
}: React.ComponentProps<typeof Separator>) {
|
|
69
|
+
return (
|
|
70
|
+
<Separator
|
|
71
|
+
data-slot="button-group-separator"
|
|
72
|
+
orientation={orientation}
|
|
73
|
+
className={cn(
|
|
74
|
+
"relative self-stretch bg-input data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
ButtonGroup,
|
|
84
|
+
ButtonGroupSeparator,
|
|
85
|
+
ButtonGroupText,
|
|
86
|
+
buttonGroupVariants,
|
|
87
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mergeProps, useRender } from "@base-ui/react";
|
|
2
|
+
import { SmoothCorners } from "@lisse/react";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
export const buttonVariants = cva(
|
|
7
|
+
"group/button inline-flex shrink-0 items-center justify-center border border-transparent font-medium whitespace-nowrap transition duration-[160ms] ease-out outline-none select-none focus-visible:ring-2 focus-visible:ring-white/20 active:scale-[0.97] disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default:
|
|
12
|
+
"bg-primary text-primary-foreground border-transparent hover:bg-primary/90",
|
|
13
|
+
outline:
|
|
14
|
+
"border-border text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
secondary:
|
|
16
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
17
|
+
ghost: "text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
destructive:
|
|
19
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
20
|
+
link: "text-foreground underline-offset-4 hover:underline",
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default: "h-9 px-[calc(--spacing(3)-1px)] sm:h-8",
|
|
24
|
+
icon: "size-9 sm:size-8",
|
|
25
|
+
"icon-lg": "size-10 sm:size-9",
|
|
26
|
+
"icon-sm": "size-8 sm:size-7",
|
|
27
|
+
"icon-xl":
|
|
28
|
+
"size-11 sm:size-10 [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
|
|
29
|
+
"icon-xs":
|
|
30
|
+
"size-7 rounded-md before:rounded-[calc(var(--radius-md)-1px)] sm:size-6 not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-4 sm:not-in-data-[slot=input-group]:[&_svg:not([class*='size-'])]:size-3.5",
|
|
31
|
+
lg: "h-10 px-[calc(--spacing(3.5)-1px)] sm:h-9",
|
|
32
|
+
sm: "h-8 gap-1.5 px-[calc(--spacing(2.5)-1px)] sm:h-7",
|
|
33
|
+
xl: "h-11 px-[calc(--spacing(4)-1px)] text-lg sm:h-10 sm:text-base [&_svg:not([class*='size-'])]:size-5 sm:[&_svg:not([class*='size-'])]:size-4.5",
|
|
34
|
+
xs: "h-7 gap-1 rounded-md px-[calc(--spacing(2)-1px)] text-sm before:rounded-[calc(var(--radius-md)-1px)] sm:h-6 sm:text-xs [&_svg:not([class*='size-'])]:size-4 sm:[&_svg:not([class*='size-'])]:size-3.5",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultVariants: {
|
|
38
|
+
variant: "default",
|
|
39
|
+
size: "default",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export interface ButtonProps extends useRender.ComponentProps<"button"> {
|
|
45
|
+
variant?: VariantProps<typeof buttonVariants>["variant"];
|
|
46
|
+
size?: VariantProps<typeof buttonVariants>["size"];
|
|
47
|
+
loading?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export function Button({
|
|
50
|
+
className,
|
|
51
|
+
variant,
|
|
52
|
+
size = "default",
|
|
53
|
+
render,
|
|
54
|
+
children,
|
|
55
|
+
loading = false,
|
|
56
|
+
disabled: disabledProp,
|
|
57
|
+
...props
|
|
58
|
+
}: ButtonProps): React.ReactElement {
|
|
59
|
+
const isDisabled: boolean = Boolean(loading || disabledProp);
|
|
60
|
+
const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>["type"] =
|
|
61
|
+
render ? undefined : "button";
|
|
62
|
+
const defaultProps = {
|
|
63
|
+
children: (
|
|
64
|
+
<>
|
|
65
|
+
{children}
|
|
66
|
+
{loading && (
|
|
67
|
+
<div
|
|
68
|
+
className="pointer-events-none absolute"
|
|
69
|
+
data-slot="button-loading-indicator"
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
</>
|
|
73
|
+
),
|
|
74
|
+
className: cn(buttonVariants({ className, size, variant })),
|
|
75
|
+
"aria-disabled": loading || undefined,
|
|
76
|
+
"data-loading": loading ? "" : undefined,
|
|
77
|
+
"data-slot": "button",
|
|
78
|
+
disabled: isDisabled,
|
|
79
|
+
type: typeValue,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Determine standard squircle radius depending on size
|
|
83
|
+
let radius = 8;
|
|
84
|
+
if (size === "default") radius = 8;
|
|
85
|
+
else if (size === "sm") radius = 7;
|
|
86
|
+
else if (size === "xs") radius = 5;
|
|
87
|
+
else if (size === "lg") radius = 10;
|
|
88
|
+
else if (size === "xl") radius = 12;
|
|
89
|
+
else if (size === "icon") radius = 8;
|
|
90
|
+
else if (size === "icon-lg") radius = 10;
|
|
91
|
+
else if (size === "icon-sm") radius = 7;
|
|
92
|
+
else if (size === "icon-xl") radius = 12;
|
|
93
|
+
else if (size === "icon-xs") radius = 5;
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<SmoothCorners asChild corners={{ radius, smoothing: 1 }}>
|
|
97
|
+
{useRender({
|
|
98
|
+
defaultTagName: "button",
|
|
99
|
+
props: mergeProps<"button">(defaultProps, props),
|
|
100
|
+
render,
|
|
101
|
+
})}
|
|
102
|
+
</SmoothCorners>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { SmoothCorners } from "@lisse/react";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
function Card({
|
|
7
|
+
className,
|
|
8
|
+
size = "default",
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
11
|
+
return (
|
|
12
|
+
<SmoothCorners asChild corners={{ radius: 24, smoothing: 1 }}>
|
|
13
|
+
<div
|
|
14
|
+
data-slot="card"
|
|
15
|
+
data-size={size}
|
|
16
|
+
className={cn(
|
|
17
|
+
"group/card flex flex-col overflow-hidden bg-card hover:bg-accent transition-colors text-card-foreground",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
</SmoothCorners>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
data-slot="card-header"
|
|
30
|
+
className={cn("flex flex-col gap-1.5 mb-4", className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="card-title"
|
|
40
|
+
className={cn("font-medium leading-none tracking-tight", className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
data-slot="card-description"
|
|
50
|
+
className={cn("text-muted-foreground", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
data-slot="card-action"
|
|
60
|
+
className={cn(
|
|
61
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
data-slot="card-content"
|
|
73
|
+
className={cn("text-muted-foreground", className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
data-slot="card-footer"
|
|
83
|
+
className={cn("flex items-center pt-4", className)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
Card,
|
|
91
|
+
CardHeader,
|
|
92
|
+
CardFooter,
|
|
93
|
+
CardTitle,
|
|
94
|
+
CardAction,
|
|
95
|
+
CardDescription,
|
|
96
|
+
CardContent,
|
|
97
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
|
|
8
|
+
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
|
|
12
|
+
return (
|
|
13
|
+
<PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function HoverCardContent({
|
|
18
|
+
className,
|
|
19
|
+
side = "bottom",
|
|
20
|
+
sideOffset = 4,
|
|
21
|
+
align = "center",
|
|
22
|
+
alignOffset = 4,
|
|
23
|
+
...props
|
|
24
|
+
}: PreviewCardPrimitive.Popup.Props &
|
|
25
|
+
Pick<
|
|
26
|
+
PreviewCardPrimitive.Positioner.Props,
|
|
27
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
28
|
+
>) {
|
|
29
|
+
return (
|
|
30
|
+
<PreviewCardPrimitive.Portal data-slot="hover-card-portal">
|
|
31
|
+
<PreviewCardPrimitive.Positioner
|
|
32
|
+
align={align}
|
|
33
|
+
alignOffset={alignOffset}
|
|
34
|
+
side={side}
|
|
35
|
+
sideOffset={sideOffset}
|
|
36
|
+
className="isolate z-50"
|
|
37
|
+
>
|
|
38
|
+
<PreviewCardPrimitive.Popup
|
|
39
|
+
data-slot="hover-card-content"
|
|
40
|
+
className={cn(
|
|
41
|
+
"z-50 w-64 origin-(--transform-origin) rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-200 ease-out 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",
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</PreviewCardPrimitive.Positioner>
|
|
47
|
+
</PreviewCardPrimitive.Portal>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { HoverCard, HoverCardTrigger, HoverCardContent };
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import { Separator } from "@/components/ui/separator";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
9
|
+
return (
|
|
10
|
+
// biome-ignore lint/a11y/useSemanticElements: ARIA list pattern
|
|
11
|
+
<div
|
|
12
|
+
role="list"
|
|
13
|
+
data-slot="item-group"
|
|
14
|
+
className={cn(
|
|
15
|
+
"group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ItemSeparator({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<typeof Separator>) {
|
|
27
|
+
return (
|
|
28
|
+
<Separator
|
|
29
|
+
data-slot="item-separator"
|
|
30
|
+
orientation="horizontal"
|
|
31
|
+
className={cn("my-2", className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const itemVariants = cva(
|
|
38
|
+
"group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition duration-[160ms] ease-out outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted active:scale-[0.97]",
|
|
39
|
+
{
|
|
40
|
+
variants: {
|
|
41
|
+
variant: {
|
|
42
|
+
default: "border-transparent",
|
|
43
|
+
outline: "border-border",
|
|
44
|
+
muted: "border-transparent bg-muted/50",
|
|
45
|
+
},
|
|
46
|
+
size: {
|
|
47
|
+
default: "gap-2.5 px-3 py-2.5",
|
|
48
|
+
sm: "gap-2.5 px-3 py-2.5",
|
|
49
|
+
xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
variant: "default",
|
|
54
|
+
size: "default",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
function Item({
|
|
60
|
+
className,
|
|
61
|
+
variant = "default",
|
|
62
|
+
size = "default",
|
|
63
|
+
render,
|
|
64
|
+
...props
|
|
65
|
+
}: useRender.ComponentProps<"div"> & VariantProps<typeof itemVariants>) {
|
|
66
|
+
return useRender({
|
|
67
|
+
defaultTagName: "div",
|
|
68
|
+
props: mergeProps<"div">(
|
|
69
|
+
{
|
|
70
|
+
className: cn(itemVariants({ variant, size, className })),
|
|
71
|
+
},
|
|
72
|
+
props,
|
|
73
|
+
),
|
|
74
|
+
render,
|
|
75
|
+
state: {
|
|
76
|
+
slot: "item",
|
|
77
|
+
variant,
|
|
78
|
+
size,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const itemMediaVariants = cva(
|
|
84
|
+
"flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
|
|
85
|
+
{
|
|
86
|
+
variants: {
|
|
87
|
+
variant: {
|
|
88
|
+
default: "bg-transparent",
|
|
89
|
+
icon: "[&_svg:not([class*='size-'])]:size-4",
|
|
90
|
+
image:
|
|
91
|
+
"size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
defaultVariants: {
|
|
95
|
+
variant: "default",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
function ItemMedia({
|
|
101
|
+
className,
|
|
102
|
+
variant = "default",
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
|
|
105
|
+
return (
|
|
106
|
+
<div
|
|
107
|
+
data-slot="item-media"
|
|
108
|
+
data-variant={variant}
|
|
109
|
+
className={cn(itemMediaVariants({ variant, className }))}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
116
|
+
return (
|
|
117
|
+
<div
|
|
118
|
+
data-slot="item-content"
|
|
119
|
+
className={cn(
|
|
120
|
+
"flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
|
|
121
|
+
className,
|
|
122
|
+
)}
|
|
123
|
+
{...props}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
129
|
+
return (
|
|
130
|
+
<div
|
|
131
|
+
data-slot="item-title"
|
|
132
|
+
className={cn(
|
|
133
|
+
"line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4",
|
|
134
|
+
className,
|
|
135
|
+
)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
142
|
+
return (
|
|
143
|
+
<p
|
|
144
|
+
data-slot="item-description"
|
|
145
|
+
className={cn(
|
|
146
|
+
"line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
147
|
+
className,
|
|
148
|
+
)}
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
|
|
155
|
+
return (
|
|
156
|
+
<div
|
|
157
|
+
data-slot="item-actions"
|
|
158
|
+
className={cn("flex items-center gap-2", className)}
|
|
159
|
+
{...props}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
165
|
+
return (
|
|
166
|
+
<div
|
|
167
|
+
data-slot="item-header"
|
|
168
|
+
className={cn(
|
|
169
|
+
"flex basis-full items-center justify-between gap-2",
|
|
170
|
+
className,
|
|
171
|
+
)}
|
|
172
|
+
{...props}
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
178
|
+
return (
|
|
179
|
+
<div
|
|
180
|
+
data-slot="item-footer"
|
|
181
|
+
className={cn(
|
|
182
|
+
"flex basis-full items-center justify-between gap-2",
|
|
183
|
+
className,
|
|
184
|
+
)}
|
|
185
|
+
{...props}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export {
|
|
191
|
+
Item,
|
|
192
|
+
ItemMedia,
|
|
193
|
+
ItemContent,
|
|
194
|
+
ItemActions,
|
|
195
|
+
ItemGroup,
|
|
196
|
+
ItemSeparator,
|
|
197
|
+
ItemTitle,
|
|
198
|
+
ItemDescription,
|
|
199
|
+
ItemHeader,
|
|
200
|
+
ItemFooter,
|
|
201
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
function Separator({
|
|
8
|
+
className,
|
|
9
|
+
orientation = "horizontal",
|
|
10
|
+
...props
|
|
11
|
+
}: SeparatorPrimitive.Props) {
|
|
12
|
+
return (
|
|
13
|
+
<SeparatorPrimitive
|
|
14
|
+
data-slot="separator"
|
|
15
|
+
orientation={orientation}
|
|
16
|
+
className={cn(
|
|
17
|
+
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { Separator };
|