Skip to content

Commit 7a75740

Browse files
feat: ColorPicker component (#486)
* wip: color picker * wip: color picker update defaultvalue * feat: colorpicker component * feat: color-picker doc
1 parent 1d754c6 commit 7a75740

File tree

19 files changed

+716
-3
lines changed

19 files changed

+716
-3
lines changed

apps/www/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@
3737
"eslint-config-next": "14",
3838
"typescript": "^5.7.3"
3939
}
40-
}
40+
}

apps/www/src/components/demo/demo.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import NextLink from 'next/link';
66
import { Suspense } from 'react';
77
import DataTableDemo from '../datatable-demo';
88
import LinearDropdownDemo from '../linear-dropdown-demo';
9+
import PopoverColorPicker from '../popover-color-picker';
910
import DemoPlayground from './demo-playground';
1011
import DemoPreview from './demo-preview';
1112
import { DemoProps } from './types';
@@ -17,6 +18,7 @@ export default function Demo(props: DemoProps) {
1718
...Apsara,
1819
DataTableDemo,
1920
LinearDropdownDemo,
21+
PopoverColorPicker,
2022
Info,
2123
X,
2224
Home,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Button, Flex, Popover } from '@raystack/apsara';
2+
import { ColorPicker } from '@raystack/apsara';
3+
import { useState } from 'react';
4+
5+
export default function PopoverColorPicker() {
6+
const [color, setColor] = useState('#DA2929');
7+
8+
return (
9+
<Popover>
10+
<Popover.Trigger asChild>
11+
<Button
12+
style={{
13+
width: 60,
14+
height: 60,
15+
background: color
16+
}}
17+
/>
18+
</Popover.Trigger>
19+
<Popover.Content>
20+
<ColorPicker
21+
value={color}
22+
onValueChange={setColor}
23+
style={{
24+
width: '100%',
25+
height: '320px'
26+
}}
27+
>
28+
<ColorPicker.Area />
29+
<ColorPicker.Hue />
30+
<ColorPicker.Alpha />
31+
<Flex direction='row' gap={2}>
32+
<ColorPicker.Mode />
33+
<ColorPicker.Input />
34+
</Flex>
35+
</ColorPicker>
36+
</Popover.Content>
37+
</Popover>
38+
);
39+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use client';
2+
3+
export const preview = {
4+
type: 'code',
5+
code: `<ColorPicker
6+
defaultValue='#DA2929'
7+
style={{
8+
width: '240px',
9+
height: '320px',
10+
padding: 20,
11+
background: 'white'
12+
}}
13+
>
14+
<ColorPicker.Area />
15+
<ColorPicker.Hue />
16+
<ColorPicker.Alpha />
17+
<Flex direction="row" gap={2}>
18+
<ColorPicker.Mode />
19+
<ColorPicker.Input />
20+
</Flex>
21+
</ColorPicker>`
22+
};
23+
24+
export const basicDemo = {
25+
type: 'code',
26+
code: `
27+
<ColorPicker defaultValue="#00bcd4" style={{
28+
width: '240px',
29+
height: '220px',
30+
padding: 20,
31+
background: 'white'
32+
}}>
33+
<ColorPicker.Area/>
34+
<ColorPicker.Hue />
35+
</ColorPicker>
36+
`
37+
};
38+
39+
export const popoverDemo = {
40+
type: 'code',
41+
previewCode: false,
42+
code: `<PopoverColorPicker />`,
43+
codePreview: [
44+
{
45+
label: 'index.tsx',
46+
code: `function PopoverColorPicker() {
47+
const [color, setColor] = useState('#DA2929');
48+
49+
return (
50+
<Popover>
51+
<Popover.Trigger asChild>
52+
<Button
53+
style={{
54+
width: 60,
55+
height: 60,
56+
background: color
57+
}}
58+
/>
59+
</Popover.Trigger>
60+
<Popover.Content>
61+
<ColorPicker
62+
value={color}
63+
onValueChange={setColor}
64+
style={{
65+
width: '100%',
66+
height: '320px'
67+
}}
68+
>
69+
<ColorPicker.Area />
70+
<ColorPicker.Hue />
71+
<ColorPicker.Alpha />
72+
<Flex direction='row' gap={2}>
73+
<ColorPicker.Mode />
74+
<ColorPicker.Input />
75+
</Flex>
76+
</ColorPicker>
77+
</Popover.Content>
78+
</Popover>
79+
);
80+
}`
81+
}
82+
]
83+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
title: Color Picker
3+
description: A composable color picker component supporting multiple color models, alpha channel, and customizable UI.
4+
tag: new
5+
---
6+
7+
import {
8+
preview,
9+
basicDemo,
10+
popoverDemo,
11+
} from "./demo.ts";
12+
13+
<Demo data={preview} />
14+
15+
## Overview
16+
17+
The `ColorPicker` component offers a flexible and composable solution for selecting colors in various formats, including HEX, RGB, and HSL. It supports optional alpha channel (transparency) and is designed for easy integration and customization within your application.
18+
19+
## Getting Started
20+
21+
To use the `ColorPicker`, import it from `@raystack/apsara` and compose its subcomponents as needed. Below is a basic example:
22+
23+
```tsx
24+
import { ColorPicker } from "@raystack/apsara";
25+
26+
<ColorPicker
27+
defaultValue="#DA2929"
28+
style={{
29+
width: '240px',
30+
height: '320px',
31+
padding: 20,
32+
background: 'white'
33+
}}
34+
>
35+
<ColorPicker.Area />
36+
<ColorPicker.Hue />
37+
<ColorPicker.Alpha />
38+
<Flex direction="row" gap={2}>
39+
<ColorPicker.Mode />
40+
<ColorPicker.Input />
41+
</Flex>
42+
</ColorPicker>
43+
```
44+
45+
## ColorPicker Props
46+
47+
The `ColorPicker` is composed of several subcomponents, each responsible for a specific aspect of color selection. The root component acts as a data provider for its children.
48+
49+
<auto-type-table path="./props.ts" name="ColorPickerProps" />
50+
51+
### ColorPicker.Area
52+
53+
Enables users to select a color from a palette. Typically used for choosing saturation and brightness.
54+
55+
### ColorPicker.Hue
56+
57+
Provides a slider for selecting the hue value of the color.
58+
59+
### ColorPicker.Alpha
60+
61+
Provides a slider for selecting the alpha value of the color.
62+
63+
### ColorPicker.Mode
64+
65+
Lets users switch between different color models (e.g., HEX, RGB, HSL) via a dropdown menu.
66+
67+
<auto-type-table path="./props.ts" name="ColorPickerModeProps" />
68+
69+
### ColorPicker.Input
70+
71+
Displays the current color value in the selected color model and allows direct text input.
72+
73+
## Examples
74+
75+
### Basic Usage
76+
77+
<Demo data={basicDemo} />
78+
79+
### Popover Integration
80+
81+
The `ColorPicker` can be embedded within a `Popover` component to create a more interactive and space-efficient color selection experience.
82+
83+
<Demo data={popoverDemo} />
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Props for the root ColorPicker component.
3+
*/
4+
export interface ColorPickerProps {
5+
/**
6+
* The controlled color value. Accepts hex, rgb, hsl, etc.
7+
*/
8+
value?: string;
9+
/**
10+
* The default color value.
11+
* @default '#ffffff'
12+
*/
13+
defaultValue?: string;
14+
/**
15+
* Callback fired when the color value changes.
16+
*/
17+
onValueChange?: (value: string, mode: string) => void;
18+
/**
19+
* The initial color mode (hex, rgb, hsl).
20+
* @default 'hex'
21+
*/
22+
defaultMode?: 'hex' | 'rgb' | 'hsl';
23+
/**
24+
* The controlled color mode.
25+
*/
26+
mode?: 'hex' | 'rgb' | 'hsl';
27+
/**
28+
* Callback fired when the color mode changes.
29+
*/
30+
onModeChange?: (mode: string) => void;
31+
}
32+
33+
/**
34+
* Props for the ColorPicker.Mode subcomponent.
35+
*/
36+
export interface ColorPickerModeProps {
37+
/**
38+
* Supported color modes for the picker.
39+
* @default ['hex', 'rgb', 'hsl']
40+
*/
41+
options?: Array<'hex' | 'rgb' | 'hsl'>;
42+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { cx } from 'class-variance-authority';
2+
import { Slider } from 'radix-ui';
3+
import { type ComponentProps } from 'react';
4+
import { useColorPicker } from './color-picker-root';
5+
import styles from './color-picker.module.css';
6+
7+
export type ColorPickerAlphaProps = ComponentProps<typeof Slider.Root>;
8+
9+
export const ColorPickerAlpha = ({
10+
className,
11+
...props
12+
}: ColorPickerAlphaProps) => {
13+
const { alpha = 1, setColor } = useColorPicker();
14+
return (
15+
<Slider.Root
16+
className={cx(styles.sliderRoot, className)}
17+
max={100}
18+
onValueChange={([alpha]) => setColor({ alpha: alpha / 100 })}
19+
step={1}
20+
value={[alpha * 100]}
21+
{...props}
22+
>
23+
<Slider.Track className={cx(styles.sliderTrack, styles.alphaTrack)}>
24+
<div className={styles.alphaTrackGradient} />
25+
<Slider.Range className={styles.sliderRange} />
26+
</Slider.Track>
27+
<Slider.Thumb className={styles.sliderThumb} />
28+
</Slider.Root>
29+
);
30+
};

0 commit comments

Comments
 (0)