@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.
Files changed (89) hide show
  1. package/.turbo/turbo-build.log +56 -0
  2. package/.turbo/turbo-fmt.log +6 -0
  3. package/.turbo/turbo-lint.log +73 -0
  4. package/CHANGELOG.md +23 -0
  5. package/LICENSE.md +595 -0
  6. package/README.md +47 -0
  7. package/dist/components/error-state.cjs +126 -0
  8. package/dist/components/error-state.d.ts +6 -0
  9. package/dist/components/error-state.d.ts.map +1 -0
  10. package/dist/components/error-state.js +110 -0
  11. package/dist/components/header.cjs +88 -0
  12. package/dist/components/header.d.ts +8 -0
  13. package/dist/components/header.d.ts.map +1 -0
  14. package/dist/components/header.js +56 -0
  15. package/dist/components/ui/accordion.cjs +119 -0
  16. package/dist/components/ui/accordion.d.ts +34 -0
  17. package/dist/components/ui/accordion.d.ts.map +1 -0
  18. package/dist/components/ui/accordion.js +84 -0
  19. package/dist/components/ui/alert.cjs +104 -0
  20. package/dist/components/ui/alert.d.ts +25 -0
  21. package/dist/components/ui/alert.d.ts.map +1 -0
  22. package/dist/components/ui/alert.js +67 -0
  23. package/dist/components/ui/button.cjs +95 -0
  24. package/dist/components/ui/button.d.ts +28 -0
  25. package/dist/components/ui/button.d.ts.map +1 -0
  26. package/dist/components/ui/button.js +55 -0
  27. package/dist/components/ui/card.cjs +120 -0
  28. package/dist/components/ui/card.d.ts +30 -0
  29. package/dist/components/ui/card.d.ts.map +1 -0
  30. package/dist/components/ui/card.js +99 -0
  31. package/dist/components/ui/expandable-tabs.cjs +201 -0
  32. package/dist/components/ui/expandable-tabs.d.ts +29 -0
  33. package/dist/components/ui/expandable-tabs.d.ts.map +1 -0
  34. package/dist/components/ui/expandable-tabs.js +170 -0
  35. package/dist/components/ui/overlay.cjs +69 -0
  36. package/dist/components/ui/overlay.d.ts +7 -0
  37. package/dist/components/ui/overlay.d.ts.map +1 -0
  38. package/dist/components/ui/overlay.js +28 -0
  39. package/dist/components/ui/scroll-area.cjs +105 -0
  40. package/dist/components/ui/scroll-area.d.ts +19 -0
  41. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  42. package/dist/components/ui/scroll-area.js +74 -0
  43. package/dist/components/ui/switch.cjs +71 -0
  44. package/dist/components/ui/switch.d.ts +11 -0
  45. package/dist/components/ui/switch.d.ts.map +1 -0
  46. package/dist/components/ui/switch.js +33 -0
  47. package/dist/components/ui/tooltip.cjs +75 -0
  48. package/dist/components/ui/tooltip.d.ts +16 -0
  49. package/dist/components/ui/tooltip.d.ts.map +1 -0
  50. package/dist/components/ui/tooltip.js +38 -0
  51. package/dist/components/wrapper.cjs +197 -0
  52. package/dist/components/wrapper.d.ts +24 -0
  53. package/dist/components/wrapper.d.ts.map +1 -0
  54. package/dist/components/wrapper.js +165 -0
  55. package/dist/dev-tool.cjs +164 -0
  56. package/dist/dev-tool.d.ts +14 -0
  57. package/dist/dev-tool.d.ts.map +1 -0
  58. package/dist/dev-tool.js +110 -0
  59. package/dist/index.cjs +46 -0
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +4 -0
  63. package/dist/libs/utils.cjs +69 -0
  64. package/dist/libs/utils.d.ts +3 -0
  65. package/dist/libs/utils.d.ts.map +1 -0
  66. package/dist/libs/utils.js +8 -0
  67. package/dist/router/router.cjs +278 -0
  68. package/dist/router/router.d.ts +8 -0
  69. package/dist/router/router.d.ts.map +1 -0
  70. package/dist/router/router.js +261 -0
  71. package/package.json +48 -0
  72. package/rslib.config.ts +28 -0
  73. package/src/components/error-state.tsx +44 -0
  74. package/src/components/header.tsx +28 -0
  75. package/src/components/ui/accordion.tsx +64 -0
  76. package/src/components/ui/alert.tsx +59 -0
  77. package/src/components/ui/button.tsx +56 -0
  78. package/src/components/ui/card.tsx +81 -0
  79. package/src/components/ui/expandable-tabs.tsx +174 -0
  80. package/src/components/ui/overlay.tsx +21 -0
  81. package/src/components/ui/scroll-area.tsx +52 -0
  82. package/src/components/ui/switch.tsx +28 -0
  83. package/src/components/ui/tooltip.tsx +32 -0
  84. package/src/components/wrapper.tsx +103 -0
  85. package/src/dev-tool.tsx +117 -0
  86. package/src/index.ts +3 -0
  87. package/src/libs/utils.ts +6 -0
  88. package/src/router/router.tsx +164 -0
  89. 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;