@sudobility/design-components 1.0.8

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.
@@ -0,0 +1,188 @@
1
+ import React, { useState } from 'react';
2
+ import { cn } from '@sudobility/components';
3
+
4
+ export interface ColorPickerProps {
5
+ /** Selected color (hex format) */
6
+ value: string;
7
+ /** Change handler */
8
+ onChange: (color: string) => void;
9
+ /** Preset colors */
10
+ presets?: string[];
11
+ /** Show input field */
12
+ showInput?: boolean;
13
+ /** Show opacity slider */
14
+ showOpacity?: boolean;
15
+ /** Additional className */
16
+ className?: string;
17
+ }
18
+
19
+ /**
20
+ * ColorPicker Component
21
+ *
22
+ * Color picker with preset colors and custom input.
23
+ * Supports hex colors and opacity.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <ColorPicker
28
+ * value={color}
29
+ * onChange={setColor}
30
+ * showInput
31
+ * />
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * <ColorPicker
37
+ * value={brandColor}
38
+ * onChange={handleColorChange}
39
+ * presets={['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A']}
40
+ * showOpacity
41
+ * />
42
+ * ```
43
+ */
44
+ export const ColorPicker: React.FC<ColorPickerProps> = ({
45
+ value,
46
+ onChange,
47
+ presets = [
48
+ '#000000',
49
+ '#FFFFFF',
50
+ '#FF6B6B',
51
+ '#4ECDC4',
52
+ '#45B7D1',
53
+ '#FFA07A',
54
+ '#F7DC6F',
55
+ '#BB8FCE',
56
+ '#85C1E2',
57
+ '#F8B500',
58
+ '#52B788',
59
+ '#E63946',
60
+ '#1D3557',
61
+ '#457B9D',
62
+ '#A8DADC',
63
+ '#F4A261',
64
+ '#264653',
65
+ '#2A9D8F',
66
+ ],
67
+ showInput = true,
68
+ showOpacity = false,
69
+ className,
70
+ }) => {
71
+ const [inputValue, setInputValue] = useState(value);
72
+ const [opacity, setOpacity] = useState(100);
73
+
74
+ const handleColorClick = (color: string) => {
75
+ onChange(color);
76
+ setInputValue(color);
77
+ };
78
+
79
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
80
+ const newValue = e.target.value;
81
+ setInputValue(newValue);
82
+
83
+ // Validate hex color
84
+ if (/^#[0-9A-F]{6}$/i.test(newValue)) {
85
+ onChange(newValue);
86
+ }
87
+ };
88
+
89
+ const handleOpacityChange = (e: React.ChangeEvent<HTMLInputElement>) => {
90
+ setOpacity(Number(e.target.value));
91
+ };
92
+
93
+ const hexToRgba = (hex: string, alpha: number) => {
94
+ const r = parseInt(hex.slice(1, 3), 16);
95
+ const g = parseInt(hex.slice(3, 5), 16);
96
+ const b = parseInt(hex.slice(5, 7), 16);
97
+ return `rgba(${r}, ${g}, ${b}, ${alpha / 100})`;
98
+ };
99
+
100
+ const displayColor = showOpacity ? hexToRgba(value, opacity) : value;
101
+
102
+ return (
103
+ <div className={cn('bg-white dark:bg-gray-900 rounded-lg p-4', className)}>
104
+ {/* Current color preview */}
105
+ <div className='mb-4'>
106
+ <div className='flex items-center gap-3'>
107
+ <div
108
+ className='w-16 h-16 rounded-lg border-2 border-gray-200 dark:border-gray-700 shadow-sm'
109
+ style={{ backgroundColor: displayColor }}
110
+ />
111
+ <div className='flex-1'>
112
+ <p className='text-sm font-medium text-gray-900 dark:text-white'>
113
+ Selected Color
114
+ </p>
115
+ <p className='text-xs text-gray-600 dark:text-gray-400 mt-1 font-mono'>
116
+ {value}
117
+ {showOpacity && ` (${opacity}%)`}
118
+ </p>
119
+ </div>
120
+ </div>
121
+ </div>
122
+
123
+ {/* Preset colors */}
124
+ <div className='mb-4'>
125
+ <p className='text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
126
+ Preset Colors
127
+ </p>
128
+ <div className='grid grid-cols-6 gap-2'>
129
+ {presets.map(preset => (
130
+ <button
131
+ key={preset}
132
+ onClick={() => handleColorClick(preset)}
133
+ className={cn(
134
+ 'w-full aspect-square rounded-md border-2 transition-all hover:scale-110',
135
+ preset === value
136
+ ? 'border-blue-500 dark:border-blue-400 ring-2 ring-blue-200 dark:ring-blue-800'
137
+ : 'border-gray-200 dark:border-gray-700'
138
+ )}
139
+ style={{ backgroundColor: preset }}
140
+ aria-label={`Select color ${preset}`}
141
+ />
142
+ ))}
143
+ </div>
144
+ </div>
145
+
146
+ {/* Custom color input */}
147
+ {showInput && (
148
+ <div className='mb-4'>
149
+ <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
150
+ Custom Color (Hex)
151
+ </label>
152
+ <div className='flex gap-2'>
153
+ <input
154
+ type='text'
155
+ value={inputValue}
156
+ onChange={handleInputChange}
157
+ placeholder='#000000'
158
+ className='flex-1 px-3 py-2 text-sm font-mono bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400'
159
+ />
160
+ <input
161
+ type='color'
162
+ value={value}
163
+ onChange={e => handleColorClick(e.target.value)}
164
+ className='w-12 h-10 rounded-md cursor-pointer'
165
+ />
166
+ </div>
167
+ </div>
168
+ )}
169
+
170
+ {/* Opacity slider */}
171
+ {showOpacity && (
172
+ <div>
173
+ <label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
174
+ Opacity: {opacity}%
175
+ </label>
176
+ <input
177
+ type='range'
178
+ min='0'
179
+ max='100'
180
+ value={opacity}
181
+ onChange={handleOpacityChange}
182
+ className='w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer'
183
+ />
184
+ </div>
185
+ )}
186
+ </div>
187
+ );
188
+ };
@@ -0,0 +1,151 @@
1
+ import React, { useState } from 'react';
2
+ import { cn } from '@sudobility/components';
3
+
4
+ export interface Color {
5
+ /** Color hex value */
6
+ hex: string;
7
+ /** Color name */
8
+ name?: string;
9
+ }
10
+
11
+ export interface ColorSwatchProps {
12
+ /** Colors to display */
13
+ colors: Color[] | string[];
14
+ /** Selected color */
15
+ selectedColor?: string;
16
+ /** Color change handler */
17
+ onColorSelect?: (color: string) => void;
18
+ /** Swatch size */
19
+ size?: 'sm' | 'md' | 'lg';
20
+ /** Show color names */
21
+ showNames?: boolean;
22
+ /** Show copy button */
23
+ showCopy?: boolean;
24
+ /** Additional className */
25
+ className?: string;
26
+ }
27
+
28
+ /**
29
+ * ColorSwatch Component
30
+ *
31
+ * Color palette display with selection and copy functionality.
32
+ * Useful for color pickers and design systems.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * <ColorSwatch
37
+ * colors={['#FF0000', '#00FF00', '#0000FF']}
38
+ * selectedColor={activeColor}
39
+ * onColorSelect={setActiveColor}
40
+ * showCopy
41
+ * />
42
+ * ```
43
+ */
44
+ export const ColorSwatch: React.FC<ColorSwatchProps> = ({
45
+ colors,
46
+ selectedColor,
47
+ onColorSelect,
48
+ size = 'md',
49
+ showNames = false,
50
+ showCopy = false,
51
+ className,
52
+ }) => {
53
+ const [copiedColor, setCopiedColor] = useState<string | null>(null);
54
+
55
+ const sizeClasses = {
56
+ sm: 'w-8 h-8',
57
+ md: 'w-12 h-12',
58
+ lg: 'w-16 h-16',
59
+ };
60
+
61
+ const normalizeColors = (): Color[] => {
62
+ return colors.map(color =>
63
+ typeof color === 'string' ? { hex: color } : color
64
+ );
65
+ };
66
+
67
+ const handleCopy = async (hex: string, e: React.MouseEvent) => {
68
+ e.stopPropagation();
69
+ await navigator.clipboard.writeText(hex);
70
+ setCopiedColor(hex);
71
+ setTimeout(() => setCopiedColor(null), 2000);
72
+ };
73
+
74
+ const normalizedColors = normalizeColors();
75
+
76
+ return (
77
+ <div className={cn('flex flex-wrap gap-3', className)}>
78
+ {normalizedColors.map((color, index) => {
79
+ const isSelected = selectedColor === color.hex;
80
+ const isCopied = copiedColor === color.hex;
81
+
82
+ return (
83
+ <div key={index} className='flex flex-col items-center gap-2'>
84
+ <button
85
+ onClick={() => onColorSelect?.(color.hex)}
86
+ className={cn(
87
+ 'rounded-lg transition-all relative group',
88
+ sizeClasses[size],
89
+ isSelected &&
90
+ 'ring-2 ring-blue-500 ring-offset-2 dark:ring-offset-gray-900',
91
+ !isSelected && 'hover:scale-110'
92
+ )}
93
+ style={{ backgroundColor: color.hex }}
94
+ aria-label={color.name || color.hex}
95
+ >
96
+ {showCopy && (
97
+ <div
98
+ className='absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/50 rounded-lg'
99
+ onClick={e => handleCopy(color.hex, e)}
100
+ >
101
+ {isCopied ? (
102
+ <svg
103
+ className='w-4 h-4 text-white'
104
+ fill='none'
105
+ stroke='currentColor'
106
+ viewBox='0 0 24 24'
107
+ >
108
+ <path
109
+ strokeLinecap='round'
110
+ strokeLinejoin='round'
111
+ strokeWidth={2}
112
+ d='M5 13l4 4L19 7'
113
+ />
114
+ </svg>
115
+ ) : (
116
+ <svg
117
+ className='w-4 h-4 text-white'
118
+ fill='none'
119
+ stroke='currentColor'
120
+ viewBox='0 0 24 24'
121
+ >
122
+ <path
123
+ strokeLinecap='round'
124
+ strokeLinejoin='round'
125
+ strokeWidth={2}
126
+ d='M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z'
127
+ />
128
+ </svg>
129
+ )}
130
+ </div>
131
+ )}
132
+ </button>
133
+
134
+ {showNames && (
135
+ <div className='text-center'>
136
+ {color.name && (
137
+ <p className='text-xs font-medium text-gray-900 dark:text-white'>
138
+ {color.name}
139
+ </p>
140
+ )}
141
+ <p className='text-xs text-gray-600 dark:text-gray-400 font-mono'>
142
+ {color.hex}
143
+ </p>
144
+ </div>
145
+ )}
146
+ </div>
147
+ );
148
+ })}
149
+ </div>
150
+ );
151
+ };
@@ -0,0 +1,106 @@
1
+ import React from 'react';
2
+ import { cn } from '@sudobility/components';
3
+
4
+ export interface GradientBannerProps {
5
+ /** Main content of the banner */
6
+ children: React.ReactNode;
7
+ /** Gradient color variant */
8
+ variant?:
9
+ | 'blue-purple'
10
+ | 'green-blue'
11
+ | 'orange-red'
12
+ | 'gray'
13
+ | 'light'
14
+ | 'custom';
15
+ /** Custom gradient classes (when variant is 'custom') */
16
+ gradientClasses?: string;
17
+ /** Size/padding variant */
18
+ size?: 'sm' | 'md' | 'lg';
19
+ /** Border radius */
20
+ rounded?: 'md' | 'lg' | 'xl' | '2xl';
21
+ /** Show border */
22
+ bordered?: boolean;
23
+ /** Border color classes (when bordered is true) */
24
+ borderClasses?: string;
25
+ /** Additional className for the container */
26
+ className?: string;
27
+ }
28
+
29
+ /**
30
+ * GradientBanner Component
31
+ *
32
+ * A versatile banner component with gradient backgrounds for highlighting
33
+ * important content, CTAs, or feature sections.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * <GradientBanner variant="blue-purple" size="lg">
38
+ * <h3 className={textVariants.heading.h3()}>Special Offer</h3>
39
+ * <p>Get started with Web3 Email today!</p>
40
+ * </GradientBanner>
41
+ * ```
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * // Light variant with border
46
+ * <GradientBanner
47
+ * variant="light"
48
+ * bordered
49
+ * borderClasses="border-2 border-blue-200"
50
+ * >
51
+ * <Alert variant="info">Important information</Alert>
52
+ * </GradientBanner>
53
+ * ```
54
+ */
55
+ export const GradientBanner: React.FC<GradientBannerProps> = ({
56
+ children,
57
+ variant = 'blue-purple',
58
+ gradientClasses,
59
+ size = 'md',
60
+ rounded = '2xl',
61
+ bordered = false,
62
+ borderClasses = 'border border-white/20',
63
+ className,
64
+ }) => {
65
+ // Gradient color variants
66
+ const gradientVariants = {
67
+ 'blue-purple': 'bg-gradient-to-r from-blue-600 to-purple-600 text-white',
68
+ 'green-blue': 'bg-gradient-to-r from-green-600 to-blue-600 text-white',
69
+ 'orange-red': 'bg-gradient-to-r from-orange-600 to-red-600 text-white',
70
+ gray: 'bg-gradient-to-r from-gray-600 to-gray-800 dark:from-gray-700 dark:to-gray-900 text-white',
71
+ light:
72
+ 'bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 text-gray-900 dark:text-gray-100',
73
+ custom:
74
+ gradientClasses ||
75
+ 'bg-gradient-to-r from-blue-600 to-purple-600 text-white',
76
+ };
77
+
78
+ // Size/padding configurations
79
+ const sizeClasses = {
80
+ sm: 'p-4',
81
+ md: 'p-6',
82
+ lg: 'p-8',
83
+ };
84
+
85
+ // Border radius configurations
86
+ const roundedClasses = {
87
+ md: 'rounded-md',
88
+ lg: 'rounded-lg',
89
+ xl: 'rounded-xl',
90
+ '2xl': 'rounded-2xl',
91
+ };
92
+
93
+ return (
94
+ <div
95
+ className={cn(
96
+ gradientVariants[variant],
97
+ sizeClasses[size],
98
+ roundedClasses[rounded],
99
+ bordered && borderClasses,
100
+ className
101
+ )}
102
+ >
103
+ {children}
104
+ </div>
105
+ );
106
+ };
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * design-components
3
+ *
4
+ * Specialized components for the design domain
5
+ *
6
+ * @package @sudobility/design-components
7
+ */
8
+
9
+ export * from './aurora-background';
10
+ export * from './badge-designer';
11
+ export * from './color-picker-advanced';
12
+ export * from './color-picker';
13
+ export * from './color-swatch';
14
+ export * from './gradient-banner';
15
+ export * from './neumorphic-button';
16
+ export * from './theme-switcher';
@@ -0,0 +1,60 @@
1
+ import { cn } from '@sudobility/components';
2
+
3
+ /**
4
+ * UneumorphicUbutton Component
5
+ *
6
+ * A reusable UneumorphicUbutton component with full dark mode support.
7
+ * Optimized for accessibility and AI-assisted development.
8
+ *
9
+ * @component
10
+ * @example
11
+ * ```tsx
12
+ * <UneumorphicUbutton className="custom-class" />
13
+ * ```
14
+ *
15
+ * @remarks
16
+ * This component supports:
17
+ * - Light and dark themes automatically
18
+ * - Responsive design
19
+ * - Accessibility features
20
+ * - TypeScript type safety
21
+ *
22
+ * @see {@link https://docs.example.com/components/neumorphic-button}
23
+ */
24
+
25
+ export interface UneumorphicUbuttonProps {
26
+ /** Additional CSS classes */
27
+ className?: string;
28
+ /** Component children */
29
+ children?: React.ReactNode;
30
+ /** Disabled state */
31
+ disabled?: boolean;
32
+ /** Callback when component is interacted with */
33
+ onClick?: () => void;
34
+ }
35
+
36
+ export const UneumorphicUbutton = ({
37
+ className,
38
+ children,
39
+ disabled = false,
40
+ onClick,
41
+ }: UneumorphicUbuttonProps) => {
42
+ return (
43
+ <div
44
+ className={cn(
45
+ 'p-4 rounded-lg border transition-colors',
46
+ 'bg-white dark:bg-gray-900',
47
+ 'border-gray-200 dark:border-gray-700',
48
+ 'text-gray-900 dark:text-white',
49
+ disabled && 'opacity-50 cursor-not-allowed',
50
+ 'hover:bg-gray-50 dark:hover:bg-gray-800',
51
+ className
52
+ )}
53
+ onClick={disabled ? undefined : onClick}
54
+ role='region'
55
+ aria-label='UneumorphicUbutton'
56
+ >
57
+ {children || 'UneumorphicUbutton Component'}
58
+ </div>
59
+ );
60
+ };
@@ -0,0 +1,60 @@
1
+ import { cn } from '@sudobility/components';
2
+
3
+ /**
4
+ * UthemeUswitcher Component
5
+ *
6
+ * A reusable UthemeUswitcher component with full dark mode support.
7
+ * Optimized for accessibility and AI-assisted development.
8
+ *
9
+ * @component
10
+ * @example
11
+ * ```tsx
12
+ * <UthemeUswitcher className="custom-class" />
13
+ * ```
14
+ *
15
+ * @remarks
16
+ * This component supports:
17
+ * - Light and dark themes automatically
18
+ * - Responsive design
19
+ * - Accessibility features
20
+ * - TypeScript type safety
21
+ *
22
+ * @see {@link https://docs.example.com/components/theme-switcher}
23
+ */
24
+
25
+ export interface UthemeUswitcherProps {
26
+ /** Additional CSS classes */
27
+ className?: string;
28
+ /** Component children */
29
+ children?: React.ReactNode;
30
+ /** Disabled state */
31
+ disabled?: boolean;
32
+ /** Callback when component is interacted with */
33
+ onClick?: () => void;
34
+ }
35
+
36
+ export const UthemeUswitcher = ({
37
+ className,
38
+ children,
39
+ disabled = false,
40
+ onClick,
41
+ }: UthemeUswitcherProps) => {
42
+ return (
43
+ <div
44
+ className={cn(
45
+ 'p-4 rounded-lg border transition-colors',
46
+ 'bg-white dark:bg-gray-900',
47
+ 'border-gray-200 dark:border-gray-700',
48
+ 'text-gray-900 dark:text-white',
49
+ disabled && 'opacity-50 cursor-not-allowed',
50
+ 'hover:bg-gray-50 dark:hover:bg-gray-800',
51
+ className
52
+ )}
53
+ onClick={disabled ? undefined : onClick}
54
+ role='region'
55
+ aria-label='UthemeUswitcher'
56
+ >
57
+ {children || 'UthemeUswitcher Component'}
58
+ </div>
59
+ );
60
+ };