Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A comprehensive React design system kit with color picker, specialized input com
- 🌈 **Gradient Support**: Create and edit linear and radial gradients
- 🎯 **Eye Dropper**: Pick colors directly from the screen
- 🔢 **Universal Input Component**: Drag-to-change numeric input for all design properties
- 🎨 **HEX Input Components**: Specialized inputs for color values with drag-to-change
- 🎛️ **Multiple Progression Types**: Linear, arithmetic, geometric, paraboloid, exponential
- 🌓 **Dark/Light Mode**: Automatic theme detection with manual override
- 📦 **Modular Architecture**: Import only what you need for optimal bundle size
Expand All @@ -36,6 +37,8 @@ import {
ColorPicker,
InputNumberSelect,
InputColorPicker,
InputHex,
InputHexWithPreview,
} from '@flowscape-ui/design-system-kit'
```

Expand All @@ -46,6 +49,8 @@ import {
import { ColorPicker } from '@flowscape-ui/design-system-kit/color-picker'
import { InputNumberSelect } from '@flowscape-ui/design-system-kit/input-number-select'
import { InputColorPicker } from '@flowscape-ui/design-system-kit/input-color-picker'
import { InputHex } from '@flowscape-ui/design-system-kit/input-hex'
import { InputHexWithPreview } from '@flowscape-ui/design-system-kit/input-hex-with-preview'
```

## 📚 Components
Expand Down Expand Up @@ -133,6 +138,140 @@ function App() {
}
```

### InputHex (1 KB)

Component for HEX color input with drag-to-change.

```tsx
import { useState } from 'react'
import { InputHex } from '@flowscape-ui/design-system-kit/input-hex'

function App() {
const [color, setColor] = useState('#ff5733')

return <InputHex hexColor={color} handleChange={setColor} />
}
```

**Key Features:**

- 🎨 Drag-to-change for color modification by dragging
- 🔤 Real-time HEX value validation

- 🎯 Customizable callbacks for click and drag events
- 🌓 Automatic light/dark theme support

### InputHexWithPreview (1.2 KB)

Extended version of InputHex with visual color preview.

```tsx
import { useState } from 'react'
import { InputHexWithPreview } from '@flowscape-ui/design-system-kit/input-hex-with-preview'

function App() {
const [color, setColor] = useState('#3498db')
const [opacity, setOpacity] = useState(1)

return (
<InputHexWithPreview
hexColor={color}
opacity={opacity}
handleChange={setColor}
/>
)
}
```

**Key Features:**

- 🎨 Everything from InputHex + visual color preview
- 👁️ Square preview with opacity support
- 🎨 Preview style customization
- 📦 Compact size for form integration

### InputHex Props

```tsx
interface InputHexProps {
// Main parameters
hexColor: string // HEX color (required)
handleChange: (hexColor: string) => void // Change callback

// Styling
className?: string // Container classes
classNameInput?: string // Input field classes
classNameIcon?: string // Icon classes

// Behavior
disabled?: boolean // Disable component
isDisabledMouseEvent?: boolean // Disable drag functionality

// Callbacks
onIconClick?: (hexColor: string) => void // Icon click
onIconPointerDown?: (hexColor: string) => void // Drag start
onIconPointerUp?: (hexColor: string) => void // Drag end

// HTML input props
...HTMLInputElement // All standard input props
}
```

### InputHexWithPreview Props

Inherits all props from `InputHexProps` plus:

```tsx
interface InputHexWithPreviewProps extends InputHexProps {
opacity?: number // Opacity (0-1), default: 1
classNamePreview?: string // Classes for color preview
}
```

### InputHex Usage Examples

```tsx
import { InputHex, InputHexWithPreview } from '@flowscape-ui/design-system-kit'

// Basic usage
<InputHex
hexColor="#ff5733"
handleChange={(color) => console.log(color)}
/>

// With preview
<InputHexWithPreview
hexColor="#2ecc71"
opacity={0.8}
handleChange={setColor}
/>

// With custom callbacks
<InputHex
hexColor={color}
handleChange={setColor}
onIconClick={(hex) => console.log('Clicked:', hex)}
onIconPointerDown={(hex) => console.log('Drag start:', hex)}
onIconPointerUp={(hex) => console.log('Drag end:', hex)}
/>

// Disabled drag
<InputHex
hexColor={color}
handleChange={setColor}
isDisabledMouseEvent={true}
/>

// Custom styles
<InputHexWithPreview
hexColor={color}
handleChange={setColor}
className="w-full"
classNameInput="font-mono"
classNamePreview="rounded-full"
/>
```

### InputNumberSelect - Usage Examples

One universal component for all design properties. Configure it through props:
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@
"types": "./dist/shared/index.d.ts",
"import": "./dist/shared/index.js",
"require": "./dist/shared/index.cjs"
},
"./input-hex": {
"types": "./dist/input-hex/index.d.ts",
"import": "./dist/input-hex/index.js",
"require": "./dist/input-hex/index.cjs"
},
"./input-hex-with-preview": {
"types": "./dist/input-hex-with-preview/index.d.ts",
"import": "./dist/input-hex-with-preview/index.js",
"require": "./dist/input-hex-with-preview/index.cjs"
}
},
"files": [
Expand Down
4 changes: 2 additions & 2 deletions src/color-picker/components/GradientControls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { usePicker } from '../context'
import { formatInputValues, low, high } from '../utils/formatters'
import { controlBtnStyles } from '../styles/styles'
import { formatInputValues, high, low } from '../utils/formatters'
import TrashIcon, {
DegreesIcon,
LinearIcon,
RadialIcon,
DegreesIcon,
StopIcon,
} from './icon'

Expand Down
140 changes: 5 additions & 135 deletions src/color-picker/components/Inputs.tsx
Original file line number Diff line number Diff line change
@@ -1,137 +1,10 @@
import React, { useEffect, useRef, useState } from 'react'
import tc from 'tinycolor2'
import { InputHex } from '../../input-hex/input-hex'
import { InputNumberSelect } from '../../input-number-select'
import { usePicker } from '../context'
import { cmykToRgb, getHexAlpha, rgb2cmyk } from '../utils/converters'
import { cmykToRgb, rgb2cmyk } from '../utils/converters'
import { round } from '../utils/formatters'

const HexInput = ({
opacity,
tinyColor,
showHexAlpha,
handleChange,
}: {
tinyColor: any
opacity: number
showHexAlpha: boolean
handleChange: (arg0: string) => void
}) => {
const [disable, setDisable] = useState('')
const hex = tinyColor.toHex()
const [newHex, setNewHex] = useState(hex)
const dragStartValue = useRef(0)
const dragStartX = useRef(0)

useEffect(() => {
if (disable !== 'hex') {
setNewHex(hex)
}
}, [tinyColor, disable, hex])

const hexFocus = () => {
setDisable('hex')
}

const hexBlur = () => {
setDisable('')
}

const handleHexInput = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value
setNewHex(val)
if (tc(val).isValid()) {
const { r, g, b } = tc(val).toRgb()
const newColor = `rgba(${r}, ${g}, ${b}, ${opacity})`
handleChange(newColor)
}
}

const handleMouseDown = (e: React.MouseEvent) => {
e.preventDefault()
dragStartValue.current = parseInt(tinyColor.toHex(), 16)
dragStartX.current = e.clientX
setDisable('hex')
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}

const handleMouseUp = () => {
setDisable('')
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}

const handleMouseMove = (e: MouseEvent) => {
const movementX = e.clientX - dragStartX.current
const step = 55000
let newColorInt = Math.round(dragStartValue.current + movementX * step)
newColorInt = Math.max(0, Math.min(0xffffff, newColorInt))

const newColorHex = newColorInt.toString(16).padStart(6, '0')
setNewHex(newColorHex)

const { r, g, b } = tc(newColorHex).toRgb()
handleChange(`rgba(${r}, ${g}, ${b}, ${opacity})`)
}

const displayValue = showHexAlpha
? `${newHex}${getHexAlpha(opacity)}`
: newHex

const wrapperStyle: React.CSSProperties = {
display: 'inline-flex',
alignItems: 'center',
overflow: 'hidden',
borderRadius: '6px',
border: '1px solid #333',
backgroundColor: '#1a1a1a',
height: 28,
width: '108px',
}

const handleStyle: React.CSSProperties = {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
width: 28,
cursor: 'ew-resize',
color: '#888',
fontWeight: 'bold',
fontFamily: 'monospace',
userSelect: 'none',
fontSize: '16px',
}

const inputStyle: React.CSSProperties = {
width: '100%',
textAlign: 'left',
paddingLeft: 8,
border: 'none',
borderRadius: 0,
height: '100%',
backgroundColor: 'transparent',
color: '#D4D4D4',
outline: 'none',
fontSize: '14px',
}

return (
<div style={wrapperStyle}>
<div onMouseDown={handleMouseDown} style={handleStyle}>
#
</div>
<input
onBlur={hexBlur}
onFocus={hexFocus}
onChange={handleHexInput}
value={displayValue?.toUpperCase()}
style={inputStyle}
/>
</div>
)
}

const RGBInputs = ({
hc,
handleChange,
Expand Down Expand Up @@ -395,7 +268,7 @@ const Inputs = () => {
inputType,
tinyColor,
hideOpacity: _hideOpacity,
showHexAlpha,
showHexAlpha: _showHexAlpha,
handleChange,
defaultStyles: _defaultStyles,
pickerIdSuffix,
Expand All @@ -407,18 +280,15 @@ const Inputs = () => {
id={`rbgcp-inputs-wrap${pickerIdSuffix}`}
>
<div className="flex gap-1">
<HexInput
opacity={hc?.a}
tinyColor={tinyColor}
showHexAlpha={showHexAlpha}
<InputHex
hexColor={tinyColor.toHexString()}
handleChange={handleChange}
/>
<InputNumberSelect
icon="A"
max={100}
min={0}
step={1}
precision={0}
value={Math.round(hc?.a * 100)}
onChange={(newVal: string | number) =>
handleChange(
Expand Down
19 changes: 0 additions & 19 deletions src/color-picker/utils/converters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,3 @@ export const cmykToRgb = ({

return { r: r, g: g, b: b }
}

export const getHexAlpha = (opacityPercent: number): string => {
if (typeof opacityPercent !== 'number') {
return 'FF'
}

if (opacityPercent < 0) {
return '00'
}

if (opacityPercent > 1) {
return 'FF'
}

return Math.round(opacityPercent * 255)
.toString(16)
.padStart(2, '0')
.toUpperCase()
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
export * from './color-picker'
export * from './input'
export * from './input-color-picker'
export * from './input-hex'
export * from './input-hex-with-preview'
export * from './input-number-select'
export * from './shared'

Expand Down
Loading