@aiready/components 0.1.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.
Files changed (91) hide show
  1. package/README.md +240 -0
  2. package/dist/charts/ForceDirectedGraph.d.ts +40 -0
  3. package/dist/charts/ForceDirectedGraph.js +294 -0
  4. package/dist/charts/ForceDirectedGraph.js.map +1 -0
  5. package/dist/components/badge.d.ts +13 -0
  6. package/dist/components/badge.js +32 -0
  7. package/dist/components/badge.js.map +1 -0
  8. package/dist/components/button.d.ts +14 -0
  9. package/dist/components/button.js +52 -0
  10. package/dist/components/button.js.map +1 -0
  11. package/dist/components/card.d.ts +10 -0
  12. package/dist/components/card.js +66 -0
  13. package/dist/components/card.js.map +1 -0
  14. package/dist/components/checkbox.d.ts +8 -0
  15. package/dist/components/checkbox.js +42 -0
  16. package/dist/components/checkbox.js.map +1 -0
  17. package/dist/components/container.d.ts +8 -0
  18. package/dist/components/container.js +36 -0
  19. package/dist/components/container.js.map +1 -0
  20. package/dist/components/grid.d.ts +9 -0
  21. package/dist/components/grid.js +44 -0
  22. package/dist/components/grid.js.map +1 -0
  23. package/dist/components/input.d.ts +7 -0
  24. package/dist/components/input.js +30 -0
  25. package/dist/components/input.js.map +1 -0
  26. package/dist/components/label.d.ts +10 -0
  27. package/dist/components/label.js +28 -0
  28. package/dist/components/label.js.map +1 -0
  29. package/dist/components/radio-group.d.ts +17 -0
  30. package/dist/components/radio-group.js +64 -0
  31. package/dist/components/radio-group.js.map +1 -0
  32. package/dist/components/select.d.ts +15 -0
  33. package/dist/components/select.js +45 -0
  34. package/dist/components/select.js.map +1 -0
  35. package/dist/components/separator.d.ts +9 -0
  36. package/dist/components/separator.js +30 -0
  37. package/dist/components/separator.js.map +1 -0
  38. package/dist/components/stack.d.ts +11 -0
  39. package/dist/components/stack.js +60 -0
  40. package/dist/components/stack.js.map +1 -0
  41. package/dist/components/switch.d.ts +9 -0
  42. package/dist/components/switch.js +49 -0
  43. package/dist/components/switch.js.map +1 -0
  44. package/dist/components/textarea.d.ts +7 -0
  45. package/dist/components/textarea.js +29 -0
  46. package/dist/components/textarea.js.map +1 -0
  47. package/dist/hooks/useD3.d.ts +6 -0
  48. package/dist/hooks/useD3.js +35 -0
  49. package/dist/hooks/useD3.js.map +1 -0
  50. package/dist/hooks/useDebounce.d.ts +3 -0
  51. package/dist/hooks/useDebounce.js +19 -0
  52. package/dist/hooks/useDebounce.js.map +1 -0
  53. package/dist/hooks/useForceSimulation.d.ts +39 -0
  54. package/dist/hooks/useForceSimulation.js +107 -0
  55. package/dist/hooks/useForceSimulation.js.map +1 -0
  56. package/dist/index.d.ts +27 -0
  57. package/dist/index.js +927 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/utils/cn.d.ts +5 -0
  60. package/dist/utils/cn.js +11 -0
  61. package/dist/utils/cn.js.map +1 -0
  62. package/dist/utils/colors.d.ts +19 -0
  63. package/dist/utils/colors.js +52 -0
  64. package/dist/utils/colors.js.map +1 -0
  65. package/dist/utils/formatters.d.ts +13 -0
  66. package/dist/utils/formatters.js +100 -0
  67. package/dist/utils/formatters.js.map +1 -0
  68. package/package.json +83 -0
  69. package/src/charts/ForceDirectedGraph.tsx +356 -0
  70. package/src/components/badge.tsx +35 -0
  71. package/src/components/button.tsx +53 -0
  72. package/src/components/card.tsx +78 -0
  73. package/src/components/checkbox.tsx +39 -0
  74. package/src/components/container.tsx +31 -0
  75. package/src/components/grid.tsx +40 -0
  76. package/src/components/input.tsx +24 -0
  77. package/src/components/label.tsx +24 -0
  78. package/src/components/radio-group.tsx +71 -0
  79. package/src/components/select.tsx +53 -0
  80. package/src/components/separator.tsx +29 -0
  81. package/src/components/stack.tsx +61 -0
  82. package/src/components/switch.tsx +49 -0
  83. package/src/components/textarea.tsx +23 -0
  84. package/src/hooks/useD3.ts +125 -0
  85. package/src/hooks/useDebounce.ts +44 -0
  86. package/src/hooks/useForceSimulation.ts +328 -0
  87. package/src/index.ts +51 -0
  88. package/src/utils/cn.ts +11 -0
  89. package/src/utils/colors.ts +58 -0
  90. package/src/utils/formatters.ts +161 -0
  91. package/tailwind.config.js +46 -0
@@ -0,0 +1,356 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import * as d3 from 'd3';
3
+ import {
4
+ useForceSimulation,
5
+ type SimulationNode,
6
+ type SimulationLink,
7
+ type ForceSimulationOptions,
8
+ } from '../hooks/useForceSimulation';
9
+ import { cn } from '../utils/cn';
10
+
11
+ export interface GraphNode extends SimulationNode {
12
+ id: string;
13
+ label?: string;
14
+ color?: string;
15
+ size?: number;
16
+ group?: string;
17
+ }
18
+
19
+ export interface GraphLink extends SimulationLink {
20
+ color?: string;
21
+ width?: number;
22
+ label?: string;
23
+ }
24
+
25
+ export interface ForceDirectedGraphProps {
26
+ /**
27
+ * Array of nodes to display
28
+ */
29
+ nodes: GraphNode[];
30
+
31
+ /**
32
+ * Array of links between nodes
33
+ */
34
+ links: GraphLink[];
35
+
36
+ /**
37
+ * Width of the graph container
38
+ */
39
+ width: number;
40
+
41
+ /**
42
+ * Height of the graph container
43
+ */
44
+ height: number;
45
+
46
+ /**
47
+ * Force simulation options
48
+ */
49
+ simulationOptions?: Partial<ForceSimulationOptions>;
50
+
51
+ /**
52
+ * Whether to enable zoom and pan
53
+ * @default true
54
+ */
55
+ enableZoom?: boolean;
56
+
57
+ /**
58
+ * Whether to enable node dragging
59
+ * @default true
60
+ */
61
+ enableDrag?: boolean;
62
+
63
+ /**
64
+ * Callback when a node is clicked
65
+ */
66
+ onNodeClick?: (node: GraphNode) => void;
67
+
68
+ /**
69
+ * Callback when a node is hovered
70
+ */
71
+ onNodeHover?: (node: GraphNode | null) => void;
72
+
73
+ /**
74
+ * Callback when a link is clicked
75
+ */
76
+ onLinkClick?: (link: GraphLink) => void;
77
+
78
+ /**
79
+ * Selected node ID
80
+ */
81
+ selectedNodeId?: string;
82
+
83
+ /**
84
+ * Hovered node ID
85
+ */
86
+ hoveredNodeId?: string;
87
+
88
+ /**
89
+ * Default node color
90
+ * @default "#69b3a2"
91
+ */
92
+ defaultNodeColor?: string;
93
+
94
+ /**
95
+ * Default node size
96
+ * @default 10
97
+ */
98
+ defaultNodeSize?: number;
99
+
100
+ /**
101
+ * Default link color
102
+ * @default "#999"
103
+ */
104
+ defaultLinkColor?: string;
105
+
106
+ /**
107
+ * Default link width
108
+ * @default 1
109
+ */
110
+ defaultLinkWidth?: number;
111
+
112
+ /**
113
+ * Whether to show node labels
114
+ * @default true
115
+ */
116
+ showNodeLabels?: boolean;
117
+
118
+ /**
119
+ * Whether to show link labels
120
+ * @default false
121
+ */
122
+ showLinkLabels?: boolean;
123
+
124
+ /**
125
+ * Additional CSS classes
126
+ */
127
+ className?: string;
128
+ }
129
+
130
+ export const ForceDirectedGraph: React.FC<ForceDirectedGraphProps> = ({
131
+ nodes: initialNodes,
132
+ links: initialLinks,
133
+ width,
134
+ height,
135
+ simulationOptions,
136
+ enableZoom = true,
137
+ enableDrag = true,
138
+ onNodeClick,
139
+ onNodeHover,
140
+ onLinkClick,
141
+ selectedNodeId,
142
+ hoveredNodeId,
143
+ defaultNodeColor = '#69b3a2',
144
+ defaultNodeSize = 10,
145
+ defaultLinkColor = '#999',
146
+ defaultLinkWidth = 1,
147
+ showNodeLabels = true,
148
+ showLinkLabels = false,
149
+ className,
150
+ }) => {
151
+ const svgRef = useRef<SVGSVGElement>(null);
152
+ const gRef = useRef<SVGGElement>(null);
153
+ const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });
154
+
155
+ // Initialize simulation
156
+ const { nodes, links, restart } = useForceSimulation(initialNodes, initialLinks, {
157
+ width,
158
+ height,
159
+ ...simulationOptions,
160
+ });
161
+
162
+ // Set up zoom behavior
163
+ useEffect(() => {
164
+ if (!enableZoom || !svgRef.current || !gRef.current) return;
165
+
166
+ const svg = d3.select(svgRef.current);
167
+ const g = d3.select(gRef.current);
168
+
169
+ const zoom = d3
170
+ .zoom<SVGSVGElement, unknown>()
171
+ .scaleExtent([0.1, 10])
172
+ .on('zoom', (event) => {
173
+ g.attr('transform', event.transform);
174
+ setTransform(event.transform);
175
+ });
176
+
177
+ svg.call(zoom);
178
+
179
+ return () => {
180
+ svg.on('.zoom', null);
181
+ };
182
+ }, [enableZoom]);
183
+
184
+ // Set up drag behavior
185
+ const handleDragStart = useCallback(
186
+ (event: React.MouseEvent, node: GraphNode) => {
187
+ if (!enableDrag) return;
188
+ event.stopPropagation();
189
+ node.fx = node.x;
190
+ node.fy = node.y;
191
+ restart();
192
+ },
193
+ [enableDrag, restart]
194
+ );
195
+
196
+ const handleDrag = useCallback(
197
+ (event: React.MouseEvent, node: GraphNode) => {
198
+ if (!enableDrag) return;
199
+ const svg = svgRef.current;
200
+ if (!svg) return;
201
+
202
+ const rect = svg.getBoundingClientRect();
203
+ const x = (event.clientX - rect.left - transform.x) / transform.k;
204
+ const y = (event.clientY - rect.top - transform.y) / transform.k;
205
+
206
+ node.fx = x;
207
+ node.fy = y;
208
+ },
209
+ [enableDrag, transform]
210
+ );
211
+
212
+ const handleDragEnd = useCallback(
213
+ (event: React.MouseEvent, node: GraphNode) => {
214
+ if (!enableDrag) return;
215
+ event.stopPropagation();
216
+ node.fx = null;
217
+ node.fy = null;
218
+ },
219
+ [enableDrag]
220
+ );
221
+
222
+ const handleNodeClick = useCallback(
223
+ (node: GraphNode) => {
224
+ onNodeClick?.(node);
225
+ },
226
+ [onNodeClick]
227
+ );
228
+
229
+ const handleNodeMouseEnter = useCallback(
230
+ (node: GraphNode) => {
231
+ onNodeHover?.(node);
232
+ },
233
+ [onNodeHover]
234
+ );
235
+
236
+ const handleNodeMouseLeave = useCallback(() => {
237
+ onNodeHover?.(null);
238
+ }, [onNodeHover]);
239
+
240
+ const handleLinkClick = useCallback(
241
+ (link: GraphLink) => {
242
+ onLinkClick?.(link);
243
+ },
244
+ [onLinkClick]
245
+ );
246
+
247
+ return (
248
+ <svg
249
+ ref={svgRef}
250
+ width={width}
251
+ height={height}
252
+ className={cn('bg-white dark:bg-gray-900', className)}
253
+ >
254
+ <defs>
255
+ {/* Arrow marker for directed graphs */}
256
+ <marker
257
+ id="arrow"
258
+ viewBox="0 0 10 10"
259
+ refX="20"
260
+ refY="5"
261
+ markerWidth="6"
262
+ markerHeight="6"
263
+ orient="auto"
264
+ >
265
+ <path d="M 0 0 L 10 5 L 0 10 z" fill={defaultLinkColor} />
266
+ </marker>
267
+ </defs>
268
+
269
+ <g ref={gRef}>
270
+ {/* Render links */}
271
+ {links.map((link, i) => {
272
+ const source = link.source as GraphNode;
273
+ const target = link.target as GraphNode;
274
+ if (!source.x || !source.y || !target.x || !target.y) return null;
275
+
276
+ return (
277
+ <g key={`link-${i}`}>
278
+ <line
279
+ x1={source.x}
280
+ y1={source.y}
281
+ x2={target.x}
282
+ y2={target.y}
283
+ stroke={link.color || defaultLinkColor}
284
+ strokeWidth={link.width || defaultLinkWidth}
285
+ opacity={0.6}
286
+ className="cursor-pointer transition-opacity hover:opacity-100"
287
+ onClick={() => handleLinkClick(link)}
288
+ />
289
+ {showLinkLabels && link.label && (
290
+ <text
291
+ x={(source.x + target.x) / 2}
292
+ y={(source.y + target.y) / 2}
293
+ fill="#666"
294
+ fontSize="10"
295
+ textAnchor="middle"
296
+ dominantBaseline="middle"
297
+ pointerEvents="none"
298
+ >
299
+ {link.label}
300
+ </text>
301
+ )}
302
+ </g>
303
+ );
304
+ })}
305
+
306
+ {/* Render nodes */}
307
+ {nodes.map((node) => {
308
+ if (!node.x || !node.y) return null;
309
+
310
+ const isSelected = selectedNodeId === node.id;
311
+ const isHovered = hoveredNodeId === node.id;
312
+ const nodeSize = node.size || defaultNodeSize;
313
+ const nodeColor = node.color || defaultNodeColor;
314
+
315
+ return (
316
+ <g
317
+ key={node.id}
318
+ transform={`translate(${node.x},${node.y})`}
319
+ className="cursor-pointer"
320
+ onClick={() => handleNodeClick(node)}
321
+ onMouseEnter={() => handleNodeMouseEnter(node)}
322
+ onMouseLeave={handleNodeMouseLeave}
323
+ onMouseDown={(e) => handleDragStart(e, node)}
324
+ onMouseMove={(e) => handleDrag(e, node)}
325
+ onMouseUp={(e) => handleDragEnd(e, node)}
326
+ >
327
+ <circle
328
+ r={nodeSize}
329
+ fill={nodeColor}
330
+ stroke={isSelected ? '#000' : isHovered ? '#666' : 'none'}
331
+ strokeWidth={isSelected ? 3 : 2}
332
+ opacity={isHovered || isSelected ? 1 : 0.9}
333
+ className="transition-all"
334
+ />
335
+ {showNodeLabels && node.label && (
336
+ <text
337
+ y={nodeSize + 15}
338
+ fill="#333"
339
+ fontSize="12"
340
+ textAnchor="middle"
341
+ dominantBaseline="middle"
342
+ pointerEvents="none"
343
+ className="select-none"
344
+ >
345
+ {node.label}
346
+ </text>
347
+ )}
348
+ </g>
349
+ );
350
+ })}
351
+ </g>
352
+ </svg>
353
+ );
354
+ };
355
+
356
+ ForceDirectedGraph.displayName = 'ForceDirectedGraph';
@@ -0,0 +1,35 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../utils/cn';
4
+
5
+ const badgeVariants = cva(
6
+ 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
12
+ secondary:
13
+ 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
14
+ destructive:
15
+ 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
16
+ outline: 'text-foreground',
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: 'default',
21
+ },
22
+ }
23
+ );
24
+
25
+ export interface BadgeProps
26
+ extends React.HTMLAttributes<HTMLDivElement>,
27
+ VariantProps<typeof badgeVariants> {}
28
+
29
+ function Badge({ className, variant, ...props }: BadgeProps) {
30
+ return (
31
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
32
+ );
33
+ }
34
+
35
+ export { Badge, badgeVariants };
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../utils/cn';
4
+
5
+ const buttonVariants = cva(
6
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium 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',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
11
+ destructive:
12
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13
+ outline:
14
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15
+ secondary:
16
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
17
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
18
+ link: 'text-primary underline-offset-4 hover:underline',
19
+ },
20
+ size: {
21
+ default: 'h-10 px-4 py-2',
22
+ sm: 'h-9 rounded-md px-3',
23
+ lg: 'h-11 rounded-md px-8',
24
+ icon: 'h-10 w-10',
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: 'default',
29
+ size: 'default',
30
+ },
31
+ }
32
+ );
33
+
34
+ export interface ButtonProps
35
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
36
+ VariantProps<typeof buttonVariants> {
37
+ asChild?: boolean;
38
+ }
39
+
40
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
41
+ ({ className, variant, size, ...props }, ref) => {
42
+ return (
43
+ <button
44
+ className={cn(buttonVariants({ variant, size, className }))}
45
+ ref={ref}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+ );
51
+ Button.displayName = 'Button';
52
+
53
+ export { Button, buttonVariants };
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn(
11
+ 'rounded-lg border bg-card text-card-foreground shadow-sm',
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ ));
17
+ Card.displayName = 'Card';
18
+
19
+ const CardHeader = React.forwardRef<
20
+ HTMLDivElement,
21
+ React.HTMLAttributes<HTMLDivElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <div
24
+ ref={ref}
25
+ className={cn('flex flex-col space-y-1.5 p-6', className)}
26
+ {...props}
27
+ />
28
+ ));
29
+ CardHeader.displayName = 'CardHeader';
30
+
31
+ const CardTitle = React.forwardRef<
32
+ HTMLParagraphElement,
33
+ React.HTMLAttributes<HTMLHeadingElement>
34
+ >(({ className, ...props }, ref) => (
35
+ <h3
36
+ ref={ref}
37
+ className={cn(
38
+ 'text-2xl font-semibold leading-none tracking-tight',
39
+ className
40
+ )}
41
+ {...props}
42
+ />
43
+ ));
44
+ CardTitle.displayName = 'CardTitle';
45
+
46
+ const CardDescription = React.forwardRef<
47
+ HTMLParagraphElement,
48
+ React.HTMLAttributes<HTMLParagraphElement>
49
+ >(({ className, ...props }, ref) => (
50
+ <p
51
+ ref={ref}
52
+ className={cn('text-sm text-muted-foreground', className)}
53
+ {...props}
54
+ />
55
+ ));
56
+ CardDescription.displayName = 'CardDescription';
57
+
58
+ const CardContent = React.forwardRef<
59
+ HTMLDivElement,
60
+ React.HTMLAttributes<HTMLDivElement>
61
+ >(({ className, ...props }, ref) => (
62
+ <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
63
+ ));
64
+ CardContent.displayName = 'CardContent';
65
+
66
+ const CardFooter = React.forwardRef<
67
+ HTMLDivElement,
68
+ React.HTMLAttributes<HTMLDivElement>
69
+ >(({ className, ...props }, ref) => (
70
+ <div
71
+ ref={ref}
72
+ className={cn('flex items-center p-6 pt-0', className)}
73
+ {...props}
74
+ />
75
+ ));
76
+ CardFooter.displayName = 'CardFooter';
77
+
78
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export interface CheckboxProps
5
+ extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
6
+ label?: string;
7
+ }
8
+
9
+ const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
10
+ ({ className, label, id, ...props }, ref) => {
11
+ const checkboxId = id || React.useId();
12
+
13
+ return (
14
+ <div className="flex items-center">
15
+ <input
16
+ type="checkbox"
17
+ id={checkboxId}
18
+ ref={ref}
19
+ className={cn(
20
+ 'h-4 w-4 rounded border-gray-300 text-primary focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
21
+ className
22
+ )}
23
+ {...props}
24
+ />
25
+ {label && (
26
+ <label
27
+ htmlFor={checkboxId}
28
+ className="ml-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
29
+ >
30
+ {label}
31
+ </label>
32
+ )}
33
+ </div>
34
+ );
35
+ }
36
+ );
37
+ Checkbox.displayName = 'Checkbox';
38
+
39
+ export { Checkbox };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
6
+ }
7
+
8
+ const Container = React.forwardRef<HTMLDivElement, ContainerProps>(
9
+ ({ className, size = 'lg', ...props }, ref) => {
10
+ return (
11
+ <div
12
+ ref={ref}
13
+ className={cn(
14
+ 'mx-auto w-full px-4 sm:px-6 lg:px-8',
15
+ {
16
+ 'max-w-screen-sm': size === 'sm',
17
+ 'max-w-screen-md': size === 'md',
18
+ 'max-w-screen-lg': size === 'lg',
19
+ 'max-w-screen-xl': size === 'xl',
20
+ 'max-w-full': size === 'full',
21
+ },
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ );
27
+ }
28
+ );
29
+ Container.displayName = 'Container';
30
+
31
+ export { Container };
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export interface GridProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ cols?: 1 | 2 | 3 | 4 | 5 | 6 | 12;
6
+ gap?: 'sm' | 'md' | 'lg' | 'xl';
7
+ }
8
+
9
+ const Grid = React.forwardRef<HTMLDivElement, GridProps>(
10
+ ({ className, cols = 3, gap = 'md', ...props }, ref) => {
11
+ return (
12
+ <div
13
+ ref={ref}
14
+ className={cn(
15
+ 'grid',
16
+ {
17
+ 'grid-cols-1': cols === 1,
18
+ 'grid-cols-1 sm:grid-cols-2': cols === 2,
19
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3': cols === 3,
20
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4': cols === 4,
21
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-5': cols === 5,
22
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-6': cols === 6,
23
+ 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-12': cols === 12,
24
+ },
25
+ {
26
+ 'gap-2': gap === 'sm',
27
+ 'gap-4': gap === 'md',
28
+ 'gap-6': gap === 'lg',
29
+ 'gap-8': gap === 'xl',
30
+ },
31
+ className
32
+ )}
33
+ {...props}
34
+ />
35
+ );
36
+ }
37
+ );
38
+ Grid.displayName = 'Grid';
39
+
40
+ export { Grid };
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export interface InputProps
5
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
6
+
7
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
+ ({ className, type, ...props }, ref) => {
9
+ return (
10
+ <input
11
+ type={type}
12
+ className={cn(
13
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
14
+ className
15
+ )}
16
+ ref={ref}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+ );
22
+ Input.displayName = 'Input';
23
+
24
+ export { Input };
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { cn } from '../utils/cn';
4
+
5
+ const labelVariants = cva(
6
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
7
+ );
8
+
9
+ export interface LabelProps
10
+ extends React.LabelHTMLAttributes<HTMLLabelElement>,
11
+ VariantProps<typeof labelVariants> {}
12
+
13
+ const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
14
+ ({ className, ...props }, ref) => (
15
+ <label
16
+ ref={ref}
17
+ className={cn(labelVariants(), className)}
18
+ {...props}
19
+ />
20
+ )
21
+ );
22
+ Label.displayName = 'Label';
23
+
24
+ export { Label };