mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-11 11:32:52 +08:00
fix: color picker controlled value (#47816)
* fix: fix control value not show in dom * chore: update title * add test case * change test title * fix: cleared color should change after controlled value changed * chore: code clean * test: change test * comment * fix: should respect empty string * test: add test case * chore: update demo --------- Co-authored-by: tanghui <yoyo837@hotmail.com> Co-authored-by: afc163 <afc163@gmail.com> Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
parent
6310bf2840
commit
1cb644d476
@ -13,7 +13,7 @@ const getAllowClear = (allowClear: AllowClear): AllowClear => {
|
|||||||
clearIcon: <CloseCircleFilled />,
|
clearIcon: <CloseCircleFilled />,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergedAllowClear;
|
return mergedAllowClear;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import type { CSSProperties, FC } from 'react';
|
import React, { useContext, useMemo, useRef } from 'react';
|
||||||
import React, { useContext, useMemo, useRef, useState } from 'react';
|
import type { HsbaColorType } from '@rc-component/color-picker';
|
||||||
import type {
|
|
||||||
HsbaColorType,
|
|
||||||
ColorPickerProps as RcColorPickerProps,
|
|
||||||
} from '@rc-component/color-picker';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||||
|
|
||||||
@ -15,7 +11,6 @@ import { ConfigContext } from '../config-provider/context';
|
|||||||
import DisabledContext from '../config-provider/DisabledContext';
|
import DisabledContext from '../config-provider/DisabledContext';
|
||||||
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
|
||||||
import useSize from '../config-provider/hooks/useSize';
|
import useSize from '../config-provider/hooks/useSize';
|
||||||
import type { SizeType } from '../config-provider/SizeContext';
|
|
||||||
import { FormItemInputContext, NoFormStyle } from '../form/context';
|
import { FormItemInputContext, NoFormStyle } from '../form/context';
|
||||||
import type { PopoverProps } from '../popover';
|
import type { PopoverProps } from '../popover';
|
||||||
import Popover from '../popover';
|
import Popover from '../popover';
|
||||||
@ -23,50 +18,10 @@ import type { Color } from './color';
|
|||||||
import ColorPickerPanel from './ColorPickerPanel';
|
import ColorPickerPanel from './ColorPickerPanel';
|
||||||
import ColorTrigger from './components/ColorTrigger';
|
import ColorTrigger from './components/ColorTrigger';
|
||||||
import useColorState from './hooks/useColorState';
|
import useColorState from './hooks/useColorState';
|
||||||
import type {
|
import type { ColorPickerBaseProps, ColorPickerProps, TriggerPlacement } from './interface';
|
||||||
ColorFormat,
|
|
||||||
ColorPickerBaseProps,
|
|
||||||
ColorValueType,
|
|
||||||
PresetsItem,
|
|
||||||
TriggerPlacement,
|
|
||||||
TriggerType,
|
|
||||||
} from './interface';
|
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import { genAlphaColor, generateColor, getAlphaColor } from './util';
|
import { genAlphaColor, generateColor, getAlphaColor } from './util';
|
||||||
|
|
||||||
export type ColorPickerProps = Omit<
|
|
||||||
RcColorPickerProps,
|
|
||||||
'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'disabledAlpha' | 'onChangeComplete'
|
|
||||||
> & {
|
|
||||||
value?: ColorValueType;
|
|
||||||
defaultValue?: ColorValueType;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
open?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
placement?: TriggerPlacement;
|
|
||||||
trigger?: TriggerType;
|
|
||||||
format?: keyof typeof ColorFormat;
|
|
||||||
defaultFormat?: keyof typeof ColorFormat;
|
|
||||||
allowClear?: boolean;
|
|
||||||
presets?: PresetsItem[];
|
|
||||||
arrow?: boolean | { pointAtCenter: boolean };
|
|
||||||
panelRender?: (
|
|
||||||
panel: React.ReactNode,
|
|
||||||
extra: { components: { Picker: FC; Presets: FC } },
|
|
||||||
) => React.ReactNode;
|
|
||||||
showText?: boolean | ((color: Color) => React.ReactNode);
|
|
||||||
size?: SizeType;
|
|
||||||
styles?: { popup?: CSSProperties; popupOverlayInner?: CSSProperties };
|
|
||||||
rootClassName?: string;
|
|
||||||
disabledAlpha?: boolean;
|
|
||||||
[key: `data-${string}`]: string;
|
|
||||||
onOpenChange?: (open: boolean) => void;
|
|
||||||
onFormatChange?: (format: ColorFormat) => void;
|
|
||||||
onChange?: (value: Color, hex: string) => void;
|
|
||||||
onClear?: () => void;
|
|
||||||
onChangeComplete?: (value: Color) => void;
|
|
||||||
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
|
||||||
|
|
||||||
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
||||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||||
};
|
};
|
||||||
@ -109,7 +64,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
const contextDisabled = useContext(DisabledContext);
|
const contextDisabled = useContext(DisabledContext);
|
||||||
const mergedDisabled = disabled ?? contextDisabled;
|
const mergedDisabled = disabled ?? contextDisabled;
|
||||||
|
|
||||||
const [colorValue, setColorValue] = useColorState('', {
|
const [colorValue, setColorValue, prevValue] = useColorState('', {
|
||||||
value,
|
value,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
});
|
});
|
||||||
@ -124,8 +79,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
onChange: onFormatChange,
|
onChange: onFormatChange,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [colorCleared, setColorCleared] = useState(!value && !defaultValue);
|
|
||||||
|
|
||||||
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
|
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
|
||||||
|
|
||||||
const isAlphaColor = useMemo(() => getAlphaColor(colorValue) < 100, [colorValue]);
|
const isAlphaColor = useMemo(() => getAlphaColor(colorValue) < 100, [colorValue]);
|
||||||
@ -167,14 +120,16 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
|
|
||||||
const handleChange = (data: Color, type?: HsbaColorType, pickColor?: boolean) => {
|
const handleChange = (data: Color, type?: HsbaColorType, pickColor?: boolean) => {
|
||||||
let color: Color = generateColor(data);
|
let color: Color = generateColor(data);
|
||||||
|
|
||||||
|
// If color is cleared, reset alpha to 100
|
||||||
const isNull = value === null || (!value && defaultValue === null);
|
const isNull = value === null || (!value && defaultValue === null);
|
||||||
if (colorCleared || isNull) {
|
if (prevValue.current?.cleared || isNull) {
|
||||||
setColorCleared(false);
|
|
||||||
// ignore alpha slider
|
// ignore alpha slider
|
||||||
if (getAlphaColor(colorValue) === 0 && type !== 'alpha') {
|
if (getAlphaColor(colorValue) === 0 && type !== 'alpha') {
|
||||||
color = genAlphaColor(color);
|
color = genAlphaColor(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore alpha color
|
// ignore alpha color
|
||||||
if (disabledAlpha && isAlphaColor) {
|
if (disabledAlpha && isAlphaColor) {
|
||||||
color = genAlphaColor(color);
|
color = genAlphaColor(color);
|
||||||
@ -192,7 +147,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
setColorCleared(true);
|
|
||||||
onClear?.();
|
onClear?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -221,7 +175,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
prefixCls,
|
prefixCls,
|
||||||
color: colorValue,
|
color: colorValue,
|
||||||
allowClear,
|
allowClear,
|
||||||
colorCleared,
|
|
||||||
disabled: mergedDisabled,
|
disabled: mergedDisabled,
|
||||||
disabledAlpha,
|
disabledAlpha,
|
||||||
presets,
|
presets,
|
||||||
@ -262,13 +215,12 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
open={popupOpen}
|
open={popupOpen}
|
||||||
className={mergedCls}
|
className={mergedCls}
|
||||||
style={mergedStyle}
|
style={mergedStyle}
|
||||||
color={value ? generateColor(value) : colorValue}
|
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
disabled={mergedDisabled}
|
disabled={mergedDisabled}
|
||||||
colorCleared={colorCleared}
|
|
||||||
showText={showText}
|
showText={showText}
|
||||||
format={formatValue}
|
format={formatValue}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
color={colorValue}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Popover>,
|
</Popover>,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { createEvent, fireEvent, render } from '@testing-library/react';
|
import { createEvent, fireEvent, render } from '@testing-library/react';
|
||||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||||
|
|
||||||
@ -11,8 +11,9 @@ import ConfigProvider from '../../config-provider';
|
|||||||
import Form from '../../form';
|
import Form from '../../form';
|
||||||
import theme from '../../theme';
|
import theme from '../../theme';
|
||||||
import type { Color } from '../color';
|
import type { Color } from '../color';
|
||||||
import type { ColorPickerProps } from '../ColorPicker';
|
|
||||||
import ColorPicker from '../ColorPicker';
|
import ColorPicker from '../ColorPicker';
|
||||||
|
import type { ColorPickerProps, ColorValueType } from '../interface';
|
||||||
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
function doMouseMove(
|
function doMouseMove(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
@ -607,4 +608,94 @@ describe('ColorPicker', () => {
|
|||||||
const { container } = render(<ColorPicker />);
|
const { container } = render(<ColorPicker />);
|
||||||
expect(container.querySelector('.ant-color-picker-clear')).toBeTruthy();
|
expect(container.querySelector('.ant-color-picker-clear')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
['', null].forEach((value) => {
|
||||||
|
it(`When controlled and without an initial value, then changing the controlled value to valid color should be reflected correctly on the DOM. [${String(
|
||||||
|
value,
|
||||||
|
)}]`, async () => {
|
||||||
|
const Demo = () => {
|
||||||
|
const [color, setColor] = useState<ColorValueType>(value);
|
||||||
|
useEffect(() => {
|
||||||
|
setColor(generateColor('red'));
|
||||||
|
}, []);
|
||||||
|
return <ColorPicker value={color} />;
|
||||||
|
};
|
||||||
|
const { container } = render(<Demo />);
|
||||||
|
await waitFakeTimer();
|
||||||
|
expect(container.querySelector('.ant-color-picker-color-block-inner')).toHaveStyle({
|
||||||
|
background: 'rgb(255, 0, 0)',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`When controlled and has an initial value, then changing the controlled value to cleared color should be reflected correctly on the DOM. [${String(
|
||||||
|
value,
|
||||||
|
)}]`, async () => {
|
||||||
|
const Demo = () => {
|
||||||
|
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||||
|
useEffect(() => {
|
||||||
|
setColor(value);
|
||||||
|
}, []);
|
||||||
|
return <ColorPicker value={color} />;
|
||||||
|
};
|
||||||
|
const { container } = render(<Demo />);
|
||||||
|
await waitFakeTimer();
|
||||||
|
expect(container.querySelector('.ant-color-picker-clear')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Controlled string value should work with allowClear correctly', async () => {
|
||||||
|
const Demo = (props: any) => {
|
||||||
|
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof props.value !== 'undefined') {
|
||||||
|
setColor(props.value);
|
||||||
|
}
|
||||||
|
}, [props.value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColorPicker value={color} onChange={(e) => setColor(e.toHexString())} open allowClear />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const { container, rerender } = render(<Demo />);
|
||||||
|
await waitFakeTimer();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeFalsy();
|
||||||
|
fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeTruthy();
|
||||||
|
rerender(<Demo value="#1677ff" />);
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Controlled value should work with allowClear correctly', async () => {
|
||||||
|
const Demo = (props: any) => {
|
||||||
|
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof props.value !== 'undefined') {
|
||||||
|
setColor(props.value);
|
||||||
|
}
|
||||||
|
}, [props.value]);
|
||||||
|
|
||||||
|
return <ColorPicker value={color} onChange={(e) => setColor(e)} open allowClear />;
|
||||||
|
};
|
||||||
|
const { container, rerender } = render(<Demo />);
|
||||||
|
await waitFakeTimer();
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeFalsy();
|
||||||
|
fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeTruthy();
|
||||||
|
rerender(<Demo value="#1677ff" />);
|
||||||
|
expect(
|
||||||
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,16 +11,21 @@ export interface Color
|
|||||||
extends Pick<
|
extends Pick<
|
||||||
RcColor,
|
RcColor,
|
||||||
'toHsb' | 'toHsbString' | 'toHex' | 'toHexString' | 'toRgb' | 'toRgbString'
|
'toHsb' | 'toHsbString' | 'toHex' | 'toHexString' | 'toRgb' | 'toRgbString'
|
||||||
> {}
|
> {
|
||||||
|
cleared: boolean | 'controlled';
|
||||||
|
}
|
||||||
|
|
||||||
export class ColorFactory {
|
export class ColorFactory implements Color {
|
||||||
/** Original Color object */
|
/** Original Color object */
|
||||||
private metaColor: RcColor;
|
private metaColor: RcColor;
|
||||||
|
|
||||||
|
public cleared: boolean = false;
|
||||||
|
|
||||||
constructor(color: ColorGenInput<Color>) {
|
constructor(color: ColorGenInput<Color>) {
|
||||||
this.metaColor = new RcColor(color as ColorGenInput);
|
this.metaColor = new RcColor(color as ColorGenInput);
|
||||||
if (!color) {
|
if (!color) {
|
||||||
this.metaColor.setAlpha(0);
|
this.metaColor.setAlpha(0);
|
||||||
|
this.cleared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,18 @@ import type { Color } from '../color';
|
|||||||
import type { ColorPickerBaseProps } from '../interface';
|
import type { ColorPickerBaseProps } from '../interface';
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
interface ColorClearProps extends Pick<ColorPickerBaseProps, 'prefixCls' | 'colorCleared'> {
|
interface ColorClearProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||||
value?: Color;
|
value?: Color;
|
||||||
onChange?: (value: Color) => void;
|
onChange?: (value: Color) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorClear: FC<ColorClearProps> = ({ prefixCls, value, colorCleared, onChange }) => {
|
const ColorClear: FC<ColorClearProps> = ({ prefixCls, value, onChange }) => {
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (value && !colorCleared) {
|
if (value && !value.cleared) {
|
||||||
const hsba = value.toHsb();
|
const hsba = value.toHsb();
|
||||||
hsba.a = 0;
|
hsba.a = 0;
|
||||||
const genColor = generateColor(hsba);
|
const genColor = generateColor(hsba);
|
||||||
|
genColor.cleared = true;
|
||||||
onChange?.(genColor);
|
onChange?.(genColor);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2,14 +2,13 @@ import { ColorBlock } from '@rc-component/color-picker';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { CSSProperties, MouseEventHandler } from 'react';
|
import type { CSSProperties, MouseEventHandler } from 'react';
|
||||||
import React, { forwardRef, useMemo } from 'react';
|
import React, { forwardRef, useMemo } from 'react';
|
||||||
import type { ColorPickerProps } from '../ColorPicker';
|
import type { ColorPickerProps, ColorPickerBaseProps } from '../interface';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import { getAlphaColor } from '../util';
|
import { getAlphaColor } from '../util';
|
||||||
import ColorClear from './ColorClear';
|
import ColorClear from './ColorClear';
|
||||||
|
|
||||||
interface colorTriggerProps
|
export interface ColorTriggerProps
|
||||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'colorCleared' | 'disabled' | 'format'> {
|
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'disabled' | 'format'> {
|
||||||
color: Exclude<ColorPickerBaseProps['color'], undefined>;
|
color: NonNullable<ColorPickerBaseProps['color']>;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
showText?: ColorPickerProps['showText'];
|
showText?: ColorPickerProps['showText'];
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -19,19 +18,18 @@ interface colorTriggerProps
|
|||||||
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorTrigger = forwardRef<HTMLDivElement, colorTriggerProps>((props, ref) => {
|
const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref) => {
|
||||||
const { color, prefixCls, open, colorCleared, disabled, format, className, showText, ...rest } =
|
const { color, prefixCls, open, disabled, format, className, showText, ...rest } = props;
|
||||||
props;
|
|
||||||
const colorTriggerPrefixCls = `${prefixCls}-trigger`;
|
const colorTriggerPrefixCls = `${prefixCls}-trigger`;
|
||||||
|
|
||||||
const containerNode = useMemo<React.ReactNode>(
|
const containerNode = useMemo<React.ReactNode>(
|
||||||
() =>
|
() =>
|
||||||
colorCleared ? (
|
color.cleared ? (
|
||||||
<ColorClear prefixCls={prefixCls} />
|
<ColorClear prefixCls={prefixCls} />
|
||||||
) : (
|
) : (
|
||||||
<ColorBlock prefixCls={prefixCls} color={color.toRgbString()} />
|
<ColorBlock prefixCls={prefixCls} color={color.toRgbString()} />
|
||||||
),
|
),
|
||||||
[color, colorCleared, prefixCls],
|
[color, prefixCls],
|
||||||
);
|
);
|
||||||
|
|
||||||
const genColorString = () => {
|
const genColorString = () => {
|
||||||
|
@ -7,11 +7,12 @@ import { PanelPickerContext } from '../context';
|
|||||||
import type { ColorPickerBaseProps } from '../interface';
|
import type { ColorPickerBaseProps } from '../interface';
|
||||||
import ColorClear from './ColorClear';
|
import ColorClear from './ColorClear';
|
||||||
import ColorInput from './ColorInput';
|
import ColorInput from './ColorInput';
|
||||||
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
export interface PanelPickerProps
|
export interface PanelPickerProps
|
||||||
extends Pick<
|
extends Pick<
|
||||||
ColorPickerBaseProps,
|
ColorPickerBaseProps,
|
||||||
'prefixCls' | 'colorCleared' | 'allowClear' | 'disabledAlpha' | 'onChangeComplete'
|
'prefixCls' | 'allowClear' | 'disabledAlpha' | 'onChangeComplete'
|
||||||
> {
|
> {
|
||||||
value?: Color;
|
value?: Color;
|
||||||
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
|
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
|
||||||
@ -21,7 +22,6 @@ export interface PanelPickerProps
|
|||||||
const PanelPicker: FC = () => {
|
const PanelPicker: FC = () => {
|
||||||
const {
|
const {
|
||||||
prefixCls,
|
prefixCls,
|
||||||
colorCleared,
|
|
||||||
allowClear,
|
allowClear,
|
||||||
value,
|
value,
|
||||||
disabledAlpha,
|
disabledAlpha,
|
||||||
@ -36,7 +36,6 @@ const PanelPicker: FC = () => {
|
|||||||
<ColorClear
|
<ColorClear
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
value={value}
|
value={value}
|
||||||
colorCleared={colorCleared}
|
|
||||||
onChange={(clearColor) => {
|
onChange={(clearColor) => {
|
||||||
onChange?.(clearColor);
|
onChange?.(clearColor);
|
||||||
onClear?.();
|
onClear?.();
|
||||||
@ -48,8 +47,12 @@ const PanelPicker: FC = () => {
|
|||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
value={value?.toHsb()}
|
value={value?.toHsb()}
|
||||||
disabledAlpha={disabledAlpha}
|
disabledAlpha={disabledAlpha}
|
||||||
onChange={(colorValue, type) => onChange?.(colorValue, type, true)}
|
onChange={(colorValue, type) => {
|
||||||
onChangeComplete={onChangeComplete}
|
onChange?.(generateColor(colorValue), type, true);
|
||||||
|
}}
|
||||||
|
onChangeComplete={(colorValue) => {
|
||||||
|
onChangeComplete?.(generateColor(colorValue));
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ColorInput
|
<ColorInput
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ColorPicker } from 'antd';
|
import { ColorPicker } from 'antd';
|
||||||
|
|
||||||
export default () => <ColorPicker defaultValue="#1677ff" allowClear />;
|
export default () => {
|
||||||
|
const [color, setColor] = React.useState<string>('#1677ff');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ColorPicker
|
||||||
|
value={color}
|
||||||
|
allowClear
|
||||||
|
onChange={(c) => {
|
||||||
|
setColor(c.toHexString());
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import type { Color } from '../color';
|
import type { Color } from '../color';
|
||||||
import type { ColorValueType } from '../interface';
|
import type { ColorValueType } from '../interface';
|
||||||
@ -11,9 +11,10 @@ function hasValue(value?: ColorValueType) {
|
|||||||
const useColorState = (
|
const useColorState = (
|
||||||
defaultStateValue: ColorValueType,
|
defaultStateValue: ColorValueType,
|
||||||
option: { defaultValue?: ColorValueType; value?: ColorValueType },
|
option: { defaultValue?: ColorValueType; value?: ColorValueType },
|
||||||
): readonly [Color, React.Dispatch<React.SetStateAction<Color>>] => {
|
) => {
|
||||||
const { defaultValue, value } = option;
|
const { defaultValue, value } = option;
|
||||||
const [colorValue, setColorValue] = useState<Color>(() => {
|
const prevColor = useRef<Color>(generateColor(''));
|
||||||
|
const [colorValue, _setColorValue] = useState<Color>(() => {
|
||||||
let mergedState: ColorValueType | undefined;
|
let mergedState: ColorValueType | undefined;
|
||||||
if (hasValue(value)) {
|
if (hasValue(value)) {
|
||||||
mergedState = value;
|
mergedState = value;
|
||||||
@ -22,16 +23,27 @@ const useColorState = (
|
|||||||
} else {
|
} else {
|
||||||
mergedState = defaultStateValue;
|
mergedState = defaultStateValue;
|
||||||
}
|
}
|
||||||
return generateColor(mergedState || '');
|
const color = generateColor(mergedState || '');
|
||||||
|
prevColor.current = color;
|
||||||
|
return color;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const setColorValue: typeof _setColorValue = (color: Color) => {
|
||||||
|
_setColorValue(color);
|
||||||
|
prevColor.current = color;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value) {
|
if (hasValue(value)) {
|
||||||
setColorValue(generateColor(value));
|
const newColor = generateColor(value || '');
|
||||||
|
if (prevColor.current.cleared === true) {
|
||||||
|
newColor.cleared = 'controlled';
|
||||||
|
}
|
||||||
|
setColorValue(newColor);
|
||||||
}
|
}
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
return [colorValue, setColorValue] as const;
|
return [colorValue, setColorValue, prevColor] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useColorState;
|
export default useColorState;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import ColorPicker from './ColorPicker';
|
import ColorPicker from './ColorPicker';
|
||||||
|
|
||||||
export type { ColorPickerProps } from './ColorPicker';
|
export type { ColorPickerProps } from './interface';
|
||||||
export type { Color } from './color';
|
export type { Color } from './color';
|
||||||
|
|
||||||
export default ColorPicker;
|
export default ColorPicker;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { CSSProperties, FC, ReactNode } from 'react';
|
||||||
import type { ColorPickerProps } from './ColorPicker';
|
|
||||||
import type { Color } from './color';
|
import type { Color } from './color';
|
||||||
|
import type { ColorPickerProps as RcColorPickerProps } from '@rc-component/color-picker';
|
||||||
|
import type { SizeType } from '../config-provider/SizeContext';
|
||||||
|
import type { PopoverProps } from '../popover';
|
||||||
|
|
||||||
export enum ColorFormat {
|
export enum ColorFormat {
|
||||||
hex = 'hex',
|
hex = 'hex',
|
||||||
@ -32,7 +34,6 @@ export interface ColorPickerBaseProps {
|
|||||||
prefixCls: string;
|
prefixCls: string;
|
||||||
format?: keyof typeof ColorFormat;
|
format?: keyof typeof ColorFormat;
|
||||||
allowClear?: boolean;
|
allowClear?: boolean;
|
||||||
colorCleared?: boolean;
|
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
disabledAlpha?: boolean;
|
disabledAlpha?: boolean;
|
||||||
presets?: PresetsItem[];
|
presets?: PresetsItem[];
|
||||||
@ -42,3 +43,36 @@ export interface ColorPickerBaseProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ColorValueType = Color | string | null;
|
export type ColorValueType = Color | string | null;
|
||||||
|
|
||||||
|
export type ColorPickerProps = Omit<
|
||||||
|
RcColorPickerProps,
|
||||||
|
'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'disabledAlpha' | 'onChangeComplete'
|
||||||
|
> & {
|
||||||
|
value?: ColorValueType;
|
||||||
|
defaultValue?: ColorValueType;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
open?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
placement?: TriggerPlacement;
|
||||||
|
trigger?: TriggerType;
|
||||||
|
format?: keyof typeof ColorFormat;
|
||||||
|
defaultFormat?: keyof typeof ColorFormat;
|
||||||
|
allowClear?: boolean;
|
||||||
|
presets?: PresetsItem[];
|
||||||
|
arrow?: boolean | { pointAtCenter: boolean };
|
||||||
|
panelRender?: (
|
||||||
|
panel: React.ReactNode,
|
||||||
|
extra: { components: { Picker: FC; Presets: FC } },
|
||||||
|
) => React.ReactNode;
|
||||||
|
showText?: boolean | ((color: Color) => React.ReactNode);
|
||||||
|
size?: SizeType;
|
||||||
|
styles?: { popup?: CSSProperties; popupOverlayInner?: CSSProperties };
|
||||||
|
rootClassName?: string;
|
||||||
|
disabledAlpha?: boolean;
|
||||||
|
[key: `data-${string}`]: string;
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
|
onFormatChange?: (format: ColorFormat) => void;
|
||||||
|
onChange?: (value: Color, hex: string) => void;
|
||||||
|
onClear?: () => void;
|
||||||
|
onChangeComplete?: (value: Color) => void;
|
||||||
|
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
||||||
|
Loading…
Reference in New Issue
Block a user