Skip to content

Commit

Permalink
feat: Support components (#257)
Browse files Browse the repository at this point in the history
* chore: my components

* chore: base components

* test: update test case

* test: add test case

* fix: lint
  • Loading branch information
zombieJ authored Jul 18, 2024
1 parent 51f998a commit d647856
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 71 deletions.
4 changes: 4 additions & 0 deletions docs/demo/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@
## Color Block

<code src="../example/block.tsx"></code>

## Components

<code src="../example/components.tsx"></code>
5 changes: 1 addition & 4 deletions docs/example/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ export default () => {
gap: 8,
}}
>
<span>
hex:{' '}
{value.getAlpha() < 1 ? value.toHex8String() : value.toHexString()}
</span>
<span>hex: {value.toHexString()}</span>
<span> rgb: {value.toRgbString()}</span>
<span> hsb: {value.toHsbString()}</span>
</div>
Expand Down
44 changes: 44 additions & 0 deletions docs/example/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import ColorPicker, {
Color,
type BaseSliderProps,
} from '@rc-component/color-picker';
import React, { useState } from 'react';
import '../../assets/index.less';

const Slider = ({ min, max }: BaseSliderProps) => {
return (
<div>
<span>{min}</span>
<span>{max}</span>
</div>
);
};

export default () => {
const [value, setValue] = useState(new Color('#163cff'));

return (
<>
<ColorPicker
color={value}
onChange={setValue}
components={{
slider: Slider,
}}
/>
<br />
<div
style={{
width: 258,
display: 'flex',
flexDirection: 'column',
gap: 8,
}}
>
<span>hex: {value.toHexString()}</span>
<span>rgb: {value.toRgbString()}</span>
<span>hsb: {value.toHsbString()}</span>
</div>
</>
);
};
136 changes: 108 additions & 28 deletions src/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,42 @@ import React, { forwardRef, useMemo } from 'react';
import { ColorPickerPrefixCls, defaultColor, generateColor } from './util';

import classNames from 'classnames';
import { Color } from './color';
import ColorBlock from './components/ColorBlock';
import Picker from './components/Picker';
import Slider from './components/Slider';
import useColorState from './hooks/useColorState';
import useComponent, { type Components } from './hooks/useComponent';
import type { BaseColorPickerProps, ColorGenInput } from './interface';

const hueColor = [
'rgb(255, 0, 0) 0%',
'rgb(255, 255, 0) 17%',
'rgb(0, 255, 0) 33%',
'rgb(0, 255, 255) 50%',
'rgb(0, 0, 255) 67%',
'rgb(255, 0, 255) 83%',
'rgb(255, 0, 0) 100%',
const HUE_COLORS = [
{
color: 'rgb(255, 0, 0)',
percent: 0,
},
{
color: 'rgb(255, 255, 0)',
percent: 17,
},
{
color: 'rgb(0, 255, 0)',
percent: 33,
},
{
color: 'rgb(0, 255, 255)',
percent: 50,
},
{
color: 'rgb(0, 0, 255)',
percent: 67,
},
{
color: 'rgb(255, 0, 255)',
percent: 83,
},
{
color: 'rgb(255, 0, 0)',
percent: 100,
},
];

export interface ColorPickerProps extends BaseColorPickerProps {
Expand All @@ -28,6 +50,7 @@ export interface ColorPickerProps extends BaseColorPickerProps {
panelRender?: (panel: React.ReactElement) => React.ReactElement;
/** Disabled alpha selection */
disabledAlpha?: boolean;
components?: Components;
}

export default forwardRef<HTMLDivElement, ColorPickerProps>((props, ref) => {
Expand All @@ -42,7 +65,13 @@ export default forwardRef<HTMLDivElement, ColorPickerProps>((props, ref) => {
panelRender,
disabledAlpha = false,
disabled = false,
components,
} = props;

// ========================== Components ==========================
const [Slider] = useComponent(components);

// ============================ Color =============================
const [colorValue, setColorValue] = useColorState(defaultColor, {
value,
defaultValue,
Expand All @@ -53,46 +82,97 @@ export default forwardRef<HTMLDivElement, ColorPickerProps>((props, ref) => {
rgb.setAlpha(1);
return rgb.toRgbString();
}, [colorValue]);
const mergeCls = classNames(`${prefixCls}-panel`, className, {
[`${prefixCls}-panel-disabled`]: disabled,
});
const basicProps = {
prefixCls,
onChangeComplete,
disabled,
};

// ============================ Events ============================
const handleChange: BaseColorPickerProps['onChange'] = (data, type) => {
if (!value) {
setColorValue(data);
}
onChange?.(data, type);
};

// Convert
const getHueColor = (hue: number) => {
const hsb = colorValue.toHsb();
hsb.h = hue;
return new Color(hsb);
};

const getAlphaColor = (alpha: number) => {
const hsb = colorValue.toHsb();
hsb.a = Math.round(alpha * 100) / 100;
return new Color(hsb);
};

// Slider change
const onHueChange = (hue: number) => {
handleChange(getHueColor(hue), 'hue');
};

const onAlphaChange = (alpha: number) => {
handleChange(getAlphaColor(alpha), 'alpha');
};

// Complete
const onHueChangeComplete = (hue: number) => {
if (onChangeComplete) {
onChangeComplete(getHueColor(hue));
}
};

const onAlphaChangeComplete = (alpha: number) => {
if (onChangeComplete) {
onChangeComplete(getAlphaColor(alpha));
}
};

// ============================ Render ============================
const mergeCls = classNames(`${prefixCls}-panel`, className, {
[`${prefixCls}-panel-disabled`]: disabled,
});

const sharedSliderProps = {
prefixCls,
disabled,
color: colorValue,
};

const defaultPanel = (
<>
<Picker color={colorValue} onChange={handleChange} {...basicProps} />
<Picker
onChange={handleChange}
{...sharedSliderProps}
onChangeComplete={onChangeComplete}
/>
<div className={`${prefixCls}-slider-container`}>
<div
className={classNames(`${prefixCls}-slider-group`, {
[`${prefixCls}-slider-group-disabled-alpha`]: disabledAlpha,
})}
>
<Slider
gradientColors={hueColor}
color={colorValue}
value={`hsl(${colorValue.toHsb().h},100%, 50%)`}
onChange={color => handleChange(color, 'hue')}
{...basicProps}
{...sharedSliderProps}
type="hue"
colors={HUE_COLORS}
min={0}
max={360}
value={colorValue.getHue()}
onChange={onHueChange}
onChangeComplete={onHueChangeComplete}
/>
{!disabledAlpha && (
<Slider
{...sharedSliderProps}
type="alpha"
gradientColors={['rgba(255, 0, 4, 0) 0%', alphaColor]}
color={colorValue}
value={colorValue.toRgbString()}
onChange={color => handleChange(color, 'alpha')}
{...basicProps}
colors={[
{ percent: 0, color: 'rgba(255, 0, 4, 0)' },
{ percent: 100, color: alphaColor },
]}
min={0}
max={100}
value={colorValue.getAlpha() * 100}
onChange={onAlphaChange}
onChangeComplete={onAlphaChangeComplete}
/>
)}
</div>
Expand Down
78 changes: 46 additions & 32 deletions src/components/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
import classNames from 'classnames';
import type { FC } from 'react';
import React, { useRef } from 'react';
import useColorDrag from '../hooks/useColorDrag';
import type {
BaseColorPickerProps,
HsbaColorType,
TransformOffset,
} from '../interface';
import { calculateColor, calculateOffset } from '../util';
import type { HsbaColorType, TransformOffset } from '../interface';
import Palette from './Palette';

import classNames from 'classnames';
import { useEvent } from 'rc-util';
import type { Color } from '../color';
import { calculateColor, calculateOffset } from '../util';
import Gradient from './Gradient';
import Handler from './Handler';
import Transform from './Transform';

interface SliderProps extends BaseColorPickerProps {
gradientColors: string[];
direction?: string;
type?: HsbaColorType;
value?: string;
export interface BaseSliderProps {
prefixCls: string;
colors: { percent: number; color: string }[];
min: number;
max: number;
value: number;
disabled: boolean;
onChange: (value: number) => void;
onChangeComplete: (value: number) => void;
type: HsbaColorType;
color: Color;
}

const Slider: FC<SliderProps> = ({
gradientColors,
direction,
type = 'hue',
color,
value,
onChange,
onChangeComplete,
disabled,
prefixCls,
}) => {
const Slider: FC<BaseSliderProps> = props => {
const {
prefixCls,
colors,
disabled,
onChange,
onChangeComplete,
color,
type,
} = props;

const sliderRef = useRef();
const transformRef = useRef();
const colorRef = useRef(color);

const getValue = (c: Color) => {
return type === 'hue' ? c.getHue() : c.getAlpha();
};

const onDragChange = useEvent((offsetValue: TransformOffset) => {
const calcColor = calculateColor({
offset: offsetValue,
Expand All @@ -45,8 +52,9 @@ const Slider: FC<SliderProps> = ({
color,
type,
});

colorRef.current = calcColor;
onChange(calcColor);
onChange(getValue(calcColor));
});

const [offset, dragStartHandle] = useColorDrag({
Expand All @@ -57,12 +65,19 @@ const Slider: FC<SliderProps> = ({
calculateOffset(containerRef, transformRef, color, type),
onDragChange,
onDragChangeComplete() {
onChangeComplete?.(colorRef.current, type);
onChangeComplete(getValue(colorRef.current));
},
direction: 'x',
disabledDrag: disabled,
});

// ========================= Gradient =========================
const gradientList = React.useMemo(
() => colors.map(info => `${info.color} ${info.percent}%`),
[colors],
);

// ========================== Render ==========================
return (
<div
ref={sliderRef}
Expand All @@ -75,14 +90,13 @@ const Slider: FC<SliderProps> = ({
>
<Palette prefixCls={prefixCls}>
<Transform offset={offset} ref={transformRef}>
<Handler size="small" color={value} prefixCls={prefixCls} />
<Handler
size="small"
color={color.toHexString()}
prefixCls={prefixCls}
/>
</Transform>
<Gradient
colors={gradientColors}
direction={direction}
type={type}
prefixCls={prefixCls}
/>
<Gradient colors={gradientList} type={type} prefixCls={prefixCls} />
</Palette>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useColorDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type EventType =
type EventHandle = (e: EventType) => void;

interface useColorDragProps {
color?: Color;
color: Color;
offset?: TransformOffset;
containerRef: React.RefObject<HTMLDivElement>;
targetRef: React.RefObject<HTMLDivElement>;
Expand Down
Loading

0 comments on commit d647856

Please sign in to comment.