@umituz/web-design-system 1.0.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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +235 -0
  3. package/package.json +64 -0
  4. package/src/domain/tokens/color.tokens.ts +95 -0
  5. package/src/domain/tokens/index.ts +44 -0
  6. package/src/domain/tokens/radius.tokens.ts +27 -0
  7. package/src/domain/tokens/shadow.tokens.ts +25 -0
  8. package/src/domain/tokens/spacing.tokens.ts +65 -0
  9. package/src/domain/tokens/typography.tokens.ts +74 -0
  10. package/src/domain/types/component.types.ts +23 -0
  11. package/src/domain/types/index.ts +14 -0
  12. package/src/index.ts +28 -0
  13. package/src/infrastructure/constants/component.constants.ts +24 -0
  14. package/src/infrastructure/constants/index.ts +13 -0
  15. package/src/infrastructure/utils/cn.util.ts +13 -0
  16. package/src/infrastructure/utils/index.ts +10 -0
  17. package/src/presentation/atoms/Badge.tsx +46 -0
  18. package/src/presentation/atoms/Button.tsx +54 -0
  19. package/src/presentation/atoms/Icon.tsx +37 -0
  20. package/src/presentation/atoms/Input.tsx +44 -0
  21. package/src/presentation/atoms/Spinner.tsx +39 -0
  22. package/src/presentation/atoms/Text.tsx +64 -0
  23. package/src/presentation/atoms/index.ts +23 -0
  24. package/src/presentation/hooks/index.ts +13 -0
  25. package/src/presentation/hooks/useLocalStorage.ts +44 -0
  26. package/src/presentation/hooks/useMediaQuery.ts +46 -0
  27. package/src/presentation/hooks/useTheme.ts +51 -0
  28. package/src/presentation/molecules/Avatar.tsx +52 -0
  29. package/src/presentation/molecules/Chip.tsx +58 -0
  30. package/src/presentation/molecules/FormField.tsx +61 -0
  31. package/src/presentation/molecules/SearchBox.tsx +45 -0
  32. package/src/presentation/molecules/Toggle.tsx +61 -0
  33. package/src/presentation/molecules/index.ts +20 -0
  34. package/src/presentation/organisms/Alert.tsx +96 -0
  35. package/src/presentation/organisms/Card.tsx +90 -0
  36. package/src/presentation/organisms/Modal.tsx +130 -0
  37. package/src/presentation/organisms/Navbar.tsx +74 -0
  38. package/src/presentation/organisms/index.ts +17 -0
  39. package/src/presentation/templates/Form.tsx +41 -0
  40. package/src/presentation/templates/List.tsx +46 -0
  41. package/src/presentation/templates/Section.tsx +39 -0
  42. package/src/presentation/templates/index.ts +14 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * FormField Component (Molecule)
3
+ * @description Label + Input combination
4
+ */
5
+
6
+ import { forwardRef, type ReactNode } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+ import { Input } from '../atoms/Input';
10
+ import { Text } from '../atoms/Text';
11
+
12
+ export interface FormFieldProps extends BaseProps {
13
+ label?: string;
14
+ error?: string;
15
+ helperText?: string;
16
+ required?: boolean;
17
+ id?: string;
18
+ inputProps?: React.ComponentProps<typeof Input>;
19
+ }
20
+
21
+ export const FormField = forwardRef<HTMLInputElement, FormFieldProps>(
22
+ ({ label, error, helperText, required, id, className, inputProps, ...props }, ref) => {
23
+ const fieldId = id || inputProps?.id || inputProps?.name;
24
+ const errorId = error ? `${fieldId}-error` : undefined;
25
+ const helperId = helperText ? `${fieldId}-helper` : undefined;
26
+
27
+ return (
28
+ <div className={cn('space-y-1.5', className)} {...props}>
29
+ {label && (
30
+ <Text as="label" variant="label" size="sm" htmlFor={fieldId}>
31
+ {label}
32
+ {required && <span className="text-destructive ml-1">*</span>}
33
+ </Text>
34
+ )}
35
+
36
+ <Input
37
+ ref={ref}
38
+ id={fieldId}
39
+ aria-invalid={!!error}
40
+ aria-describedby={cn(errorId, helperId)}
41
+ error={!!error}
42
+ {...inputProps}
43
+ />
44
+
45
+ {error && (
46
+ <Text as="p" variant="caption" size="sm" id={errorId} className="text-destructive">
47
+ {error}
48
+ </Text>
49
+ )}
50
+
51
+ {helperText && !error && (
52
+ <Text as="p" variant="caption" size="sm" id={helperId} className="text-muted-foreground">
53
+ {helperText}
54
+ </Text>
55
+ )}
56
+ </div>
57
+ );
58
+ }
59
+ );
60
+
61
+ FormField.displayName = 'FormField';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * SearchBox Component (Molecule)
3
+ * @description Input with search icon
4
+ */
5
+
6
+ import { forwardRef } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+ import { Input } from '../atoms/Input';
10
+ import { Icon } from '../atoms/Icon';
11
+
12
+ export interface SearchBoxProps extends BaseProps {
13
+ placeholder?: string;
14
+ value?: string;
15
+ onChange?: (value: string) => void;
16
+ }
17
+
18
+ export const SearchBox = forwardRef<HTMLInputElement, SearchBoxProps>(
19
+ ({ placeholder = 'Search...', value, onChange, className, ...props }, ref) => {
20
+ return (
21
+ <div className={cn('relative', className)} {...props}>
22
+ <Icon
23
+ className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground"
24
+ size="sm"
25
+ >
26
+ <path
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
30
+ />
31
+ </Icon>
32
+ <Input
33
+ ref={ref}
34
+ type="text"
35
+ placeholder={placeholder}
36
+ value={value}
37
+ onChange={(e) => onChange?.(e.target.value)}
38
+ className="pl-9"
39
+ />
40
+ </div>
41
+ );
42
+ }
43
+ );
44
+
45
+ SearchBox.displayName = 'SearchBox';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Toggle Component (Molecule)
3
+ * @description Switch/toggle button
4
+ */
5
+
6
+ import { forwardRef, type ButtonHTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps } from '../../domain/types';
9
+
10
+ export interface ToggleProps extends ButtonHTMLAttributes<HTMLButtonElement>, BaseProps {
11
+ pressed?: boolean;
12
+ onPressedChange?: (pressed: boolean) => void;
13
+ size?: 'sm' | 'md' | 'lg';
14
+ }
15
+
16
+ const sizeStyles = {
17
+ sm: 'h-5 w-9',
18
+ md: 'h-6 w-11',
19
+ lg: 'h-7 w-13',
20
+ };
21
+
22
+ const thumbSizeStyles = {
23
+ sm: 'h-4 w-4 data-[state=checked:translate-x-4',
24
+ md: 'h-5 w-5 data-[state=checked:translate-x-5',
25
+ lg: 'h-6 w-6 data-[state=checked:translate-x-6',
26
+ };
27
+
28
+ export const Toggle = forwardRef<HTMLButtonElement, ToggleProps>(
29
+ ({ className, pressed = false, onPressedChange, size = 'md', disabled, ...props }, ref) => {
30
+ return (
31
+ <button
32
+ ref={ref}
33
+ type="button"
34
+ role="switch"
35
+ aria-checked={pressed}
36
+ disabled={disabled}
37
+ data-state={pressed ? 'checked' : 'unchecked'}
38
+ onClick={() => onPressedChange?.(!pressed)}
39
+ className={cn(
40
+ 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent',
41
+ 'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
42
+ 'disabled:cursor-not-allowed disabled:opacity-50',
43
+ 'data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
44
+ sizeStyles[size],
45
+ className
46
+ )}
47
+ {...props}
48
+ >
49
+ <span
50
+ className={cn(
51
+ 'pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform',
52
+ thumbSizeStyles[size],
53
+ 'data-[state=unchecked]:translate-x-0'
54
+ )}
55
+ />
56
+ </button>
57
+ );
58
+ }
59
+ );
60
+
61
+ Toggle.displayName = 'Toggle';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Molecules Export
3
+ * @description Molecule components - combinations of atoms
4
+ * Subpath: @umituz/web-design-system/molecules
5
+ */
6
+
7
+ export { FormField } from './FormField';
8
+ export type { FormFieldProps } from './FormField';
9
+
10
+ export { SearchBox } from './SearchBox';
11
+ export type { SearchBoxProps } from './SearchBox';
12
+
13
+ export { Avatar } from './Avatar';
14
+ export type { AvatarProps } from './Avatar';
15
+
16
+ export { Chip } from './Chip';
17
+ export type { ChipProps } from './Chip';
18
+
19
+ export { Toggle } from './Toggle';
20
+ export type { ToggleProps } from './Toggle';
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Alert Component (Organism)
3
+ * @description Feedback message with icon
4
+ */
5
+
6
+ import { forwardRef, type HTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps, ColorVariant } from '../../domain/types';
9
+ import { Icon } from '../atoms/Icon';
10
+
11
+ export type AlertVariant = Extract<ColorVariant, 'success' | 'warning' | 'destructive'> | 'info';
12
+
13
+ export interface AlertProps extends HTMLAttributes<HTMLDivElement>, BaseProps, ChildrenProps {
14
+ variant?: AlertVariant;
15
+ showIcon?: boolean;
16
+ }
17
+
18
+ const variantStyles: Record<AlertVariant, { container: string; iconColor: string }> = {
19
+ success: {
20
+ container: 'border-success bg-success/10 text-success',
21
+ iconColor: 'text-success',
22
+ },
23
+ warning: {
24
+ container: 'border-warning bg-warning/10 text-warning',
25
+ iconColor: 'text-warning',
26
+ },
27
+ destructive: {
28
+ container: 'border-destructive bg-destructive/10 text-destructive',
29
+ iconColor: 'text-destructive',
30
+ },
31
+ info: {
32
+ container: 'border-primary bg-primary/10 text-primary',
33
+ iconColor: 'text-primary',
34
+ },
35
+ };
36
+
37
+ const icons: Record<AlertVariant, React.ReactNode> = {
38
+ success: (
39
+ <path
40
+ strokeLinecap="round"
41
+ strokeLinejoin="round"
42
+ d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
43
+ />
44
+ ),
45
+ warning: (
46
+ <path
47
+ strokeLinecap="round"
48
+ strokeLinejoin="round"
49
+ d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
50
+ />
51
+ ),
52
+ destructive: (
53
+ <path
54
+ strokeLinecap="round"
55
+ strokeLinejoin="round"
56
+ d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z"
57
+ />
58
+ ),
59
+ info: (
60
+ <path
61
+ strokeLinecap="round"
62
+ strokeLinejoin="round"
63
+ d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
64
+ />
65
+ ),
66
+ };
67
+
68
+ export const Alert = forwardRef<HTMLDivElement, AlertProps>(
69
+ ({ className, variant = 'info', showIcon = true, children, ...props }, ref) => {
70
+ const styles = variantStyles[variant];
71
+
72
+ return (
73
+ <div
74
+ ref={ref}
75
+ role="alert"
76
+ className={cn(
77
+ 'relative w-full rounded-lg border p-4',
78
+ styles.container,
79
+ className
80
+ )}
81
+ {...props}
82
+ >
83
+ <div className="flex items-start gap-3">
84
+ {showIcon && (
85
+ <Icon className={cn('shrink-0 mt-0.5', styles.iconColor)} size="sm">
86
+ {icons[variant]}
87
+ </Icon>
88
+ )}
89
+ <div className="flex-1">{children}</div>
90
+ </div>
91
+ </div>
92
+ );
93
+ }
94
+ );
95
+
96
+ Alert.displayName = 'Alert';
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Card Component (Organism)
3
+ * @description Content container with header, body, footer
4
+ */
5
+
6
+ import { forwardRef, type HTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps } from '../../domain/types';
9
+
10
+ export interface CardProps extends HTMLAttributes<HTMLDivElement>, BaseProps, ChildrenProps {
11
+ variant?: 'default' | 'outlined' | 'elevated';
12
+ }
13
+
14
+ const variantStyles: Record<'default' | 'outlined' | 'elevated', string> = {
15
+ default: 'border border-border bg-card text-card-foreground',
16
+ outlined: 'border-2 border-border bg-transparent',
17
+ elevated: 'border-0 shadow-lg bg-card text-card-foreground',
18
+ };
19
+
20
+ export const Card = forwardRef<HTMLDivElement, CardProps>(
21
+ ({ className, variant = 'default', children, ...props }, ref) => {
22
+ return (
23
+ <div
24
+ ref={ref}
25
+ className={cn('rounded-lg', variantStyles[variant], className)}
26
+ {...props}
27
+ >
28
+ {children}
29
+ </div>
30
+ );
31
+ }
32
+ );
33
+
34
+ Card.displayName = 'Card';
35
+
36
+ export const CardHeader = forwardRef<HTMLDivElement, CardProps>(
37
+ ({ className, ...props }, ref) => (
38
+ <div
39
+ ref={ref}
40
+ className={cn('flex flex-col space-y-1.5 p-6', className)}
41
+ {...props}
42
+ />
43
+ )
44
+ );
45
+
46
+ CardHeader.displayName = 'CardHeader';
47
+
48
+ export const CardTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>(
49
+ ({ className, ...props }, ref) => (
50
+ <h3
51
+ ref={ref}
52
+ className={cn('text-lg font-semibold leading-none tracking-tight', className)}
53
+ {...props}
54
+ />
55
+ )
56
+ );
57
+
58
+ CardTitle.displayName = 'CardTitle';
59
+
60
+ export const CardDescription = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLParagraphElement>>(
61
+ ({ className, ...props }, ref) => (
62
+ <p
63
+ ref={ref}
64
+ className={cn('text-sm text-muted-foreground', className)}
65
+ {...props}
66
+ />
67
+ )
68
+ );
69
+
70
+ CardDescription.displayName = 'CardDescription';
71
+
72
+ export const CardContent = forwardRef<HTMLDivElement, CardProps>(
73
+ ({ className, ...props }, ref) => (
74
+ <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
75
+ )
76
+ );
77
+
78
+ CardContent.displayName = 'CardContent';
79
+
80
+ export const CardFooter = forwardRef<HTMLDivElement, CardProps>(
81
+ ({ className, ...props }, ref) => (
82
+ <div
83
+ ref={ref}
84
+ className={cn('flex items-center p-6 pt-0', className)}
85
+ {...props}
86
+ />
87
+ )
88
+ );
89
+
90
+ CardFooter.displayName = 'CardFooter';
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Modal Component (Organism)
3
+ * @description Dialog/overlay container
4
+ */
5
+
6
+ import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps } from '../../domain/types';
9
+
10
+ export interface ModalProps extends HTMLAttributes<HTMLDivElement>, BaseProps {
11
+ open?: boolean;
12
+ onClose?: () => void;
13
+ showCloseButton?: boolean;
14
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
15
+ }
16
+
17
+ const sizeStyles: Record<'sm' | 'md' | 'lg' | 'xl' | 'full', string> = {
18
+ sm: 'max-w-sm',
19
+ md: 'max-w-md',
20
+ lg: 'max-w-lg',
21
+ xl: 'max-w-xl',
22
+ full: 'max-w-full mx-4',
23
+ };
24
+
25
+ export const Modal = forwardRef<HTMLDivElement, ModalProps>(
26
+ ({ open = false, onClose, showCloseButton = true, size = 'md', className, children, ...props }, ref) => {
27
+ if (!open) return null;
28
+
29
+ return (
30
+ <div
31
+ className="fixed inset-0 z-50 flex items-center justify-center"
32
+ {...props}
33
+ >
34
+ {/* Backdrop */}
35
+ <div
36
+ className="fixed inset-0 bg-background/80 backdrop-blur-sm"
37
+ onClick={onClose}
38
+ />
39
+
40
+ {/* Modal */}
41
+ <div
42
+ ref={ref}
43
+ className={cn(
44
+ 'relative z-50 w-full rounded-lg border bg-card p-6 shadow-lg',
45
+ 'animate-in fade-in-0 zoom-in-95',
46
+ sizeStyles[size],
47
+ className
48
+ )}
49
+ role="dialog"
50
+ aria-modal="true"
51
+ >
52
+ {showCloseButton && (
53
+ <button
54
+ onClick={onClose}
55
+ className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
56
+ >
57
+ <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
58
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
59
+ </svg>
60
+ <span className="sr-only">Close</span>
61
+ </button>
62
+ )}
63
+
64
+ {children}
65
+ </div>
66
+ </div>
67
+ );
68
+ }
69
+ );
70
+
71
+ Modal.displayName = 'Modal';
72
+
73
+ export const ModalHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
74
+ ({ className, ...props }, ref) => (
75
+ <div
76
+ ref={ref}
77
+ className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)}
78
+ {...props}
79
+ />
80
+ )
81
+ );
82
+
83
+ ModalHeader.displayName = 'ModalHeader';
84
+
85
+ export const ModalTitle = forwardRef<HTMLHeadingElement, HTMLAttributes<HTMLHeadingElement>>(
86
+ ({ className, ...props }, ref) => (
87
+ <h2
88
+ ref={ref}
89
+ className={cn('text-lg font-semibold leading-none tracking-tight', className)}
90
+ {...props}
91
+ />
92
+ )
93
+ );
94
+
95
+ ModalTitle.displayName = 'ModalTitle';
96
+
97
+ export const ModalDescription = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLParagraphElement>>(
98
+ ({ className, ...props }, ref) => (
99
+ <p
100
+ ref={ref}
101
+ className={cn('text-sm text-muted-foreground', className)}
102
+ {...props}
103
+ />
104
+ )
105
+ );
106
+
107
+ ModalDescription.displayName = 'ModalDescription';
108
+
109
+ export const ModalContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
110
+ ({ className, ...props }, ref) => (
111
+ <div ref={ref} className={cn('mt-4', className)} {...props} />
112
+ )
113
+ );
114
+
115
+ ModalContent.displayName = 'ModalContent';
116
+
117
+ export const ModalFooter = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
118
+ ({ className, ...props }, ref) => (
119
+ <div
120
+ ref={ref}
121
+ className={cn(
122
+ 'mt-4 flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
123
+ className
124
+ )}
125
+ {...props}
126
+ />
127
+ )
128
+ );
129
+
130
+ ModalFooter.displayName = 'ModalFooter';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Navbar Component (Organism)
3
+ * @description Navigation bar with logo and links
4
+ */
5
+
6
+ import { forwardRef, type HTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps } from '../../domain/types';
9
+
10
+ export interface NavbarProps extends HTMLAttributes<HTMLElement>, BaseProps, ChildrenProps {
11
+ variant?: 'default' | 'sticky' | 'fixed';
12
+ }
13
+
14
+ const variantStyles: Record<'default' | 'sticky' | 'fixed', string> = {
15
+ default: 'relative',
16
+ sticky: 'sticky top-0 z-50',
17
+ fixed: 'fixed top-0 left-0 right-0 z-50',
18
+ };
19
+
20
+ export const Navbar = forwardRef<HTMLElement, NavbarProps>(
21
+ ({ className, variant = 'default', children, ...props }, ref) => {
22
+ return (
23
+ <nav
24
+ ref={ref}
25
+ className={cn(
26
+ 'flex h-16 items-center gap-4 border-b border-border/40 bg-background/95 px-4 backdrop-blur supports-[backdrop-filter]:bg-background/60',
27
+ variantStyles[variant],
28
+ className
29
+ )}
30
+ {...props}
31
+ >
32
+ {children}
33
+ </nav>
34
+ );
35
+ }
36
+ );
37
+
38
+ Navbar.displayName = 'Navbar';
39
+
40
+ export const NavbarBrand = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
41
+ ({ className, ...props }, ref) => (
42
+ <div
43
+ ref={ref}
44
+ className={cn('flex items-center gap-2 font-bold text-lg', className)}
45
+ {...props}
46
+ />
47
+ )
48
+ );
49
+
50
+ NavbarBrand.displayName = 'NavbarBrand';
51
+
52
+ export const NavbarLinks = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
53
+ ({ className, ...props }, ref) => (
54
+ <div
55
+ ref={ref}
56
+ className={cn('hidden items-center gap-6 md:flex', className)}
57
+ {...props}
58
+ />
59
+ )
60
+ );
61
+
62
+ NavbarLinks.displayName = 'NavbarLinks';
63
+
64
+ export const NavbarActions = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
65
+ ({ className, ...props }, ref) => (
66
+ <div
67
+ ref={ref}
68
+ className={cn('ml-auto flex items-center gap-2', className)}
69
+ {...props}
70
+ />
71
+ )
72
+ );
73
+
74
+ NavbarActions.displayName = 'NavbarActions';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Organisms Export
3
+ * @description Organism components - complex UI combinations
4
+ * Subpath: @umituz/web-design-system/organisms
5
+ */
6
+
7
+ export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
8
+ export type { CardProps } from './Card';
9
+
10
+ export { Alert } from './Alert';
11
+ export type { AlertProps, AlertVariant } from './Alert';
12
+
13
+ export { Modal, ModalHeader, ModalTitle, ModalDescription, ModalContent, ModalFooter } from './Modal';
14
+ export type { ModalProps } from './Modal';
15
+
16
+ export { Navbar, NavbarBrand, NavbarLinks, NavbarActions } from './Navbar';
17
+ export type { NavbarProps } from './Navbar';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Form Template (Template)
3
+ * @description Complete form structure
4
+ */
5
+
6
+ import { forwardRef, type FormHTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps } from '../../domain/types';
9
+
10
+ export interface FormProps extends FormHTMLAttributes<HTMLFormElement>, BaseProps, ChildrenProps {
11
+ onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void;
12
+ }
13
+
14
+ export const Form = forwardRef<HTMLFormElement, FormProps>(
15
+ ({ className, onSubmit, children, ...props }, ref) => {
16
+ return (
17
+ <form
18
+ ref={ref}
19
+ onSubmit={onSubmit}
20
+ className={cn('space-y-4', className)}
21
+ {...props}
22
+ >
23
+ {children}
24
+ </form>
25
+ );
26
+ }
27
+ );
28
+
29
+ Form.displayName = 'Form';
30
+
31
+ export const FormActions = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
32
+ ({ className, ...props }, ref) => (
33
+ <div
34
+ ref={ref}
35
+ className={cn('flex items-center gap-2 justify-end', className)}
36
+ {...props}
37
+ />
38
+ )
39
+ );
40
+
41
+ FormActions.displayName = 'FormActions';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * List Template (Template)
3
+ * @description Structured list container
4
+ */
5
+
6
+ import { forwardRef, type HTMLAttributes } from 'react';
7
+ import { cn } from '../../infrastructure/utils';
8
+ import type { BaseProps, ChildrenProps } from '../../domain/types';
9
+
10
+ export interface ListProps extends HTMLAttributes<HTMLUListElement>, BaseProps, ChildrenProps {
11
+ variant?: 'default' | 'bordered' | 'spaced';
12
+ }
13
+
14
+ const variantStyles: Record<'default' | 'bordered' | 'spaced', string> = {
15
+ default: '',
16
+ bordered: 'divide-y divide-border',
17
+ spaced: 'space-y-2',
18
+ };
19
+
20
+ export const List = forwardRef<HTMLUListElement, ListProps>(
21
+ ({ className, variant = 'default', children, ...props }, ref) => {
22
+ return (
23
+ <ul
24
+ ref={ref}
25
+ className={cn(variantStyles[variant], className)}
26
+ {...props}
27
+ >
28
+ {children}
29
+ </ul>
30
+ );
31
+ }
32
+ );
33
+
34
+ List.displayName = 'List';
35
+
36
+ export const ListItem = forwardRef<HTMLLIElement, HTMLAttributes<HTMLLIElement>>(
37
+ ({ className, ...props }, ref) => (
38
+ <li
39
+ ref={ref}
40
+ className={cn('py-2', className)}
41
+ {...props}
42
+ />
43
+ )
44
+ );
45
+
46
+ ListItem.displayName = 'ListItem';