red64-cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/cli/parseArgs.d.ts.map +1 -1
  2. package/dist/cli/parseArgs.js +5 -0
  3. package/dist/cli/parseArgs.js.map +1 -1
  4. package/dist/components/init/CompleteStep.d.ts.map +1 -1
  5. package/dist/components/init/CompleteStep.js +2 -2
  6. package/dist/components/init/CompleteStep.js.map +1 -1
  7. package/dist/components/init/TestCheckStep.d.ts +16 -0
  8. package/dist/components/init/TestCheckStep.d.ts.map +1 -0
  9. package/dist/components/init/TestCheckStep.js +120 -0
  10. package/dist/components/init/TestCheckStep.js.map +1 -0
  11. package/dist/components/init/index.d.ts +1 -0
  12. package/dist/components/init/index.d.ts.map +1 -1
  13. package/dist/components/init/index.js +1 -0
  14. package/dist/components/init/index.js.map +1 -1
  15. package/dist/components/init/types.d.ts +9 -0
  16. package/dist/components/init/types.d.ts.map +1 -1
  17. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  18. package/dist/components/screens/InitScreen.js +69 -6
  19. package/dist/components/screens/InitScreen.js.map +1 -1
  20. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  21. package/dist/components/screens/StartScreen.js +89 -3
  22. package/dist/components/screens/StartScreen.js.map +1 -1
  23. package/dist/services/ConfigService.d.ts +1 -0
  24. package/dist/services/ConfigService.d.ts.map +1 -1
  25. package/dist/services/ConfigService.js.map +1 -1
  26. package/dist/services/ProjectDetector.d.ts +28 -0
  27. package/dist/services/ProjectDetector.d.ts.map +1 -0
  28. package/dist/services/ProjectDetector.js +236 -0
  29. package/dist/services/ProjectDetector.js.map +1 -0
  30. package/dist/services/TestRunner.d.ts +46 -0
  31. package/dist/services/TestRunner.d.ts.map +1 -0
  32. package/dist/services/TestRunner.js +85 -0
  33. package/dist/services/TestRunner.js.map +1 -0
  34. package/dist/services/index.d.ts +2 -0
  35. package/dist/services/index.d.ts.map +1 -1
  36. package/dist/services/index.js +2 -0
  37. package/dist/services/index.js.map +1 -1
  38. package/dist/types/index.d.ts +1 -0
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/index.js.map +1 -1
  41. package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
  42. package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
  43. package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
  44. package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
  45. package/framework/stacks/generic/feedback.md +80 -0
  46. package/framework/stacks/nextjs/accessibility.md +437 -0
  47. package/framework/stacks/nextjs/api.md +431 -0
  48. package/framework/stacks/nextjs/coding-style.md +282 -0
  49. package/framework/stacks/nextjs/commenting.md +226 -0
  50. package/framework/stacks/nextjs/components.md +411 -0
  51. package/framework/stacks/nextjs/conventions.md +333 -0
  52. package/framework/stacks/nextjs/css.md +310 -0
  53. package/framework/stacks/nextjs/error-handling.md +442 -0
  54. package/framework/stacks/nextjs/feedback.md +124 -0
  55. package/framework/stacks/nextjs/migrations.md +332 -0
  56. package/framework/stacks/nextjs/models.md +362 -0
  57. package/framework/stacks/nextjs/queries.md +410 -0
  58. package/framework/stacks/nextjs/responsive.md +338 -0
  59. package/framework/stacks/nextjs/tech-stack.md +177 -0
  60. package/framework/stacks/nextjs/test-writing.md +475 -0
  61. package/framework/stacks/nextjs/validation.md +467 -0
  62. package/framework/stacks/python/api.md +468 -0
  63. package/framework/stacks/python/authentication.md +342 -0
  64. package/framework/stacks/python/code-quality.md +283 -0
  65. package/framework/stacks/python/code-refactoring.md +315 -0
  66. package/framework/stacks/python/coding-style.md +462 -0
  67. package/framework/stacks/python/conventions.md +399 -0
  68. package/framework/stacks/python/error-handling.md +512 -0
  69. package/framework/stacks/python/feedback.md +92 -0
  70. package/framework/stacks/python/implement-ai-llm.md +468 -0
  71. package/framework/stacks/python/migrations.md +388 -0
  72. package/framework/stacks/python/models.md +399 -0
  73. package/framework/stacks/python/python.md +232 -0
  74. package/framework/stacks/python/queries.md +451 -0
  75. package/framework/stacks/python/structure.md +245 -58
  76. package/framework/stacks/python/tech.md +92 -35
  77. package/framework/stacks/python/testing.md +380 -0
  78. package/framework/stacks/python/validation.md +471 -0
  79. package/framework/stacks/rails/authentication.md +176 -0
  80. package/framework/stacks/rails/code-quality.md +287 -0
  81. package/framework/stacks/rails/code-refactoring.md +299 -0
  82. package/framework/stacks/rails/feedback.md +130 -0
  83. package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
  84. package/framework/stacks/rails/rails.md +301 -0
  85. package/framework/stacks/rails/rails8-best-practices.md +498 -0
  86. package/framework/stacks/rails/rails8-css.md +573 -0
  87. package/framework/stacks/rails/structure.md +140 -0
  88. package/framework/stacks/rails/tech.md +108 -0
  89. package/framework/stacks/react/code-quality.md +521 -0
  90. package/framework/stacks/react/components.md +625 -0
  91. package/framework/stacks/react/data-fetching.md +586 -0
  92. package/framework/stacks/react/feedback.md +110 -0
  93. package/framework/stacks/react/forms.md +694 -0
  94. package/framework/stacks/react/performance.md +640 -0
  95. package/framework/stacks/react/product.md +22 -9
  96. package/framework/stacks/react/state-management.md +472 -0
  97. package/framework/stacks/react/structure.md +351 -44
  98. package/framework/stacks/react/tech.md +219 -30
  99. package/framework/stacks/react/testing.md +690 -0
  100. package/package.json +1 -1
  101. package/framework/stacks/node/product.md +0 -27
  102. package/framework/stacks/node/structure.md +0 -82
  103. package/framework/stacks/node/tech.md +0 -63
@@ -0,0 +1,625 @@
1
+ # Component Patterns
2
+
3
+ Modern React component patterns for building maintainable, accessible, and reusable UI.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Composition over inheritance**: Build complex UIs from simple, composable pieces
10
+ - **Single responsibility**: Each component does one thing well
11
+ - **Props down, events up**: Data flows down, actions flow up
12
+ - **Accessibility first**: Build accessible components from the start
13
+
14
+ ---
15
+
16
+ ## Component Structure
17
+
18
+ ### Basic Component
19
+
20
+ ```typescript
21
+ // components/ui/Button/Button.tsx
22
+ import { forwardRef, type ComponentPropsWithoutRef } from 'react';
23
+ import { cn } from '@/utils/cn';
24
+
25
+ interface ButtonProps extends ComponentPropsWithoutRef<'button'> {
26
+ variant?: 'primary' | 'secondary' | 'ghost' | 'destructive';
27
+ size?: 'sm' | 'md' | 'lg';
28
+ isLoading?: boolean;
29
+ }
30
+
31
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
32
+ ({ className, variant = 'primary', size = 'md', isLoading, children, disabled, ...props }, ref) => {
33
+ return (
34
+ <button
35
+ ref={ref}
36
+ className={cn(
37
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
38
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
39
+ 'disabled:pointer-events-none disabled:opacity-50',
40
+ {
41
+ 'bg-primary text-primary-foreground hover:bg-primary/90': variant === 'primary',
42
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80': variant === 'secondary',
43
+ 'hover:bg-accent hover:text-accent-foreground': variant === 'ghost',
44
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90': variant === 'destructive',
45
+ },
46
+ {
47
+ 'h-8 px-3 text-sm': size === 'sm',
48
+ 'h-10 px-4': size === 'md',
49
+ 'h-12 px-6 text-lg': size === 'lg',
50
+ },
51
+ className
52
+ )}
53
+ disabled={disabled || isLoading}
54
+ {...props}
55
+ >
56
+ {isLoading && <Spinner className="mr-2 h-4 w-4" />}
57
+ {children}
58
+ </button>
59
+ );
60
+ }
61
+ );
62
+
63
+ Button.displayName = 'Button';
64
+ ```
65
+
66
+ ### Component with Children
67
+
68
+ ```typescript
69
+ // components/ui/Card/Card.tsx
70
+ import type { ReactNode } from 'react';
71
+ import { cn } from '@/utils/cn';
72
+
73
+ interface CardProps {
74
+ children: ReactNode;
75
+ className?: string;
76
+ }
77
+
78
+ export function Card({ children, className }: CardProps) {
79
+ return (
80
+ <div className={cn('rounded-lg border bg-card p-6 shadow-sm', className)}>
81
+ {children}
82
+ </div>
83
+ );
84
+ }
85
+
86
+ // Compound components
87
+ function CardHeader({ children, className }: CardProps) {
88
+ return <div className={cn('mb-4', className)}>{children}</div>;
89
+ }
90
+
91
+ function CardTitle({ children, className }: CardProps) {
92
+ return <h3 className={cn('text-lg font-semibold', className)}>{children}</h3>;
93
+ }
94
+
95
+ function CardContent({ children, className }: CardProps) {
96
+ return <div className={cn('', className)}>{children}</div>;
97
+ }
98
+
99
+ function CardFooter({ children, className }: CardProps) {
100
+ return <div className={cn('mt-4 flex items-center gap-2', className)}>{children}</div>;
101
+ }
102
+
103
+ // Attach compound components
104
+ Card.Header = CardHeader;
105
+ Card.Title = CardTitle;
106
+ Card.Content = CardContent;
107
+ Card.Footer = CardFooter;
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Composition Patterns
113
+
114
+ ### Compound Components
115
+
116
+ ```typescript
117
+ // Usage of compound components
118
+ <Card>
119
+ <Card.Header>
120
+ <Card.Title>User Profile</Card.Title>
121
+ </Card.Header>
122
+ <Card.Content>
123
+ <p>{user.bio}</p>
124
+ </Card.Content>
125
+ <Card.Footer>
126
+ <Button variant="secondary">Edit</Button>
127
+ <Button>Save</Button>
128
+ </Card.Footer>
129
+ </Card>
130
+ ```
131
+
132
+ ### Slot Pattern (Render Props)
133
+
134
+ ```typescript
135
+ // components/DataTable/DataTable.tsx
136
+ interface DataTableProps<T> {
137
+ data: T[];
138
+ columns: Column<T>[];
139
+ renderEmpty?: () => ReactNode;
140
+ renderLoading?: () => ReactNode;
141
+ isLoading?: boolean;
142
+ }
143
+
144
+ export function DataTable<T>({
145
+ data,
146
+ columns,
147
+ renderEmpty = () => <p>No data</p>,
148
+ renderLoading = () => <Spinner />,
149
+ isLoading,
150
+ }: DataTableProps<T>) {
151
+ if (isLoading) {
152
+ return renderLoading();
153
+ }
154
+
155
+ if (!data.length) {
156
+ return renderEmpty();
157
+ }
158
+
159
+ return (
160
+ <table>
161
+ <thead>
162
+ <tr>
163
+ {columns.map((col) => (
164
+ <th key={col.key}>{col.header}</th>
165
+ ))}
166
+ </tr>
167
+ </thead>
168
+ <tbody>
169
+ {data.map((row, i) => (
170
+ <tr key={i}>
171
+ {columns.map((col) => (
172
+ <td key={col.key}>{col.render(row)}</td>
173
+ ))}
174
+ </tr>
175
+ ))}
176
+ </tbody>
177
+ </table>
178
+ );
179
+ }
180
+
181
+ // Usage
182
+ <DataTable
183
+ data={users}
184
+ columns={[
185
+ { key: 'name', header: 'Name', render: (user) => user.name },
186
+ { key: 'email', header: 'Email', render: (user) => user.email },
187
+ { key: 'actions', header: '', render: (user) => <UserActions user={user} /> },
188
+ ]}
189
+ renderEmpty={() => <EmptyState icon={UsersIcon} message="No users found" />}
190
+ />
191
+ ```
192
+
193
+ ### Children as Function
194
+
195
+ ```typescript
196
+ // components/Disclosure/Disclosure.tsx
197
+ interface DisclosureProps {
198
+ children: (props: { isOpen: boolean; toggle: () => void }) => ReactNode;
199
+ defaultOpen?: boolean;
200
+ }
201
+
202
+ export function Disclosure({ children, defaultOpen = false }: DisclosureProps) {
203
+ const [isOpen, setIsOpen] = useState(defaultOpen);
204
+ const toggle = () => setIsOpen((prev) => !prev);
205
+
206
+ return <>{children({ isOpen, toggle })}</>;
207
+ }
208
+
209
+ // Usage
210
+ <Disclosure>
211
+ {({ isOpen, toggle }) => (
212
+ <div>
213
+ <button onClick={toggle}>
214
+ {isOpen ? 'Hide' : 'Show'} Details
215
+ </button>
216
+ {isOpen && <div>Hidden content here</div>}
217
+ </div>
218
+ )}
219
+ </Disclosure>
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Props Patterns
225
+
226
+ ### Polymorphic Components (as prop)
227
+
228
+ ```typescript
229
+ // components/ui/Box/Box.tsx
230
+ import { type ElementType, type ComponentPropsWithoutRef } from 'react';
231
+
232
+ type BoxProps<T extends ElementType> = {
233
+ as?: T;
234
+ children?: ReactNode;
235
+ } & Omit<ComponentPropsWithoutRef<T>, 'as'>;
236
+
237
+ export function Box<T extends ElementType = 'div'>({
238
+ as,
239
+ children,
240
+ ...props
241
+ }: BoxProps<T>) {
242
+ const Component = as || 'div';
243
+ return <Component {...props}>{children}</Component>;
244
+ }
245
+
246
+ // Usage
247
+ <Box as="section" className="p-4">Content</Box>
248
+ <Box as="article">Article content</Box>
249
+ <Box as="a" href="/about">Link styled as box</Box>
250
+ ```
251
+
252
+ ### Spreading Native Props
253
+
254
+ ```typescript
255
+ // Always extend native element props for flexibility
256
+ interface InputProps extends ComponentPropsWithoutRef<'input'> {
257
+ label: string;
258
+ error?: string;
259
+ }
260
+
261
+ export function Input({ label, error, className, ...props }: InputProps) {
262
+ return (
263
+ <div>
264
+ <label>{label}</label>
265
+ <input
266
+ className={cn('input', error && 'input-error', className)}
267
+ {...props} // Spread all native input props
268
+ />
269
+ {error && <span className="text-red-500">{error}</span>}
270
+ </div>
271
+ );
272
+ }
273
+
274
+ // All native props work
275
+ <Input
276
+ label="Email"
277
+ type="email"
278
+ placeholder="Enter email"
279
+ required
280
+ autoComplete="email"
281
+ onChange={handleChange}
282
+ />
283
+ ```
284
+
285
+ ### Default Props Pattern
286
+
287
+ ```typescript
288
+ // Use default values in destructuring
289
+ interface AvatarProps {
290
+ src?: string;
291
+ alt: string;
292
+ size?: 'sm' | 'md' | 'lg';
293
+ fallback?: string;
294
+ }
295
+
296
+ export function Avatar({
297
+ src,
298
+ alt,
299
+ size = 'md', // Default value
300
+ fallback,
301
+ }: AvatarProps) {
302
+ const [error, setError] = useState(false);
303
+
304
+ const sizeClasses = {
305
+ sm: 'h-8 w-8',
306
+ md: 'h-10 w-10',
307
+ lg: 'h-14 w-14',
308
+ };
309
+
310
+ if (error || !src) {
311
+ return (
312
+ <div className={cn('rounded-full bg-gray-200', sizeClasses[size])}>
313
+ {fallback || alt.charAt(0).toUpperCase()}
314
+ </div>
315
+ );
316
+ }
317
+
318
+ return (
319
+ <img
320
+ src={src}
321
+ alt={alt}
322
+ className={cn('rounded-full object-cover', sizeClasses[size])}
323
+ onError={() => setError(true)}
324
+ />
325
+ );
326
+ }
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Controlled vs Uncontrolled
332
+
333
+ ### Controlled Component
334
+
335
+ ```typescript
336
+ // Parent controls the state
337
+ interface ControlledInputProps {
338
+ value: string;
339
+ onChange: (value: string) => void;
340
+ }
341
+
342
+ export function ControlledInput({ value, onChange }: ControlledInputProps) {
343
+ return (
344
+ <input
345
+ value={value}
346
+ onChange={(e) => onChange(e.target.value)}
347
+ />
348
+ );
349
+ }
350
+
351
+ // Usage
352
+ const [email, setEmail] = useState('');
353
+ <ControlledInput value={email} onChange={setEmail} />
354
+ ```
355
+
356
+ ### Uncontrolled Component
357
+
358
+ ```typescript
359
+ // Component manages its own state
360
+ export function UncontrolledInput({ defaultValue }: { defaultValue?: string }) {
361
+ const inputRef = useRef<HTMLInputElement>(null);
362
+
363
+ const getValue = () => inputRef.current?.value;
364
+
365
+ return <input ref={inputRef} defaultValue={defaultValue} />;
366
+ }
367
+ ```
368
+
369
+ ### Hybrid (Controlled with Default)
370
+
371
+ ```typescript
372
+ // Support both controlled and uncontrolled usage
373
+ interface HybridInputProps {
374
+ value?: string;
375
+ defaultValue?: string;
376
+ onChange?: (value: string) => void;
377
+ }
378
+
379
+ export function HybridInput({ value, defaultValue, onChange }: HybridInputProps) {
380
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '');
381
+
382
+ // Use controlled value if provided, otherwise use internal
383
+ const currentValue = value !== undefined ? value : internalValue;
384
+
385
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
386
+ const newValue = e.target.value;
387
+ if (value === undefined) {
388
+ setInternalValue(newValue);
389
+ }
390
+ onChange?.(newValue);
391
+ };
392
+
393
+ return <input value={currentValue} onChange={handleChange} />;
394
+ }
395
+ ```
396
+
397
+ ---
398
+
399
+ ## Event Handling
400
+
401
+ ### Event Handler Types
402
+
403
+ ```typescript
404
+ interface FormProps {
405
+ onSubmit: (data: FormData) => void;
406
+ // Use React event types
407
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
408
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
409
+ onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>;
410
+ }
411
+ ```
412
+
413
+ ### Preventing Default
414
+
415
+ ```typescript
416
+ function Form({ onSubmit }: FormProps) {
417
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
418
+ e.preventDefault();
419
+ // Process form
420
+ onSubmit(new FormData(e.currentTarget));
421
+ };
422
+
423
+ return <form onSubmit={handleSubmit}>{/* ... */}</form>;
424
+ }
425
+ ```
426
+
427
+ ### Event Delegation
428
+
429
+ ```typescript
430
+ function List({ items, onItemClick }: { items: Item[]; onItemClick: (id: string) => void }) {
431
+ // Single handler on parent, not individual handlers
432
+ const handleClick = (e: React.MouseEvent<HTMLUListElement>) => {
433
+ const target = e.target as HTMLElement;
434
+ const item = target.closest('[data-item-id]');
435
+ if (item) {
436
+ onItemClick(item.getAttribute('data-item-id')!);
437
+ }
438
+ };
439
+
440
+ return (
441
+ <ul onClick={handleClick}>
442
+ {items.map((item) => (
443
+ <li key={item.id} data-item-id={item.id}>
444
+ {item.name}
445
+ </li>
446
+ ))}
447
+ </ul>
448
+ );
449
+ }
450
+ ```
451
+
452
+ ---
453
+
454
+ ## Accessibility Patterns
455
+
456
+ ### Keyboard Navigation
457
+
458
+ ```typescript
459
+ function Menu({ items }: { items: MenuItem[] }) {
460
+ const [focusIndex, setFocusIndex] = useState(0);
461
+
462
+ const handleKeyDown = (e: React.KeyboardEvent) => {
463
+ switch (e.key) {
464
+ case 'ArrowDown':
465
+ e.preventDefault();
466
+ setFocusIndex((i) => (i + 1) % items.length);
467
+ break;
468
+ case 'ArrowUp':
469
+ e.preventDefault();
470
+ setFocusIndex((i) => (i - 1 + items.length) % items.length);
471
+ break;
472
+ case 'Enter':
473
+ case ' ':
474
+ e.preventDefault();
475
+ items[focusIndex].action();
476
+ break;
477
+ }
478
+ };
479
+
480
+ return (
481
+ <ul role="menu" onKeyDown={handleKeyDown}>
482
+ {items.map((item, index) => (
483
+ <li
484
+ key={item.id}
485
+ role="menuitem"
486
+ tabIndex={index === focusIndex ? 0 : -1}
487
+ >
488
+ {item.label}
489
+ </li>
490
+ ))}
491
+ </ul>
492
+ );
493
+ }
494
+ ```
495
+
496
+ ### ARIA Attributes
497
+
498
+ ```typescript
499
+ function ExpandableSection({ title, children }: { title: string; children: ReactNode }) {
500
+ const [isExpanded, setIsExpanded] = useState(false);
501
+ const contentId = useId();
502
+
503
+ return (
504
+ <div>
505
+ <button
506
+ aria-expanded={isExpanded}
507
+ aria-controls={contentId}
508
+ onClick={() => setIsExpanded(!isExpanded)}
509
+ >
510
+ {title}
511
+ <ChevronIcon className={isExpanded ? 'rotate-180' : ''} />
512
+ </button>
513
+ <div
514
+ id={contentId}
515
+ role="region"
516
+ aria-labelledby={contentId}
517
+ hidden={!isExpanded}
518
+ >
519
+ {children}
520
+ </div>
521
+ </div>
522
+ );
523
+ }
524
+ ```
525
+
526
+ ### Focus Management
527
+
528
+ ```typescript
529
+ function Modal({ isOpen, onClose, children }: ModalProps) {
530
+ const closeButtonRef = useRef<HTMLButtonElement>(null);
531
+
532
+ // Focus close button when modal opens
533
+ useEffect(() => {
534
+ if (isOpen) {
535
+ closeButtonRef.current?.focus();
536
+ }
537
+ }, [isOpen]);
538
+
539
+ // Trap focus inside modal
540
+ const handleKeyDown = (e: React.KeyboardEvent) => {
541
+ if (e.key === 'Escape') {
542
+ onClose();
543
+ }
544
+ };
545
+
546
+ if (!isOpen) return null;
547
+
548
+ return (
549
+ <div
550
+ role="dialog"
551
+ aria-modal="true"
552
+ onKeyDown={handleKeyDown}
553
+ >
554
+ <button ref={closeButtonRef} onClick={onClose}>
555
+ Close
556
+ </button>
557
+ {children}
558
+ </div>
559
+ );
560
+ }
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Using Radix UI Primitives
566
+
567
+ ```typescript
568
+ // Build accessible components on top of Radix
569
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
570
+ import { cn } from '@/utils/cn';
571
+
572
+ export function Dialog({ children, ...props }: DialogPrimitive.DialogProps) {
573
+ return <DialogPrimitive.Root {...props}>{children}</DialogPrimitive.Root>;
574
+ }
575
+
576
+ export function DialogTrigger({ children, ...props }: DialogPrimitive.DialogTriggerProps) {
577
+ return <DialogPrimitive.Trigger asChild {...props}>{children}</DialogPrimitive.Trigger>;
578
+ }
579
+
580
+ export function DialogContent({ children, className, ...props }: DialogPrimitive.DialogContentProps) {
581
+ return (
582
+ <DialogPrimitive.Portal>
583
+ <DialogPrimitive.Overlay className="fixed inset-0 bg-black/50" />
584
+ <DialogPrimitive.Content
585
+ className={cn(
586
+ 'fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
587
+ 'rounded-lg bg-white p-6 shadow-lg',
588
+ className
589
+ )}
590
+ {...props}
591
+ >
592
+ {children}
593
+ </DialogPrimitive.Content>
594
+ </DialogPrimitive.Portal>
595
+ );
596
+ }
597
+
598
+ // Usage
599
+ <Dialog>
600
+ <DialogTrigger>
601
+ <Button>Open Dialog</Button>
602
+ </DialogTrigger>
603
+ <DialogContent>
604
+ <h2>Dialog Title</h2>
605
+ <p>Dialog content here</p>
606
+ </DialogContent>
607
+ </Dialog>
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Anti-Patterns
613
+
614
+ | Anti-Pattern | Problem | Correct Approach |
615
+ |--------------|---------|------------------|
616
+ | Props drilling | Hard to maintain | Use context or composition |
617
+ | Huge components | Hard to test, understand | Split into smaller components |
618
+ | Business logic in components | Not reusable, hard to test | Extract to hooks or services |
619
+ | Inline styles everywhere | No consistency | Use Tailwind or CSS modules |
620
+ | Missing key prop | Performance issues, bugs | Always provide stable keys |
621
+ | forwardRef forgotten | Can't access ref | Use forwardRef for reusable components |
622
+
623
+ ---
624
+
625
+ _Components are the atoms of your UI. Keep them small, focused, and composable._