@c15t/dev-tools 0.0.1-rc.3
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/.turbo/turbo-build.log +56 -0
- package/.turbo/turbo-fmt.log +6 -0
- package/.turbo/turbo-lint.log +73 -0
- package/CHANGELOG.md +23 -0
- package/LICENSE.md +595 -0
- package/README.md +47 -0
- package/dist/components/error-state.cjs +126 -0
- package/dist/components/error-state.d.ts +6 -0
- package/dist/components/error-state.d.ts.map +1 -0
- package/dist/components/error-state.js +110 -0
- package/dist/components/header.cjs +88 -0
- package/dist/components/header.d.ts +8 -0
- package/dist/components/header.d.ts.map +1 -0
- package/dist/components/header.js +56 -0
- package/dist/components/ui/accordion.cjs +119 -0
- package/dist/components/ui/accordion.d.ts +34 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/accordion.js +84 -0
- package/dist/components/ui/alert.cjs +104 -0
- package/dist/components/ui/alert.d.ts +25 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.js +67 -0
- package/dist/components/ui/button.cjs +95 -0
- package/dist/components/ui/button.d.ts +28 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +55 -0
- package/dist/components/ui/card.cjs +120 -0
- package/dist/components/ui/card.d.ts +30 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +99 -0
- package/dist/components/ui/expandable-tabs.cjs +201 -0
- package/dist/components/ui/expandable-tabs.d.ts +29 -0
- package/dist/components/ui/expandable-tabs.d.ts.map +1 -0
- package/dist/components/ui/expandable-tabs.js +170 -0
- package/dist/components/ui/overlay.cjs +69 -0
- package/dist/components/ui/overlay.d.ts +7 -0
- package/dist/components/ui/overlay.d.ts.map +1 -0
- package/dist/components/ui/overlay.js +28 -0
- package/dist/components/ui/scroll-area.cjs +105 -0
- package/dist/components/ui/scroll-area.d.ts +19 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +74 -0
- package/dist/components/ui/switch.cjs +71 -0
- package/dist/components/ui/switch.d.ts +11 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +33 -0
- package/dist/components/ui/tooltip.cjs +75 -0
- package/dist/components/ui/tooltip.d.ts +16 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +38 -0
- package/dist/components/wrapper.cjs +197 -0
- package/dist/components/wrapper.d.ts +24 -0
- package/dist/components/wrapper.d.ts.map +1 -0
- package/dist/components/wrapper.js +165 -0
- package/dist/dev-tool.cjs +164 -0
- package/dist/dev-tool.d.ts +14 -0
- package/dist/dev-tool.d.ts.map +1 -0
- package/dist/dev-tool.js +110 -0
- package/dist/index.cjs +46 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/libs/utils.cjs +69 -0
- package/dist/libs/utils.d.ts +3 -0
- package/dist/libs/utils.d.ts.map +1 -0
- package/dist/libs/utils.js +8 -0
- package/dist/router/router.cjs +278 -0
- package/dist/router/router.d.ts +8 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +261 -0
- package/package.json +48 -0
- package/rslib.config.ts +28 -0
- package/src/components/error-state.tsx +44 -0
- package/src/components/header.tsx +28 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +81 -0
- package/src/components/ui/expandable-tabs.tsx +174 -0
- package/src/components/ui/overlay.tsx +21 -0
- package/src/components/ui/scroll-area.tsx +52 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/components/wrapper.tsx +103 -0
- package/src/dev-tool.tsx +117 -0
- package/src/index.ts +3 -0
- package/src/libs/utils.ts +6 -0
- package/src/router/router.tsx +164 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
4
|
+
import { ChevronDown } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type ComponentPropsWithoutRef,
|
|
8
|
+
type ComponentRef,
|
|
9
|
+
forwardRef,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { cn } from '../../libs/utils';
|
|
12
|
+
|
|
13
|
+
const Accordion = AccordionPrimitive.Root;
|
|
14
|
+
|
|
15
|
+
const AccordionItem = forwardRef<
|
|
16
|
+
ComponentRef<typeof AccordionPrimitive.Item>,
|
|
17
|
+
ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<AccordionPrimitive.Item
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn('border-b', className)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
));
|
|
25
|
+
AccordionItem.displayName = 'AccordionItem';
|
|
26
|
+
|
|
27
|
+
const AccordionTrigger = forwardRef<
|
|
28
|
+
ComponentRef<typeof AccordionPrimitive.Trigger>,
|
|
29
|
+
ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
30
|
+
>(({ className, children, ...props }, ref) => (
|
|
31
|
+
<AccordionPrimitive.Header className="flex">
|
|
32
|
+
<AccordionPrimitive.Trigger
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
42
|
+
</AccordionPrimitive.Trigger>
|
|
43
|
+
</AccordionPrimitive.Header>
|
|
44
|
+
));
|
|
45
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
46
|
+
|
|
47
|
+
const AccordionContent = forwardRef<
|
|
48
|
+
ComponentRef<typeof AccordionPrimitive.Content>,
|
|
49
|
+
ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
50
|
+
>(({ className, children, ...props }, ref) => (
|
|
51
|
+
<AccordionPrimitive.Content
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn(
|
|
54
|
+
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
<div className="pt-0 pb-4">{children}</div>
|
|
60
|
+
</AccordionPrimitive.Content>
|
|
61
|
+
));
|
|
62
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
63
|
+
|
|
64
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type VariantProps, cva } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { type HTMLAttributes, forwardRef } from 'react';
|
|
4
|
+
import { cn } from '../../libs/utils';
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva(
|
|
7
|
+
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:top-4 [&>svg]:left-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-background text-foreground',
|
|
12
|
+
destructive:
|
|
13
|
+
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: 'default',
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const Alert = forwardRef<
|
|
23
|
+
HTMLDivElement,
|
|
24
|
+
HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
|
25
|
+
>(({ className, variant, ...props }, ref) => (
|
|
26
|
+
<div
|
|
27
|
+
ref={ref}
|
|
28
|
+
role="alert"
|
|
29
|
+
className={cn(alertVariants({ variant }), className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
33
|
+
Alert.displayName = 'Alert';
|
|
34
|
+
|
|
35
|
+
const AlertTitle = forwardRef<
|
|
36
|
+
HTMLParagraphElement,
|
|
37
|
+
HTMLAttributes<HTMLHeadingElement>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<h5
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
));
|
|
45
|
+
AlertTitle.displayName = 'AlertTitle';
|
|
46
|
+
|
|
47
|
+
const AlertDescription = forwardRef<
|
|
48
|
+
HTMLParagraphElement,
|
|
49
|
+
HTMLAttributes<HTMLParagraphElement>
|
|
50
|
+
>(({ className, ...props }, ref) => (
|
|
51
|
+
<div
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn('text-sm [&_p]:leading-relaxed', className)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
));
|
|
57
|
+
AlertDescription.displayName = 'AlertDescription';
|
|
58
|
+
|
|
59
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
+
import { type VariantProps, cva } from 'class-variance-authority';
|
|
3
|
+
|
|
4
|
+
import { type ButtonHTMLAttributes, forwardRef } from 'react';
|
|
5
|
+
import { cn } from '../../libs/utils';
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center rounded-md font-medium text-sm ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
13
|
+
destructive:
|
|
14
|
+
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
15
|
+
outline:
|
|
16
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
17
|
+
secondary:
|
|
18
|
+
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
19
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
20
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default: 'h-10 px-4 py-2',
|
|
24
|
+
sm: 'h-9 rounded-md px-3',
|
|
25
|
+
lg: 'h-11 rounded-md px-8',
|
|
26
|
+
icon: 'h-10 w-10',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: 'default',
|
|
31
|
+
size: 'default',
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export interface ButtonProps
|
|
37
|
+
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
|
38
|
+
VariantProps<typeof buttonVariants> {
|
|
39
|
+
asChild?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
43
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
44
|
+
const Comp = asChild ? Slot : 'button';
|
|
45
|
+
return (
|
|
46
|
+
<Comp
|
|
47
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
48
|
+
ref={ref}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
Button.displayName = 'Button';
|
|
55
|
+
|
|
56
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type HTMLAttributes, forwardRef } from 'react';
|
|
2
|
+
import { cn } from '../../libs/utils';
|
|
3
|
+
|
|
4
|
+
const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div
|
|
7
|
+
ref={ref}
|
|
8
|
+
className={cn(
|
|
9
|
+
'rounded-lg border bg-card text-card-foreground shadow-sm',
|
|
10
|
+
className
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
);
|
|
16
|
+
Card.displayName = 'Card';
|
|
17
|
+
|
|
18
|
+
const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
19
|
+
({ className, ...props }, ref) => (
|
|
20
|
+
<div
|
|
21
|
+
ref={ref}
|
|
22
|
+
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
CardHeader.displayName = 'CardHeader';
|
|
28
|
+
|
|
29
|
+
const CardTitle = forwardRef<
|
|
30
|
+
HTMLParagraphElement,
|
|
31
|
+
HTMLAttributes<HTMLHeadingElement>
|
|
32
|
+
>(({ className, ...props }, ref) => (
|
|
33
|
+
<h3
|
|
34
|
+
ref={ref}
|
|
35
|
+
className={cn(
|
|
36
|
+
'font-semibold text-2xl leading-none tracking-tight',
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
));
|
|
42
|
+
CardTitle.displayName = 'CardTitle';
|
|
43
|
+
|
|
44
|
+
const CardDescription = forwardRef<
|
|
45
|
+
HTMLParagraphElement,
|
|
46
|
+
HTMLAttributes<HTMLParagraphElement>
|
|
47
|
+
>(({ className, ...props }, ref) => (
|
|
48
|
+
<p
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
));
|
|
54
|
+
CardDescription.displayName = 'CardDescription';
|
|
55
|
+
|
|
56
|
+
const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
57
|
+
({ className, ...props }, ref) => (
|
|
58
|
+
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
CardContent.displayName = 'CardContent';
|
|
62
|
+
|
|
63
|
+
const CardFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
64
|
+
({ className, ...props }, ref) => (
|
|
65
|
+
<div
|
|
66
|
+
ref={ref}
|
|
67
|
+
className={cn('flex items-center p-6 pt-0', className)}
|
|
68
|
+
{...props}
|
|
69
|
+
/>
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
CardFooter.displayName = 'CardFooter';
|
|
73
|
+
|
|
74
|
+
export {
|
|
75
|
+
Card,
|
|
76
|
+
CardHeader,
|
|
77
|
+
CardFooter,
|
|
78
|
+
CardTitle,
|
|
79
|
+
CardDescription,
|
|
80
|
+
CardContent,
|
|
81
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { LucideIcon } from 'lucide-react';
|
|
4
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
5
|
+
|
|
6
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
+
import { useOnClickOutside } from 'usehooks-ts';
|
|
8
|
+
import { cn } from '../../libs/utils';
|
|
9
|
+
|
|
10
|
+
interface Tab {
|
|
11
|
+
title: string;
|
|
12
|
+
icon: LucideIcon;
|
|
13
|
+
type?: never;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Separator {
|
|
17
|
+
type: 'separator';
|
|
18
|
+
title?: never;
|
|
19
|
+
icon?: never;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type TabItem = Tab | Separator;
|
|
23
|
+
|
|
24
|
+
interface ExpandableTabsProps {
|
|
25
|
+
tabs: TabItem[];
|
|
26
|
+
className?: string;
|
|
27
|
+
activeColor?: string;
|
|
28
|
+
onChange?: (index: number | null) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const buttonVariants = {
|
|
32
|
+
initial: {
|
|
33
|
+
gap: 0,
|
|
34
|
+
paddingLeft: '.5rem',
|
|
35
|
+
paddingRight: '.5rem',
|
|
36
|
+
},
|
|
37
|
+
animate: (isSelected: boolean) => ({
|
|
38
|
+
gap: isSelected ? '.5rem' : 0,
|
|
39
|
+
paddingLeft: isSelected ? '1rem' : '.5rem',
|
|
40
|
+
paddingRight: isSelected ? '1rem' : '.5rem',
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const spanVariants = {
|
|
45
|
+
initial: { width: 0, opacity: 0 },
|
|
46
|
+
animate: { width: 'auto', opacity: 1 },
|
|
47
|
+
exit: { width: 0, opacity: 0 },
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const transition = { delay: 0.1, type: 'spring', bounce: 0, duration: 0.6 };
|
|
51
|
+
|
|
52
|
+
const Separator = memo(() => (
|
|
53
|
+
<div className="mx-1 h-[24px] w-[1.2px] bg-border" aria-hidden="true" />
|
|
54
|
+
));
|
|
55
|
+
Separator.displayName = 'Separator';
|
|
56
|
+
|
|
57
|
+
const TabButton = memo(
|
|
58
|
+
({
|
|
59
|
+
tab,
|
|
60
|
+
index,
|
|
61
|
+
isSelected,
|
|
62
|
+
activeColor,
|
|
63
|
+
onClick,
|
|
64
|
+
}: {
|
|
65
|
+
tab: Tab;
|
|
66
|
+
index: number;
|
|
67
|
+
isSelected: boolean;
|
|
68
|
+
activeColor: string;
|
|
69
|
+
onClick: (index: number) => void;
|
|
70
|
+
}) => {
|
|
71
|
+
const Icon = tab.icon;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<motion.button
|
|
75
|
+
variants={buttonVariants}
|
|
76
|
+
initial={false}
|
|
77
|
+
animate="animate"
|
|
78
|
+
custom={isSelected}
|
|
79
|
+
onClick={() => onClick(index)}
|
|
80
|
+
transition={transition}
|
|
81
|
+
className={cn(
|
|
82
|
+
'relative flex flex-grow items-center justify-center rounded-xl px-4 py-2 font-medium text-sm transition-colors duration-300',
|
|
83
|
+
isSelected
|
|
84
|
+
? cn('bg-muted', activeColor)
|
|
85
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
<Icon size={20} />
|
|
89
|
+
<AnimatePresence initial={false}>
|
|
90
|
+
{isSelected && (
|
|
91
|
+
<motion.span
|
|
92
|
+
variants={spanVariants}
|
|
93
|
+
initial="initial"
|
|
94
|
+
animate="animate"
|
|
95
|
+
exit="exit"
|
|
96
|
+
transition={transition}
|
|
97
|
+
className="overflow-hidden whitespace-nowrap"
|
|
98
|
+
>
|
|
99
|
+
{tab.title}
|
|
100
|
+
</motion.span>
|
|
101
|
+
)}
|
|
102
|
+
</AnimatePresence>
|
|
103
|
+
</motion.button>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
TabButton.displayName = 'TabButton';
|
|
108
|
+
|
|
109
|
+
export function ExpandableTabs({
|
|
110
|
+
tabs,
|
|
111
|
+
className,
|
|
112
|
+
activeColor = 'text-primary',
|
|
113
|
+
onChange,
|
|
114
|
+
}: ExpandableTabsProps) {
|
|
115
|
+
const [selected, setSelected] = useState<number | null>(0);
|
|
116
|
+
const outsideClickRef = useRef<HTMLDivElement>(null);
|
|
117
|
+
|
|
118
|
+
const handleInitialChange = useCallback(() => {
|
|
119
|
+
onChange?.(0);
|
|
120
|
+
}, [onChange]);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
handleInitialChange();
|
|
124
|
+
}, [handleInitialChange]);
|
|
125
|
+
|
|
126
|
+
const handleOutsideClick = useCallback(() => {
|
|
127
|
+
setSelected(null);
|
|
128
|
+
onChange?.(null);
|
|
129
|
+
}, [onChange]);
|
|
130
|
+
|
|
131
|
+
//@ts-expect-error
|
|
132
|
+
useOnClickOutside(outsideClickRef, handleOutsideClick);
|
|
133
|
+
|
|
134
|
+
const handleSelect = useCallback(
|
|
135
|
+
(index: number) => {
|
|
136
|
+
setSelected(index);
|
|
137
|
+
onChange?.(index);
|
|
138
|
+
},
|
|
139
|
+
[onChange]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const containerClassName = useMemo(
|
|
143
|
+
() =>
|
|
144
|
+
cn(
|
|
145
|
+
'flex flex-wrap items-center gap-2 rounded-2xl border bg-background p-1 shadow-sm',
|
|
146
|
+
className
|
|
147
|
+
),
|
|
148
|
+
[className]
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
<div ref={outsideClickRef} className={containerClassName}>
|
|
153
|
+
{tabs.map((tab, index) =>
|
|
154
|
+
tab.type === 'separator' ? (
|
|
155
|
+
<Separator
|
|
156
|
+
key={`separator-${
|
|
157
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
|
158
|
+
index
|
|
159
|
+
}`}
|
|
160
|
+
/>
|
|
161
|
+
) : (
|
|
162
|
+
<TabButton
|
|
163
|
+
key={`${tab.title}-${index}`}
|
|
164
|
+
tab={tab}
|
|
165
|
+
index={index}
|
|
166
|
+
isSelected={selected === index}
|
|
167
|
+
activeColor={activeColor}
|
|
168
|
+
onClick={handleSelect}
|
|
169
|
+
/>
|
|
170
|
+
)
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
2
|
+
import type { FC } from 'react';
|
|
3
|
+
|
|
4
|
+
interface OverlayProps {
|
|
5
|
+
show: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Overlay: FC<OverlayProps> = ({ show }) => {
|
|
9
|
+
return (
|
|
10
|
+
<AnimatePresence>
|
|
11
|
+
{show && (
|
|
12
|
+
<motion.div
|
|
13
|
+
className="data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 flex flex-col items-center justify-center overflow-y-auto bg-overlay p-4 backdrop-blur-[10px] data-[state=closed]:animate-out data-[state=open]:animate-in"
|
|
14
|
+
initial={{ opacity: 0 }}
|
|
15
|
+
animate={{ opacity: 1 }}
|
|
16
|
+
exit={{ opacity: 0 }}
|
|
17
|
+
/>
|
|
18
|
+
)}
|
|
19
|
+
</AnimatePresence>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type ComponentPropsWithoutRef,
|
|
7
|
+
type ElementRef,
|
|
8
|
+
forwardRef,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { cn } from '../../libs/utils';
|
|
11
|
+
|
|
12
|
+
const ScrollArea = forwardRef<
|
|
13
|
+
ElementRef<typeof ScrollAreaPrimitive.Root>,
|
|
14
|
+
ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
|
15
|
+
>(({ className, children, ...props }, ref) => (
|
|
16
|
+
<ScrollAreaPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn('relative overflow-hidden', className)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
|
22
|
+
{children}
|
|
23
|
+
</ScrollAreaPrimitive.Viewport>
|
|
24
|
+
<ScrollBar />
|
|
25
|
+
<ScrollAreaPrimitive.Corner />
|
|
26
|
+
</ScrollAreaPrimitive.Root>
|
|
27
|
+
));
|
|
28
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
29
|
+
|
|
30
|
+
const ScrollBar = forwardRef<
|
|
31
|
+
ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
|
32
|
+
ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
33
|
+
>(({ className, orientation = 'vertical', ...props }, ref) => (
|
|
34
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
35
|
+
ref={ref}
|
|
36
|
+
orientation={orientation}
|
|
37
|
+
className={cn(
|
|
38
|
+
'flex touch-none select-none transition-colors',
|
|
39
|
+
orientation === 'vertical' &&
|
|
40
|
+
'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
|
41
|
+
orientation === 'horizontal' &&
|
|
42
|
+
'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
|
|
48
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
49
|
+
));
|
|
50
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
51
|
+
|
|
52
|
+
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../../libs/utils';
|
|
6
|
+
|
|
7
|
+
const Switch = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
|
10
|
+
>(({ className, ...props }, ref) => (
|
|
11
|
+
<SwitchPrimitives.Root
|
|
12
|
+
className={cn(
|
|
13
|
+
'peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
ref={ref}
|
|
18
|
+
>
|
|
19
|
+
<SwitchPrimitives.Thumb
|
|
20
|
+
className={cn(
|
|
21
|
+
'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0'
|
|
22
|
+
)}
|
|
23
|
+
/>
|
|
24
|
+
</SwitchPrimitives.Root>
|
|
25
|
+
));
|
|
26
|
+
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
27
|
+
|
|
28
|
+
export { Switch };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '../../libs/utils';
|
|
7
|
+
|
|
8
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
9
|
+
|
|
10
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
11
|
+
|
|
12
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
13
|
+
|
|
14
|
+
const TooltipContent = React.forwardRef<
|
|
15
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
16
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
17
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
18
|
+
<TooltipPrimitive.Portal>
|
|
19
|
+
<TooltipPrimitive.Content
|
|
20
|
+
ref={ref}
|
|
21
|
+
sideOffset={sideOffset}
|
|
22
|
+
className={cn(
|
|
23
|
+
'fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 animate-in overflow-hidden rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out',
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
</TooltipPrimitive.Portal>
|
|
29
|
+
));
|
|
30
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
31
|
+
|
|
32
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Shield } from 'lucide-react';
|
|
4
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
5
|
+
|
|
6
|
+
import { type ReactNode, useEffect, useState } from 'react';
|
|
7
|
+
import { createPortal } from 'react-dom';
|
|
8
|
+
import { cn } from '../libs/utils';
|
|
9
|
+
import { Button } from './ui/button';
|
|
10
|
+
import { Card } from './ui/card';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Dev Tool Wrapper Component
|
|
14
|
+
*
|
|
15
|
+
* This component serves as both an icon and a wrapper for the development tool interface.
|
|
16
|
+
* It provides a button that, when clicked, toggles the visibility of a pop-up containing
|
|
17
|
+
* the router pages.
|
|
18
|
+
*
|
|
19
|
+
* @component
|
|
20
|
+
* @returns {JSX.Element} The rendered component
|
|
21
|
+
*/
|
|
22
|
+
export function DevToolWrapper({
|
|
23
|
+
children,
|
|
24
|
+
isOpen,
|
|
25
|
+
toggleOpen,
|
|
26
|
+
position = 'bottom-right',
|
|
27
|
+
}: {
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
isOpen: boolean;
|
|
30
|
+
toggleOpen: () => void;
|
|
31
|
+
position?: 'bottom-right' | 'top-right' | 'bottom-left' | 'top-left';
|
|
32
|
+
}) {
|
|
33
|
+
// Track whether component is mounted to handle client-side only features
|
|
34
|
+
const [isMounted, setIsMounted] = useState(false);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setIsMounted(true);
|
|
38
|
+
return () => setIsMounted(false);
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const DevToolContent = (
|
|
42
|
+
<AnimatePresence>
|
|
43
|
+
{isOpen && (
|
|
44
|
+
<motion.div
|
|
45
|
+
className="fixed inset-0 z-50 flex items-end justify-center sm:items-center"
|
|
46
|
+
initial={{ opacity: 0 }}
|
|
47
|
+
animate={{ opacity: 1 }}
|
|
48
|
+
exit={{ opacity: 0 }}
|
|
49
|
+
>
|
|
50
|
+
<motion.div
|
|
51
|
+
className="fixed inset-0 bg-background/10 backdrop-blur-sm"
|
|
52
|
+
initial={{ opacity: 0 }}
|
|
53
|
+
animate={{ opacity: 1 }}
|
|
54
|
+
exit={{ opacity: 0 }}
|
|
55
|
+
onClick={toggleOpen}
|
|
56
|
+
/>
|
|
57
|
+
<motion.div
|
|
58
|
+
className={cn(
|
|
59
|
+
'fixed z-[9999]',
|
|
60
|
+
position === 'bottom-right' && 'right-4 bottom-4',
|
|
61
|
+
position === 'top-right' && 'top-4 right-4',
|
|
62
|
+
position === 'bottom-left' && 'bottom-4 left-4',
|
|
63
|
+
position === 'top-left' && 'top-4 left-4'
|
|
64
|
+
)}
|
|
65
|
+
initial={{ opacity: 0, y: 50 }}
|
|
66
|
+
animate={{ opacity: 1, y: 0 }}
|
|
67
|
+
exit={{ opacity: 0, y: 50 }}
|
|
68
|
+
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
|
|
69
|
+
>
|
|
70
|
+
<Card className="w-[350px] shadow-lg">{children}</Card>
|
|
71
|
+
</motion.div>
|
|
72
|
+
</motion.div>
|
|
73
|
+
)}
|
|
74
|
+
</AnimatePresence>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<AnimatePresence>
|
|
80
|
+
{!isOpen && (
|
|
81
|
+
<motion.div
|
|
82
|
+
initial={{ scale: 0.9, opacity: 0 }}
|
|
83
|
+
animate={{ scale: 1, opacity: 1 }}
|
|
84
|
+
exit={{ scale: 0.9, opacity: 0 }}
|
|
85
|
+
className="fixed right-4 bottom-4 z-[9999]"
|
|
86
|
+
>
|
|
87
|
+
<Button
|
|
88
|
+
variant="outline"
|
|
89
|
+
size="icon"
|
|
90
|
+
className="h-10 w-10 rounded-full shadow-lg"
|
|
91
|
+
onClick={toggleOpen}
|
|
92
|
+
>
|
|
93
|
+
<Shield className="h-4 w-4" />
|
|
94
|
+
</Button>
|
|
95
|
+
</motion.div>
|
|
96
|
+
)}
|
|
97
|
+
</AnimatePresence>
|
|
98
|
+
{isMounted && createPortal(DevToolContent, document.body)}
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default DevToolWrapper;
|