@playfast/reform-better-hub-ui-primitives 0.0.2 → 0.0.4
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/package.json +2 -2
- package/src/components/agent-icon.tsx +21 -0
- package/src/components/badge.tsx +54 -0
- package/src/components/button.tsx +72 -0
- package/src/components/combobox.tsx +290 -0
- package/src/components/command.tsx +170 -0
- package/src/components/dialog.tsx +129 -0
- package/src/components/dropdown-menu.tsx +149 -0
- package/src/components/github-background.tsx +280 -0
- package/src/components/halftone-background.tsx +289 -0
- package/src/components/input-group.tsx +175 -0
- package/src/components/input.tsx +25 -0
- package/src/components/live-duration.tsx +70 -0
- package/src/components/logo.tsx +20 -0
- package/src/components/popover.tsx +82 -0
- package/src/components/resize-handle.tsx +86 -0
- package/src/components/sheet.tsx +147 -0
- package/src/components/textarea.tsx +21 -0
- package/src/components/time-ago.tsx +57 -0
- package/src/components/tooltip.tsx +64 -0
- package/src/foundation/cn.ts +8 -0
- package/src/foundation/styles.css +156 -0
- package/{dist/index.js → src/index.tsx} +2 -1
- package/dist/components/agent-icon.d.ts +0 -5
- package/dist/components/agent-icon.d.ts.map +0 -1
- package/dist/components/agent-icon.js +0 -5
- package/dist/components/agent-icon.js.map +0 -1
- package/dist/components/badge.d.ts +0 -11
- package/dist/components/badge.d.ts.map +0 -1
- package/dist/components/badge.js +0 -28
- package/dist/components/badge.js.map +0 -1
- package/dist/components/button.d.ts +0 -12
- package/dist/components/button.d.ts.map +0 -1
- package/dist/components/button.js +0 -34
- package/dist/components/button.js.map +0 -1
- package/dist/components/combobox.d.ts +0 -25
- package/dist/components/combobox.d.ts.map +0 -1
- package/dist/components/combobox.js +0 -60
- package/dist/components/combobox.js.map +0 -1
- package/dist/components/command.d.ts +0 -19
- package/dist/components/command.d.ts.map +0 -1
- package/dist/components/command.js +0 -36
- package/dist/components/command.js.map +0 -1
- package/dist/components/dialog.d.ts +0 -16
- package/dist/components/dialog.d.ts.map +0 -1
- package/dist/components/dialog.js +0 -38
- package/dist/components/dialog.js.map +0 -1
- package/dist/components/dropdown-menu.d.ts +0 -17
- package/dist/components/dropdown-menu.d.ts.map +0 -1
- package/dist/components/dropdown-menu.js +0 -35
- package/dist/components/dropdown-menu.js.map +0 -1
- package/dist/components/github-background.d.ts +0 -3
- package/dist/components/github-background.d.ts.map +0 -1
- package/dist/components/github-background.js +0 -51
- package/dist/components/github-background.js.map +0 -1
- package/dist/components/halftone-background.d.ts +0 -3
- package/dist/components/halftone-background.d.ts.map +0 -1
- package/dist/components/halftone-background.js +0 -248
- package/dist/components/halftone-background.js.map +0 -1
- package/dist/components/input-group.d.ts +0 -19
- package/dist/components/input-group.d.ts.map +0 -1
- package/dist/components/input-group.js +0 -63
- package/dist/components/input-group.js.map +0 -1
- package/dist/components/input.d.ts +0 -5
- package/dist/components/input.d.ts.map +0 -1
- package/dist/components/input.js +0 -7
- package/dist/components/input.js.map +0 -1
- package/dist/components/live-duration.d.ts +0 -9
- package/dist/components/live-duration.d.ts.map +0 -1
- package/dist/components/live-duration.js +0 -55
- package/dist/components/live-duration.js.map +0 -1
- package/dist/components/logo.d.ts +0 -8
- package/dist/components/logo.d.ts.map +0 -1
- package/dist/components/logo.js +0 -9
- package/dist/components/logo.js.map +0 -1
- package/dist/components/popover.d.ts +0 -12
- package/dist/components/popover.d.ts.map +0 -1
- package/dist/components/popover.js +0 -26
- package/dist/components/popover.js.map +0 -1
- package/dist/components/resize-handle.d.ts +0 -15
- package/dist/components/resize-handle.d.ts.map +0 -1
- package/dist/components/resize-handle.js +0 -36
- package/dist/components/resize-handle.js.map +0 -1
- package/dist/components/sheet.d.ts +0 -18
- package/dist/components/sheet.d.ts.map +0 -1
- package/dist/components/sheet.js +0 -44
- package/dist/components/sheet.js.map +0 -1
- package/dist/components/textarea.d.ts +0 -5
- package/dist/components/textarea.d.ts.map +0 -1
- package/dist/components/textarea.js +0 -7
- package/dist/components/textarea.js.map +0 -1
- package/dist/components/time-ago.d.ts +0 -8
- package/dist/components/time-ago.d.ts.map +0 -1
- package/dist/components/time-ago.js +0 -40
- package/dist/components/time-ago.js.map +0 -1
- package/dist/components/tooltip.d.ts +0 -10
- package/dist/components/tooltip.d.ts.map +0 -1
- package/dist/components/tooltip.js +0 -25
- package/dist/components/tooltip.js.map +0 -1
- package/dist/foundation/cn.d.ts +0 -7
- package/dist/foundation/cn.d.ts.map +0 -1
- package/dist/foundation/cn.js +0 -8
- package/dist/foundation/cn.js.map +0 -1
- package/dist/index.d.ts +0 -21
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/dialog.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
|
+
import { XIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
import { cn } from "../foundation/cn";
|
|
8
|
+
|
|
9
|
+
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>): React.JSX.Element {
|
|
10
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>): React.JSX.Element {
|
|
14
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>): React.JSX.Element {
|
|
18
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>): React.JSX.Element {
|
|
22
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function DialogOverlay({
|
|
26
|
+
className,
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>): React.JSX.Element {
|
|
29
|
+
return (
|
|
30
|
+
<DialogPrimitive.Overlay
|
|
31
|
+
data-slot="dialog-overlay"
|
|
32
|
+
className={cn(
|
|
33
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/25 dark:bg-black/70 backdrop-blur-[2px]",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function DialogContent({
|
|
42
|
+
className,
|
|
43
|
+
children,
|
|
44
|
+
showCloseButton = true,
|
|
45
|
+
...props
|
|
46
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
47
|
+
showCloseButton?: boolean;
|
|
48
|
+
}): React.JSX.Element {
|
|
49
|
+
return (
|
|
50
|
+
<DialogPortal>
|
|
51
|
+
<DialogOverlay />
|
|
52
|
+
<DialogPrimitive.Content
|
|
53
|
+
data-slot="dialog-content"
|
|
54
|
+
className={cn(
|
|
55
|
+
"overflow-y-auto bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] max-h-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
{showCloseButton && (
|
|
62
|
+
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
|
63
|
+
<XIcon />
|
|
64
|
+
<span className="sr-only">Close</span>
|
|
65
|
+
</DialogPrimitive.Close>
|
|
66
|
+
)}
|
|
67
|
+
</DialogPrimitive.Content>
|
|
68
|
+
</DialogPortal>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
data-slot="dialog-header"
|
|
76
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
data-slot="dialog-footer"
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
88
|
+
className,
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>): React.JSX.Element {
|
|
96
|
+
return (
|
|
97
|
+
<DialogPrimitive.Title
|
|
98
|
+
data-slot="dialog-title"
|
|
99
|
+
className={cn("text-lg leading-none font-semibold", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function DialogDescription({
|
|
106
|
+
className,
|
|
107
|
+
...props
|
|
108
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>): React.JSX.Element {
|
|
109
|
+
return (
|
|
110
|
+
<DialogPrimitive.Description
|
|
111
|
+
data-slot="dialog-description"
|
|
112
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
Dialog,
|
|
120
|
+
DialogClose,
|
|
121
|
+
DialogContent,
|
|
122
|
+
DialogDescription,
|
|
123
|
+
DialogFooter,
|
|
124
|
+
DialogHeader,
|
|
125
|
+
DialogOverlay,
|
|
126
|
+
DialogPortal,
|
|
127
|
+
DialogTitle,
|
|
128
|
+
DialogTrigger,
|
|
129
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/dropdown-menu.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
5
|
+
import { CheckIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
import { cn } from "../foundation/cn";
|
|
8
|
+
|
|
9
|
+
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>): React.JSX.Element {
|
|
10
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuTrigger({
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>): React.JSX.Element {
|
|
16
|
+
return (
|
|
17
|
+
<DropdownMenuPrimitive.Trigger
|
|
18
|
+
data-slot="dropdown-menu-trigger"
|
|
19
|
+
{...props}
|
|
20
|
+
className={cn("rounded-sm", props.className)}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function DropdownMenuContent({
|
|
26
|
+
className,
|
|
27
|
+
sideOffset = 4,
|
|
28
|
+
...props
|
|
29
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>): React.JSX.Element {
|
|
30
|
+
return (
|
|
31
|
+
<DropdownMenuPrimitive.Portal>
|
|
32
|
+
<DropdownMenuPrimitive.Content
|
|
33
|
+
data-slot="dropdown-menu-content"
|
|
34
|
+
sideOffset={sideOffset}
|
|
35
|
+
className={cn(
|
|
36
|
+
"bg-background text-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
</DropdownMenuPrimitive.Portal>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>): React.JSX.Element {
|
|
46
|
+
return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function DropdownMenuItem({
|
|
50
|
+
className,
|
|
51
|
+
inset,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
54
|
+
inset?: boolean;
|
|
55
|
+
}): React.JSX.Element {
|
|
56
|
+
return (
|
|
57
|
+
<DropdownMenuPrimitive.Item
|
|
58
|
+
data-slot="dropdown-menu-item"
|
|
59
|
+
data-inset={inset}
|
|
60
|
+
className={cn(
|
|
61
|
+
"focus:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function DropdownMenuCheckboxItem({
|
|
70
|
+
className,
|
|
71
|
+
children,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): React.JSX.Element {
|
|
74
|
+
return (
|
|
75
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
76
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
77
|
+
className={cn(
|
|
78
|
+
"focus:bg-accent relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
79
|
+
className,
|
|
80
|
+
)}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
84
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
85
|
+
<CheckIcon className="size-4" />
|
|
86
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
87
|
+
</span>
|
|
88
|
+
{children}
|
|
89
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DropdownMenuLabel({
|
|
94
|
+
className,
|
|
95
|
+
inset,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
98
|
+
inset?: boolean;
|
|
99
|
+
}): React.JSX.Element {
|
|
100
|
+
return (
|
|
101
|
+
<DropdownMenuPrimitive.Label
|
|
102
|
+
data-slot="dropdown-menu-label"
|
|
103
|
+
data-inset={inset}
|
|
104
|
+
className={cn(
|
|
105
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
106
|
+
className,
|
|
107
|
+
)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function DropdownMenuSeparator({
|
|
114
|
+
className,
|
|
115
|
+
...props
|
|
116
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>): React.JSX.Element {
|
|
117
|
+
return (
|
|
118
|
+
<DropdownMenuPrimitive.Separator
|
|
119
|
+
data-slot="dropdown-menu-separator"
|
|
120
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element {
|
|
127
|
+
return (
|
|
128
|
+
<span
|
|
129
|
+
data-slot="dropdown-menu-shortcut"
|
|
130
|
+
className={cn(
|
|
131
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
132
|
+
className,
|
|
133
|
+
)}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
DropdownMenu,
|
|
141
|
+
DropdownMenuTrigger,
|
|
142
|
+
DropdownMenuContent,
|
|
143
|
+
DropdownMenuGroup,
|
|
144
|
+
DropdownMenuLabel,
|
|
145
|
+
DropdownMenuItem,
|
|
146
|
+
DropdownMenuCheckboxItem,
|
|
147
|
+
DropdownMenuSeparator,
|
|
148
|
+
DropdownMenuShortcut,
|
|
149
|
+
};
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// Adapted from better-hub by Better Auth — https://github.com/better-auth/better-hub
|
|
2
|
+
// (apps/web/src/components/ui/github-background.tsx). Full credit to the Better Auth team.
|
|
3
|
+
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
|
|
7
|
+
function createRng(seed: number) {
|
|
8
|
+
let s = seed;
|
|
9
|
+
return () => {
|
|
10
|
+
s = (s * 16807) % 2147483647;
|
|
11
|
+
return (s - 1) / 2147483646;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const LEVELS = [
|
|
16
|
+
"rgba(255,255,255,0.02)",
|
|
17
|
+
"rgba(255,255,255,0.10)",
|
|
18
|
+
"rgba(255,255,255,0.18)",
|
|
19
|
+
"rgba(255,255,255,0.28)",
|
|
20
|
+
"rgba(255,255,255,0.40)",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const CELL = 15;
|
|
24
|
+
|
|
25
|
+
export function GitHubBackground(): React.JSX.Element {
|
|
26
|
+
const grid = useMemo(() => {
|
|
27
|
+
const rng = createRng(42);
|
|
28
|
+
const cells: { w: number; d: number; level: number }[] = [];
|
|
29
|
+
for (let w = 0; w < 52; w++) {
|
|
30
|
+
for (let d = 0; d < 7; d++) {
|
|
31
|
+
const r = rng();
|
|
32
|
+
const level =
|
|
33
|
+
r < 0.3 ? 0 : r < 0.5 ? 1 : r < 0.7 ? 2 : r < 0.85 ? 3 : 4;
|
|
34
|
+
cells.push({ w, d, level });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return cells;
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
className="absolute inset-0 pointer-events-none overflow-hidden"
|
|
43
|
+
style={{ zIndex: 2 }}
|
|
44
|
+
>
|
|
45
|
+
{/* Radial mask — fades edges into the shader */}
|
|
46
|
+
<div
|
|
47
|
+
className="absolute inset-0"
|
|
48
|
+
style={{
|
|
49
|
+
maskImage: "radial-gradient(ellipse 85% 75% at 45% 35%, black 15%, transparent 60%)",
|
|
50
|
+
WebkitMaskImage:
|
|
51
|
+
"radial-gradient(ellipse 85% 75% at 45% 35%, black 15%, transparent 60%)",
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{/* ── Contribution Graph ── */}
|
|
55
|
+
<div
|
|
56
|
+
className="absolute"
|
|
57
|
+
style={{
|
|
58
|
+
top: "8%",
|
|
59
|
+
left: "50%",
|
|
60
|
+
transform: "translate(-50%, 0) perspective(500px) rotateX(30deg) rotateZ(-2deg) scale(1.3)",
|
|
61
|
+
transformOrigin: "center top",
|
|
62
|
+
opacity: 0.4,
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<svg
|
|
66
|
+
width={52 * CELL}
|
|
67
|
+
height={7 * CELL}
|
|
68
|
+
viewBox={`0 0 ${52 * CELL} ${7 * CELL}`}
|
|
69
|
+
>
|
|
70
|
+
{grid.map(({ w, d, level }, i) => (
|
|
71
|
+
<rect
|
|
72
|
+
key={i}
|
|
73
|
+
x={w * CELL}
|
|
74
|
+
y={d * CELL}
|
|
75
|
+
width={12}
|
|
76
|
+
height={12}
|
|
77
|
+
rx={2}
|
|
78
|
+
fill={LEVELS[level]}
|
|
79
|
+
opacity={0}
|
|
80
|
+
>
|
|
81
|
+
<animate
|
|
82
|
+
attributeName="opacity"
|
|
83
|
+
from="0"
|
|
84
|
+
to="1"
|
|
85
|
+
dur="0.5s"
|
|
86
|
+
begin={`${w * 0.04 + d * 0.015}s`}
|
|
87
|
+
fill="freeze"
|
|
88
|
+
/>
|
|
89
|
+
</rect>
|
|
90
|
+
))}
|
|
91
|
+
</svg>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
{/* ── Git Network Graph ── */}
|
|
95
|
+
<svg
|
|
96
|
+
className="absolute inset-0 w-full h-full"
|
|
97
|
+
viewBox="0 0 400 900"
|
|
98
|
+
preserveAspectRatio="xMidYMid slice"
|
|
99
|
+
style={{ opacity: 0.45 }}
|
|
100
|
+
>
|
|
101
|
+
{/* Main branch */}
|
|
102
|
+
<line
|
|
103
|
+
x1="200"
|
|
104
|
+
y1="0"
|
|
105
|
+
x2="200"
|
|
106
|
+
y2="900"
|
|
107
|
+
stroke="rgba(255,255,255,0.15)"
|
|
108
|
+
strokeWidth="2"
|
|
109
|
+
pathLength={1}
|
|
110
|
+
className="git-line-anim"
|
|
111
|
+
style={{ animationDelay: "0.3s" }}
|
|
112
|
+
/>
|
|
113
|
+
|
|
114
|
+
{/* Feature branch 1 — forks right */}
|
|
115
|
+
<path
|
|
116
|
+
d="M 200 150 C 200 180, 280 200, 280 230 L 280 350 C 280 380, 200 395, 200 400"
|
|
117
|
+
stroke="rgba(255,255,255,0.12)"
|
|
118
|
+
strokeWidth="2"
|
|
119
|
+
fill="none"
|
|
120
|
+
pathLength={1}
|
|
121
|
+
className="git-line-anim"
|
|
122
|
+
style={{ animationDelay: "0.8s" }}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
{/* Feature branch 2 — forks left */}
|
|
126
|
+
<path
|
|
127
|
+
d="M 200 300 C 200 325, 130 340, 130 360 L 130 500 C 130 525, 200 540, 200 550"
|
|
128
|
+
stroke="rgba(255,255,255,0.10)"
|
|
129
|
+
strokeWidth="2"
|
|
130
|
+
fill="none"
|
|
131
|
+
pathLength={1}
|
|
132
|
+
className="git-line-anim"
|
|
133
|
+
style={{ animationDelay: "1.3s" }}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
{/* Hotfix branch — short fork right */}
|
|
137
|
+
<path
|
|
138
|
+
d="M 200 550 C 200 570, 260 580, 260 600 L 260 650 C 260 670, 200 680, 200 690"
|
|
139
|
+
stroke="rgba(255,255,255,0.08)"
|
|
140
|
+
strokeWidth="2"
|
|
141
|
+
fill="none"
|
|
142
|
+
pathLength={1}
|
|
143
|
+
className="git-line-anim"
|
|
144
|
+
style={{ animationDelay: "1.8s" }}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
{/* Feature branch 3 — wide fork left */}
|
|
148
|
+
<path
|
|
149
|
+
d="M 200 620 C 200 640, 110 660, 110 680 L 110 800 C 110 820, 200 835, 200 850"
|
|
150
|
+
stroke="rgba(255,255,255,0.07)"
|
|
151
|
+
strokeWidth="2"
|
|
152
|
+
fill="none"
|
|
153
|
+
pathLength={1}
|
|
154
|
+
className="git-line-anim"
|
|
155
|
+
style={{ animationDelay: "2.2s" }}
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
{/* Commit nodes — main branch */}
|
|
159
|
+
{[80, 150, 230, 300, 400, 480, 550, 620, 690, 770, 850].map(
|
|
160
|
+
(y, i) => (
|
|
161
|
+
<circle
|
|
162
|
+
key={`m${i}`}
|
|
163
|
+
cx={200}
|
|
164
|
+
cy={y}
|
|
165
|
+
r={4}
|
|
166
|
+
fill="rgba(255,255,255,0.25)"
|
|
167
|
+
opacity={0}
|
|
168
|
+
>
|
|
169
|
+
<animate
|
|
170
|
+
attributeName="opacity"
|
|
171
|
+
from="0"
|
|
172
|
+
to="1"
|
|
173
|
+
dur="0.3s"
|
|
174
|
+
begin={`${0.5 + i * 0.15}s`}
|
|
175
|
+
fill="freeze"
|
|
176
|
+
/>
|
|
177
|
+
</circle>
|
|
178
|
+
),
|
|
179
|
+
)}
|
|
180
|
+
|
|
181
|
+
{/* Commit nodes — feature 1 */}
|
|
182
|
+
{[230, 280, 330].map((y, i) => (
|
|
183
|
+
<circle
|
|
184
|
+
key={`f1${i}`}
|
|
185
|
+
cx={280}
|
|
186
|
+
cy={y}
|
|
187
|
+
r={3.5}
|
|
188
|
+
fill="rgba(255,255,255,0.20)"
|
|
189
|
+
opacity={0}
|
|
190
|
+
>
|
|
191
|
+
<animate
|
|
192
|
+
attributeName="opacity"
|
|
193
|
+
from="0"
|
|
194
|
+
to="1"
|
|
195
|
+
dur="0.3s"
|
|
196
|
+
begin={`${1.0 + i * 0.2}s`}
|
|
197
|
+
fill="freeze"
|
|
198
|
+
/>
|
|
199
|
+
</circle>
|
|
200
|
+
))}
|
|
201
|
+
|
|
202
|
+
{/* Commit nodes — feature 2 */}
|
|
203
|
+
{[360, 420, 470].map((y, i) => (
|
|
204
|
+
<circle
|
|
205
|
+
key={`f2${i}`}
|
|
206
|
+
cx={130}
|
|
207
|
+
cy={y}
|
|
208
|
+
r={3.5}
|
|
209
|
+
fill="rgba(255,255,255,0.18)"
|
|
210
|
+
opacity={0}
|
|
211
|
+
>
|
|
212
|
+
<animate
|
|
213
|
+
attributeName="opacity"
|
|
214
|
+
from="0"
|
|
215
|
+
to="1"
|
|
216
|
+
dur="0.3s"
|
|
217
|
+
begin={`${1.5 + i * 0.2}s`}
|
|
218
|
+
fill="freeze"
|
|
219
|
+
/>
|
|
220
|
+
</circle>
|
|
221
|
+
))}
|
|
222
|
+
|
|
223
|
+
{/* Commit nodes — hotfix */}
|
|
224
|
+
{[600, 630, 660].map((y, i) => (
|
|
225
|
+
<circle
|
|
226
|
+
key={`hf${i}`}
|
|
227
|
+
cx={260}
|
|
228
|
+
cy={y}
|
|
229
|
+
r={3}
|
|
230
|
+
fill="rgba(255,255,255,0.15)"
|
|
231
|
+
opacity={0}
|
|
232
|
+
>
|
|
233
|
+
<animate
|
|
234
|
+
attributeName="opacity"
|
|
235
|
+
from="0"
|
|
236
|
+
to="1"
|
|
237
|
+
dur="0.3s"
|
|
238
|
+
begin={`${2.0 + i * 0.2}s`}
|
|
239
|
+
fill="freeze"
|
|
240
|
+
/>
|
|
241
|
+
</circle>
|
|
242
|
+
))}
|
|
243
|
+
|
|
244
|
+
{/* Commit nodes — feature 3 */}
|
|
245
|
+
{[680, 730, 780].map((y, i) => (
|
|
246
|
+
<circle
|
|
247
|
+
key={`f3${i}`}
|
|
248
|
+
cx={110}
|
|
249
|
+
cy={y}
|
|
250
|
+
r={3}
|
|
251
|
+
fill="rgba(255,255,255,0.12)"
|
|
252
|
+
opacity={0}
|
|
253
|
+
>
|
|
254
|
+
<animate
|
|
255
|
+
attributeName="opacity"
|
|
256
|
+
from="0"
|
|
257
|
+
to="1"
|
|
258
|
+
dur="0.3s"
|
|
259
|
+
begin={`${2.4 + i * 0.2}s`}
|
|
260
|
+
fill="freeze"
|
|
261
|
+
/>
|
|
262
|
+
</circle>
|
|
263
|
+
))}
|
|
264
|
+
</svg>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{/* CSS for stroke-dash draw animation */}
|
|
268
|
+
<style>{`
|
|
269
|
+
.git-line-anim {
|
|
270
|
+
stroke-dasharray: 1;
|
|
271
|
+
stroke-dashoffset: 1;
|
|
272
|
+
animation: gitDraw 2.5s ease-out forwards;
|
|
273
|
+
}
|
|
274
|
+
@keyframes gitDraw {
|
|
275
|
+
to { stroke-dashoffset: 0; }
|
|
276
|
+
}
|
|
277
|
+
`}</style>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|