@sudobility/design-components 1.1.0 → 2.0.1
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.
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +196 -458
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +0 -4
- package/dist/badge-designer.d.ts +0 -33
- package/dist/badge-designer.d.ts.map +0 -1
- package/dist/color-picker-advanced.d.ts +0 -8
- package/dist/color-picker-advanced.d.ts.map +0 -1
- package/dist/color-picker.d.ts +0 -42
- package/dist/color-picker.d.ts.map +0 -1
- package/dist/color-swatch.d.ts +0 -41
- package/dist/color-swatch.d.ts.map +0 -1
- package/src/badge-designer.tsx +0 -60
- package/src/color-picker-advanced.tsx +0 -51
- package/src/color-picker.tsx +0 -188
- package/src/color-swatch.tsx +0 -151
package/dist/color-picker.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
export interface ColorPickerProps {
|
|
3
|
-
/** Selected color (hex format) */
|
|
4
|
-
value: string;
|
|
5
|
-
/** Change handler */
|
|
6
|
-
onChange: (color: string) => void;
|
|
7
|
-
/** Preset colors */
|
|
8
|
-
presets?: string[];
|
|
9
|
-
/** Show input field */
|
|
10
|
-
showInput?: boolean;
|
|
11
|
-
/** Show opacity slider */
|
|
12
|
-
showOpacity?: boolean;
|
|
13
|
-
/** Additional className */
|
|
14
|
-
className?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* ColorPicker Component
|
|
18
|
-
*
|
|
19
|
-
* Color picker with preset colors and custom input.
|
|
20
|
-
* Supports hex colors and opacity.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```tsx
|
|
24
|
-
* <ColorPicker
|
|
25
|
-
* value={color}
|
|
26
|
-
* onChange={setColor}
|
|
27
|
-
* showInput
|
|
28
|
-
* />
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* <ColorPicker
|
|
34
|
-
* value={brandColor}
|
|
35
|
-
* onChange={handleColorChange}
|
|
36
|
-
* presets={['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A']}
|
|
37
|
-
* showOpacity
|
|
38
|
-
* />
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare const ColorPicker: React.FC<ColorPickerProps>;
|
|
42
|
-
//# sourceMappingURL=color-picker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../src/color-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAgJlD,CAAC"}
|
package/dist/color-swatch.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
export interface Color {
|
|
3
|
-
/** Color hex value */
|
|
4
|
-
hex: string;
|
|
5
|
-
/** Color name */
|
|
6
|
-
name?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ColorSwatchProps {
|
|
9
|
-
/** Colors to display */
|
|
10
|
-
colors: Color[] | string[];
|
|
11
|
-
/** Selected color */
|
|
12
|
-
selectedColor?: string;
|
|
13
|
-
/** Color change handler */
|
|
14
|
-
onColorSelect?: (color: string) => void;
|
|
15
|
-
/** Swatch size */
|
|
16
|
-
size?: 'sm' | 'md' | 'lg';
|
|
17
|
-
/** Show color names */
|
|
18
|
-
showNames?: boolean;
|
|
19
|
-
/** Show copy button */
|
|
20
|
-
showCopy?: boolean;
|
|
21
|
-
/** Additional className */
|
|
22
|
-
className?: string;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* ColorSwatch Component
|
|
26
|
-
*
|
|
27
|
-
* Color palette display with selection and copy functionality.
|
|
28
|
-
* Useful for color pickers and design systems.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```tsx
|
|
32
|
-
* <ColorSwatch
|
|
33
|
-
* colors={['#FF0000', '#00FF00', '#0000FF']}
|
|
34
|
-
* selectedColor={activeColor}
|
|
35
|
-
* onColorSelect={setActiveColor}
|
|
36
|
-
* showCopy
|
|
37
|
-
* />
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
export declare const ColorSwatch: React.FC<ColorSwatchProps>;
|
|
41
|
-
//# sourceMappingURL=color-swatch.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"color-swatch.d.ts","sourceRoot":"","sources":["../src/color-swatch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,WAAW,KAAK;IACpB,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC;IAC3B,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,kBAAkB;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,uBAAuB;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA2GlD,CAAC"}
|
package/src/badge-designer.tsx
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { cn } from '@sudobility/components';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* UbadgeUdesigner Component
|
|
5
|
-
*
|
|
6
|
-
* A reusable UbadgeUdesigner component with full dark mode support.
|
|
7
|
-
* Optimized for accessibility and AI-assisted development.
|
|
8
|
-
*
|
|
9
|
-
* @component
|
|
10
|
-
* @example
|
|
11
|
-
* ```tsx
|
|
12
|
-
* <UbadgeUdesigner 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/badge-designer}
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
export interface UbadgeUdesignerProps {
|
|
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 UbadgeUdesigner = ({
|
|
37
|
-
className,
|
|
38
|
-
children,
|
|
39
|
-
disabled = false,
|
|
40
|
-
onClick,
|
|
41
|
-
}: UbadgeUdesignerProps) => {
|
|
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='UbadgeUdesigner'
|
|
56
|
-
>
|
|
57
|
-
{children || 'UbadgeUdesigner Component'}
|
|
58
|
-
</div>
|
|
59
|
-
);
|
|
60
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { cn } from '@sudobility/components';
|
|
2
|
-
|
|
3
|
-
export interface ColorPickerAdvancedProps {
|
|
4
|
-
value: string;
|
|
5
|
-
onChange: (color: string) => void;
|
|
6
|
-
presets?: string[];
|
|
7
|
-
className?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const ColorPickerAdvanced = ({
|
|
11
|
-
value,
|
|
12
|
-
onChange,
|
|
13
|
-
presets,
|
|
14
|
-
className,
|
|
15
|
-
}: ColorPickerAdvancedProps) => {
|
|
16
|
-
const defaultPresets = [
|
|
17
|
-
'#000000',
|
|
18
|
-
'#ffffff',
|
|
19
|
-
'#ff0000',
|
|
20
|
-
'#00ff00',
|
|
21
|
-
'#0000ff',
|
|
22
|
-
'#ffff00',
|
|
23
|
-
'#ff00ff',
|
|
24
|
-
'#00ffff',
|
|
25
|
-
];
|
|
26
|
-
const colors = presets || defaultPresets;
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div className={cn('space-y-3', className)}>
|
|
30
|
-
<input
|
|
31
|
-
type='color'
|
|
32
|
-
value={value}
|
|
33
|
-
onChange={e => onChange(e.target.value)}
|
|
34
|
-
className='w-full h-12 rounded cursor-pointer'
|
|
35
|
-
/>
|
|
36
|
-
<div className='grid grid-cols-8 gap-2'>
|
|
37
|
-
{colors.map(color => (
|
|
38
|
-
<button
|
|
39
|
-
key={color}
|
|
40
|
-
onClick={() => onChange(color)}
|
|
41
|
-
className={cn(
|
|
42
|
-
'w-8 h-8 rounded border-2',
|
|
43
|
-
value === color ? 'border-blue-500' : 'border-transparent'
|
|
44
|
-
)}
|
|
45
|
-
style={{ backgroundColor: color }}
|
|
46
|
-
/>
|
|
47
|
-
))}
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
};
|
package/src/color-picker.tsx
DELETED
|
@@ -1,188 +0,0 @@
|
|
|
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
|
-
};
|
package/src/color-swatch.tsx
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
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
|
-
};
|