Skip to content

Commit 31bf4b3

Browse files
committed
update: input color picker
1 parent bc91893 commit 31bf4b3

File tree

13 files changed

+624
-313
lines changed

13 files changed

+624
-313
lines changed

src/index.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,41 @@ export * from './shared'
77

88
// Re-export inputs with types
99
export {
10-
AngleInput, BlurInput, BorderRadiusInput,
11-
BorderRadiusMultiInput, FontSizeInput, HeightInput, LetterSpacingInput, LineHeightInput, OpacityInput, ScaleInput, SpacingInput, WidthInput, ZIndexInput
10+
AngleInput,
11+
BlurInput,
12+
BorderRadiusInput,
13+
BorderRadiusMultiInput,
14+
FontSizeInput,
15+
HeightInput,
16+
LetterSpacingInput,
17+
LineHeightInput,
18+
OpacityInput,
19+
ScaleInput,
20+
SpacingInput,
21+
WidthInput,
22+
ZIndexInput,
1223
} from './inputs'
1324

1425
export type {
15-
AngleInputProps, BaseInputProps,
16-
BaseInputPropsWithoutUnit, BlurInputProps, BorderRadiusInputProps,
17-
BorderRadiusMultiInputProps, CommonInputProps, FontSizeInputProps, HeightInputProps, LetterSpacingInputProps, LineHeightInputProps, OpacityInputProps, Orientation,
18-
Progression, ScaleInputProps, SpacingInputProps, Unit, WidthInputProps, ZIndexInputProps
26+
AngleInputProps,
27+
BaseInputProps,
28+
BaseInputPropsWithoutUnit,
29+
BlurInputProps,
30+
BorderRadiusInputProps,
31+
BorderRadiusMultiInputProps,
32+
CommonInputProps,
33+
FontSizeInputProps,
34+
HeightInputProps,
35+
LetterSpacingInputProps,
36+
LineHeightInputProps,
37+
OpacityInputProps,
38+
Orientation,
39+
Progression,
40+
ScaleInputProps,
41+
SpacingInputProps,
42+
Unit,
43+
WidthInputProps,
44+
ZIndexInputProps,
1945
} from './inputs'
2046

2147
// Default export for backward compatibility
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './use-color-picker-state'
2+
export * from './use-draggable'
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { useEffect, useState } from 'react'
2+
import { filterHexInput, hexToRgb, parseColor, rgbToHex } from '../utils/color-utils'
3+
4+
interface UseColorPickerStateProps {
5+
value: string
6+
onChange?: (color: string) => void
7+
onOpacityChange?: (opacity: number) => void
8+
}
9+
10+
export const useColorPickerState = ({
11+
value,
12+
onChange,
13+
onOpacityChange,
14+
}: UseColorPickerStateProps) => {
15+
const { hex, opacity } = parseColor(value)
16+
const [color, setColor] = useState(value)
17+
const [inputValue, setInputValue] = useState(hex)
18+
const [livePreviewColor, setLivePreviewColor] = useState(value)
19+
const [colorType, setColorType] = useState<'color' | 'gradient'>('color')
20+
const [opacityValue, setOpacityValue] = useState(isNaN(opacity) ? 100 : opacity)
21+
22+
useEffect(() => {
23+
const { hex: newHex, opacity: newOpacity } = parseColor(value)
24+
setInputValue(newHex)
25+
setColor(value)
26+
setLivePreviewColor(value)
27+
setOpacityValue(isNaN(newOpacity) ? 100 : newOpacity)
28+
}, [value])
29+
30+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
31+
const finalValue = filterHexInput(e.target.value)
32+
setInputValue(finalValue)
33+
34+
if (finalValue.length === 3 || finalValue.length === 6) {
35+
const newHex = `#${finalValue}`
36+
const rgb = hexToRgb(newHex)
37+
if (rgb) {
38+
const newRgbaColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
39+
opacityValue / 100
40+
})`
41+
setLivePreviewColor(newRgbaColor)
42+
}
43+
}
44+
}
45+
46+
const handleInputFocus = () => {
47+
if (inputValue.startsWith('#')) {
48+
setInputValue(inputValue.substring(1))
49+
}
50+
}
51+
52+
const handleHexChange = () => {
53+
if (hex === 'Mixed') return
54+
55+
let newHex = inputValue.trim()
56+
if (!newHex.startsWith('#')) {
57+
newHex = `#${newHex}`
58+
}
59+
60+
const rgb = hexToRgb(newHex)
61+
62+
if (rgb) {
63+
const newRgbaColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
64+
opacityValue / 100
65+
})`
66+
onChange?.(newRgbaColor)
67+
setInputValue(newHex.toUpperCase())
68+
} else {
69+
const { hex: originalHex } = parseColor(value)
70+
setInputValue(originalHex)
71+
setLivePreviewColor(value)
72+
}
73+
}
74+
75+
const handleOpacityChange = (newOpacity: number) => {
76+
// Проверка на валидность значения
77+
const validOpacity = isNaN(newOpacity) ? 100 : newOpacity
78+
onOpacityChange?.(validOpacity)
79+
setOpacityValue(validOpacity)
80+
81+
if (color.includes('gradient')) {
82+
return
83+
}
84+
85+
const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/)
86+
if (rgbMatch) {
87+
const r = rgbMatch[1]
88+
const g = rgbMatch[2]
89+
const b = rgbMatch[3]
90+
const newRgbaColor = `rgba(${r}, ${g}, ${b}, ${validOpacity / 100})`
91+
setColor(newRgbaColor)
92+
onChange?.(newRgbaColor)
93+
} else {
94+
const rgb = hexToRgb(color)
95+
if (rgb) {
96+
const newRgbaColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
97+
validOpacity / 100
98+
})`
99+
setColor(newRgbaColor)
100+
onChange?.(newRgbaColor)
101+
}
102+
}
103+
}
104+
105+
const handleColorChange = (newColor: string) => {
106+
onChange?.(newColor)
107+
setColor(newColor)
108+
109+
if (newColor.includes('gradient')) {
110+
setLivePreviewColor(newColor)
111+
112+
if (colorType === 'color') {
113+
setColorType('gradient')
114+
}
115+
116+
if (newColor.includes('linear-gradient')) {
117+
setInputValue('Linear')
118+
} else if (newColor.includes('radial-gradient')) {
119+
setInputValue('Radial')
120+
} else {
121+
setInputValue('Gradient')
122+
}
123+
124+
return
125+
}
126+
127+
if (colorType === 'gradient') {
128+
setColorType('color')
129+
return
130+
}
131+
132+
const rgbaMatch = newColor.match(
133+
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/
134+
)
135+
if (rgbaMatch) {
136+
const alpha = rgbaMatch[4] !== undefined ? parseFloat(rgbaMatch[4]) : 1
137+
const newOpacity = Math.round(alpha * 100)
138+
setOpacityValue(isNaN(newOpacity) ? 100 : newOpacity)
139+
}
140+
141+
setLivePreviewColor(newColor)
142+
setInputValue(rgbToHex(newColor))
143+
}
144+
145+
return {
146+
hex,
147+
color,
148+
inputValue,
149+
livePreviewColor,
150+
colorType,
151+
opacityValue,
152+
handleInputChange,
153+
handleInputFocus,
154+
handleHexChange,
155+
handleOpacityChange,
156+
handleColorChange,
157+
}
158+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useRef, useState } from 'react'
2+
import type { DragStart, Position } from '../types'
3+
4+
export const useDraggable = () => {
5+
const [position, setPosition] = useState<Position>({
6+
top: window.innerHeight / 2,
7+
left: window.innerWidth / 2,
8+
})
9+
const dragStart = useRef<DragStart | null>(null)
10+
11+
const handleDragStart = (
12+
e: React.MouseEvent,
13+
element: HTMLDivElement | null
14+
) => {
15+
if (!element) return
16+
17+
dragStart.current = {
18+
x: e.clientX,
19+
y: e.clientY,
20+
top: element.offsetTop,
21+
left: element.offsetLeft,
22+
}
23+
24+
const handleMouseMove = (moveEvent: MouseEvent) => {
25+
if (!dragStart.current) return
26+
const dx = moveEvent.clientX - dragStart.current.x
27+
const dy = moveEvent.clientY - dragStart.current.y
28+
setPosition({
29+
top: dragStart.current.top + dy,
30+
left: dragStart.current.left + dx,
31+
})
32+
}
33+
34+
const handleMouseUp = () => {
35+
dragStart.current = null
36+
window.removeEventListener('mousemove', handleMouseMove)
37+
window.removeEventListener('mouseup', handleMouseUp)
38+
}
39+
40+
window.addEventListener('mousemove', handleMouseMove)
41+
window.addEventListener('mouseup', handleMouseUp)
42+
}
43+
44+
const resetPosition = () => {
45+
setPosition({
46+
top: window.innerHeight / 2,
47+
left: window.innerWidth / 2,
48+
})
49+
}
50+
51+
return {
52+
position,
53+
handleDragStart,
54+
resetPosition,
55+
}
56+
}

src/input-color-picker/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export { InputColorPicker } from './input-color-picker'
2-
export type { InputColorPickerProps } from './input-color-picker'
2+
export type { InputColorPickerProps } from './types'
3+
export * from './utils'
4+
export * from './hooks'

0 commit comments

Comments
 (0)