From 752f1a4b332006ccbc8865bb0df08c768132fa6a Mon Sep 17 00:00:00 2001 From: binary-shadow Date: Fri, 10 Oct 2025 13:12:59 +0300 Subject: [PATCH 1/2] feat: add alpha channel support for InputHex and InputHexWithPreview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add showAlpha prop for 8-symbol HEX input (RRGGBBAA) - Implement smart drag behavior (preserves alpha during drag) - Add uppercase formatting for all HEX values - Update InputHex and InputHexWithPreview components - Refactor state management (disable → isEditing, newHex → localHex) - Add comprehensive documentation and examples - Update Storybook stories with alpha channel demos - Update README.md with new features and API reference - Add v1.2.0 to CHANGELOG.md (prepared for future release) Breaking changes: None - fully backward compatible --- CHANGELOG.md | 126 ++- README.md | 739 ++++++------------ package.json | 2 +- .../input-hex-with-preview.tsx | 131 ++-- src/input-hex/input-hex.tsx | 115 +-- .../input-number-select.tsx | 6 +- src/stories/InputHex.stories.tsx | 63 +- src/stories/InputHexWithPreview.stories.tsx | 100 +-- 8 files changed, 611 insertions(+), 671 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b73f47f..d7aebe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,113 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2025-10-10 + +### Added + +**🎨 Alpha Channel Support for HEX Inputs** + +- **`showAlpha` prop** for `InputHex` and `InputHexWithPreview` components + - Enable 8-symbol HEX input (RRGGBBAA format) + - Supports alpha values from 00 (transparent) to FF (opaque) + - Backward compatible — defaults to 6-symbol input + +- **Automatic Preview Transparency** + - `InputHexWithPreview` now displays alpha channel in preview + - Preview shows real transparency on checkerboard background + - Alpha affects preview color automatically + +- **Smart Drag Behavior** + - Drag changes only base color (first 6 symbols) + - Alpha channel is preserved during drag operations + - No conflicts between drag and alpha input + +- **Uppercase Formatting** + - All HEX values automatically converted to uppercase + - Consistent formatting across all inputs + - Applied to both manual input and drag operations + +### Changed + +- **InputHex Component** + - Refactored state management (`disable` → `isEditing`) + - Improved local state handling (`newHex` → `localHex`) + - Enhanced input validation and filtering + - Better synchronization with parent component + +- **InputHexWithPreview Component** + - Removed `opacity` prop (replaced by alpha channel in hex value) + - Preview now uses alpha from hex color directly + - Simplified API — one source of truth for transparency + +### Fixed + +- 🐛 Fixed bug where drag would reset to previous value when alpha present +- 🐛 Fixed race condition in state synchronization after drag +- 🐛 Fixed alpha preservation during drag operations +- 🐛 Improved `useEffect` dependencies for better state management + +### Documentation + +- Updated README.md with alpha channel examples +- Added comprehensive API reference for `showAlpha` prop +- Enhanced Storybook examples with alpha demonstrations +- Improved component descriptions and feature lists +- Added migration notes for `opacity` prop removal + +### Technical Improvements + +- **TypeScript** + - Updated interfaces with `showAlpha?: boolean` + - Better type safety for hex color validation + - Improved prop type definitions + +- **Code Quality** + - Consistent code formatting across components + - Better error handling for edge cases + - Improved component architecture + +- **Bundle Size** + - No increase in bundle size + - Tree-shakeable as before + - Optimized imports + +### Breaking Changes + +**None** — This release is fully backward compatible. + +- Components without `showAlpha` prop work exactly as before (6 symbols) +- Removed `opacity` prop from `InputHexWithPreview` (use alpha in hex value instead) +- All existing code continues to work without modifications + +### Migration Guide + +#### Using Alpha Channel (Optional) + +```tsx +// Before (v1.1.0) + + +// After (v1.2.0) - Use alpha in hex value + +``` + +#### No Changes Required + +If you're not using alpha channel, no changes are needed: + +```tsx +// Still works exactly the same + + +``` + ## [1.1.0] - 2025-10-08 ### Added **🎉 13 New Specialized Input Components** + - `OpacityInput` - Opacity control (0-100%) - `AngleInput` - Rotation angle control (0-360°) - `BorderRadiusInput` - Border radius control with unit support @@ -25,6 +127,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `BlurInput` - Blur effect control **Advanced Input Features** + - 🎛️ **5 Progression Types**: linear, arithmetic, geometric, paraboloid, exponential - 🔄 **Drag-to-change** functionality for all numeric inputs - 📐 **Dual orientation**: horizontal and vertical layouts @@ -35,6 +138,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 🎨 **Full customization**: className props for all elements **Modular Architecture** + - Refactored `InputColorPicker` into modular structure: - `hooks/` - useColorPickerState, useDraggable - `utils/` - color conversion utilities @@ -43,6 +147,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Consistent component patterns across all inputs **Documentation** + - Added `INPUTS_GUIDE.md` with comprehensive input documentation - Updated README.md with new components section - Added Storybook examples for all input components @@ -99,6 +204,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0] - 2025-10-06 ### Changed + - **Project Renamed**: `@flowscape-ui/color-picker` → `@flowscape-ui/design-system-kit` - Updated package name to reflect expanded scope as a comprehensive design system - Enhanced keywords for better discoverability (design-system, design-system-kit, ui-components, component-library, input-range, ui-kit) @@ -106,11 +212,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Prepared for future expansion with additional UI components beyond color picker ### Note + This is the initial release under the new name. The library now serves as a foundation for a complete design system kit, with plans to include input range components and other essential UI elements. ## [1.1.0] - 2024-12-19 ### Added + - **Advanced Input Components System** - New `InputNumberSelect` component with drag-and-drop functionality - Support for multiple progression types: linear, arithmetic, geometric, paraboloid, exponential @@ -140,6 +248,7 @@ This is the initial release under the new name. The library now serves as a foun - `tailwind-merge` for intelligent class merging ### Changed + - **Component Architecture** - Moved `InputNumberSelect` to `src/components/input/` directory - Updated import paths in `Inputs.tsx` component @@ -159,6 +268,7 @@ This is the initial release under the new name. The library now serves as a foun - Smooth transitions and animations ### Technical Improvements + - **TypeScript Enhancements** - Extended interfaces with better type safety - Improved component prop definitions @@ -175,6 +285,7 @@ This is the initial release under the new name. The library now serves as a foun - Better code organization and maintainability ### Dependencies + - Added `lucide-react@^0.544.0` - Added `react-icons@^5.5.0` - Added `tailwind-merge@^3.3.1` @@ -183,6 +294,7 @@ This is the initial release under the new name. The library now serves as a foun ## [1.0.0] - 2024-12-19 ### Added + - **Core Color Picker Component** - Full-featured React color picker for whiteboard systems - Support for multiple color formats: RGB, HSL, HSV, CMYK, HEX @@ -222,12 +334,14 @@ This is the initial release under the new name. The library now serves as a foun - Vitest for testing ### Dependencies + - `react@>=18` (peer dependency) - `react-dom@>=18` (peer dependency) - `tinycolor2@^1.6.0` for color manipulation - `@types/tinycolor2@^1.4.4` for TypeScript support ### Documentation + - Comprehensive README.md with installation and usage examples - Complete API documentation - Multiple playground examples demonstrating all features @@ -239,11 +353,13 @@ This is the initial release under the new name. The library now serves as a foun ## Version History Summary ### v1.1.0 - Major Enhancement Release + - **Focus**: Advanced input components and improved UX - **Key Features**: Drag-and-drop inputs, specialized color picker, Tailwind CSS integration - **Impact**: Significantly enhanced user experience and component flexibility ### v1.0.0 - Initial Release + - **Focus**: Core color picker functionality - **Key Features**: Multi-format color support, gradients, eye dropper, customization - **Impact**: Solid foundation for whiteboard color selection needs @@ -255,20 +371,24 @@ This is the initial release under the new name. The library now serves as a foun ### From v1.0.0 to v1.1.0 #### Breaking Changes + - `InputNumberSelect` component moved from `src/components/InputNumberSelect.tsx` to `src/components/input/input-number-select.tsx` - Import path changed: `import InputNumberSelect from './InputNumberSelect'` → `import { InputNumberSelect } from './input/input-number-select'` #### New Dependencies Required + ```bash npm install lucide-react react-icons tailwind-merge ``` #### New Components Available + - `InputColorPicker` - Specialized color input with integrated picker - Enhanced `InputNumberSelect` - Advanced numeric input with drag-and-drop - `Input` - Base input component with Tailwind styling #### Recommended Updates + - Consider using new `InputColorPicker` for color selection interfaces - Leverage enhanced `InputNumberSelect` for better numeric input UX - Integrate Tailwind CSS for consistent styling @@ -278,6 +398,7 @@ npm install lucide-react react-icons tailwind-merge ## Future Roadmap ### Planned Features (v1.2.0) + - [ ] Accessibility improvements (ARIA labels, keyboard navigation) - [ ] Additional color format support (LAB, XYZ) - [ ] Color palette management @@ -286,6 +407,7 @@ npm install lucide-react react-icons tailwind-merge - [ ] Export/import color schemes ### Long-term Goals + - [ ] Web Components support - [ ] React Native compatibility - [ ] Performance optimizations for large color palettes @@ -295,6 +417,4 @@ npm install lucide-react react-icons tailwind-merge --- -*This changelog follows [Keep a Changelog](https://keepachangelog.com/) format and uses [Semantic Versioning](https://semver.org/) for version numbers.* - - +_This changelog follows [Keep a Changelog](https://keepachangelog.com/) format and uses [Semantic Versioning](https://semver.org/) for version numbers._ diff --git a/README.md b/README.md index 558a129..d738394 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,33 @@ -# @flowscape-ui/design-system-kit +
-[![npm version](https://img.shields.io/npm/v/@flowscape-ui/design-system-kit.svg)](https://www.npmjs.com/package/@flowscape-ui/design-system-kit) -[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@flowscape-ui/design-system-kit)](https://bundlephobia.com/package/@flowscape-ui/design-system-kit) -[![license](https://img.shields.io/npm/l/@flowscape-ui/design-system-kit.svg)](https://github.com/flowscape-ui/design-system-kit/blob/main/LICENSE) -[![Buy Me a Coffee](https://img.shields.io/badge/Donate-Buy%20Me%20a%20Coffee-FFDD00?logo=buymeacoffee&logoColor=000)](https://buymeacoffee.com/flowscape) +# 🎨 @flowscape-ui/design-system-kit -A comprehensive React design system kit with color picker, specialized input components, and other essential UI elements. Built with TypeScript, modular architecture, and optimized for modern web applications. +[![npm version](https://img.shields.io/npm/v/@flowscape-ui/design-system-kit.svg?style=flat-square)](https://www.npmjs.com/package/@flowscape-ui/design-system-kit) +[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@flowscape-ui/design-system-kit?style=flat-square)](https://bundlephobia.com/package/@flowscape-ui/design-system-kit) +[![license](https://img.shields.io/npm/l/@flowscape-ui/design-system-kit.svg?style=flat-square)](https://github.com/flowscape-ui/design-system-kit/blob/main/LICENSE) +[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/) + +**Professional React UI components for color management and design systems** + +[📚 Documentation](https://flowscape-ui.github.io/design-system-kit) • [🎨 Storybook](https://flowscape-ui.github.io/design-system-kit) • [🐛 Report Bug](https://github.com/flowscape-ui/design-system-kit/issues) • [☕ Support](https://buymeacoffee.com/flowscape) + +
+ +--- ## ✨ Features -- 🎨 **Multiple Color Formats**: Support for RGB, HSL, HSV, CMYK, and HEX -- 🌈 **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 -- ⚡ **Tree-shakeable**: Reduce bundle size by up to 94% -- 🎨 **Highly Customizable**: Hide/show components and customize styling -- 🔧 **TypeScript Support**: Full type definitions included -- 🎪 **Advanced Controls**: Fine-tune colors and values with precision sliders +- 🎨 **Multiple Color Formats** — RGB, HSL, HSV, CMYK, HEX with alpha channel support +- 🌈 **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 with drag-to-change and alpha channel +- 🎛️ **5 Progression Types** — Linear, arithmetic, geometric, paraboloid, exponential +- 🌓 **Dark/Light Mode** — Automatic theme detection with manual override +- 📦 **Modular Architecture** — Import only what you need (tree-shakeable) +- ⚡ **Lightweight** — From 1KB to 62KB per component +- 🔧 **TypeScript** — Full type definitions and IntelliSense support +- 🎪 **Advanced Controls** — Fine-tune colors with precision sliders ## 📦 Installation @@ -28,6 +35,12 @@ A comprehensive React design system kit with color picker, specialized input com npm install @flowscape-ui/design-system-kit ``` +**Peer Dependencies:** + +```bash +npm install react react-dom framer-motion lucide-react react-icons +``` + ## 🚀 Quick Start ### Option 1: Import from main module (convenient) @@ -53,9 +66,22 @@ import { InputHex } from '@flowscape-ui/design-system-kit/input-hex' import { InputHexWithPreview } from '@flowscape-ui/design-system-kit/input-hex-with-preview' ``` -## 📚 Components +## 📚 Components Overview + +| Component | Size | Description | +| ----------------------- | ------ | ----------------------------------------------------------------------------- | +| **ColorPicker** | 134 KB | Full-featured color picker with gradients, eye dropper, and all color formats | +| **InputColorPicker** | 142 KB | Compact color input with popup picker | +| **InputNumberSelect** | 12 KB | Universal drag-to-change numeric input | +| **InputHexWithPreview** | 7.5 KB | HEX input with color preview and alpha support | +| **InputHex** | 6.7 KB | Lightweight HEX input with alpha support | +| **Input** | 847 B | Base input component | + +--- -### ColorPicker (59 KB) +## 🎨 ColorPicker + +Full-featured color picker supporting all color formats, gradients, and advanced controls. ```tsx import { useState } from 'react' @@ -63,598 +89,319 @@ import { ColorPicker } from '@flowscape-ui/design-system-kit/color-picker' function App() { const [color, setColor] = useState('rgba(175, 51, 242, 1)') - return } ``` -### InputNumberSelect (3 KB) +**Key Features:** + +- ✅ RGB, HSL, HSV, CMYK, HEX formats +- ✅ Linear and radial gradients +- ✅ Eye dropper tool +- ✅ Color presets +- ✅ Advanced sliders +- ✅ Dark/light themes + +--- -Universal drag-to-change numeric input component for all design properties. Supports custom icons, units, precision, and multiple progression types. +## 🔢 InputNumberSelect + +Universal drag-to-change numeric input for all design properties. ```tsx -import { useState } from 'react' import { InputNumberSelect } from '@flowscape-ui/design-system-kit/input-number-select' import { RotateCw } from 'lucide-react' -function App() { - const [opacity, setOpacity] = useState(75) - const [angle, setAngle] = useState(45) - - return ( - <> - {/* Opacity control */} - - - {/* Angle control with custom icon */} - } - /> - - ) -} +// Opacity (0-100%) + + +// Angle with custom icon +} +/> ``` **Key Features:** -- 🎯 Drag-to-change with mouse/keyboard support -- 🎨 Custom icons (string or React components) -- 📊 5 progression types (linear, arithmetic, geometric, paraboloid, exponential) -- 🌓 Automatic dark/light theme support via Tailwind CSS -- 📝 Unit display (px, %, rem, em, deg, etc.) -- 🔄 Horizontal/vertical orientation -- ⌨️ Keyboard navigation (Arrow keys, Page Up/Down, Home/End) +- ✅ Drag-to-change with mouse/keyboard +- ✅ 5 progression types (linear, arithmetic, geometric, paraboloid, exponential) +- ✅ Custom icons (string or React components) +- ✅ Unit display (px, %, rem, em, deg, etc.) +- ✅ Horizontal/vertical orientation +- ✅ Keyboard navigation (Arrow keys, Page Up/Down, Home/End) + +--- + +## 🎨 InputColorPicker -### InputColorPicker (62 KB) +Compact color input with popup picker. ```tsx -import { useState } from 'react' import { InputColorPicker } from '@flowscape-ui/design-system-kit/input-color-picker' -function App() { - const [color, setColor] = useState('rgba(255, 255, 255, 1)') - - return ( - - ) -} +; ``` -### InputHex (1 KB) +--- -Component for HEX color input with drag-to-change. +## 🔤 InputHex + +Lightweight HEX color input with drag-to-change and alpha channel support. ```tsx -import { useState } from 'react' import { InputHex } from '@flowscape-ui/design-system-kit/input-hex' -function App() { - const [color, setColor] = useState('#ff5733') +// Basic usage (6 symbols) + - return -} +// With alpha channel (8 symbols) + ``` **Key Features:** -- 🎨 Drag-to-change for color modification by dragging -- 🔤 Real-time HEX value validation +- ✅ Drag `#` icon to change color +- ✅ Alpha channel support (`showAlpha` prop) +- ✅ Real-time HEX validation +- ✅ Uppercase formatting +- ✅ Customizable callbacks (click, drag start/end) +- ✅ Dark/light theme support -- 🎯 Customizable callbacks for click and drag events -- 🌓 Automatic light/dark theme support +--- -### InputHexWithPreview (1.2 KB) +## 🎨 InputHexWithPreview -Extended version of InputHex with visual color preview. +HEX input with visual color preview and alpha channel support. ```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 ( - - ) -} +// Basic usage + + +// With alpha channel + ``` **Key Features:** -- 🎨 Everything from InputHex + visual color preview -- 👁️ Square preview with opacity support -- 🎨 Preview style customization -- 📦 Compact size for form integration +- ✅ Everything from InputHex + visual preview +- ✅ Color preview square with alpha support +- ✅ Drag to change color, alpha preserved +- ✅ Customizable preview styles +- ✅ Compact size for forms -### InputHex Props +--- + +## 📋 API Reference + +### InputHex / InputHexWithPreview Props ```tsx interface InputHexProps { - // Main parameters - hexColor: string // HEX color (required) + // Required + hexColor: string // HEX color value handleChange: (hexColor: string) => void // Change callback + // Alpha channel + showAlpha?: boolean // Enable 8-symbol input (RRGGBBAA) + // Styling className?: string // Container classes classNameInput?: string // Input field classes classNameIcon?: string // Icon classes + classNamePreview?: string // Preview classes (InputHexWithPreview only) // 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 + onIconClick?: (hexColor: string) => void + onIconPointerDown?: (hexColor: string) => void + onIconPointerUp?: (hexColor: string) => void } ``` -### InputHexWithPreview Props - -Inherits all props from `InputHexProps` plus: +**Alpha Channel Behavior:** -```tsx -interface InputHexWithPreviewProps extends InputHexProps { - opacity?: number // Opacity (0-1), default: 1 - classNamePreview?: string // Classes for color preview -} -``` +- When `showAlpha={true}`, input accepts 8 symbols (RRGGBBAA) +- Drag changes only first 6 symbols (base color), alpha is preserved +- Alpha affects preview transparency automatically +- Format: `#RRGGBBAA` where AA is alpha (00=transparent, FF=opaque) -### InputHex Usage Examples +### Usage Examples ```tsx import { InputHex, InputHexWithPreview } from '@flowscape-ui/design-system-kit' -// Basic usage - console.log(color)} -/> +// Basic usage (6 symbols) + + +// With alpha channel (8 symbols) + // With preview - + -// With custom callbacks - console.log('Clicked:', hex)} - onIconPointerDown={(hex) => console.log('Drag start:', hex)} - onIconPointerUp={(hex) => console.log('Drag end:', hex)} -/> +// With preview and alpha + -// Disabled drag +// Custom callbacks console.log('Clicked:', hex)} + onIconPointerDown={(hex) => console.log('Drag start:', hex)} + onIconPointerUp={(hex) => console.log('Drag end:', hex)} /> +// Disabled drag (keyboard only) + + // Custom styles ``` -### InputNumberSelect - Usage Examples - -One universal component for all design properties. Configure it through props: - -```tsx -import { InputNumberSelect } from '@flowscape-ui/design-system-kit/input-number-select' -import { RotateCw, SquareRoundCorner, Type, Blend } from 'lucide-react' - -// Opacity (0-100%) -} -/> - -// Angle (0-360°) -} -/> - -// Border Radius with units -} -/> - -// Font Size -} -/> - -// Line Height (unitless) - - -// Vertical orientation - -``` +--- ### InputNumberSelect Props ```tsx interface InputNumberSelectProps { - // Value control + // Required value: number onChange?: (value: number) => void - // Range configuration + // Range min?: number // Default: 0 max?: number // Default: 100 step?: number // Default: 1 precision?: number // Decimal places, default: 0 - // Progression type for dragging - progression?: 'linear' | 'arithmetic' | 'geometric' | 'paraboloid' | 'exponential' + // Progression type + progression?: + | 'linear' + | 'arithmetic' + | 'geometric' + | 'paraboloid' + | 'exponential' - // Visual configuration + // Visual orientation?: 'horizontal' | 'vertical' // Default: 'horizontal' icon?: React.ReactNode | string // Custom icon or text - - // Unit display unit?: 'px' | '%' | 'rem' | 'em' | 'deg' | 'none' showUnit?: boolean // Show unit after value // Styling - className?: string // Container classes - classNameInput?: string // Input field classes - classNameIcon?: string // Icon container classes + className?: string + classNameInput?: string + classNameIcon?: string - // State - disabled?: boolean // Disable component - isDisabledMouseEvent?: boolean // Disable drag functionality - - // HTML input props - ...HTMLInputElement // All standard input props + // Behavior + disabled?: boolean + isDisabledMouseEvent?: boolean } ``` -### Progression Types - -- **linear** - Linear change (default) -- **arithmetic** - Arithmetic progression (×2) -- **geometric** - Geometric progression (×1.05) -- **paraboloid** - Parabolic acceleration -- **exponential** - Exponential change - -## Basic Usage - -### Simple Color Picker - -```tsx -import { ColorPicker } from '@flowscape-ui/design-system-kit' -; console.log(color)} /> -``` - -### With Custom Dimensions - -```tsx - -``` +**Progression Types:** -### Gradient Support +- `linear` — Linear change (default) +- `arithmetic` — Arithmetic progression (×2) +- `geometric` — Geometric progression (×1.05) +- `paraboloid` — Parabolic acceleration +- `exponential` — Exponential change -```tsx - -``` +--- -## Advanced Configuration +## 🎨 ColorPicker Advanced Configuration -### Hide Specific Components +### Hide Components ```tsx -``` - -### Custom Presets - -```tsx -const customPresets = [ - '#ff0000', - '#00ff00', - '#0000ff', - 'rgba(255, 255, 0, 1)', - 'linear-gradient(45deg, #ff0000, #0000ff)' -] - - -``` - -### Theme Configuration - -```tsx - ``` -### Custom Styling +### Custom Presets & Styling ```tsx -const customStyles = { - body: { - backgroundColor: '#1a1a1a', - borderRadius: '8px', - padding: '16px' - }, - rbgcpInput: { - backgroundColor: '#2a2a2a', - color: '#ffffff', - border: '1px solid #444' - } -} +const presets = ['#ff0000', '#00ff00', '#0000ff'] ``` -### Configuration Options +--- -```tsx -const config = { - barSize: 18, // Size of slider bars - crossSize: 18, // Size of color picker crosshair - defaultColor: 'rgba(175, 51, 242, 1)', - defaultGradient: 'linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)' -} +## 🔧 Advanced: useColorPicker Hook - -``` - -## Using the Hook - -For more control, you can use the `useColorPicker` hook directly: +For custom implementations, use the `useColorPicker` hook: ```tsx import { useColorPicker } from '@flowscape-ui/design-system-kit' -function CustomColorPicker() { - const [color, setColor] = useState('rgba(175, 51, 242, 1)') - - const { - setR, - setG, - setB, - setA, - setHue, - setSaturation, - setLightness, - valueToHex, - valueToHSL, - valueToHSV, - valueToCmyk, - isGradient, - gradientType, - degrees, - rgbaArr, - hslArr, - previousColors, - } = useColorPicker(color, setColor) - - return ( -
- setR(Number(e.target.value))} - /> -

Current color: {valueToHex()}

-

Previous colors: {previousColors.length}

-
- ) -} -``` - -## Props API - -### ColorPicker Props - -| Prop | Type | Default | Description | -| ---------------------- | ------------------------- | ------------------------- | ------------------------------------------------ | -| `value` | `string` | `'rgba(175, 51, 242, 1)'` | Current color value (RGB, HEX, HSL, or gradient) | -| `onChange` | `(value: string) => void` | **Required** | Callback when color changes | -| `width` | `number` | `294` | Width of the color picker | -| `height` | `number` | `294` | Height of the color picker | -| `hideControls` | `boolean` | `false` | Hide control buttons | -| `hideInputs` | `boolean` | `false` | Hide input fields | -| `hideOpacity` | `boolean` | `false` | Hide opacity slider | -| `hidePresets` | `boolean` | `false` | Hide preset colors | -| `hideHue` | `boolean` | `false` | Hide hue slider | -| `hideEyeDrop` | `boolean` | `false` | Hide eye dropper | -| `hideAdvancedSliders` | `boolean` | `false` | Hide advanced sliders | -| `hideColorGuide` | `boolean` | `false` | Hide color guide | -| `hideInputType` | `boolean` | `false` | Hide input type dropdown | -| `hideColorTypeBtns` | `boolean` | `false` | Hide solid/gradient buttons | -| `hideGradientType` | `boolean` | `false` | Hide gradient type controls | -| `hideGradientAngle` | `boolean` | `false` | Hide gradient angle controls | -| `hideGradientStop` | `boolean` | `false` | Hide gradient stop controls | -| `hideGradientControls` | `boolean` | `false` | Hide all gradient controls | -| `hidePickerSquare` | `boolean` | `false` | Hide main color square | -| `presets` | `string[]` | `[]` | Custom preset colors | -| `disableDarkMode` | `boolean` | `false` | Disable dark mode | -| `disableLightMode` | `boolean` | `false` | Disable light mode | -| `showHexAlpha` | `boolean` | `false` | Show alpha in hex values | -| `style` | `Styles` | `{}` | Custom styles object | -| `className` | `string` | `undefined` | CSS class name | -| `config` | `PassedConfig` | `{}` | Configuration options | -| `locales` | `LocalesProps` | `defaultLocales` | Localization strings | -| `idSuffix` | `string` | `undefined` | Suffix for element IDs | - -### Configuration Object - -```tsx -interface PassedConfig { - barSize?: number // Size of slider bars (default: 18) - crossSize?: number // Size of color picker crosshair (default: 18) - defaultColor?: string // Default color value - defaultGradient?: string // Default gradient value -} +const { setR, setG, setB, valueToHex, rgbaArr } = useColorPicker( + color, + setColor +) ``` -### Styles Object +--- -```tsx -interface Styles { - body?: React.CSSProperties - rbgcpControlBtn?: React.CSSProperties - rbgcpControlIcon?: React.CSSProperties - rbgcpInput?: React.CSSProperties - rbgcpHandle?: React.CSSProperties - // ... and many more style properties -} -``` +### ColorPicker Key Props -## Color Format Support +| Prop | Type | Default | Description | +| ---------------------- | ------------------------- | -------- | ------------------------------------- | +| `value` | `string` | Required | Color value (RGB, HEX, HSL, gradient) | +| `onChange` | `(value: string) => void` | Required | Change callback | +| `width` / `height` | `number` | `294` | Picker dimensions | +| `hideOpacity` | `boolean` | `false` | Hide opacity slider | +| `hidePresets` | `boolean` | `false` | Hide preset colors | +| `hideEyeDrop` | `boolean` | `false` | Hide eye dropper | +| `hideGradientControls` | `boolean` | `false` | Hide gradient controls | +| `presets` | `string[]` | `[]` | Custom preset colors | +| `className` | `string` | - | CSS class name | -The color picker supports multiple color formats: +**Supported Color Formats:** -- **RGB**: `rgb(255, 0, 0)` or `rgba(255, 0, 0, 0.5)` -- **HEX**: `#ff0000` or `#ff0000ff` -- **HSL**: `hsl(0, 100%, 50%)` or `hsla(0, 100%, 50%, 0.5)` -- **HSV**: `hsv(0, 100%, 100%)` -- **CMYK**: `cmyk(0, 100%, 100%, 0)` -- **Gradients**: `linear-gradient(90deg, #ff0000 0%, #0000ff 100%)` +- RGB/RGBA: `rgb(255, 0, 0)`, `rgba(255, 0, 0, 0.5)` +- HEX: `#FF0000`, `#FF0000FF` (with alpha) +- HSL/HSLA: `hsl(0, 100%, 50%)`, `hsla(0, 100%, 50%, 0.5)` +- HSV: `hsv(0, 100%, 100%)` +- CMYK: `cmyk(0, 100%, 100%, 0)` +- Gradients: `linear-gradient(90deg, #ff0000 0%, #0000ff 100%)` -## Gradient Support - -The color picker includes comprehensive gradient support: - -```tsx -// Linear gradient - - -// Radial gradient - -``` +--- ## Browser Support @@ -699,40 +446,36 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - **🐛 Report Issues**: https://github.com/flowscape-ui/design-system-kit/issues - **☕ Support the Project**: [Buy Me a Coffee](https://buymeacoffee.com/flowscape) -## Changelog +## 📝 Changelog -See [CHANGELOG.md](CHANGELOG.md) for a detailed list of changes. +### v1.2.0 (Latest) -### v1.1.0 (Latest) +**🎨 Alpha Channel Support** -**🎉 Major Update: Specialized Input Components** +- ✨ **Alpha channel support** for InputHex and InputHexWithPreview +- 🔤 **`showAlpha` prop** — Enable 8-symbol HEX input (RRGGBBAA) +- 🎨 **Automatic preview** — Alpha affects transparency in InputHexWithPreview +- 🖱️ **Smart drag behavior** — Drag changes base color, alpha preserved +- 🔄 **Uppercase formatting** — All HEX values in uppercase +- 📚 **Enhanced documentation** with alpha channel examples -- ✨ **13 new specialized input components** for design properties -- 🎛️ **5 progression types**: linear, arithmetic, geometric, paraboloid, exponential -- 🔄 **Drag-to-change** functionality for all numeric inputs -- 🎨 **Theme support**: light, dark, auto -- 📐 **Vertical and horizontal** orientation -- 🔧 **Modular architecture** for InputColorPicker (hooks, utils, types) -- 🐛 **Bug fixes**: NaN% in gradients, opacity validation -- 📚 **Enhanced documentation** and Storybook examples +**Breaking Changes:** None — fully backward compatible -**Key Features:** +### v1.1.0 + +**🎉 Specialized Input Components** -- Universal InputNumberSelect component for all design properties -- Drag-to-change functionality with 5 progression types -- Custom icons support (string or React components) -- Automatic dark/light theme via Tailwind CSS -- Horizontal and vertical orientation -- Full keyboard navigation support +- ✨ Universal InputNumberSelect component +- 🎛️ 5 progression types (linear, arithmetic, geometric, paraboloid, exponential) +- 🔄 Drag-to-change functionality +- 🎨 Theme support (light, dark, auto) +- 📐 Horizontal/vertical orientation +- 🔧 Modular architecture ### v1.0.0 -- Initial release as `@flowscape-ui/design-system-kit` -- Full color picker functionality -- Gradient support (linear and radial) -- Multiple color format support (RGB, HSL, HSV, CMYK, HEX) -- Dark/light mode with automatic detection -- Eye dropper functionality -- Advanced controls and sliders -- Full TypeScript support with type definitions -- Optimized bundle size (43KB minified) +- 🎉 Initial release +- 🎨 Full-featured ColorPicker +- 🌈 Gradient support +- 🎯 Eye dropper tool +- 🔧 TypeScript support diff --git a/package.json b/package.json index b59a23c..4a5cffb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@flowscape-ui/design-system-kit", "version": "1.0.0", - "description": "A comprehensive React design system kit with color picker, input range, and other essential UI components. Built with TypeScript and optimized for modern web applications", + "description": "Professional React UI components for color management and design systems. Features ColorPicker, InputHex with alpha channel, InputNumberSelect, and more. Built with TypeScript.", "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.js", diff --git a/src/input-hex-with-preview/input-hex-with-preview.tsx b/src/input-hex-with-preview/input-hex-with-preview.tsx index 10ddaf0..57c608d 100644 --- a/src/input-hex-with-preview/input-hex-with-preview.tsx +++ b/src/input-hex-with-preview/input-hex-with-preview.tsx @@ -6,7 +6,6 @@ import { cn } from '../shared/utils/cn' export interface InputHexWithPreviewProps extends Omit, 'onChange' | 'value'> { hexColor: string - opacity?: number handleChange: (hexColor: string) => void className?: string classNameInput?: string @@ -16,6 +15,11 @@ export interface InputHexWithPreviewProps onIconClick?: (hexColor: string) => void onIconPointerDown?: (hexColor: string) => void onIconPointerUp?: (hexColor: string) => void + /** + * Show and allow alpha channel input (another 2 hex symbols). + * By default disabled (input only 6 symbols). + */ + showAlpha?: boolean } const THEME_CLASSES = { @@ -43,7 +47,6 @@ export const InputHexWithPreview = React.forwardRef< ( { hexColor, - opacity = 1, handleChange, className, classNameInput, @@ -54,55 +57,67 @@ export const InputHexWithPreview = React.forwardRef< onIconClick, onIconPointerDown, onIconPointerUp, + showAlpha = false, ...props }, ref ) => { - const dragRef = useRef(null) - const [disable, setDisable] = useState('') - const hex = tc(hexColor).toHex() - const [newHex, setNewHex] = useState(hex) + const [isEditing, setIsEditing] = useState(false) + + // Normalize input color through tinycolor2 + const color = tc(hexColor) + const hex = color.toHex() // 6 symbols without # + const alpha = color.getAlpha() // 0-1 + const alphaHex = Math.round(alpha * 255) + .toString(16) + .padStart(2, '0') + // Full value: if showAlpha and alpha < 1, add alpha + const hexFromProp = showAlpha && alpha < 1 ? hex + alphaHex : hex + + const [localHex, setLocalHex] = useState(hexFromProp) const dragStartValue = useRef(0) useEffect(() => { - if (disable !== 'hex') { - setNewHex(hex) + if (!isEditing) { + setLocalHex(hexFromProp) } - }, [hexColor, disable, hex]) - const hexFocus = () => { - setDisable('hex') - } - - const hexBlur = () => { - setDisable('') - } + }, [hexColor, isEditing, hexFromProp]) - const getCurrenthexColor = () => { - return newHex || hex + // Helper: converts 8-symbol hex to format with alpha + const convertToHex8 = (hex8: string) => { + const base = hex8.slice(0, 6) + const alphaHex = hex8.slice(6, 8) + const alphaDecimal = parseInt(alphaHex, 16) / 255 + return tc(`#${base}`).setAlpha(alphaDecimal).toHex8String().toUpperCase() } const handleHexInput = (e: React.ChangeEvent) => { - const val = e.target.value - setNewHex(val) - if (tc(val).isValid()) { - const hexWithHash = val.startsWith('#') ? val : `#${val}` - handleChange(hexWithHash) + const maxLen = showAlpha ? 8 : 6 + // Filter only hex symbols, truncate by length + const filtered = e.target.value + .replace(/[^0-9a-fA-F]/g, '') + .slice(0, maxLen) + .toUpperCase() + + setLocalHex(filtered) + + // Emit change only when full length + if (filtered.length === 6) { + handleChange(`#${filtered}`) + } else if (filtered.length === 8 && showAlpha) { + handleChange(convertToHex8(filtered)) } } const handleIconClick = () => { - if (onIconClick) { - onIconClick(getCurrenthexColor()) - } + onIconClick?.(localHex) } const handlePointerDown = (e: React.PointerEvent) => { if (disabled || isDisabledMouseEvent) return e.preventDefault() - if (onIconPointerDown) { - onIconPointerDown(getCurrenthexColor()) - } + onIconPointerDown?.(localHex) const target = e.currentTarget target.setPointerCapture(e.pointerId) @@ -119,7 +134,11 @@ export const InputHexWithPreview = React.forwardRef< dragStartValue.current = parseInt(hex, 16) const startX = e.clientX - setDisable('hex') + setIsEditing(true) + + // Save initial alpha to preserve it during drag + const initialAlpha = + showAlpha && localHex.length >= 8 ? localHex.slice(6, 8) : '' const handlePointerMove = (event: PointerEvent) => { const movementX = event.clientX - startX @@ -129,18 +148,27 @@ export const InputHexWithPreview = React.forwardRef< ) newhexColorInt = Math.max(0, Math.min(0xffffff, newhexColorInt)) - const newhexColorHex = newhexColorInt.toString(16).padStart(6, '0') - setNewHex(newhexColorHex) - handleChange(`#${newhexColorHex}`) + // Drag changes only base color (6 symbols) + const newBaseHex = newhexColorInt + .toString(16) + .padStart(6, '0') + .toUpperCase() + + // Update local state with new base color + preserved alpha + setLocalHex(newBaseHex + initialAlpha) + + // Emit with alpha if it exists, otherwise just base color + if (showAlpha && initialAlpha) { + handleChange(convertToHex8(newBaseHex + initialAlpha)) + } else { + handleChange(`#${newBaseHex}`) + } } const handlePointerUp = (event: PointerEvent) => { target.releasePointerCapture(event.pointerId) - setDisable('') - - if (onIconPointerUp) { - onIconPointerUp(getCurrenthexColor()) - } + setIsEditing(false) + onIconPointerUp?.(localHex) const styleToRemove = document.getElementById('dragging-cursor-style') if (styleToRemove) { @@ -154,13 +182,11 @@ export const InputHexWithPreview = React.forwardRef< document.addEventListener('pointerup', handlePointerUp) } - const displayValue = newHex + // Color for preview with opacity + const previewColor = tc(hexColor).toRgbString() - const hexhexColor = tc(hexColor).toHex() - const rgbahexColor = tc(hexhexColor).setAlpha(opacity).toRgbString() - - const renderIcon = useMemo(() => { - return ( + const renderIcon = useMemo( + () => (
- ) - }, [rgbahexColor, classNamePreview]) + ), + [previewColor, classNamePreview] + ) return (
))} @@ -318,7 +298,7 @@ export const ColorScheme: Story = { style={{ backgroundColor: color.toHexString() }} /> - {color.toHexString().toUpperCase()} + {color.toHexString()}
))} @@ -336,7 +316,6 @@ export const ColorScheme: Story = { export const TintsAndShades: Story = { args: { hexColor: '#AF33F2', - opacity: 1, handleChange: (newColor: string) => {}, }, render: () => { @@ -361,11 +340,7 @@ export const TintsAndShades: Story = {

Base color

- +
@@ -411,7 +386,6 @@ export const TintsAndShades: Story = { export const RandomColors: Story = { args: { hexColor: '#AF33F2', - opacity: 1, handleChange: (newColor: string) => {}, }, render: () => { @@ -446,14 +420,13 @@ export const RandomColors: Story = { ))}
+ ) +} export const InputColorPicker = ({ title = 'Background Color', className, + classNameGradientInput, value = 'rgba(255,255,255,1)', onChange, onShowPicker, @@ -33,8 +127,8 @@ export const InputColorPicker = ({ colorType, opacityValue, handleInputChange, - handleInputFocus, - handleHexChange, + handleInputFocus: _handleInputFocus, + handleHexChange: _handleHexChange, handleOpacityChange, handleColorChange, } = useColorPickerState({ value, onChange, onOpacityChange }) @@ -51,11 +145,23 @@ export const InputColorPicker = ({ const displayColor = createDisplayColor(livePreviewColor, opacityValue) + const handleCopyGradient = async () => { + if (colorType === 'gradient' && color) { + try { + await navigator.clipboard.writeText(color) + } catch (err) { + console.error('Failed to copy gradient:', err) + } + } + } + return (
@@ -76,32 +182,68 @@ export const InputColorPicker = ({ className="absolute inset-0" style={{ background: displayColor, - opacity: opacityValue / 100, }} /> - { - if (e.key === 'Enter') { - handleHexChange() - ;(e.target as HTMLInputElement).blur() + { + const cleanHex = newHex.replace('#', '').toUpperCase() + + // Извлекаем base color (первые 6 символов) + const baseColor = cleanHex.slice(0, 6) + + // Извлекаем alpha (последние 2 символа, если есть) + let newOpacity = opacityValue + if (cleanHex.length >= 8) { + const alphaHex = cleanHex.slice(6, 8) + const parsedOpacity = Math.round( + (parseInt(alphaHex, 16) / 255) * 100 + ) + if ( + !isNaN(parsedOpacity) && + parsedOpacity >= 0 && + parsedOpacity <= 100 + ) { + newOpacity = parsedOpacity + handleOpacityChange(parsedOpacity) + } + } + + // Обновляем основной цвет только если он валидный + if ( + baseColor.length === 6 && + /^[0-9A-F]{6}$/i.test(baseColor) + ) { + // Конвертируем HEX в RGB + const r = parseInt(baseColor.slice(0, 2), 16) + const g = parseInt(baseColor.slice(2, 4), 16) + const b = parseInt(baseColor.slice(4, 6), 16) + const newRgbaColor = `rgba(${r}, ${g}, ${b}, ${newOpacity / 100})` + + // Обновляем состояние + onChange?.(newRgbaColor) + handleInputChange({ + target: { value: baseColor }, + } as any) } }} - className="bg-transparent outline-none px-2 text-gray-200 font-mono text-xs" - disabled={hex === 'Mixed'} + showAlpha={true} + className="ml-1 bg-transparent border-none h-auto focus-within:ring-0 flex-1" + classNameInput={cn( + 'bg-transparent px-2 font-mono text-xs', + THEME_CLASSES.light.text, + THEME_CLASSES.dark.text + )} + // disabled={hex === 'Mixed'} />
) : ( - + + +
)} -
- handleOpacityChange(Number(value) || 0)} - step={1} - min={0} - icon="%" - max={100} - className="bg-transparent dark:bg-transparent flex-row-reverse px-0 py-0 h-4 outline-none border-none font-mono text-xs text-gray-400" - classNameInput="w-10 text-gray-400 font-mono text-xs px-0 py-0 text-right" +
+ +
-
diff --git a/src/input-color-picker/types.ts b/src/input-color-picker/types.ts index 87bb2fd..e1b1c7f 100644 --- a/src/input-color-picker/types.ts +++ b/src/input-color-picker/types.ts @@ -2,6 +2,7 @@ export interface InputColorPickerProps { title?: string value?: string className?: string + classNameGradientInput?: string showOpacity?: boolean showGradient?: boolean pickerSize?: number diff --git a/src/input-color-picker/utils/color-utils.ts b/src/input-color-picker/utils/color-utils.ts index 3227b0d..0c8810e 100644 --- a/src/input-color-picker/utils/color-utils.ts +++ b/src/input-color-picker/utils/color-utils.ts @@ -8,7 +8,7 @@ export const parseColor = (colorStr: string): ParsedColor => { // Обработка градиентов if (colorStr.includes('gradient')) { - return { hex: 'Mixed', opacity: 100 } + return { hex: 'Linear', opacity: 100 } } // Обработка HEX @@ -84,45 +84,66 @@ export const hexToRgb = (hexStr: string): RGB | null => { * Конвертирует прозрачность (0-100) в HEX (00-FF) */ export const opacityToHex = (opacity: number): string => { - if (opacity < 0 || opacity > 100) return 'ff' - const alpha = Math.round((opacity / 100) * 255) - return alpha.toString(16).padStart(2, '0') + if (opacity < 0 || opacity > 100) return 'ff' + const alpha = Math.round((opacity / 100) * 255) + return alpha.toString(16).padStart(2, '0') } /** * Создает display color с учетом прозрачности */ export const createDisplayColor = ( - livePreviewColor: string, - opacityValue: number + livePreviewColor: string, + opacityValue: number ): string => { - let displayColor = - livePreviewColor && - (livePreviewColor.includes('gradient') || - livePreviewColor.startsWith('rgba') || - livePreviewColor.startsWith('#')) - ? livePreviewColor - : '#FFFFFF' - - if (displayColor.startsWith('#')) { - let hexVal = displayColor.substring(1) - if (hexVal.length === 3 || hexVal.length === 4) { - hexVal = hexVal - .split('') - .map(char => char + char) - .join('') - } - const rgbHex = hexVal.substring(0, 6) - displayColor = `#${rgbHex}${opacityToHex(opacityValue)}` - } - - return displayColor + let displayColor = + livePreviewColor && + (livePreviewColor.includes('gradient') || + livePreviewColor.startsWith('rgba') || + livePreviewColor.startsWith('rgb') || + livePreviewColor.startsWith('#')) + ? livePreviewColor + : '#FFFFFF' + + // Обработка градиентов - возвращаем как есть + // Opacity для градиентов применяется напрямую к rgba цветам в handleOpacityChange + if (displayColor.includes('gradient')) { + return displayColor + } + + // Нормализация rgb/rgba -> rgba с учётом текущей opacityValue + if (displayColor.startsWith('rgb')) { + const match = displayColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/) + if (match) { + const r = parseInt(match[1], 10) + const g = parseInt(match[2], 10) + const b = parseInt(match[3], 10) + const a = match[4] !== undefined ? parseFloat(match[4]) : undefined + const finalA = isNaN(opacityValue) ? (typeof a === 'number' ? a : 1) : opacityValue / 100 + displayColor = `rgba(${r}, ${g}, ${b}, ${finalA})` + } + } + + // HEX -> HEXA с учётом opacityValue + if (displayColor.startsWith('#')) { + let hexVal = displayColor.substring(1) + if (hexVal.length === 3 || hexVal.length === 4) { + hexVal = hexVal + .split('') + .map(char => char + char) + .join('') + } + const rgbHex = hexVal.substring(0, 6) + displayColor = `#${rgbHex}${opacityToHex(opacityValue)}` + } + + return displayColor } /** * Фильтрует ввод для HEX значения */ export const filterHexInput = (value: string): string => { - const filteredValue = value.replace(/[^0-9a-fA-F]/g, '') - return filteredValue.substring(0, 6).toUpperCase() + const filteredValue = value.replace(/[^0-9a-fA-F]/g, '') + return filteredValue.substring(0, 6).toUpperCase() } diff --git a/src/stories/InputColorPicker.stories.tsx b/src/stories/InputColorPicker.stories.tsx index 4bd2c1c..dc13222 100644 --- a/src/stories/InputColorPicker.stories.tsx +++ b/src/stories/InputColorPicker.stories.tsx @@ -1,86 +1,250 @@ import type { Meta, StoryObj } from '@storybook/react' -import React, { useState } from 'react' +import { useState } from 'react' import { InputColorPicker } from '../input-color-picker' -const meta: Meta = { +const meta = { title: 'Components/InputColorPicker', component: InputColorPicker, parameters: { layout: 'centered', + docs: { + toc: { + title: 'Table of Contents', + }, + }, + backgrounds: { + default: 'light', + values: [ + { name: 'light', value: '#ffffff' }, + { name: 'dark', value: '#1a1a1a' }, + ], + }, }, + tags: ['autodocs'], argTypes: { value: { - control: 'color', - description: 'The current color value', + control: 'text', + description: 'Current color value (solid color or gradient)', }, onChange: { - action: 'changed', description: 'Callback when color changes', }, + onOpacityChange: { + description: 'Callback when opacity changes', + }, + className: { + control: 'text', + description: 'Custom classes for the container', + }, + classNameGradientInput: { + control: 'text', + description: 'Custom classes for gradient input)', + }, showOpacity: { control: 'boolean', description: 'Show opacity control', }, showGradient: { control: 'boolean', - description: 'Show gradient controls', + description: 'Show gradient controls in picker', }, pickerSize: { control: 'number', - description: 'Size of the color picker', + description: 'Size of the color picker popup', + }, + title: { + control: 'text', + description: 'Title shown in picker header', }, }, -} +} satisfies Meta export default meta type Story = StoryObj -// Basic Input Color Picker -export const Basic: Story = { +/** + * Basic example of InputColorPicker component. + * Use Controls to change parameters. + * Switch background in toolbar to change theme (light/dark). + */ +export const Default: Story = { args: { value: 'rgba(175, 51, 242, 1)', showOpacity: true, showGradient: false, pickerSize: 250, }, - render: args => { - const [color, setColor] = useState(args.value) + render: () => { + const [color, setColor] = useState('rgba(175, 51, 242, 1)') return ( -
-

Basic Input Color Picker

- -
-

Selected Color: {color}

-
+
+ +

+ Current color: {color} +

+
+ ) + }, +} + +/** + * ## Background Color + * Component for controlling element background color with opacity. + */ +export const BackgroundColor: Story = { + args: { + value: 'rgba(59, 130, 246, 1)', + }, + render: () => { + const [bgColor, setBgColor] = useState('rgba(59, 130, 246, 1)') + return ( +
+ +
+ Background Preview
) }, } -// With Gradient Support -export const WithGradient: Story = { +/** + * ## Text Color + * Component for controlling text color. + */ +export const TextColor: Story = { args: { - value: 'linear-gradient(45deg, #ff6b6b, #4ecdc4)', - showOpacity: true, - showGradient: true, - pickerSize: 250, + value: 'rgba(239, 68, 68, 1)', + }, + render: () => { + const [textColor, setTextColor] = useState('rgba(239, 68, 68, 1)') + return ( +
+ +
+

+ Colored Heading +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+
+
+ ) + }, +} + +/** + * ## Gradient Background + * Component with gradient support for creating beautiful backgrounds. + */ +export const GradientBackground: Story = { + args: { + value: + 'linear-gradient(135deg, rgba(255, 107, 107, 1) 0%, rgba(78, 205, 196, 1) 100%)', + }, + render: () => { + const [gradient, setGradient] = useState( + 'linear-gradient(135deg, rgba(255, 107, 107, 1) 0%, rgba(78, 205, 196, 1) 100%)' + ) + return ( +
+ +
+ Gradient Preview +
+

+ Click "Linear" to copy gradient CSS. Use opacity control to adjust + transparency. +

+
+ ) + }, +} + +/** + * ## Multiple Colors + * Managing multiple colors for a design system. + */ +export const MultipleColors: Story = { + args: { + value: 'rgba(59, 130, 246, 1)', }, - render: args => { - const [color, setColor] = useState(args.value) + render: () => { + const [primary, setPrimary] = useState('rgba(59, 130, 246, 1)') + const [secondary, setSecondary] = useState('rgba(139, 92, 246, 1)') + const [accent, setAccent] = useState('rgba(236, 72, 153, 1)') + return ( -
-

With Gradient Support

- -
-

Selected Color/Gradient:

-

{color}

+
+
+
+

+ Primary Color +

+ +
+
+

+ Secondary Color +

+ +
+
+

+ Accent Color +

+ +
+
+ +
+
+
@@ -88,66 +252,123 @@ export const WithGradient: Story = { }, } -// Interactive Demo -export const InteractiveDemo: Story = { +/** + * ## Card Design + * Using color picker to design a card component. + */ +export const CardDesign: Story = { + args: { + value: 'rgba(255, 255, 255, 1)', + }, render: () => { - const [color, setColor] = useState('#3498db') - const [showOpacity, setShowOpacity] = useState(true) - const [showGradient, setShowGradient] = useState(false) - const [pickerSize, setPickerSize] = useState(250) + const [cardBg, setCardBg] = useState('rgba(255, 255, 255, 1)') + const [headerBg, setHeaderBg] = useState( + 'linear-gradient(135deg, rgba(99, 102, 241, 1) 0%, rgba(139, 92, 246, 1) 100%)' + ) + const [textColor, setTextColor] = useState('rgba(31, 41, 55, 1)') return ( -
-

Interactive Demo

- -
-