mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
commit
d9b550a25d
@ -7,7 +7,7 @@ import classNames from 'classnames';
|
||||
|
||||
import { PRESET_COLORS } from './colorUtil';
|
||||
|
||||
type Color = GetProp<ColorPickerProps, 'value'>;
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||
|
||||
const useStyle = createStyles(({ token, css }) => ({
|
||||
color: css`
|
||||
|
@ -40,7 +40,7 @@ import RadiusPicker from './RadiusPicker';
|
||||
import type { THEME } from './ThemePicker';
|
||||
import ThemePicker from './ThemePicker';
|
||||
|
||||
type Color = GetProp<ColorPickerProps, 'value'>;
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||
|
||||
const { Header, Content, Sider } = Layout;
|
||||
|
||||
|
@ -18,13 +18,8 @@ export type CustomComponent<P = AnyObject> = React.ComponentType<P> | string;
|
||||
* ```
|
||||
* @since 5.13.0
|
||||
*/
|
||||
export type GetProps<T extends React.ComponentType<any> | object> = T extends React.ComponentType<
|
||||
infer P
|
||||
>
|
||||
? P
|
||||
: T extends object
|
||||
? T
|
||||
: never;
|
||||
export type GetProps<T extends React.ComponentType<any> | object> =
|
||||
T extends React.ComponentType<infer P> ? P : T extends object ? T : never;
|
||||
|
||||
/**
|
||||
* Get component props by component name
|
||||
@ -71,3 +66,10 @@ export type GetRef<T extends ReactRefComponent<any> | React.Component<any>> =
|
||||
: T extends React.ComponentType<infer P>
|
||||
? ExtractRefAttributesRef<P>
|
||||
: never;
|
||||
|
||||
export type GetContextProps<T> = T extends React.Context<infer P> ? P : never;
|
||||
|
||||
export type GetContextProp<
|
||||
T extends React.Context<any>,
|
||||
PropName extends keyof GetContextProps<T>,
|
||||
> = NonNullable<GetContextProps<T>[PropName]>;
|
||||
|
@ -1,12 +1,7 @@
|
||||
import { Keyframes, unit } from '@ant-design/cssinjs';
|
||||
|
||||
import { resetComponent } from '../../style';
|
||||
import type {
|
||||
FullToken,
|
||||
GenerateStyle,
|
||||
GenStyleFn,
|
||||
GetDefaultToken,
|
||||
} from '../../theme/internal';
|
||||
import type { FullToken, GenerateStyle, GenStyleFn, GetDefaultToken } from '../../theme/internal';
|
||||
import { genPresetColor, genStyleHooks, mergeToken } from '../../theme/internal';
|
||||
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
@ -21,12 +16,12 @@ export interface ComponentToken {
|
||||
* @desc 徽标高度
|
||||
* @descEN Height of badge
|
||||
*/
|
||||
indicatorHeight: number;
|
||||
indicatorHeight: number | string;
|
||||
/**
|
||||
* @desc 小号徽标高度
|
||||
* @descEN Height of small badge
|
||||
*/
|
||||
indicatorHeightSM: number;
|
||||
indicatorHeightSM: number | string;
|
||||
/**
|
||||
* @desc 点状徽标尺寸
|
||||
* @descEN Size of dot badge
|
||||
|
@ -1,10 +1,6 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
|
||||
import type {
|
||||
FullToken,
|
||||
GetDefaultToken,
|
||||
GenStyleFn,
|
||||
} from '../../theme/internal';
|
||||
import type { FullToken, GetDefaultToken, GenStyleFn } from '../../theme/internal';
|
||||
import { getLineHeight, mergeToken } from '../../theme/internal';
|
||||
|
||||
/** Component only token. Which will handle additional calculation of alias token */
|
||||
|
@ -16,17 +16,17 @@ export interface ComponentToken {
|
||||
* @desc 年选择器宽度
|
||||
* @descEN Width of year select
|
||||
*/
|
||||
yearControlWidth: number;
|
||||
yearControlWidth: number | string;
|
||||
/**
|
||||
* @desc 月选择器宽度
|
||||
* @descEN Width of month select
|
||||
*/
|
||||
monthControlWidth: number;
|
||||
monthControlWidth: number | string;
|
||||
/**
|
||||
* @desc 迷你日历内容高度
|
||||
* @descEN Height of mini calendar content
|
||||
*/
|
||||
miniContentHeight: number;
|
||||
miniContentHeight: number | string;
|
||||
/**
|
||||
* @desc 完整日历背景色
|
||||
* @descEN Background color of full calendar
|
||||
@ -46,9 +46,9 @@ export interface ComponentToken {
|
||||
|
||||
interface CalendarToken extends FullToken<'Calendar'>, PickerPanelToken, PanelComponentToken {
|
||||
calendarCls: string;
|
||||
dateValueHeight: number;
|
||||
weekHeight: number;
|
||||
dateContentHeight: number;
|
||||
dateValueHeight: number | string;
|
||||
weekHeight: number | string;
|
||||
dateContentHeight: number | string;
|
||||
}
|
||||
|
||||
export const genCalendarStyles = (token: CalendarToken): CSSObject => {
|
||||
|
@ -15,22 +15,22 @@ export interface ComponentToken {
|
||||
* @desc 卡片头部文字大小
|
||||
* @descEN Font size of card header
|
||||
*/
|
||||
headerFontSize: number;
|
||||
headerFontSize: number | string;
|
||||
/**
|
||||
* @desc 小号卡片头部文字大小
|
||||
* @descEN Font size of small card header
|
||||
*/
|
||||
headerFontSizeSM: number;
|
||||
headerFontSizeSM: number | string;
|
||||
/**
|
||||
* @desc 卡片头部高度
|
||||
* @descEN Height of card header
|
||||
*/
|
||||
headerHeight: number;
|
||||
headerHeight: number | string;
|
||||
/**
|
||||
* @desc 小号卡片头部高度
|
||||
* @descEN Height of small card header
|
||||
*/
|
||||
headerHeightSM: number;
|
||||
headerHeightSM: number | string;
|
||||
/**
|
||||
* @desc 操作区背景色
|
||||
* @descEN Background color of card actions
|
||||
|
@ -31,6 +31,8 @@ export interface CarouselRef {
|
||||
innerSlider: any;
|
||||
}
|
||||
|
||||
const dotsClass = 'slick-dots';
|
||||
|
||||
interface ArrowType extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
currentSlide?: number;
|
||||
slideCount?: number;
|
||||
@ -40,8 +42,6 @@ const ArrowButton: React.FC<ArrowType> = ({ currentSlide, slideCount, ...rest })
|
||||
<button type="button" {...rest} />
|
||||
);
|
||||
|
||||
const dotsClass = 'slick-dots';
|
||||
|
||||
const Carousel = React.forwardRef<CarouselRef, CarouselProps>((props, ref) => {
|
||||
const {
|
||||
dots = true,
|
||||
|
@ -9,12 +9,12 @@ export interface ComponentToken {
|
||||
* @desc 指示点宽度
|
||||
* @descEN Width of indicator
|
||||
*/
|
||||
dotWidth: number;
|
||||
dotWidth: number | string;
|
||||
/**
|
||||
* @desc 指示点高度
|
||||
* @descEN Height of indicator
|
||||
*/
|
||||
dotHeight: number;
|
||||
dotHeight: number | string;
|
||||
/**
|
||||
* @desc 指示点之间的间距
|
||||
* @descEN gap between indicator
|
||||
@ -31,7 +31,7 @@ export interface ComponentToken {
|
||||
* @desc 激活态指示点宽度
|
||||
* @descEN Width of active indicator
|
||||
*/
|
||||
dotActiveWidth: number;
|
||||
dotActiveWidth: number | string;
|
||||
/**
|
||||
* @desc 切换箭头大小
|
||||
* @descEN Size of arrows
|
||||
|
@ -10,17 +10,17 @@ export interface ComponentToken {
|
||||
* @desc 选择器宽度
|
||||
* @descEN Width of Cascader
|
||||
*/
|
||||
controlWidth: number;
|
||||
controlWidth: number | string;
|
||||
/**
|
||||
* @desc 选项宽度
|
||||
* @descEN Width of item
|
||||
*/
|
||||
controlItemWidth: number;
|
||||
controlItemWidth: number | string;
|
||||
/**
|
||||
* @desc 下拉菜单高度
|
||||
* @descEN Height of dropdown
|
||||
*/
|
||||
dropdownHeight: number;
|
||||
dropdownHeight: number | string;
|
||||
/**
|
||||
* @desc 选项选中时背景色
|
||||
* @descEN Background color of selected item
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useMemo, useRef } from 'react';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
|
||||
@ -14,14 +14,14 @@ import useSize from '../config-provider/hooks/useSize';
|
||||
import { FormItemInputContext } from '../form/context';
|
||||
import type { PopoverProps } from '../popover';
|
||||
import Popover from '../popover';
|
||||
import type { Color } from './color';
|
||||
import { AggregationColor } from './color';
|
||||
import type { ColorPickerPanelProps } from './ColorPickerPanel';
|
||||
import ColorPickerPanel from './ColorPickerPanel';
|
||||
import ColorTrigger from './components/ColorTrigger';
|
||||
import useColorState from './hooks/useColorState';
|
||||
import type { ColorPickerBaseProps, ColorPickerProps, TriggerPlacement } from './interface';
|
||||
import useModeColor from './hooks/useModeColor';
|
||||
import type { ColorPickerProps, ModeType, TriggerPlacement } from './interface';
|
||||
import useStyle from './style';
|
||||
import { genAlphaColor, generateColor, getAlphaColor } from './util';
|
||||
import { genAlphaColor, generateColor, getColorAlpha } from './util';
|
||||
|
||||
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||
@ -29,6 +29,7 @@ type CompoundedComponent = React.FC<ColorPickerProps> & {
|
||||
|
||||
const ColorPicker: CompoundedComponent = (props) => {
|
||||
const {
|
||||
mode,
|
||||
value,
|
||||
defaultValue,
|
||||
format,
|
||||
@ -65,10 +66,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
const contextDisabled = useContext(DisabledContext);
|
||||
const mergedDisabled = disabled ?? contextDisabled;
|
||||
|
||||
const [colorValue, setColorValue, prevValue] = useColorState('', {
|
||||
value,
|
||||
defaultValue,
|
||||
});
|
||||
const [popupOpen, setPopupOpen] = useMergedState(false, {
|
||||
value: open,
|
||||
postState: (openData) => !mergedDisabled && openData,
|
||||
@ -82,9 +79,91 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
|
||||
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
|
||||
|
||||
const isAlphaColor = useMemo(() => getAlphaColor(colorValue) < 100, [colorValue]);
|
||||
// ================== Value & Mode =================
|
||||
const [mergedColor, setColor, modeState, setModeState, modeOptions] = useModeColor(
|
||||
defaultValue,
|
||||
value,
|
||||
mode,
|
||||
);
|
||||
|
||||
// ===================== Form Status =====================
|
||||
const isAlphaColor = useMemo(() => getColorAlpha(mergedColor) < 100, [mergedColor]);
|
||||
|
||||
// ==================== Change =====================
|
||||
// To enhance user experience, we cache the gradient color when switch from gradient to single
|
||||
// If user not modify single color, we will use the cached gradient color.
|
||||
const [cachedGradientColor, setCachedGradientColor] = React.useState<AggregationColor | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const onInternalChangeComplete: ColorPickerProps['onChangeComplete'] = (color) => {
|
||||
if (onChangeComplete) {
|
||||
let changeColor = generateColor(color);
|
||||
|
||||
// ignore alpha color
|
||||
if (disabledAlpha && isAlphaColor) {
|
||||
changeColor = genAlphaColor(color);
|
||||
}
|
||||
onChangeComplete(changeColor);
|
||||
}
|
||||
};
|
||||
|
||||
const onInternalChange: ColorPickerPanelProps['onChange'] = (data, pickColor) => {
|
||||
let color: AggregationColor = generateColor(data as AggregationColor);
|
||||
|
||||
// ignore alpha color
|
||||
if (disabledAlpha && isAlphaColor) {
|
||||
color = genAlphaColor(color);
|
||||
}
|
||||
|
||||
setColor(color);
|
||||
setCachedGradientColor(null);
|
||||
|
||||
// Trigger change event
|
||||
if (onChange) {
|
||||
onChange(color, color.toCssString());
|
||||
}
|
||||
|
||||
// Only for drag-and-drop color picking
|
||||
if (!pickColor) {
|
||||
onInternalChangeComplete(color);
|
||||
}
|
||||
};
|
||||
|
||||
// =================== Gradient ====================
|
||||
const [activeIndex, setActiveIndex] = React.useState(0);
|
||||
const [gradientDragging, setGradientDragging] = React.useState(false);
|
||||
|
||||
// Mode change should also trigger color change
|
||||
const onInternalModeChange = (newMode: ModeType) => {
|
||||
setModeState(newMode);
|
||||
|
||||
if (newMode === 'single' && mergedColor.isGradient()) {
|
||||
setActiveIndex(0);
|
||||
onInternalChange(new AggregationColor(mergedColor.getColors()[0].color));
|
||||
|
||||
// Should after `onInternalChange` since it will clear the cached color
|
||||
setCachedGradientColor(mergedColor);
|
||||
} else if (newMode === 'gradient' && !mergedColor.isGradient()) {
|
||||
const baseColor = isAlphaColor ? genAlphaColor(mergedColor) : mergedColor;
|
||||
|
||||
onInternalChange(
|
||||
new AggregationColor(
|
||||
cachedGradientColor || [
|
||||
{
|
||||
percent: 0,
|
||||
color: baseColor,
|
||||
},
|
||||
{
|
||||
percent: 100,
|
||||
color: baseColor,
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// ================== Form Status ==================
|
||||
const { status: contextStatus } = React.useContext(FormItemInputContext);
|
||||
|
||||
// ===================== Style =====================
|
||||
@ -106,8 +185,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
);
|
||||
const mergedPopupCls = classNames(prefixCls, mergedRootCls);
|
||||
|
||||
const popupAllowCloseRef = useRef(true);
|
||||
|
||||
// ===================== Warning ======================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const warning = devUseWarning('ColorPicker');
|
||||
@ -119,48 +196,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
const handleChange: ColorPickerPanelProps['onChange'] = (data, type, pickColor) => {
|
||||
let color: Color = generateColor(data as Color);
|
||||
|
||||
// If color is cleared, reset alpha to 100
|
||||
const isNull = value === null || (!value && defaultValue === null);
|
||||
if (prevValue.current?.cleared || isNull) {
|
||||
// ignore alpha slider
|
||||
if (getAlphaColor(colorValue) === 0 && type !== 'alpha') {
|
||||
color = genAlphaColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore alpha color
|
||||
if (disabledAlpha && isAlphaColor) {
|
||||
color = genAlphaColor(color);
|
||||
}
|
||||
|
||||
// Only for drag-and-drop color picking
|
||||
if (pickColor) {
|
||||
popupAllowCloseRef.current = false;
|
||||
} else {
|
||||
onChangeComplete?.(color);
|
||||
}
|
||||
|
||||
setColorValue(color);
|
||||
onChange?.(color, color.toHexString());
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
onClear?.();
|
||||
};
|
||||
|
||||
const handleChangeComplete: ColorPickerProps['onChangeComplete'] = (color) => {
|
||||
popupAllowCloseRef.current = true;
|
||||
let changeColor = generateColor(color);
|
||||
// ignore alpha color
|
||||
if (disabledAlpha && isAlphaColor) {
|
||||
changeColor = genAlphaColor(color);
|
||||
}
|
||||
onChangeComplete?.(changeColor);
|
||||
};
|
||||
|
||||
const popoverProps: PopoverProps = {
|
||||
open: popupOpen,
|
||||
trigger,
|
||||
@ -172,19 +207,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
destroyTooltipOnHide,
|
||||
};
|
||||
|
||||
const colorBaseProps: ColorPickerBaseProps = {
|
||||
prefixCls,
|
||||
color: colorValue,
|
||||
allowClear,
|
||||
disabled: mergedDisabled,
|
||||
disabledAlpha,
|
||||
presets,
|
||||
panelRender,
|
||||
format: formatValue,
|
||||
onFormatChange: setFormatValue,
|
||||
onChangeComplete: handleChangeComplete,
|
||||
};
|
||||
|
||||
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };
|
||||
|
||||
// ============================ zIndex ============================
|
||||
@ -194,17 +216,32 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
style={styles?.popup}
|
||||
overlayInnerStyle={styles?.popupOverlayInner}
|
||||
onOpenChange={(visible) => {
|
||||
if (popupAllowCloseRef.current && !mergedDisabled) {
|
||||
if (!visible || !mergedDisabled) {
|
||||
setPopupOpen(visible);
|
||||
}
|
||||
}}
|
||||
content={
|
||||
<ContextIsolator form>
|
||||
<ColorPickerPanel
|
||||
{...colorBaseProps}
|
||||
onChange={handleChange}
|
||||
onChangeComplete={handleChangeComplete}
|
||||
onClear={handleClear}
|
||||
mode={modeState}
|
||||
onModeChange={onInternalModeChange}
|
||||
modeOptions={modeOptions}
|
||||
prefixCls={prefixCls}
|
||||
value={mergedColor}
|
||||
allowClear={allowClear}
|
||||
disabled={mergedDisabled}
|
||||
disabledAlpha={disabledAlpha}
|
||||
presets={presets}
|
||||
panelRender={panelRender}
|
||||
format={formatValue}
|
||||
onFormatChange={setFormatValue}
|
||||
onChange={onInternalChange}
|
||||
onChangeComplete={onInternalChangeComplete}
|
||||
onClear={onClear}
|
||||
activeIndex={activeIndex}
|
||||
onActive={setActiveIndex}
|
||||
gradientDragging={gradientDragging}
|
||||
onGradientDragging={setGradientDragging}
|
||||
/>
|
||||
</ContextIsolator>
|
||||
}
|
||||
@ -213,6 +250,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
>
|
||||
{children || (
|
||||
<ColorTrigger
|
||||
activeIndex={popupOpen ? activeIndex : -1}
|
||||
open={popupOpen}
|
||||
className={mergedCls}
|
||||
style={mergedStyle}
|
||||
@ -221,7 +259,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
||||
showText={showText}
|
||||
format={formatValue}
|
||||
{...rest}
|
||||
color={colorValue}
|
||||
color={mergedColor}
|
||||
/>
|
||||
)}
|
||||
</Popover>,
|
||||
|
@ -1,40 +1,91 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { HsbaColorType } from '@rc-component/color-picker';
|
||||
|
||||
import Divider from '../divider';
|
||||
import type { Color } from './color';
|
||||
import PanelPicker from './components/PanelPicker';
|
||||
import PanelPresets from './components/PanelPresets';
|
||||
import { PanelPickerProvider, PanelPresetsProvider } from './context';
|
||||
import type { ColorPickerBaseProps } from './interface';
|
||||
import { PanelPickerContext, PanelPresetsContext } from './context';
|
||||
import type { PanelPickerContextProps, PanelPresetsContextProps } from './context';
|
||||
import type { ColorPickerProps } from './interface';
|
||||
|
||||
export interface ColorPickerPanelProps extends ColorPickerBaseProps {
|
||||
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
|
||||
export interface ColorPickerPanelProps
|
||||
extends PanelPickerContextProps,
|
||||
Omit<PanelPresetsContextProps, 'onChange'> {
|
||||
onClear?: () => void;
|
||||
panelRender?: ColorPickerProps['panelRender'];
|
||||
}
|
||||
|
||||
const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
||||
const { prefixCls, presets, panelRender, color, onChange, onClear, ...injectProps } = props;
|
||||
const colorPickerPanelPrefixCls = `${prefixCls}-inner`;
|
||||
|
||||
// ==== Inject props ===
|
||||
const panelPickerProps = {
|
||||
const {
|
||||
prefixCls,
|
||||
value: color,
|
||||
presets,
|
||||
panelRender,
|
||||
value,
|
||||
onChange,
|
||||
onClear,
|
||||
...injectProps,
|
||||
};
|
||||
allowClear,
|
||||
disabledAlpha,
|
||||
mode,
|
||||
onModeChange,
|
||||
modeOptions,
|
||||
onChangeComplete,
|
||||
activeIndex,
|
||||
onActive,
|
||||
format,
|
||||
onFormatChange,
|
||||
gradientDragging,
|
||||
onGradientDragging,
|
||||
} = props;
|
||||
const colorPickerPanelPrefixCls = `${prefixCls}-inner`;
|
||||
|
||||
const panelPresetsProps = React.useMemo(
|
||||
// ===================== Context ======================
|
||||
const panelContext: PanelPickerContextProps = React.useMemo(
|
||||
() => ({
|
||||
prefixCls,
|
||||
value: color,
|
||||
value,
|
||||
onChange,
|
||||
onClear,
|
||||
allowClear,
|
||||
disabledAlpha,
|
||||
mode,
|
||||
onModeChange,
|
||||
modeOptions,
|
||||
onChangeComplete,
|
||||
activeIndex,
|
||||
onActive,
|
||||
format,
|
||||
onFormatChange,
|
||||
gradientDragging,
|
||||
onGradientDragging,
|
||||
}),
|
||||
[
|
||||
prefixCls,
|
||||
value,
|
||||
onChange,
|
||||
onClear,
|
||||
allowClear,
|
||||
disabledAlpha,
|
||||
mode,
|
||||
onModeChange,
|
||||
modeOptions,
|
||||
onChangeComplete,
|
||||
activeIndex,
|
||||
onActive,
|
||||
format,
|
||||
onFormatChange,
|
||||
gradientDragging,
|
||||
onGradientDragging,
|
||||
],
|
||||
);
|
||||
|
||||
const presetContext: PanelPresetsContextProps = React.useMemo(
|
||||
() => ({
|
||||
prefixCls,
|
||||
value,
|
||||
presets,
|
||||
onChange,
|
||||
}),
|
||||
[prefixCls, color, presets, onChange],
|
||||
[prefixCls, value, presets, onChange],
|
||||
);
|
||||
|
||||
// ====================== Render ======================
|
||||
@ -47,8 +98,8 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelPickerProvider value={panelPickerProps}>
|
||||
<PanelPresetsProvider value={panelPresetsProps}>
|
||||
<PanelPickerContext.Provider value={panelContext}>
|
||||
<PanelPresetsContext.Provider value={presetContext}>
|
||||
<div className={colorPickerPanelPrefixCls}>
|
||||
{typeof panelRender === 'function'
|
||||
? panelRender(innerPanel, {
|
||||
@ -59,8 +110,8 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
||||
})
|
||||
: innerPanel}
|
||||
</div>
|
||||
</PanelPresetsProvider>
|
||||
</PanelPickerProvider>
|
||||
</PanelPresetsContext.Provider>
|
||||
</PanelPickerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ exports[`renders components/color-picker/demo/allowClear.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,7 +24,7 @@ exports[`renders components/color-picker/demo/base.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -42,7 +42,7 @@ exports[`renders components/color-picker/demo/change-completed.tsx correctly 1`]
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -58,7 +58,7 @@ exports[`renders components/color-picker/demo/controlled.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -73,7 +73,7 @@ exports[`renders components/color-picker/demo/disabled.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -93,7 +93,7 @@ exports[`renders components/color-picker/demo/disabled-alpha.tsx correctly 1`] =
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -121,7 +121,7 @@ exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -154,7 +154,7 @@ exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(23, 120, 255)"
|
||||
style="background:rgb(23,120,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -187,7 +187,7 @@ exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -206,6 +206,93 @@ exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/color-picker/demo/line-gradient.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
||||
>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:linear-gradient(90deg, rgb(16,142,233) 0%, rgb(135,208,104) 100%)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
<span
|
||||
class="ant-color-picker-trigger-text-cell"
|
||||
>
|
||||
rgb(16,142,233)
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
0
|
||||
<!-- -->
|
||||
%
|
||||
</span>
|
||||
<span
|
||||
class="ant-color-picker-trigger-text-cell"
|
||||
>
|
||||
rgb(135,208,104)
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
100
|
||||
<!-- -->
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-space-item"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-trigger"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:linear-gradient(90deg, rgb(16,142,233) 0%, rgb(135,208,104) 100%)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-trigger-text"
|
||||
>
|
||||
<span
|
||||
class="ant-color-picker-trigger-text-cell"
|
||||
>
|
||||
rgb(16,142,233)
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
0
|
||||
<!-- -->
|
||||
%
|
||||
</span>
|
||||
<span
|
||||
class="ant-color-picker-trigger-text-cell"
|
||||
>
|
||||
rgb(135,208,104)
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
100
|
||||
<!-- -->
|
||||
%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/color-picker/demo/panel-render.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
||||
@ -234,7 +321,7 @@ exports[`renders components/color-picker/demo/panel-render.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -265,7 +352,7 @@ exports[`renders components/color-picker/demo/panel-render.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -284,7 +371,7 @@ exports[`renders components/color-picker/demo/presets.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -306,7 +393,7 @@ exports[`renders components/color-picker/demo/pure-panel.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -335,7 +422,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -351,7 +438,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -367,7 +454,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -391,7 +478,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -412,7 +499,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -433,7 +520,7 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -463,7 +550,7 @@ exports[`renders components/color-picker/demo/text-render.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -484,7 +571,7 @@ exports[`renders components/color-picker/demo/text-render.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -511,7 +598,7 @@ exports[`renders components/color-picker/demo/text-render.tsx correctly 1`] = `
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@ -564,7 +651,7 @@ exports[`renders components/color-picker/demo/trigger-event.tsx correctly 1`] =
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-color-block-inner"
|
||||
style="background:rgb(22, 119, 255)"
|
||||
style="background:rgb(22,119,255)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
@ -59,7 +59,7 @@ exports[`ColorPicker Should panelRender work 1`] = `
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: 50px; z-index: 1;"
|
||||
style="position: absolute; left: 0%; top: 100%; z-index: 1; transform: translate(-50%, -50%);"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler"
|
||||
@ -79,46 +79,46 @@ exports[`ColorPicker Should panelRender work 1`] = `
|
||||
class="ant-color-picker-slider-group"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
||||
>
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="359"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgb(255, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgba(0, 0, 0, 0);"
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgba(0, 0, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -343,7 +343,7 @@ exports[`ColorPicker Should panelRender work 2`] = `
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: 50px; z-index: 1;"
|
||||
style="position: absolute; left: 0%; top: 100%; z-index: 1; transform: translate(-50%, -50%);"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler"
|
||||
@ -363,46 +363,46 @@ exports[`ColorPicker Should panelRender work 2`] = `
|
||||
class="ant-color-picker-slider-group"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
||||
>
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="359"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgb(255, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgba(0, 0, 0, 0);"
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgba(0, 0, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -1,3 +0,0 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('color-picker');
|
9
components/color-picker/__tests__/demo.test.tsx
Normal file
9
components/color-picker/__tests__/demo.test.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import demoTest, { rootPropsTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('color-picker', {
|
||||
testRootProps: false,
|
||||
});
|
||||
|
||||
rootPropsTest('color-picker', (ColorPicker, props) => <ColorPicker {...props} value={undefined} />);
|
309
components/color-picker/__tests__/gradient.test.tsx
Normal file
309
components/color-picker/__tests__/gradient.test.tsx
Normal file
@ -0,0 +1,309 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
|
||||
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import { createEvent, fireEvent } from '../../../tests/utils';
|
||||
import { AggregationColor } from '../color';
|
||||
import ColorPicker from '../ColorPicker';
|
||||
|
||||
describe('ColorPicker.gradient', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
beforeAll(() => {
|
||||
spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 100,
|
||||
right: 100,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetWarned();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
errorSpy.mockReset();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
function doMouseDown(
|
||||
container: HTMLElement,
|
||||
start: number,
|
||||
query: string | HTMLElement = '.ant-slider-handle',
|
||||
skipEventCheck = false,
|
||||
) {
|
||||
const ele = typeof query === 'object' ? query : container.querySelector(query)!;
|
||||
const mouseDown = createEvent.mouseDown(ele);
|
||||
(mouseDown as any).pageX = start;
|
||||
(mouseDown as any).pageY = start;
|
||||
|
||||
const preventDefault = jest.fn();
|
||||
|
||||
Object.defineProperties(mouseDown, {
|
||||
clientX: { get: () => start },
|
||||
clientY: { get: () => start },
|
||||
preventDefault: { value: preventDefault },
|
||||
});
|
||||
|
||||
fireEvent.mouseEnter(ele);
|
||||
fireEvent(ele, mouseDown);
|
||||
|
||||
// Should not prevent default since focus will not change
|
||||
if (!skipEventCheck) {
|
||||
expect(preventDefault).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
fireEvent.focus(ele);
|
||||
}
|
||||
|
||||
function doMouseMove(end: number) {
|
||||
const mouseMove = createEvent.mouseMove(document);
|
||||
(mouseMove as any).pageX = end;
|
||||
(mouseMove as any).pageY = end;
|
||||
fireEvent(document, mouseMove);
|
||||
}
|
||||
|
||||
function doDrag(
|
||||
container: HTMLElement,
|
||||
start: number,
|
||||
end: number,
|
||||
query: string | HTMLElement = '.ant-slider-handle',
|
||||
skipEventCheck = false,
|
||||
) {
|
||||
doMouseDown(container, start, query, skipEventCheck);
|
||||
|
||||
// Drag
|
||||
doMouseMove(end);
|
||||
|
||||
// Up
|
||||
fireEvent.mouseUp(typeof query === 'object' ? query : container.querySelector(query)!);
|
||||
}
|
||||
|
||||
it('switch', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker mode={['single', 'gradient']} defaultValue="#123456" open onChange={onChange} />,
|
||||
);
|
||||
|
||||
// Switch to gradient
|
||||
fireEvent.click(container.querySelectorAll(`.ant-segmented-item-input`)[1]);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(18,52,86) 0%, rgb(18,52,86) 100%)',
|
||||
);
|
||||
});
|
||||
|
||||
it('change color position', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker
|
||||
mode={['single', 'gradient']}
|
||||
defaultValue={[
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: '#0000FF',
|
||||
percent: 100,
|
||||
},
|
||||
]}
|
||||
open
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Move
|
||||
doDrag(container, 0, 80);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(255,0,0) 80%, rgb(0,0,255) 100%)',
|
||||
);
|
||||
});
|
||||
|
||||
it('change color hex', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker
|
||||
mode={['single', 'gradient']}
|
||||
defaultValue={[
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: '#0000FF',
|
||||
percent: 100,
|
||||
},
|
||||
]}
|
||||
open
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Move
|
||||
doDrag(
|
||||
container,
|
||||
0,
|
||||
80,
|
||||
container.querySelector<HTMLElement>(
|
||||
'.ant-color-picker-slider-container .ant-slider-handle',
|
||||
)!,
|
||||
true,
|
||||
);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(200,0,255) 0%, rgb(0,0,255) 100%)',
|
||||
);
|
||||
});
|
||||
|
||||
it('new color', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker
|
||||
mode={['single', 'gradient']}
|
||||
defaultValue={[
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: '#0000FF',
|
||||
percent: 100,
|
||||
},
|
||||
]}
|
||||
open
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Move
|
||||
doDrag(container, 20, 30, '.ant-slider', true);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(255,0,0) 0%, rgb(204,0,51) 20%, rgb(0,0,255) 100%)',
|
||||
);
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(255,0,0) 0%, rgb(204,0,51) 30%, rgb(0,0,255) 100%)',
|
||||
);
|
||||
});
|
||||
|
||||
it('remove color', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker
|
||||
mode={['single', 'gradient']}
|
||||
defaultValue={[
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: '#00FF00',
|
||||
percent: 50,
|
||||
},
|
||||
{
|
||||
color: '#000FF0',
|
||||
percent: 80,
|
||||
},
|
||||
{
|
||||
color: '#0000FF',
|
||||
percent: 100,
|
||||
},
|
||||
]}
|
||||
open
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Delete remove first
|
||||
fireEvent.keyDown(container.querySelector<HTMLElement>('.ant-slider-handle-1')!, {
|
||||
key: 'Delete',
|
||||
});
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(0,255,0) 50%, rgb(0,15,240) 80%, rgb(0,0,255) 100%)',
|
||||
);
|
||||
|
||||
// Drag remove last
|
||||
onChange.mockReset();
|
||||
doDrag(
|
||||
container,
|
||||
0,
|
||||
9999999,
|
||||
container.querySelector<HTMLElement>('.ant-slider-handle-3')!,
|
||||
true,
|
||||
);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
'linear-gradient(90deg, rgb(0,255,0) 50%, rgb(0,15,240) 80%)',
|
||||
);
|
||||
});
|
||||
|
||||
it('invalid not crash', async () => {
|
||||
render(<ColorPicker mode={['single', 'gradient']} defaultValue={[]} open />);
|
||||
});
|
||||
|
||||
it('change to single', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container } = render(
|
||||
<ColorPicker
|
||||
mode={['single', 'gradient']}
|
||||
defaultValue={[
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: '#0000FF',
|
||||
percent: 100,
|
||||
},
|
||||
]}
|
||||
open
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Switch to gradient
|
||||
fireEvent.click(container.querySelector(`.ant-segmented-item-input`)!);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(expect.anything(), 'rgb(255,0,0)');
|
||||
});
|
||||
|
||||
it('not crash when pass gradient color', async () => {
|
||||
const color = new AggregationColor([
|
||||
{
|
||||
color: '#FF0000',
|
||||
percent: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
const newColor = new AggregationColor(color);
|
||||
expect(newColor.toCssString()).toEqual('linear-gradient(90deg, rgb(255,0,0) 0%)');
|
||||
});
|
||||
|
||||
it('mode fallback', () => {
|
||||
const { container } = render(<ColorPicker mode={['gradient']} defaultValue="#F00" open />);
|
||||
|
||||
expect(container.querySelector('.ant-color-picker-gradient-slider')).toBeTruthy();
|
||||
});
|
||||
});
|
@ -10,7 +10,7 @@ import Button from '../../button';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Form from '../../form';
|
||||
import theme from '../../theme';
|
||||
import type { Color } from '../color';
|
||||
import { AggregationColor } from '../color';
|
||||
import ColorPicker from '../ColorPicker';
|
||||
import type { ColorPickerProps, ColorValueType } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
@ -19,13 +19,21 @@ function doMouseMove(
|
||||
container: HTMLElement,
|
||||
start: number,
|
||||
end: number,
|
||||
element = 'ant-color-picker-handler',
|
||||
element: string | HTMLElement = 'ant-color-picker-handler',
|
||||
) {
|
||||
const mouseDown = createEvent.mouseDown(container.getElementsByClassName(element)[0], {
|
||||
const ele =
|
||||
element instanceof HTMLElement ? element : container.getElementsByClassName(element)[0];
|
||||
|
||||
const mouseDown = createEvent.mouseDown(ele, {
|
||||
pageX: start,
|
||||
pageY: start,
|
||||
});
|
||||
fireEvent(container.getElementsByClassName(element)[0], mouseDown);
|
||||
Object.defineProperties(mouseDown, {
|
||||
pageX: { get: () => start },
|
||||
pageY: { get: () => start },
|
||||
});
|
||||
|
||||
fireEvent(ele, mouseDown);
|
||||
// Drag
|
||||
const mouseMove: any = new Event('mousemove');
|
||||
mouseMove.pageX = end;
|
||||
@ -65,7 +73,7 @@ describe('ColorPicker', () => {
|
||||
|
||||
it('Should component custom trigger work', async () => {
|
||||
const App: React.FC = () => {
|
||||
const [color, setColor] = useState<Color | string>('hsb(215, 91%, 100%)');
|
||||
const [color, setColor] = useState<AggregationColor | string>('hsb(215, 91%, 100%)');
|
||||
const colorString = useMemo(
|
||||
() => (typeof color === 'string' ? color : color.toHsbString()),
|
||||
[color],
|
||||
@ -340,7 +348,7 @@ describe('ColorPicker', () => {
|
||||
});
|
||||
|
||||
it('Should fix hover boundary issues', async () => {
|
||||
spyElementPrototypes(HTMLElement, {
|
||||
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
@ -356,6 +364,8 @@ describe('ColorPicker', () => {
|
||||
fireEvent.mouseLeave(container.querySelector('.ant-color-picker-trigger')!);
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-popover-hidden')).toBeTruthy();
|
||||
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
|
||||
it('Should work at dark mode', async () => {
|
||||
@ -385,6 +395,12 @@ describe('ColorPicker', () => {
|
||||
expect(targetEle?.innerHTML).toBe('#1677ff');
|
||||
});
|
||||
|
||||
it('showText with transparent', async () => {
|
||||
const { container } = render(<ColorPicker defaultValue={null} showText />);
|
||||
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
||||
expect(targetEle?.textContent).toBe('Transparent');
|
||||
});
|
||||
|
||||
it('Should showText work', async () => {
|
||||
const { container } = render(<ColorPicker defaultValue="#1677ff" open showText />);
|
||||
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
||||
@ -404,7 +420,7 @@ describe('ColorPicker', () => {
|
||||
await waitFakeTimer();
|
||||
fireEvent.click(container.querySelector('.ant-select-item[title="RGB"]')!);
|
||||
await waitFakeTimer();
|
||||
expect(targetEle?.innerHTML).toEqual('rgb(22, 119, 255)');
|
||||
expect(targetEle?.innerHTML).toEqual('rgb(22,119,255)');
|
||||
|
||||
fireEvent.mouseDown(
|
||||
container.querySelector('.ant-color-picker-format-select .ant-select-selector')!,
|
||||
@ -448,7 +464,7 @@ describe('ColorPicker', () => {
|
||||
});
|
||||
|
||||
it('Should null work as expect', async () => {
|
||||
spyElementPrototypes(HTMLElement, {
|
||||
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
@ -456,7 +472,8 @@ describe('ColorPicker', () => {
|
||||
height: 100,
|
||||
}),
|
||||
});
|
||||
const { container } = render(<ColorPicker value={null} open />);
|
||||
|
||||
const { container } = render(<ColorPicker defaultValue={null} open />);
|
||||
expect(
|
||||
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
||||
).toEqual('0%');
|
||||
@ -467,6 +484,8 @@ describe('ColorPicker', () => {
|
||||
expect(
|
||||
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
||||
).toEqual('100%');
|
||||
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
|
||||
it('should support valid in form', async () => {
|
||||
@ -501,17 +520,37 @@ describe('ColorPicker', () => {
|
||||
});
|
||||
|
||||
it('Should onChangeComplete work', async () => {
|
||||
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}),
|
||||
});
|
||||
|
||||
const handleChangeComplete = jest.fn();
|
||||
const { container } = render(
|
||||
<ColorPicker open onChangeComplete={handleChangeComplete} allowClear />,
|
||||
);
|
||||
|
||||
// Move
|
||||
doMouseMove(container, 0, 999);
|
||||
fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
|
||||
expect(handleChangeComplete).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Clear
|
||||
fireEvent.click(
|
||||
container.querySelector('.ant-color-picker-operation .ant-color-picker-clear')!,
|
||||
);
|
||||
expect(handleChangeComplete).toHaveBeenCalledTimes(2);
|
||||
|
||||
// Change
|
||||
fireEvent.change(container.querySelector('.ant-color-picker-hex-input input')!, {
|
||||
target: { value: '#273B57' },
|
||||
});
|
||||
expect(handleChangeComplete).toHaveBeenCalledTimes(3);
|
||||
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
|
||||
it('Should disabledAlpha work', async () => {
|
||||
@ -522,7 +561,7 @@ describe('ColorPicker', () => {
|
||||
});
|
||||
|
||||
it('Should disabledAlpha work with value', async () => {
|
||||
spyElementPrototypes(HTMLElement, {
|
||||
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
@ -542,10 +581,12 @@ describe('ColorPicker', () => {
|
||||
onChangeComplete={setChangedValue}
|
||||
>
|
||||
<div className="color-value">
|
||||
{typeof value === 'string' ? value : value?.toHexString()}
|
||||
{value instanceof AggregationColor ? value.toHexString() : String(value)}
|
||||
</div>
|
||||
<div className="color-value-changed">
|
||||
{typeof changedValue === 'string' ? changedValue : changedValue?.toHexString()}
|
||||
{changedValue instanceof AggregationColor
|
||||
? changedValue.toHexString()
|
||||
: String(changedValue)}
|
||||
</div>
|
||||
</ColorPicker>
|
||||
);
|
||||
@ -555,6 +596,8 @@ describe('ColorPicker', () => {
|
||||
doMouseMove(container, 0, 999);
|
||||
expect(container.querySelector('.color-value')?.innerHTML).toEqual('#000000');
|
||||
expect(container.querySelector('.color-value-changed')?.innerHTML).toEqual('#000000');
|
||||
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
|
||||
it('Should warning work when set disabledAlpha true and color is alpha color', () => {
|
||||
@ -616,7 +659,7 @@ describe('ColorPicker', () => {
|
||||
const Demo = () => {
|
||||
const [color, setColor] = useState<ColorValueType>(value);
|
||||
useEffect(() => {
|
||||
setColor(generateColor('red'));
|
||||
setColor(generateColor('#FF0000'));
|
||||
}, []);
|
||||
return <ColorPicker value={color} />;
|
||||
};
|
||||
@ -645,7 +688,7 @@ describe('ColorPicker', () => {
|
||||
|
||||
it('Controlled string value should work with allowClear correctly', async () => {
|
||||
const Demo = (props: any) => {
|
||||
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
||||
const [color, setColor] = useState<ColorValueType>(generateColor('#FF0000'));
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof props.value !== 'undefined') {
|
||||
@ -654,7 +697,14 @@ describe('ColorPicker', () => {
|
||||
}, [props.value]);
|
||||
|
||||
return (
|
||||
<ColorPicker value={color} onChange={(e) => setColor(e.toHexString())} open allowClear />
|
||||
<ColorPicker
|
||||
value={color}
|
||||
onChange={(e) => {
|
||||
setColor(e.toHexString());
|
||||
}}
|
||||
open
|
||||
allowClear
|
||||
/>
|
||||
);
|
||||
};
|
||||
const { container, rerender } = render(<Demo />);
|
||||
@ -662,10 +712,13 @@ describe('ColorPicker', () => {
|
||||
expect(
|
||||
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||
).toBeFalsy();
|
||||
|
||||
// Clear
|
||||
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'),
|
||||
@ -740,4 +793,59 @@ describe('ColorPicker', () => {
|
||||
expect(container.querySelector('.ant-color-picker-clear')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('toHex', async () => {
|
||||
const { container } = render(
|
||||
<ColorPicker defaultValue="#123456" showText={(color) => color.toHex()} />,
|
||||
);
|
||||
expect(container.querySelector('.ant-color-picker-trigger-text')?.innerHTML).toBe('123456');
|
||||
});
|
||||
|
||||
describe('transparent to valuable', () => {
|
||||
let spyRect: ReturnType<typeof spyElementPrototypes>;
|
||||
|
||||
beforeEach(() => {
|
||||
spyRect = spyElementPrototypes(HTMLElement, {
|
||||
getBoundingClientRect: () => ({
|
||||
x: 0,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 100,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
spyRect.mockRestore();
|
||||
});
|
||||
|
||||
it('init with hue', async () => {
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(<ColorPicker defaultValue={null} open onChange={onChange} />);
|
||||
doMouseMove(container, 0, 50, 'ant-color-picker-slider-handle');
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
// Safe to change with any value but (0/0/0/0)
|
||||
'rgb(0,255,255)',
|
||||
);
|
||||
});
|
||||
|
||||
it('init with alpha', async () => {
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(<ColorPicker defaultValue={null} open onChange={onChange} />);
|
||||
doMouseMove(
|
||||
container,
|
||||
0,
|
||||
50,
|
||||
container.querySelectorAll<HTMLElement>('.ant-color-picker-slider-handle')[1]!,
|
||||
);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
// Safe to change with any value but (0/0/0/0)
|
||||
'rgba(255,0,0,0.5)',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,30 +1,52 @@
|
||||
/* eslint-disable class-methods-use-this */
|
||||
import type { ColorGenInput } from '@rc-component/color-picker';
|
||||
import { Color as RcColor } from '@rc-component/color-picker';
|
||||
|
||||
import type { ColorGenInput, Colors } from './interface';
|
||||
|
||||
export const toHexFormat = (value?: string, alpha?: boolean) =>
|
||||
value?.replace(/[^\w/]/gi, '').slice(0, alpha ? 8 : 6) || '';
|
||||
|
||||
export const getHex = (value?: string, alpha?: boolean) => (value ? toHexFormat(value, alpha) : '');
|
||||
|
||||
export interface Color
|
||||
extends Pick<
|
||||
RcColor,
|
||||
'toHsb' | 'toHsbString' | 'toHex' | 'toHexString' | 'toRgb' | 'toRgbString'
|
||||
> {
|
||||
cleared: boolean | 'controlled';
|
||||
}
|
||||
export type GradientColor = {
|
||||
color: AggregationColor;
|
||||
percent: number;
|
||||
}[];
|
||||
|
||||
export class ColorFactory implements Color {
|
||||
export class AggregationColor {
|
||||
/** Original Color object */
|
||||
private metaColor: RcColor;
|
||||
|
||||
private colors: GradientColor | undefined;
|
||||
|
||||
public cleared = false;
|
||||
|
||||
constructor(color: ColorGenInput<Color>) {
|
||||
this.metaColor = new RcColor(color as ColorGenInput);
|
||||
if (!color) {
|
||||
this.metaColor.setAlpha(0);
|
||||
constructor(color: ColorGenInput<AggregationColor> | Colors<AggregationColor>) {
|
||||
// Clone from another AggregationColor
|
||||
if (color instanceof AggregationColor) {
|
||||
this.metaColor = color.metaColor.clone();
|
||||
this.colors = color.colors?.map((info) => ({
|
||||
color: new AggregationColor(info.color),
|
||||
percent: info.percent,
|
||||
}));
|
||||
this.cleared = color.cleared;
|
||||
return;
|
||||
}
|
||||
|
||||
const isArray = Array.isArray(color);
|
||||
|
||||
if (isArray && color.length) {
|
||||
this.colors = color.map(({ color: c, percent }) => ({
|
||||
color: new AggregationColor(c),
|
||||
percent,
|
||||
}));
|
||||
this.metaColor = new RcColor(this.colors[0].color.metaColor);
|
||||
} else {
|
||||
this.metaColor = new RcColor(isArray ? '' : color);
|
||||
}
|
||||
|
||||
if (!color || (isArray && !this.colors)) {
|
||||
this.metaColor = this.metaColor.setA(0);
|
||||
this.cleared = true;
|
||||
}
|
||||
}
|
||||
@ -38,13 +60,11 @@ export class ColorFactory implements Color {
|
||||
}
|
||||
|
||||
toHex() {
|
||||
return getHex(this.toHexString(), this.metaColor.getAlpha() < 1);
|
||||
return getHex(this.toHexString(), this.metaColor.a < 1);
|
||||
}
|
||||
|
||||
toHexString() {
|
||||
return this.metaColor.getAlpha() === 1
|
||||
? this.metaColor.toHexString()
|
||||
: this.metaColor.toHex8String();
|
||||
return this.metaColor.toHexString();
|
||||
}
|
||||
|
||||
toRgb() {
|
||||
@ -54,4 +74,42 @@ export class ColorFactory implements Color {
|
||||
toRgbString() {
|
||||
return this.metaColor.toRgbString();
|
||||
}
|
||||
|
||||
isGradient(): boolean {
|
||||
return !!this.colors && !this.cleared;
|
||||
}
|
||||
|
||||
getColors(): GradientColor {
|
||||
return this.colors || [{ color: this, percent: 0 }];
|
||||
}
|
||||
|
||||
toCssString(): string {
|
||||
const { colors } = this;
|
||||
|
||||
// CSS line-gradient
|
||||
if (colors) {
|
||||
const colorsStr = colors.map((c) => `${c.color.toRgbString()} ${c.percent}%`).join(', ');
|
||||
return `linear-gradient(90deg, ${colorsStr})`;
|
||||
}
|
||||
|
||||
return this.metaColor.toRgbString();
|
||||
}
|
||||
|
||||
equals(color: AggregationColor | null): boolean {
|
||||
if (!color || this.isGradient() !== color.isGradient()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isGradient()) {
|
||||
return this.toHexString() === color.toHexString();
|
||||
}
|
||||
|
||||
return (
|
||||
this.colors!.length === color.colors!.length &&
|
||||
this.colors!.every((c, i) => {
|
||||
const target = color.colors![i];
|
||||
return c.percent === target.percent && c.color.equals(target.color);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import { generateColor, getAlphaColor } from '../util';
|
||||
import type { AggregationColor } from '../color';
|
||||
import { generateColor, getColorAlpha } from '../util';
|
||||
import ColorSteppers from './ColorSteppers';
|
||||
|
||||
interface ColorAlphaInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorAlphaInputProps {
|
||||
prefixCls: string;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const ColorAlphaInput: FC<ColorAlphaInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorAlphaInputPrefixCls = `${prefixCls}-alpha-input`;
|
||||
const [alphaValue, setAlphaValue] = useState<Color>(generateColor(value || '#000'));
|
||||
const [alphaValue, setAlphaValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
@ -34,7 +34,7 @@ const ColorAlphaInput: FC<ColorAlphaInputProps> = ({ prefixCls, value, onChange
|
||||
|
||||
return (
|
||||
<ColorSteppers
|
||||
value={getAlphaColor(alphaValue)}
|
||||
value={getColorAlpha(alphaValue)}
|
||||
prefixCls={prefixCls}
|
||||
formatter={(step) => `${step}%`}
|
||||
className={colorAlphaInputPrefixCls}
|
||||
|
@ -1,23 +1,24 @@
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import type { AggregationColor } from '../color';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
interface ColorClearProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorClearProps {
|
||||
prefixCls: string;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const ColorClear: FC<ColorClearProps> = ({ prefixCls, value, onChange }) => {
|
||||
const handleClick = () => {
|
||||
if (value && !value.cleared) {
|
||||
if (onChange && value && !value.cleared) {
|
||||
const hsba = value.toHsb();
|
||||
hsba.a = 0;
|
||||
const genColor = generateColor(hsba);
|
||||
genColor.cleared = true;
|
||||
onChange?.(genColor);
|
||||
|
||||
onChange(genColor);
|
||||
}
|
||||
};
|
||||
return <div className={`${prefixCls}-clear`} onClick={handleClick} />;
|
||||
|
@ -2,14 +2,14 @@ import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import Input from '../../input';
|
||||
import type { Color } from '../color';
|
||||
import type { AggregationColor } from '../color';
|
||||
import { toHexFormat } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
interface ColorHexInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorHexInputProps {
|
||||
prefixCls: string;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const hexReg = /(^#[\da-f]{6}$)|(^#[\da-f]{8}$)/i;
|
||||
@ -17,13 +17,14 @@ const isHexString = (hex?: string) => hexReg.test(`#${hex}`);
|
||||
|
||||
const ColorHexInput: FC<ColorHexInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorHexInputPrefixCls = `${prefixCls}-hex-input`;
|
||||
const [hexValue, setHexValue] = useState(value?.toHex());
|
||||
const [hexValue, setHexValue] = useState(() =>
|
||||
value ? toHexFormat(value.toHexString()) : undefined,
|
||||
);
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
const hex = value?.toHex();
|
||||
if (isHexString(hex) && value) {
|
||||
setHexValue(toHexFormat(hex));
|
||||
if (value) {
|
||||
setHexValue(toHexFormat(value.toHexString()));
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
|
@ -2,19 +2,19 @@ import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { HSB } from '@rc-component/color-picker';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import type { AggregationColor } from '../color';
|
||||
import { generateColor, getRoundNumber } from '../util';
|
||||
import ColorSteppers from './ColorSteppers';
|
||||
|
||||
interface ColorHsbInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorHsbInputProps {
|
||||
prefixCls: string;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const ColorHsbInput: FC<ColorHsbInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorHsbInputPrefixCls = `${prefixCls}-hsb-input`;
|
||||
const [hsbValue, setHsbValue] = useState<Color>(generateColor(value || '#000'));
|
||||
const [hsbValue, setHsbValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
|
@ -3,18 +3,21 @@ import React, { useMemo } from 'react';
|
||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
||||
|
||||
import Select from '../../select';
|
||||
import type { Color } from '../color';
|
||||
import type { ColorFormatType, ColorPickerBaseProps } from '../interface';
|
||||
import type { AggregationColor } from '../color';
|
||||
import type { ColorFormatType } from '../interface';
|
||||
import { ColorFormat } from '../interface';
|
||||
import ColorAlphaInput from './ColorAlphaInput';
|
||||
import ColorHexInput from './ColorHexInput';
|
||||
import ColorHsbInput from './ColorHsbInput';
|
||||
import ColorRgbInput from './ColorRgbInput';
|
||||
|
||||
interface ColorInputProps
|
||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'format' | 'onFormatChange' | 'disabledAlpha'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorInputProps {
|
||||
prefixCls: string;
|
||||
format?: ColorFormatType;
|
||||
onFormatChange?: (format: ColorFormatType) => void;
|
||||
disabledAlpha?: boolean;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const selectOptions = [ColorFormat.hex, ColorFormat.hsb, ColorFormat.rgb].map((format) => ({
|
||||
|
@ -8,14 +8,15 @@ import type { CollapseProps } from '../../collapse';
|
||||
import Collapse from '../../collapse';
|
||||
import { useLocale } from '../../locale';
|
||||
import { useToken } from '../../theme/internal';
|
||||
import type { Color } from '../color';
|
||||
import type { ColorPickerBaseProps, PresetsItem } from '../interface';
|
||||
import type { AggregationColor } from '../color';
|
||||
import type { PresetsItem } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
interface ColorPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
interface ColorPresetsProps {
|
||||
prefixCls: string;
|
||||
presets: PresetsItem[];
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const genPresetColor = (list: PresetsItem[]) =>
|
||||
@ -24,7 +25,7 @@ const genPresetColor = (list: PresetsItem[]) =>
|
||||
return value;
|
||||
});
|
||||
|
||||
const isBright = (value: Color, bgColorToken: string) => {
|
||||
const isBright = (value: AggregationColor, bgColorToken: string) => {
|
||||
const { r, g, b, a } = value.toRgb();
|
||||
const hsv = new RcColor(value.toRgbString()).onBackground(bgColorToken).toHsv();
|
||||
if (a <= 0.5) {
|
||||
@ -55,7 +56,7 @@ const ColorPresets: FC<ColorPresetsProps> = ({ prefixCls, presets, value: color,
|
||||
[presetsValue],
|
||||
);
|
||||
|
||||
const handleClick = (colorValue: Color) => {
|
||||
const handleClick = (colorValue: AggregationColor) => {
|
||||
onChange?.(colorValue);
|
||||
};
|
||||
|
||||
@ -65,7 +66,7 @@ const ColorPresets: FC<ColorPresetsProps> = ({ prefixCls, presets, value: color,
|
||||
children: (
|
||||
<div className={`${colorPresetsPrefixCls}-items`}>
|
||||
{Array.isArray(preset?.colors) && preset.colors?.length > 0 ? (
|
||||
(preset.colors as Color[]).map((presetColor, index) => (
|
||||
(preset.colors as AggregationColor[]).map((presetColor, index) => (
|
||||
<ColorBlock
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={`preset-${index}-${presetColor.toHexString()}`}
|
||||
|
@ -2,19 +2,19 @@ import type { FC } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { RGB } from '@rc-component/color-picker';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import type { AggregationColor } from '../color';
|
||||
import { generateColor } from '../util';
|
||||
import ColorSteppers from './ColorSteppers';
|
||||
|
||||
interface ColorRgbInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
interface ColorRgbInputProps {
|
||||
prefixCls: string;
|
||||
value?: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
const ColorRgbInput: FC<ColorRgbInputProps> = ({ prefixCls, value, onChange }) => {
|
||||
const colorRgbInputPrefixCls = `${prefixCls}-rgb-input`;
|
||||
const [rgbValue, setRgbValue] = useState<Color>(generateColor(value || '#000'));
|
||||
const [rgbValue, setRgbValue] = useState<AggregationColor>(generateColor(value || '#000'));
|
||||
|
||||
// Update step value
|
||||
useEffect(() => {
|
||||
|
177
components/color-picker/components/ColorSlider.tsx
Normal file
177
components/color-picker/components/ColorSlider.tsx
Normal file
@ -0,0 +1,177 @@
|
||||
import * as React from 'react';
|
||||
import type { BaseSliderProps } from '@rc-component/color-picker';
|
||||
import classNames from 'classnames';
|
||||
import { UnstableContext } from 'rc-slider';
|
||||
import { useEvent } from 'rc-util';
|
||||
|
||||
import type { GetContextProp, GetProp } from '../../_util/type';
|
||||
import Slider from '../../slider';
|
||||
import SliderInternalContext from '../../slider/Context';
|
||||
import type { SliderInternalContextProps } from '../../slider/Context';
|
||||
import { getGradientPercentColor } from '../util';
|
||||
|
||||
export interface GradientColorSliderProps
|
||||
extends Omit<BaseSliderProps, 'value' | 'onChange' | 'onChangeComplete' | 'type'> {
|
||||
value: number[];
|
||||
onChange?: (value: number[]) => void;
|
||||
onChangeComplete: (value: number[]) => void;
|
||||
range?: boolean;
|
||||
className?: string;
|
||||
activeIndex?: number;
|
||||
onActive?: (index: number) => void;
|
||||
type: BaseSliderProps['type'] | 'gradient';
|
||||
|
||||
// Drag events
|
||||
onDragStart?: GetContextProp<typeof UnstableContext, 'onDragStart'>;
|
||||
onDragChange?: GetContextProp<typeof UnstableContext, 'onDragChange'>;
|
||||
|
||||
// Key event
|
||||
onKeyDelete?: (index: number) => void;
|
||||
}
|
||||
|
||||
export const GradientColorSlider = (props: GradientColorSliderProps) => {
|
||||
const {
|
||||
prefixCls,
|
||||
colors,
|
||||
type,
|
||||
color,
|
||||
range = false,
|
||||
className,
|
||||
activeIndex,
|
||||
onActive,
|
||||
|
||||
onDragStart,
|
||||
onDragChange,
|
||||
onKeyDelete,
|
||||
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const sliderProps = {
|
||||
...restProps,
|
||||
track: false,
|
||||
};
|
||||
|
||||
// ========================== Background ==========================
|
||||
const linearCss = React.useMemo(() => {
|
||||
const colorsStr = colors.map((c) => `${c.color} ${c.percent}%`).join(', ');
|
||||
return `linear-gradient(90deg, ${colorsStr})`;
|
||||
}, [colors]);
|
||||
|
||||
const pointColor = React.useMemo(() => {
|
||||
if (!color || !type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (type === 'alpha') {
|
||||
return color.toRgbString();
|
||||
}
|
||||
|
||||
return `hsl(${color.toHsb().h}, 100%, 50%)`;
|
||||
}, [color, type]);
|
||||
|
||||
// ======================= Context: Slider ========================
|
||||
const onInternalDragStart: GetContextProp<typeof UnstableContext, 'onDragStart'> = useEvent(
|
||||
onDragStart!,
|
||||
);
|
||||
|
||||
const onInternalDragChange: GetContextProp<typeof UnstableContext, 'onDragChange'> = useEvent(
|
||||
onDragChange!,
|
||||
);
|
||||
|
||||
const unstableContext = React.useMemo(
|
||||
() => ({
|
||||
onDragStart: onInternalDragStart,
|
||||
onDragChange: onInternalDragChange,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// ======================= Context: Render ========================
|
||||
const handleRender: GetProp<SliderInternalContextProps, 'handleRender'> = useEvent(
|
||||
(ori, info) => {
|
||||
const { onFocus, style, className: handleCls, onKeyDown } = ori.props;
|
||||
|
||||
// Point Color
|
||||
const mergedStyle = { ...style };
|
||||
if (type === 'gradient') {
|
||||
mergedStyle.background = getGradientPercentColor(colors, info.value);
|
||||
}
|
||||
|
||||
return React.cloneElement(ori, {
|
||||
onFocus: (e: React.FocusEvent<HTMLDivElement>) => {
|
||||
onActive?.(info.index);
|
||||
onFocus?.(e);
|
||||
},
|
||||
style: mergedStyle,
|
||||
className: classNames(handleCls, {
|
||||
[`${prefixCls}-slider-handle-active`]: activeIndex === info.index,
|
||||
}),
|
||||
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && onKeyDelete) {
|
||||
onKeyDelete(info.index);
|
||||
}
|
||||
|
||||
onKeyDown?.(e);
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const sliderContext: SliderInternalContextProps = React.useMemo(
|
||||
() => ({
|
||||
direction: 'ltr',
|
||||
handleRender,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// ============================ Render ============================
|
||||
return (
|
||||
<SliderInternalContext.Provider value={sliderContext}>
|
||||
<UnstableContext.Provider value={unstableContext}>
|
||||
<Slider
|
||||
{...sliderProps}
|
||||
className={classNames(className, `${prefixCls}-slider`)}
|
||||
tooltip={{ open: false }}
|
||||
range={{
|
||||
editable: range,
|
||||
minCount: 2,
|
||||
}}
|
||||
styles={{
|
||||
rail: {
|
||||
background: linearCss,
|
||||
},
|
||||
handle: pointColor
|
||||
? {
|
||||
background: pointColor,
|
||||
}
|
||||
: {},
|
||||
}}
|
||||
classNames={{
|
||||
rail: `${prefixCls}-slider-rail`,
|
||||
handle: `${prefixCls}-slider-handle`,
|
||||
}}
|
||||
/>
|
||||
</UnstableContext.Provider>
|
||||
</SliderInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const SingleColorSlider = (props: BaseSliderProps) => {
|
||||
const { value, onChange, onChangeComplete } = props;
|
||||
|
||||
const singleOnChange = (v: number[]) => onChange(v[0]);
|
||||
const singleOnChangeComplete = (v: number[]) => onChangeComplete(v[0]);
|
||||
|
||||
return (
|
||||
<GradientColorSlider
|
||||
{...props}
|
||||
value={[value]}
|
||||
onChange={singleOnChange}
|
||||
onChangeComplete={singleOnChangeComplete}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SingleColorSlider;
|
@ -4,9 +4,9 @@ import classNames from 'classnames';
|
||||
|
||||
import type { InputNumberProps } from '../../input-number';
|
||||
import InputNumber from '../../input-number';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
|
||||
interface ColorSteppersProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
||||
interface ColorSteppersProps {
|
||||
prefixCls: string;
|
||||
value?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
|
@ -1,15 +1,21 @@
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
import type { CSSProperties, MouseEventHandler } from 'react';
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import { ColorBlock } from '@rc-component/color-picker';
|
||||
import classNames from 'classnames';
|
||||
import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||
|
||||
import type { ColorPickerBaseProps, ColorPickerProps } from '../interface';
|
||||
import { getAlphaColor } from '../util';
|
||||
import { useLocale } from '../../locale';
|
||||
import type { AggregationColor } from '../color';
|
||||
import type { ColorFormatType, ColorPickerProps } from '../interface';
|
||||
import { getColorAlpha } from '../util';
|
||||
import ColorClear from './ColorClear';
|
||||
|
||||
export interface ColorTriggerProps
|
||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'disabled' | 'format'> {
|
||||
color: NonNullable<ColorPickerBaseProps['color']>;
|
||||
export interface ColorTriggerProps {
|
||||
prefixCls: string;
|
||||
disabled?: boolean;
|
||||
format?: ColorFormatType;
|
||||
color: AggregationColor;
|
||||
open?: boolean;
|
||||
showText?: ColorPickerProps['showText'];
|
||||
className?: string;
|
||||
@ -17,25 +23,57 @@ export interface ColorTriggerProps
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
onMouseEnter?: MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
||||
activeIndex: number;
|
||||
}
|
||||
|
||||
const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref) => {
|
||||
const { color, prefixCls, open, disabled, format, className, showText, ...rest } = props;
|
||||
const { color, prefixCls, open, disabled, format, className, showText, activeIndex, ...rest } =
|
||||
props;
|
||||
|
||||
const colorTriggerPrefixCls = `${prefixCls}-trigger`;
|
||||
const colorTextPrefixCls = `${colorTriggerPrefixCls}-text`;
|
||||
const colorTextCellPrefixCls = `${colorTextPrefixCls}-cell`;
|
||||
|
||||
const containerNode = useMemo<React.ReactNode>(
|
||||
() =>
|
||||
color.cleared ? (
|
||||
<ColorClear prefixCls={prefixCls} />
|
||||
) : (
|
||||
<ColorBlock prefixCls={prefixCls} color={color.toRgbString()} />
|
||||
),
|
||||
[color, prefixCls],
|
||||
const [locale] = useLocale('ColorPicker');
|
||||
|
||||
// ============================== Text ==============================
|
||||
const desc: React.ReactNode = React.useMemo(() => {
|
||||
if (!showText) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (typeof showText === 'function') {
|
||||
return showText(color);
|
||||
}
|
||||
|
||||
if (color.cleared) {
|
||||
return locale.transparent;
|
||||
}
|
||||
|
||||
if (color.isGradient()) {
|
||||
// return color
|
||||
// .getColors()
|
||||
// .map((c) => `${c.color.toRgbString()} ${c.percent}%`)
|
||||
// .join(', ');
|
||||
return color.getColors().map((c, index) => {
|
||||
const inactive = activeIndex !== -1 && activeIndex !== index;
|
||||
|
||||
return (
|
||||
<span
|
||||
key={index}
|
||||
className={classNames(
|
||||
colorTextCellPrefixCls,
|
||||
inactive && `${colorTextCellPrefixCls}-inactive`,
|
||||
)}
|
||||
>
|
||||
{c.color.toRgbString()} {c.percent}%
|
||||
</span>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const genColorString = () => {
|
||||
const hexString = color.toHexString().toUpperCase();
|
||||
const alpha = getAlphaColor(color);
|
||||
const alpha = getColorAlpha(color);
|
||||
switch (format) {
|
||||
case 'rgb':
|
||||
return color.toRgbString();
|
||||
@ -45,16 +83,18 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
|
||||
default:
|
||||
return alpha < 100 ? `${hexString.slice(0, 7)},${alpha}%` : hexString;
|
||||
}
|
||||
};
|
||||
}, [color, format, showText, activeIndex]);
|
||||
|
||||
const renderText = () => {
|
||||
if (typeof showText === 'function') {
|
||||
return showText(color);
|
||||
}
|
||||
if (showText) {
|
||||
return genColorString();
|
||||
}
|
||||
};
|
||||
// ============================= Render =============================
|
||||
const containerNode = useMemo<React.ReactNode>(
|
||||
() =>
|
||||
color.cleared ? (
|
||||
<ColorClear prefixCls={prefixCls} />
|
||||
) : (
|
||||
<ColorBlock prefixCls={prefixCls} color={color.toCssString()} />
|
||||
),
|
||||
[color, prefixCls],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -63,10 +103,10 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
|
||||
[`${colorTriggerPrefixCls}-active`]: open,
|
||||
[`${colorTriggerPrefixCls}-disabled`]: disabled,
|
||||
})}
|
||||
{...rest}
|
||||
{...pickAttrs(rest)}
|
||||
>
|
||||
{containerNode}
|
||||
{showText && <div className={`${colorTriggerPrefixCls}-text`}>{renderText()}</div>}
|
||||
{showText && <div className={colorTextPrefixCls}>{desc}</div>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,68 +0,0 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import type { HsbaColorType } from '@rc-component/color-picker';
|
||||
import RcColorPicker from '@rc-component/color-picker';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import { PanelPickerContext } from '../context';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
import ColorClear from './ColorClear';
|
||||
import ColorInput from './ColorInput';
|
||||
|
||||
export interface PanelPickerProps
|
||||
extends Pick<
|
||||
ColorPickerBaseProps,
|
||||
'prefixCls' | 'allowClear' | 'disabledAlpha' | 'onChangeComplete'
|
||||
> {
|
||||
value?: Color;
|
||||
onChange?: (value?: Color, type?: HsbaColorType, pickColor?: boolean) => void;
|
||||
onClear?: () => void;
|
||||
}
|
||||
|
||||
const PanelPicker: FC = () => {
|
||||
const {
|
||||
prefixCls,
|
||||
allowClear,
|
||||
value,
|
||||
disabledAlpha,
|
||||
onChange,
|
||||
onClear,
|
||||
onChangeComplete,
|
||||
...injectProps
|
||||
} = useContext(PanelPickerContext);
|
||||
return (
|
||||
<>
|
||||
{allowClear && (
|
||||
<ColorClear
|
||||
prefixCls={prefixCls}
|
||||
value={value}
|
||||
onChange={(clearColor) => {
|
||||
onChange?.(clearColor);
|
||||
onClear?.();
|
||||
}}
|
||||
{...injectProps}
|
||||
/>
|
||||
)}
|
||||
<RcColorPicker
|
||||
prefixCls={prefixCls}
|
||||
value={value?.toHsb()}
|
||||
disabledAlpha={disabledAlpha}
|
||||
onChange={(colorValue, type) => {
|
||||
onChange?.(generateColor(colorValue), type, true);
|
||||
}}
|
||||
onChangeComplete={(colorValue) => {
|
||||
onChangeComplete?.(generateColor(colorValue));
|
||||
}}
|
||||
/>
|
||||
<ColorInput
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
prefixCls={prefixCls}
|
||||
disabledAlpha={disabledAlpha}
|
||||
{...injectProps}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PanelPicker;
|
@ -0,0 +1,149 @@
|
||||
import * as React from 'react';
|
||||
import type { UnstableContext } from 'rc-slider';
|
||||
|
||||
import type { GetContextProp } from '../../../_util/type';
|
||||
import { AggregationColor } from '../../color';
|
||||
import type { GradientColor } from '../../color';
|
||||
import type { PanelPickerContextProps } from '../../context';
|
||||
import { getGradientPercentColor } from '../../util';
|
||||
import { GradientColorSlider } from '../ColorSlider';
|
||||
|
||||
function sortColors(colors: { percent: number; color: string }[]) {
|
||||
return [...colors].sort((a, b) => a.percent - b.percent);
|
||||
}
|
||||
|
||||
export interface GradientColorBarProps extends PanelPickerContextProps {
|
||||
colors: GradientColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* GradientColorBar will auto show when the mode is `gradient`.
|
||||
*/
|
||||
const GradientColorBar = (props: GradientColorBarProps) => {
|
||||
const {
|
||||
prefixCls,
|
||||
mode,
|
||||
onChange,
|
||||
onChangeComplete,
|
||||
onActive,
|
||||
activeIndex,
|
||||
onGradientDragging,
|
||||
colors,
|
||||
} = props;
|
||||
|
||||
const isGradient = mode === 'gradient';
|
||||
|
||||
// ============================= Colors =============================
|
||||
const colorList = React.useMemo(
|
||||
() =>
|
||||
colors.map((info) => ({
|
||||
percent: info.percent,
|
||||
color: info.color.toRgbString(),
|
||||
})),
|
||||
[colors],
|
||||
);
|
||||
|
||||
const values = React.useMemo(() => colorList.map((info) => info.percent), [colorList]);
|
||||
|
||||
// ============================== Drag ==============================
|
||||
const colorsRef = React.useRef(colorList);
|
||||
|
||||
// Record current colors
|
||||
const onDragStart: GetContextProp<typeof UnstableContext, 'onDragStart'> = ({
|
||||
rawValues,
|
||||
draggingIndex,
|
||||
draggingValue,
|
||||
}) => {
|
||||
if (rawValues.length > colorList.length) {
|
||||
// Add new node
|
||||
const newPointColor = getGradientPercentColor(colorList, draggingValue);
|
||||
const nextColors = [...colorList];
|
||||
nextColors.splice(draggingIndex, 0, {
|
||||
percent: draggingValue,
|
||||
color: newPointColor,
|
||||
});
|
||||
|
||||
colorsRef.current = nextColors;
|
||||
} else {
|
||||
colorsRef.current = colorList;
|
||||
}
|
||||
|
||||
onGradientDragging(true);
|
||||
onChange(new AggregationColor(sortColors(colorsRef.current)), true);
|
||||
};
|
||||
|
||||
// Adjust color when dragging
|
||||
const onDragChange: GetContextProp<typeof UnstableContext, 'onDragChange'> = ({
|
||||
deleteIndex,
|
||||
draggingIndex,
|
||||
draggingValue,
|
||||
}) => {
|
||||
let nextColors = [...colorsRef.current];
|
||||
|
||||
if (deleteIndex !== -1) {
|
||||
nextColors.splice(deleteIndex, 1);
|
||||
} else {
|
||||
nextColors[draggingIndex] = {
|
||||
...nextColors[draggingIndex],
|
||||
percent: draggingValue,
|
||||
};
|
||||
|
||||
nextColors = sortColors(nextColors);
|
||||
}
|
||||
|
||||
onChange(new AggregationColor(nextColors), true);
|
||||
};
|
||||
|
||||
// ============================== Key ===============================
|
||||
const onKeyDelete = (index: number) => {
|
||||
const nextColors = [...colorList];
|
||||
nextColors.splice(index, 1);
|
||||
|
||||
const nextColor = new AggregationColor(nextColors);
|
||||
|
||||
onChange(nextColor);
|
||||
onChangeComplete(nextColor);
|
||||
};
|
||||
|
||||
// ============================= Change =============================
|
||||
const onInternalChangeComplete = (nextValues: number[]) => {
|
||||
onChangeComplete(new AggregationColor(colorList));
|
||||
|
||||
// Reset `activeIndex` if out of range
|
||||
if (activeIndex >= nextValues.length) {
|
||||
onActive(nextValues.length - 1);
|
||||
}
|
||||
|
||||
onGradientDragging(false);
|
||||
};
|
||||
|
||||
// ============================= Render =============================
|
||||
if (!isGradient) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<GradientColorSlider
|
||||
min={0}
|
||||
max={100}
|
||||
prefixCls={prefixCls}
|
||||
className={`${prefixCls}-gradient-slider`}
|
||||
colors={colorList}
|
||||
color={null!}
|
||||
value={values}
|
||||
range
|
||||
onChangeComplete={onInternalChangeComplete}
|
||||
disabled={false}
|
||||
type="gradient"
|
||||
// Active
|
||||
activeIndex={activeIndex}
|
||||
onActive={onActive}
|
||||
// Drag
|
||||
onDragStart={onDragStart}
|
||||
onDragChange={onDragChange}
|
||||
onKeyDelete={onKeyDelete}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(GradientColorBar);
|
188
components/color-picker/components/PanelPicker/index.tsx
Normal file
188
components/color-picker/components/PanelPicker/index.tsx
Normal file
@ -0,0 +1,188 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import RcColorPicker from '@rc-component/color-picker';
|
||||
import type { Color } from '@rc-component/color-picker';
|
||||
import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
|
||||
|
||||
import Segmented from '../../../segmented';
|
||||
import { AggregationColor } from '../../color';
|
||||
import { PanelPickerContext } from '../../context';
|
||||
import { genAlphaColor, generateColor } from '../../util';
|
||||
import ColorClear from '../ColorClear';
|
||||
import ColorInput from '../ColorInput';
|
||||
import ColorSlider from '../ColorSlider';
|
||||
import GradientColorBar from './GradientColorBar';
|
||||
|
||||
const components = {
|
||||
slider: ColorSlider,
|
||||
};
|
||||
|
||||
const PanelPicker: FC = () => {
|
||||
const panelPickerContext = useContext(PanelPickerContext);
|
||||
|
||||
const {
|
||||
mode,
|
||||
onModeChange,
|
||||
modeOptions,
|
||||
prefixCls,
|
||||
allowClear,
|
||||
value,
|
||||
disabledAlpha,
|
||||
onChange,
|
||||
onClear,
|
||||
onChangeComplete,
|
||||
activeIndex,
|
||||
gradientDragging,
|
||||
...injectProps
|
||||
} = panelPickerContext;
|
||||
|
||||
// ============================ Colors ============================
|
||||
const colors = React.useMemo(() => {
|
||||
if (!value.cleared) {
|
||||
return value.getColors();
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
percent: 0,
|
||||
color: new AggregationColor(''),
|
||||
},
|
||||
{
|
||||
percent: 100,
|
||||
color: new AggregationColor(''),
|
||||
},
|
||||
];
|
||||
}, [value]);
|
||||
|
||||
// ========================= Single Color =========================
|
||||
const isSingle = !value.isGradient();
|
||||
|
||||
// We cache the point color in case user drag the gradient point across another one
|
||||
const [lockedColor, setLockedColor] = React.useState<AggregationColor>(value);
|
||||
|
||||
// Use layout effect here since `useEffect` will cause a blink when mouseDown
|
||||
useLayoutEffect(() => {
|
||||
if (!isSingle) {
|
||||
setLockedColor(colors[activeIndex]?.color);
|
||||
}
|
||||
}, [gradientDragging, activeIndex]);
|
||||
|
||||
const activeColor = React.useMemo(() => {
|
||||
if (isSingle) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Use cache when dragging. User can not operation panel when dragging.
|
||||
if (gradientDragging) {
|
||||
return lockedColor;
|
||||
}
|
||||
|
||||
return colors[activeIndex]?.color;
|
||||
}, [value, activeIndex, isSingle, lockedColor, gradientDragging]);
|
||||
|
||||
// ============================ Change ============================
|
||||
const fillColor = (nextColor: AggregationColor) => {
|
||||
if (mode === 'single') {
|
||||
return nextColor;
|
||||
}
|
||||
|
||||
const nextColors = [...colors];
|
||||
nextColors[activeIndex] = {
|
||||
...nextColors[activeIndex],
|
||||
color: nextColor,
|
||||
};
|
||||
|
||||
return new AggregationColor(nextColors);
|
||||
};
|
||||
|
||||
const onInternalChange = (
|
||||
colorValue: AggregationColor | Color,
|
||||
fromPicker?: boolean,
|
||||
info?: {
|
||||
type?: 'hue' | 'alpha';
|
||||
value?: number;
|
||||
},
|
||||
) => {
|
||||
const nextColor = generateColor(colorValue);
|
||||
|
||||
let submitColor = nextColor;
|
||||
|
||||
if (value.cleared) {
|
||||
const rgb = submitColor.toRgb();
|
||||
|
||||
// Auto fill color if origin is `0/0/0` to enhance user experience
|
||||
if (!rgb.r && !rgb.g && !rgb.b && info) {
|
||||
const { type: infoType, value: infoValue = 0 } = info;
|
||||
|
||||
submitColor = new AggregationColor({
|
||||
h: infoType === 'hue' ? infoValue : 0,
|
||||
s: 1,
|
||||
b: 1,
|
||||
a: infoType === 'alpha' ? infoValue / 100 : 1,
|
||||
});
|
||||
} else {
|
||||
submitColor = genAlphaColor(submitColor);
|
||||
}
|
||||
}
|
||||
|
||||
onChange(fillColor(submitColor), fromPicker);
|
||||
};
|
||||
|
||||
const onInternalChangeComplete = (nextColor: AggregationColor) => {
|
||||
onChangeComplete(fillColor(nextColor));
|
||||
};
|
||||
|
||||
// ============================ Render ============================
|
||||
// Operation bar
|
||||
let operationNode: React.ReactNode = null;
|
||||
const showMode = modeOptions.length > 1;
|
||||
|
||||
if (allowClear || showMode) {
|
||||
operationNode = (
|
||||
<div className={`${prefixCls}-operation`}>
|
||||
{showMode && (
|
||||
<Segmented size="small" options={modeOptions} value={mode} onChange={onModeChange} />
|
||||
)}
|
||||
<ColorClear
|
||||
prefixCls={prefixCls}
|
||||
value={value}
|
||||
onChange={(clearColor) => {
|
||||
onChange(clearColor);
|
||||
onClear?.();
|
||||
}}
|
||||
{...injectProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Return
|
||||
return (
|
||||
<>
|
||||
{operationNode}
|
||||
|
||||
<GradientColorBar {...panelPickerContext} colors={colors} />
|
||||
|
||||
<RcColorPicker
|
||||
prefixCls={prefixCls}
|
||||
value={activeColor?.toHsb()}
|
||||
disabledAlpha={disabledAlpha}
|
||||
onChange={(colorValue, info) => {
|
||||
onInternalChange(colorValue, true, info);
|
||||
}}
|
||||
onChangeComplete={(colorValue) => {
|
||||
onInternalChangeComplete(generateColor(colorValue));
|
||||
}}
|
||||
components={components}
|
||||
/>
|
||||
<ColorInput
|
||||
value={activeColor}
|
||||
onChange={onInternalChange}
|
||||
prefixCls={prefixCls}
|
||||
disabledAlpha={disabledAlpha}
|
||||
{...injectProps}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PanelPicker;
|
@ -1,16 +1,9 @@
|
||||
import type { FC } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import { PanelPresetsContext } from '../context';
|
||||
import type { ColorPickerBaseProps } from '../interface';
|
||||
import ColorPresets from './ColorPresets';
|
||||
|
||||
export interface PanelPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls' | 'presets'> {
|
||||
value?: Color;
|
||||
onChange?: (value: Color) => void;
|
||||
}
|
||||
|
||||
const PanelPresets: FC = () => {
|
||||
const { prefixCls, value, presets, onChange } = useContext(PanelPresetsContext);
|
||||
return Array.isArray(presets) ? (
|
||||
|
@ -1,11 +1,50 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { PanelPickerProps } from './components/PanelPicker';
|
||||
import type { PanelPresetsProps } from './components/PanelPresets';
|
||||
import type { GetProp } from '../_util/type';
|
||||
import type { AggregationColor } from './color';
|
||||
import type { ModeOptions } from './hooks/useModeColor';
|
||||
import type { ColorFormatType, ColorPickerProps, ModeType, PresetsItem } from './interface';
|
||||
|
||||
export const PanelPickerContext = React.createContext<PanelPickerProps>({} as PanelPickerProps);
|
||||
export interface PanelPickerContextProps {
|
||||
prefixCls: string;
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
disabledAlpha?: boolean;
|
||||
mode: ModeType;
|
||||
onModeChange: (mode: ModeType) => void;
|
||||
modeOptions: ModeOptions;
|
||||
|
||||
export const PanelPresetsContext = React.createContext<PanelPresetsProps>({} as PanelPresetsProps);
|
||||
value: AggregationColor;
|
||||
onChange: (value?: AggregationColor, pickColor?: boolean) => void;
|
||||
onChangeComplete: GetProp<ColorPickerProps, 'onChangeComplete'>;
|
||||
|
||||
export const { Provider: PanelPickerProvider } = PanelPickerContext;
|
||||
export const { Provider: PanelPresetsProvider } = PanelPresetsContext;
|
||||
format?: ColorFormatType;
|
||||
onFormatChange?: ColorPickerProps['onFormatChange'];
|
||||
|
||||
/** The gradient Slider active handle */
|
||||
activeIndex: number;
|
||||
/** The gradient Slider handle active changed */
|
||||
onActive: (index: number) => void;
|
||||
/** Is gradient Slider dragging */
|
||||
gradientDragging: boolean;
|
||||
/** The gradient Slider dragging changed */
|
||||
onGradientDragging: (dragging: boolean) => void;
|
||||
|
||||
onClear?: () => void;
|
||||
}
|
||||
|
||||
export interface PanelPresetsContextProps {
|
||||
prefixCls: string;
|
||||
presets?: PresetsItem[];
|
||||
disabled?: boolean;
|
||||
value: AggregationColor;
|
||||
onChange?: (value: AggregationColor) => void;
|
||||
}
|
||||
|
||||
export const PanelPickerContext = React.createContext<PanelPickerContextProps>(
|
||||
{} as PanelPickerContextProps,
|
||||
);
|
||||
|
||||
export const PanelPresetsContext = React.createContext<PanelPresetsContextProps>(
|
||||
{} as PanelPresetsContextProps,
|
||||
);
|
||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { ColorPicker, Space } from 'antd';
|
||||
import type { ColorPickerProps, GetProp } from 'antd';
|
||||
|
||||
type Color = GetProp<ColorPickerProps, 'value'>;
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||
type Format = GetProp<ColorPickerProps, 'format'>;
|
||||
|
||||
const HexCase: React.FC = () => {
|
||||
@ -28,7 +28,7 @@ const HexCase: React.FC = () => {
|
||||
};
|
||||
|
||||
const HsbCase: React.FC = () => {
|
||||
const [colorHsb, setColorHsb] = useState<ColorPickerProps['value']>('hsb(215, 91%, 100%)');
|
||||
const [colorHsb, setColorHsb] = useState<Color>('hsb(215, 91%, 100%)');
|
||||
const [formatHsb, setFormatHsb] = useState<ColorPickerProps['format']>('hsb');
|
||||
|
||||
const hsbString = React.useMemo(
|
||||
@ -50,7 +50,7 @@ const HsbCase: React.FC = () => {
|
||||
};
|
||||
|
||||
const RgbCase: React.FC = () => {
|
||||
const [colorRgb, setColorRgb] = useState<ColorPickerProps['value']>('rgb(22, 119, 255)');
|
||||
const [colorRgb, setColorRgb] = useState<Color>('rgb(22, 119, 255)');
|
||||
const [formatRgb, setFormatRgb] = useState<ColorPickerProps['format']>('rgb');
|
||||
|
||||
const rgbString = React.useMemo(
|
||||
|
7
components/color-picker/demo/line-gradient.md
Normal file
7
components/color-picker/demo/line-gradient.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
点击添加,拖拽或者删除。
|
||||
|
||||
## en-US
|
||||
|
||||
Click to add, drag out or keyboard delete.
|
38
components/color-picker/demo/line-gradient.tsx
Normal file
38
components/color-picker/demo/line-gradient.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { ColorPicker, Space } from 'antd';
|
||||
|
||||
const DEFAULT_COLOR = [
|
||||
{
|
||||
color: 'rgb(16, 142, 233)',
|
||||
percent: 0,
|
||||
},
|
||||
{
|
||||
color: 'rgb(135, 208, 104)',
|
||||
percent: 100,
|
||||
},
|
||||
];
|
||||
|
||||
const Demo = () => (
|
||||
<Space direction="vertical">
|
||||
<ColorPicker
|
||||
defaultValue={DEFAULT_COLOR}
|
||||
allowClear
|
||||
showText
|
||||
mode={['single', 'gradient']}
|
||||
onChangeComplete={(color) => {
|
||||
console.log(color.toCssString());
|
||||
}}
|
||||
/>
|
||||
<ColorPicker
|
||||
defaultValue={DEFAULT_COLOR}
|
||||
allowClear
|
||||
showText
|
||||
mode="gradient"
|
||||
onChangeComplete={(color) => {
|
||||
console.log(color.toCssString());
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
);
|
||||
|
||||
export default Demo;
|
@ -6,7 +6,7 @@ const Demo = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<ColorPicker defaultValue="#1677ff" showText />
|
||||
<ColorPicker defaultValue="#1677ff" showText allowClear />
|
||||
<ColorPicker
|
||||
defaultValue="#1677ff"
|
||||
showText={(color) => <span>Custom Text ({color.toHexString()})</span>}
|
||||
|
@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
|
||||
import { Button, ColorPicker } from 'antd';
|
||||
import type { ColorPickerProps, GetProp } from 'antd';
|
||||
|
||||
type Color = GetProp<ColorPickerProps, 'value'>;
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [color, setColor] = useState<Color>('#1677ff');
|
||||
|
@ -1,56 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { Color } from '../color';
|
||||
import type { ColorValueType } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
const INIT_COLOR_REF = {} as ColorValueType;
|
||||
|
||||
function hasValue(value?: ColorValueType) {
|
||||
return value !== undefined;
|
||||
}
|
||||
|
||||
const useColorState = (
|
||||
defaultStateValue: ColorValueType,
|
||||
option: { defaultValue?: ColorValueType; value?: ColorValueType },
|
||||
) => {
|
||||
const { defaultValue, value } = option;
|
||||
const prevColor = useRef<Color>(generateColor(''));
|
||||
const [colorValue, _setColorValue] = useState<Color>(() => {
|
||||
let mergedState: ColorValueType | undefined;
|
||||
if (hasValue(value)) {
|
||||
mergedState = value;
|
||||
} else if (hasValue(defaultValue)) {
|
||||
mergedState = defaultValue;
|
||||
} else {
|
||||
mergedState = defaultStateValue;
|
||||
}
|
||||
const color = generateColor(mergedState || '');
|
||||
prevColor.current = color;
|
||||
return color;
|
||||
});
|
||||
|
||||
const setColorValue = (color: Color) => {
|
||||
_setColorValue(color);
|
||||
prevColor.current = color;
|
||||
};
|
||||
|
||||
const prevValue = useRef<ColorValueType | undefined>(INIT_COLOR_REF);
|
||||
useEffect(() => {
|
||||
// `useEffect` will be executed twice in strict mode even if the deps are the same
|
||||
// So we compare the value manually to avoid unnecessary update
|
||||
if (prevValue.current === value) {
|
||||
return;
|
||||
}
|
||||
prevValue.current = value;
|
||||
const newColor = generateColor(hasValue(value) ? value || '' : prevColor.current);
|
||||
if (prevColor.current.cleared === true) {
|
||||
newColor.cleared = 'controlled';
|
||||
}
|
||||
setColorValue(newColor);
|
||||
}, [value]);
|
||||
|
||||
return [colorValue, setColorValue, prevColor] as const;
|
||||
};
|
||||
|
||||
export default useColorState;
|
96
components/color-picker/hooks/useModeColor.ts
Normal file
96
components/color-picker/hooks/useModeColor.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import * as React from 'react';
|
||||
import { useEvent, useMergedState } from 'rc-util';
|
||||
|
||||
import { useLocale } from '../../locale';
|
||||
import type { AggregationColor } from '../color';
|
||||
import type { ColorPickerProps, ColorValueType, ModeType } from '../interface';
|
||||
import { generateColor } from '../util';
|
||||
|
||||
export type ModeOptions = {
|
||||
label: React.ReactNode;
|
||||
value: ModeType;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* Combine the `color` and `mode` to make sure sync of state.
|
||||
*/
|
||||
export default function useModeColor(
|
||||
defaultValue?: ColorValueType,
|
||||
value?: ColorValueType,
|
||||
mode?: ColorPickerProps['mode'],
|
||||
): [
|
||||
color: AggregationColor,
|
||||
setColor: (color: AggregationColor) => void,
|
||||
mode: ModeType,
|
||||
setMode: (mode: ModeType) => void,
|
||||
modeOptionList: ModeOptions,
|
||||
] {
|
||||
const [locale] = useLocale('ColorPicker');
|
||||
|
||||
// ======================== Base ========================
|
||||
// Color
|
||||
const [mergedColor, setMergedColor] = useMergedState(defaultValue, { value });
|
||||
|
||||
// Mode
|
||||
const [modeState, setModeState] = React.useState<ModeType>('single');
|
||||
|
||||
const [modeOptionList, modeSet] = React.useMemo(() => {
|
||||
const list = (Array.isArray(mode) ? mode : [mode]).filter((m) => m);
|
||||
if (!list.length) {
|
||||
list.push('single');
|
||||
}
|
||||
|
||||
const modes = new Set(list);
|
||||
const optionList: ModeOptions = [];
|
||||
|
||||
const pushOption = (modeType: ModeType, localeTxt: string) => {
|
||||
if (modes.has(modeType)) {
|
||||
optionList.push({
|
||||
label: localeTxt,
|
||||
value: modeType,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pushOption('single', locale.singleColor);
|
||||
pushOption('gradient', locale.gradientColor);
|
||||
|
||||
return [optionList, modes];
|
||||
}, [mode]);
|
||||
|
||||
// ======================== Post ========================
|
||||
// We need align `mode` with `color` state
|
||||
|
||||
// >>>>> Color
|
||||
const [cacheColor, setCacheColor] = React.useState<AggregationColor | null>(null);
|
||||
|
||||
const setColor = useEvent((nextColor: AggregationColor) => {
|
||||
setCacheColor(nextColor);
|
||||
setMergedColor(nextColor);
|
||||
});
|
||||
|
||||
const postColor = React.useMemo(() => {
|
||||
const colorObj = generateColor(mergedColor || '');
|
||||
|
||||
// Use `cacheColor` in case the color is `cleared`
|
||||
return colorObj.equals(cacheColor) ? cacheColor! : colorObj;
|
||||
}, [mergedColor, cacheColor]);
|
||||
|
||||
// >>>>> Mode
|
||||
const postMode = React.useMemo(() => {
|
||||
if (modeSet.has(modeState)) {
|
||||
return modeState;
|
||||
}
|
||||
|
||||
return modeOptionList[0]?.value;
|
||||
}, [modeSet, modeState, modeOptionList]);
|
||||
|
||||
// ======================= Effect =======================
|
||||
// Dynamic update mode when color change
|
||||
React.useEffect(() => {
|
||||
setModeState(postColor.isGradient() ? 'gradient' : 'single');
|
||||
}, [postColor]);
|
||||
|
||||
// ======================= Return =======================
|
||||
return [postColor, setColor, postMode, setModeState, modeOptionList];
|
||||
}
|
@ -22,6 +22,7 @@ Used when the user needs to make a customized color selection.
|
||||
<code src="./demo/size.tsx">Trigger size</code>
|
||||
<code src="./demo/controlled.tsx">controlled mode</code>
|
||||
<code src="./demo/change-completed.tsx">Color change completed</code>
|
||||
<code src="./demo/line-gradient.tsx" version="5.20.0">Line Gradient</code>
|
||||
<code src="./demo/text-render.tsx">Rendering Trigger Text</code>
|
||||
<code src="./demo/disabled.tsx">Disable</code>
|
||||
<code src="./demo/disabled-alpha.tsx">Disabled Alpha</code>
|
||||
@ -51,6 +52,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |
|
||||
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
|
||||
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` | |
|
||||
| mode | Configure single or gradient color | `('single' \| 'gradient')[]` | `single` | 5.20.0 |
|
||||
| open | Whether to show popup | boolean | - | |
|
||||
| presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color>, defaultOpen?: boolean }[]` | - | `defaultOpen: 5.11.0` |
|
||||
| placement | Placement of popup | The design of the [placement](/components/tooltip/#api) parameter is the same as the `Tooltips` component. | `bottomLeft` | |
|
||||
@ -68,8 +70,9 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
### Color
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
| Property | Description | Type | Default |
|
||||
| Property | Description | Type | Version |
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| toCssString | Convert to CSS support format | `() => string` | 5.20.0 |
|
||||
| toHex | Convert to `hex` format characters, the return type like: `1677ff` | `() => string` | - |
|
||||
| toHexString | Convert to `hex` format color string, the return type like: `#1677ff` | `() => string` | - |
|
||||
| toHsb | Convert to `hsb` object | `() => ({ h: number, s: number, b: number, a number })` | - |
|
||||
|
@ -1,6 +1,6 @@
|
||||
import ColorPicker from './ColorPicker';
|
||||
|
||||
export type { ColorPickerProps } from './interface';
|
||||
export type { Color } from './color';
|
||||
export type { AggregationColor as Color } from './color';
|
||||
|
||||
export default ColorPicker;
|
||||
|
@ -23,6 +23,7 @@ group:
|
||||
<code src="./demo/size.tsx">触发器尺寸大小</code>
|
||||
<code src="./demo/controlled.tsx">受控模式</code>
|
||||
<code src="./demo/change-completed.tsx">颜色完成选择</code>
|
||||
<code src="./demo/line-gradient.tsx" version="5.20.0">渐变色</code>
|
||||
<code src="./demo/text-render.tsx">渲染触发器文本</code>
|
||||
<code src="./demo/disabled.tsx">禁用</code>
|
||||
<code src="./demo/disabled-alpha.tsx">禁用透明度</code>
|
||||
@ -52,6 +53,7 @@ group:
|
||||
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
|
||||
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
||||
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | |
|
||||
| mode | 选择器模式,用于配置单色与渐变 | `('single' \| 'gradient')[]` | `single` | 5.20.0 |
|
||||
| open | 是否显示弹出窗口 | boolean | - | |
|
||||
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color>, defaultOpen?: boolean }[]` | - | `defaultOpen: 5.11.0` |
|
||||
| placement | 弹出窗口的位置 | 同 `Tooltips` 组件的 [placement](/components/tooltip-cn/#api) 参数设计 | `bottomLeft` | |
|
||||
@ -69,8 +71,9 @@ group:
|
||||
### Color
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| 参数 | 说明 | 类型 | 版本 |
|
||||
| :-- | :-- | :-- | :-- |
|
||||
| toCssString | 转换成 CSS 支持的格式 | `() => string` | 5.20.0 |
|
||||
| toHex | 转换成 `hex` 格式字符,返回格式如:`1677ff` | `() => string` | - |
|
||||
| toHexString | 转换成 `hex` 格式颜色字符串,返回格式如:`#1677ff` | `() => string` | - |
|
||||
| toHsb | 转换成 `hsb` 对象 | `() => ({ h: number, s: number, b: number, a number })` | - |
|
||||
|
@ -1,10 +1,20 @@
|
||||
import type { CSSProperties, FC, ReactNode } from 'react';
|
||||
import type { ColorPickerProps as RcColorPickerProps } from '@rc-component/color-picker';
|
||||
import type {
|
||||
ColorGenInput,
|
||||
ColorPickerProps as RcColorPickerProps,
|
||||
} from '@rc-component/color-picker';
|
||||
|
||||
import type { SizeType } from '../config-provider/SizeContext';
|
||||
import type { PopoverProps } from '../popover';
|
||||
import type { TooltipPlacement } from '../tooltip';
|
||||
import type { Color } from './color';
|
||||
import type { AggregationColor } from './color';
|
||||
|
||||
export type { ColorGenInput };
|
||||
|
||||
export type Colors<T> = {
|
||||
color: ColorGenInput<T>;
|
||||
percent: number;
|
||||
}[];
|
||||
|
||||
export enum ColorFormat {
|
||||
hex = 'hex',
|
||||
@ -16,7 +26,7 @@ export type ColorFormatType = keyof typeof ColorFormat;
|
||||
|
||||
export interface PresetsItem {
|
||||
label: ReactNode;
|
||||
colors: (string | Color)[];
|
||||
colors: (string | AggregationColor)[];
|
||||
/**
|
||||
* Whether the initial state is collapsed
|
||||
* @since 5.11.0
|
||||
@ -28,25 +38,29 @@ export type TriggerType = 'click' | 'hover';
|
||||
|
||||
export type TriggerPlacement = TooltipPlacement; // Alias, to prevent breaking changes.
|
||||
|
||||
export interface ColorPickerBaseProps {
|
||||
color?: Color;
|
||||
prefixCls: string;
|
||||
format?: ColorFormatType;
|
||||
allowClear?: boolean;
|
||||
disabled?: boolean;
|
||||
disabledAlpha?: boolean;
|
||||
presets?: PresetsItem[];
|
||||
panelRender?: ColorPickerProps['panelRender'];
|
||||
onFormatChange?: ColorPickerProps['onFormatChange'];
|
||||
onChangeComplete?: ColorPickerProps['onChangeComplete'];
|
||||
}
|
||||
export type SingleValueType = AggregationColor | string;
|
||||
|
||||
export type ColorValueType = Color | string | null;
|
||||
export type ColorValueType =
|
||||
| SingleValueType
|
||||
| null
|
||||
| {
|
||||
color: SingleValueType;
|
||||
percent: number;
|
||||
}[];
|
||||
|
||||
export type ModeType = 'single' | 'gradient';
|
||||
|
||||
export type ColorPickerProps = Omit<
|
||||
RcColorPickerProps,
|
||||
'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'disabledAlpha' | 'onChangeComplete'
|
||||
| 'onChange'
|
||||
| 'value'
|
||||
| 'defaultValue'
|
||||
| 'panelRender'
|
||||
| 'disabledAlpha'
|
||||
| 'onChangeComplete'
|
||||
| 'components'
|
||||
> & {
|
||||
mode?: ModeType | ModeType[];
|
||||
value?: ColorValueType;
|
||||
defaultValue?: ColorValueType;
|
||||
children?: React.ReactNode;
|
||||
@ -63,7 +77,7 @@ export type ColorPickerProps = Omit<
|
||||
panel: React.ReactNode,
|
||||
extra: { components: { Picker: FC; Presets: FC } },
|
||||
) => React.ReactNode;
|
||||
showText?: boolean | ((color: Color) => React.ReactNode);
|
||||
showText?: boolean | ((color: AggregationColor) => React.ReactNode);
|
||||
size?: SizeType;
|
||||
styles?: { popup?: CSSProperties; popupOverlayInner?: CSSProperties };
|
||||
rootClassName?: string;
|
||||
@ -71,7 +85,7 @@ export type ColorPickerProps = Omit<
|
||||
[key: `data-${string}`]: string;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onFormatChange?: (format?: ColorFormatType) => void;
|
||||
onChange?: (value: Color, hex: string) => void;
|
||||
onChange?: (value: AggregationColor, hex: string) => void;
|
||||
onClear?: () => void;
|
||||
onChangeComplete?: (value: Color) => void;
|
||||
onChangeComplete?: (value: AggregationColor) => void;
|
||||
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
|
||||
|
@ -21,11 +21,13 @@ const genColorBlockStyle = (token: ColorPickerToken, size: number): CSSObject =>
|
||||
width: size,
|
||||
height: size,
|
||||
boxShadow: colorPickerInsetShadow,
|
||||
flex: 'none',
|
||||
|
||||
...getTransBg('50%', token.colorFillSecondary),
|
||||
[`${componentCls}-color-block-inner`]: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: `${unit(lineWidth)} solid ${colorFillSecondary}`,
|
||||
boxShadow: `inset 0 0 0 ${unit(lineWidth)} ${colorFillSecondary}`,
|
||||
borderRadius: 'inherit',
|
||||
},
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ import genColorBlockStyle from './color-block';
|
||||
import genInputStyle from './input';
|
||||
import genPickerStyle from './picker';
|
||||
import genPresetsStyle from './presets';
|
||||
import genSliderStyle from './slider';
|
||||
|
||||
// biome-ignore lint/suspicious/noEmptyInterface: ComponentToken need to be empty by default
|
||||
export interface ComponentToken {}
|
||||
@ -74,12 +75,12 @@ const genClearStyle = (
|
||||
'&::after': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
insetInlineEnd: lineWidth,
|
||||
top: 0,
|
||||
insetInlineEnd: token.calc(lineWidth).mul(-1).equal(),
|
||||
top: token.calc(lineWidth).mul(-1).equal(),
|
||||
display: 'block',
|
||||
width: 40, // maximum
|
||||
height: 2, // fixed
|
||||
transformOrigin: 'right',
|
||||
transformOrigin: `calc(100% - 1px) 1px`,
|
||||
transform: 'rotate(-45deg)',
|
||||
backgroundColor: red6,
|
||||
},
|
||||
@ -138,7 +139,7 @@ const genSizeStyle = (token: ColorPickerToken): CSSObject => {
|
||||
return {
|
||||
[`&${componentCls}-lg`]: {
|
||||
minWidth: controlHeightLG,
|
||||
height: controlHeightLG,
|
||||
minHeight: controlHeightLG,
|
||||
borderRadius: borderRadiusLG,
|
||||
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
||||
width: controlHeight,
|
||||
@ -151,13 +152,17 @@ const genSizeStyle = (token: ColorPickerToken): CSSObject => {
|
||||
},
|
||||
[`&${componentCls}-sm`]: {
|
||||
minWidth: controlHeightSM,
|
||||
height: controlHeightSM,
|
||||
minHeight: controlHeightSM,
|
||||
borderRadius: borderRadiusSM,
|
||||
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
||||
width: controlHeightXS,
|
||||
height: controlHeightXS,
|
||||
borderRadius: borderRadiusXS,
|
||||
},
|
||||
|
||||
[`${componentCls}-trigger-text`]: {
|
||||
lineHeight: controlHeightXS,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -206,23 +211,30 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
[`${componentCls}-panel`]: {
|
||||
...genPickerStyle(token),
|
||||
},
|
||||
...genSliderStyle(token),
|
||||
...genColorBlockStyle(token, colorPickerPreviewSize),
|
||||
...genInputStyle(token),
|
||||
...genPresetsStyle(token),
|
||||
...genClearStyle(token, colorPickerPresetColorSize, {
|
||||
marginInlineStart: 'auto',
|
||||
marginBottom: marginXS,
|
||||
}),
|
||||
|
||||
// Operation bar
|
||||
[`${componentCls}-operation`]: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: marginXS,
|
||||
},
|
||||
},
|
||||
|
||||
'&-trigger': {
|
||||
minWidth: controlHeight,
|
||||
height: controlHeight,
|
||||
minHeight: controlHeight,
|
||||
borderRadius,
|
||||
border: `${unit(lineWidth)} solid ${colorBorder}`,
|
||||
cursor: 'pointer',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
transition: `all ${motionDurationMid}`,
|
||||
background: colorBgElevated,
|
||||
@ -235,6 +247,17 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
||||
.equal(),
|
||||
fontSize,
|
||||
color: colorText,
|
||||
alignSelf: 'center',
|
||||
|
||||
'&-cell': {
|
||||
'&:not(:last-child):after': {
|
||||
content: '", "',
|
||||
},
|
||||
|
||||
'&-inactive': {
|
||||
color: colorTextDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
'&:hover': {
|
||||
borderColor: colorPrimaryHover,
|
||||
@ -275,7 +298,7 @@ export default genStyleHooks('ColorPicker', (token) => {
|
||||
colorPickerHandlerSizeSM: 12,
|
||||
colorPickerAlphaInputWidth: 44,
|
||||
colorPickerInputNumberHandleWidth: 16,
|
||||
colorPickerPresetColorSize: 18,
|
||||
colorPickerPresetColorSize: 24,
|
||||
colorPickerInsetShadow: `inset 0 0 1px 0 ${colorTextQuaternary}`,
|
||||
colorPickerSliderHeight,
|
||||
colorPickerPreviewSize: token
|
||||
|
@ -2,7 +2,6 @@ import { unit } from '@ant-design/cssinjs';
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
|
||||
import type { GenerateStyle } from '../../theme/internal';
|
||||
import { getTransBg } from './color-block';
|
||||
import type { ColorPickerToken } from './index';
|
||||
|
||||
const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||
@ -16,11 +15,11 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||
colorFillSecondary,
|
||||
lineWidthBold,
|
||||
colorPickerHandlerSize,
|
||||
colorPickerHandlerSizeSM,
|
||||
colorPickerSliderHeight,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
userSelect: 'none',
|
||||
|
||||
[`${componentCls}-select`]: {
|
||||
[`${componentCls}-palette`]: {
|
||||
minHeight: token.calc(controlHeightLG).mul(4).equal(),
|
||||
@ -36,6 +35,7 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||
marginBottom: marginSM,
|
||||
},
|
||||
|
||||
// ======================== Panel =========================
|
||||
[`${componentCls}-handler`]: {
|
||||
width: colorPickerHandlerSize,
|
||||
height: colorPickerHandlerSize,
|
||||
@ -44,40 +44,6 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||
borderRadius: '50%',
|
||||
cursor: 'pointer',
|
||||
boxShadow: `${colorPickerInsetShadow}, 0 0 0 1px ${colorFillSecondary}`,
|
||||
'&-sm': {
|
||||
width: colorPickerHandlerSizeSM,
|
||||
height: colorPickerHandlerSizeSM,
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-slider`]: {
|
||||
borderRadius: token.calc(colorPickerSliderHeight).div(2).equal(),
|
||||
[`${componentCls}-palette`]: {
|
||||
height: colorPickerSliderHeight,
|
||||
},
|
||||
[`${componentCls}-gradient`]: {
|
||||
borderRadius: token.calc(colorPickerSliderHeight).div(2).equal(),
|
||||
boxShadow: colorPickerInsetShadow,
|
||||
},
|
||||
'&-alpha': getTransBg(`${unit(colorPickerSliderHeight)}`, token.colorFillSecondary),
|
||||
'&-hue': { marginBottom: marginSM },
|
||||
},
|
||||
|
||||
[`${componentCls}-slider-container`]: {
|
||||
display: 'flex',
|
||||
gap: marginSM,
|
||||
marginBottom: marginSM,
|
||||
[`${componentCls}-slider-group`]: {
|
||||
flex: 1,
|
||||
'&-disabled-alpha': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
[`${componentCls}-slider`]: {
|
||||
flex: 1,
|
||||
marginBottom: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
126
components/color-picker/style/slider.ts
Normal file
126
components/color-picker/style/slider.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import { unit } from '@ant-design/cssinjs';
|
||||
import type { CSSObject } from '@ant-design/cssinjs';
|
||||
|
||||
import type { GenerateStyle } from '../../theme/internal';
|
||||
import { getTransBg } from './color-block';
|
||||
import type { ColorPickerToken } from './index';
|
||||
|
||||
const genSliderStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||
const {
|
||||
componentCls,
|
||||
colorPickerInsetShadow,
|
||||
colorBgElevated,
|
||||
colorFillSecondary,
|
||||
lineWidthBold,
|
||||
colorPickerHandlerSizeSM,
|
||||
colorPickerSliderHeight,
|
||||
marginSM,
|
||||
marginXS,
|
||||
} = token;
|
||||
|
||||
const handleInnerSize = token
|
||||
.calc(colorPickerHandlerSizeSM)
|
||||
.sub(token.calc(lineWidthBold).mul(2).equal())
|
||||
.equal();
|
||||
|
||||
const handleHoverSize = token
|
||||
.calc(colorPickerHandlerSizeSM)
|
||||
.add(token.calc(lineWidthBold).mul(2).equal())
|
||||
.equal();
|
||||
|
||||
const activeHandleStyle = {
|
||||
'&:after': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: `${colorPickerInsetShadow}, 0 0 0 1px ${token.colorPrimaryActive}`,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
// ======================== Slider ========================
|
||||
[`${componentCls}-slider`]: [
|
||||
getTransBg(`${unit(colorPickerSliderHeight)}`, token.colorFillSecondary),
|
||||
|
||||
{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
height: colorPickerSliderHeight,
|
||||
borderRadius: token.calc(colorPickerSliderHeight).div(2).equal(),
|
||||
|
||||
'&-rail': {
|
||||
height: colorPickerSliderHeight,
|
||||
borderRadius: token.calc(colorPickerSliderHeight).div(2).equal(),
|
||||
boxShadow: colorPickerInsetShadow,
|
||||
},
|
||||
|
||||
[`& ${componentCls}-slider-handle`]: {
|
||||
width: handleInnerSize,
|
||||
height: handleInnerSize,
|
||||
top: 0,
|
||||
borderRadius: '100%',
|
||||
|
||||
'&:before': {
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
background: 'transparent',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: '50%',
|
||||
},
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: handleHoverSize,
|
||||
height: handleHoverSize,
|
||||
borderRadius: '100%',
|
||||
},
|
||||
|
||||
'&:after': {
|
||||
width: colorPickerHandlerSizeSM,
|
||||
height: colorPickerHandlerSizeSM,
|
||||
border: `${unit(lineWidthBold)} solid ${colorBgElevated}`,
|
||||
boxShadow: `${colorPickerInsetShadow}, 0 0 0 1px ${colorFillSecondary}`,
|
||||
outline: 'none',
|
||||
insetInlineStart: token.calc(lineWidthBold).mul(-1).equal(),
|
||||
top: token.calc(lineWidthBold).mul(-1).equal(),
|
||||
background: 'transparent',
|
||||
transition: 'none',
|
||||
},
|
||||
|
||||
'&:focus': activeHandleStyle,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// ======================== Layout ========================
|
||||
[`${componentCls}-slider-container`]: {
|
||||
display: 'flex',
|
||||
gap: marginSM,
|
||||
marginBottom: marginSM,
|
||||
|
||||
// Group
|
||||
[`${componentCls}-slider-group`]: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
display: 'flex',
|
||||
|
||||
'&-disabled-alpha': {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-gradient-slider`]: {
|
||||
marginBottom: marginXS,
|
||||
|
||||
[`& ${componentCls}-slider-handle`]: {
|
||||
'&:after': {
|
||||
transform: 'scale(0.8)',
|
||||
},
|
||||
|
||||
'&-active, &:focus': activeHandleStyle,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default genSliderStyle;
|
@ -1,21 +1,69 @@
|
||||
import type { ColorGenInput } from '@rc-component/color-picker';
|
||||
import { Color as RcColor } from '@rc-component/color-picker';
|
||||
|
||||
import type { Color } from './color';
|
||||
import { ColorFactory } from './color';
|
||||
import { AggregationColor } from './color';
|
||||
import type { ColorValueType } from './interface';
|
||||
|
||||
export const generateColor = (color: ColorGenInput<Color>): Color => {
|
||||
if (color instanceof ColorFactory) {
|
||||
export const generateColor = (
|
||||
color: ColorGenInput<AggregationColor> | Exclude<ColorValueType, null>,
|
||||
): AggregationColor => {
|
||||
if (color instanceof AggregationColor) {
|
||||
return color;
|
||||
}
|
||||
return new ColorFactory(color);
|
||||
return new AggregationColor(color);
|
||||
};
|
||||
|
||||
export const getRoundNumber = (value: number) => Math.round(Number(value || 0));
|
||||
|
||||
export const getAlphaColor = (color: Color) => getRoundNumber(color.toHsb().a * 100);
|
||||
export const getColorAlpha = (color: AggregationColor) => getRoundNumber(color.toHsb().a * 100);
|
||||
|
||||
export const genAlphaColor = (color: Color, alpha?: number) => {
|
||||
/** Return the color whose `alpha` is 1 */
|
||||
export const genAlphaColor = (color: AggregationColor, alpha?: number) => {
|
||||
const hsba = color.toHsb();
|
||||
hsba.a = alpha || 1;
|
||||
return generateColor(hsba);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get percent position color. e.g. [10%-#fff, 20%-#000], 15% => #888
|
||||
*/
|
||||
export const getGradientPercentColor = (
|
||||
colors: { percent: number; color: string }[],
|
||||
percent: number,
|
||||
): string => {
|
||||
const filledColors = [
|
||||
{
|
||||
percent: 0,
|
||||
color: colors[0].color,
|
||||
},
|
||||
...colors,
|
||||
{
|
||||
percent: 100,
|
||||
color: colors[colors.length - 1].color,
|
||||
},
|
||||
];
|
||||
|
||||
for (let i = 0; i < filledColors.length - 1; i += 1) {
|
||||
const startPtg = filledColors[i].percent;
|
||||
const endPtg = filledColors[i + 1].percent;
|
||||
const startColor = filledColors[i].color;
|
||||
const endColor = filledColors[i + 1].color;
|
||||
|
||||
if (startPtg <= percent && percent <= endPtg) {
|
||||
const dist = endPtg - startPtg;
|
||||
if (dist === 0) {
|
||||
return startColor;
|
||||
}
|
||||
|
||||
const ratio = ((percent - startPtg) / dist) * 100;
|
||||
const startRcColor = new RcColor(startColor);
|
||||
const endRcColor = new RcColor(endColor);
|
||||
|
||||
return startRcColor.mix(endRcColor, ratio).toRgbString();
|
||||
}
|
||||
}
|
||||
|
||||
// This will never reach
|
||||
/* istanbul ignore next */
|
||||
return '';
|
||||
};
|
||||
|
@ -0,0 +1,438 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renderEmpty should render Cascader empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render List empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render Mentions empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render Select empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render Table empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render Table.filter empty 1`] = `null`;
|
||||
|
||||
exports[`renderEmpty should render Transfer empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should render TreeSelect empty 1`] = `
|
||||
<div
|
||||
class="ant-empty ant-empty-normal ant-empty-small"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="41"
|
||||
viewBox="0 0 64 41"
|
||||
width="64"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
Simple Empty
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
transform="translate(0 1)"
|
||||
>
|
||||
<ellipse
|
||||
cx="32"
|
||||
cy="33"
|
||||
fill="#f5f5f5"
|
||||
rx="32"
|
||||
ry="7"
|
||||
/>
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
stroke="#d9d9d9"
|
||||
>
|
||||
<path
|
||||
d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"
|
||||
/>
|
||||
<path
|
||||
d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
|
||||
fill="#fafafa"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renderEmpty should return empty when componentName is not matched 1`] = `
|
||||
<div
|
||||
class="ant-empty"
|
||||
>
|
||||
<div
|
||||
class="ant-empty-image"
|
||||
>
|
||||
<svg
|
||||
height="152"
|
||||
viewBox="0 0 184 152"
|
||||
width="184"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>
|
||||
empty image
|
||||
</title>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
>
|
||||
<g
|
||||
transform="translate(24 31.67)"
|
||||
>
|
||||
<ellipse
|
||||
cx="67.797"
|
||||
cy="106.89"
|
||||
fill="#F5F5F7"
|
||||
fill-opacity=".8"
|
||||
rx="67.797"
|
||||
ry="12.668"
|
||||
/>
|
||||
<path
|
||||
d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
|
||||
fill="#AEB8C2"
|
||||
/>
|
||||
<path
|
||||
d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
|
||||
fill="url(#linearGradient-1)"
|
||||
transform="translate(13.56)"
|
||||
/>
|
||||
<path
|
||||
d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
|
||||
fill="#F5F5F7"
|
||||
/>
|
||||
<path
|
||||
d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
|
||||
fill="#DCE0E6"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
|
||||
fill="#DCE0E6"
|
||||
/>
|
||||
<g
|
||||
fill="#FFF"
|
||||
transform="translate(149.65 15.383)"
|
||||
>
|
||||
<ellipse
|
||||
cx="20.654"
|
||||
cy="3.167"
|
||||
rx="2.849"
|
||||
ry="2.815"
|
||||
/>
|
||||
<path
|
||||
d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="ant-empty-description"
|
||||
>
|
||||
No data
|
||||
</div>
|
||||
</div>
|
||||
`;
|
30
components/config-provider/__tests__/renderEmpty.test.tsx
Normal file
30
components/config-provider/__tests__/renderEmpty.test.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import RenderEmpty from '../defaultRenderEmpty';
|
||||
import { render } from '../../../tests/utils';
|
||||
|
||||
describe('renderEmpty', () => {
|
||||
it.each([
|
||||
'Table',
|
||||
'Table.filter' /* 👈 5.19.0+ */,
|
||||
'List',
|
||||
'Select',
|
||||
'TreeSelect',
|
||||
'Cascader',
|
||||
'Transfer',
|
||||
'Mentions',
|
||||
])('should render %s empty', (componentName: any) => {
|
||||
const { container } = render(<RenderEmpty componentName={componentName} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/pull/49613#issuecomment-2198857047
|
||||
it('should return false when componentName is `Table.filter`', () => {
|
||||
const { container } = render(<RenderEmpty componentName="Table.filter" />);
|
||||
expect(container.firstChild).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should return empty when componentName is not matched', () => {
|
||||
const { container } = render(<RenderEmpty componentName={`not_match` as any} />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -24,6 +24,7 @@ import type { ArgsProps } from '../notification/interface';
|
||||
import type { PaginationProps } from '../pagination';
|
||||
import type { SelectProps } from '../select';
|
||||
import type { SpaceProps } from '../space';
|
||||
import type { SpinProps } from '../spin';
|
||||
import type { TableProps } from '../table';
|
||||
import type { TabsProps } from '../tabs';
|
||||
import type { TagProps } from '../tag';
|
||||
@ -175,6 +176,8 @@ export type SelectConfig = ComponentStyleConfig & Pick<SelectProps, 'showSearch'
|
||||
|
||||
export type SpaceConfig = ComponentStyleConfig & Pick<SpaceProps, 'size' | 'classNames' | 'styles'>;
|
||||
|
||||
export type SpinConfig = ComponentStyleConfig & Pick<SpinProps, 'indicator'>;
|
||||
|
||||
export type InputNumberConfig = ComponentStyleConfig & Pick<InputNumberProps, 'variant'>;
|
||||
|
||||
export type CascaderConfig = ComponentStyleConfig & Pick<CascaderProps, 'variant'>;
|
||||
@ -254,7 +257,7 @@ export interface ConfigConsumerProps {
|
||||
floatButtonGroup?: FloatButtonGroupConfig;
|
||||
typography?: ComponentStyleConfig;
|
||||
skeleton?: ComponentStyleConfig;
|
||||
spin?: ComponentStyleConfig;
|
||||
spin?: SpinConfig;
|
||||
segmented?: ComponentStyleConfig;
|
||||
steps?: ComponentStyleConfig;
|
||||
statistic?: ComponentStyleConfig;
|
||||
|
@ -6,6 +6,7 @@ import Empty from '../empty';
|
||||
|
||||
type ComponentName =
|
||||
| 'Table'
|
||||
| 'Table.filter' /* 👈 5.20.0+ */
|
||||
| 'List'
|
||||
| 'Select'
|
||||
| 'TreeSelect'
|
||||
@ -31,7 +32,14 @@ const DefaultRenderEmpty: React.FC<EmptyProps> = (props) => {
|
||||
case 'Transfer':
|
||||
case 'Mentions':
|
||||
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} className={`${prefix}-small`} />;
|
||||
/* istanbul ignore next */
|
||||
/**
|
||||
* This type of component should satisfy the nullish coalescing operator(??) on the left-hand side.
|
||||
* to let the component itself implement the logic.
|
||||
* For example `Table.filter`.
|
||||
*/
|
||||
case 'Table.filter':
|
||||
// why `null`? legacy react16 node type `undefined` is not allowed.
|
||||
return null;
|
||||
default:
|
||||
// Should never hit if we take all the component into consider.
|
||||
return <Empty />;
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
} from 'antd';
|
||||
import type { ColorPickerProps, GetProp } from 'antd';
|
||||
|
||||
type Color = Exclude<GetProp<ColorPickerProps, 'value'>, string>;
|
||||
type Color = Extract<GetProp<ColorPickerProps, 'value'>, { cleared: any }>;
|
||||
|
||||
type ThemeData = {
|
||||
borderRadius: number;
|
||||
|
@ -152,7 +152,7 @@ const {
|
||||
| slider | Set Slider common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| switch | Set Switch common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| spin | Set Spin common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| spin | Set Spin common props | { className?: string, style?: React.CSSProperties, indicator?: React.ReactElement } | - | 5.7.0, indicator: 5.20.0 |
|
||||
| statistic | Set Statistic common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| steps | Set Steps common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| table | Set Table common props | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode } } | - | 5.7.0, expandable: 5.14.0 |
|
||||
|
@ -43,6 +43,7 @@ import type {
|
||||
RangePickerConfig,
|
||||
SelectConfig,
|
||||
SpaceConfig,
|
||||
SpinConfig,
|
||||
TableConfig,
|
||||
TabsConfig,
|
||||
TagConfig,
|
||||
@ -189,7 +190,7 @@ export interface ConfigProviderProps {
|
||||
drawer?: DrawerConfig;
|
||||
typography?: ComponentStyleConfig;
|
||||
skeleton?: ComponentStyleConfig;
|
||||
spin?: ComponentStyleConfig;
|
||||
spin?: SpinConfig;
|
||||
segmented?: ComponentStyleConfig;
|
||||
statistic?: ComponentStyleConfig;
|
||||
steps?: ComponentStyleConfig;
|
||||
|
@ -154,7 +154,7 @@ const {
|
||||
| slider | 设置 Slider 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| switch | 设置 Switch 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: { item: string }, styles?: { item: React.CSSProperties } } | - | 5.6.0 |
|
||||
| spin | 设置 Spin 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| spin | 设置 Spin 组件的通用属性 | { className?: string, style?: React.CSSProperties, indicator?: React.ReactElement } | - | 5.7.0, indicator: 5.20.0 |
|
||||
| statistic | 设置 Statistic 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| steps | 设置 Steps 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
|
||||
| table | 设置 Table 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandable?: { expandIcon?: props => React.ReactNode } } | - | 5.7.0, expandable: 5.14.0 |
|
||||
|
@ -203,6 +203,58 @@ Array [
|
||||
|
||||
exports[`renders components/divider/demo/plain.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/divider/demo/variant.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
|
||||
role="separator"
|
||||
style="border-color: #7cb305;"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Solid
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-dotted"
|
||||
role="separator"
|
||||
style="border-color: #7cb305;"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Dotted
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-dashed"
|
||||
role="separator"
|
||||
style="border-color: #7cb305;"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Dashed
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/variant.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/divider/demo/vertical.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
Text,
|
||||
|
@ -195,6 +195,56 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/variant.tsx correctly 1`] = `
|
||||
Array [
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center"
|
||||
role="separator"
|
||||
style="border-color:#7cb305"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Solid
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-dotted"
|
||||
role="separator"
|
||||
style="border-color:#7cb305"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Dotted
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
<div
|
||||
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-dashed"
|
||||
role="separator"
|
||||
style="border-color:#7cb305"
|
||||
>
|
||||
<span
|
||||
class="ant-divider-inner-text"
|
||||
>
|
||||
Dashed
|
||||
</span>
|
||||
</div>,
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/divider/demo/vertical.tsx correctly 1`] = `
|
||||
Array [
|
||||
Text,
|
||||
|
@ -37,4 +37,15 @@ describe('Divider', () => {
|
||||
borderStyle: 'dashed',
|
||||
});
|
||||
});
|
||||
|
||||
it('support string variant', () => {
|
||||
const { container } = render(
|
||||
<Divider variant="dotted">
|
||||
test dotted
|
||||
</Divider>,
|
||||
);
|
||||
expect(container?.querySelector<HTMLSpanElement>('.ant-divider-dotted')).toHaveStyle({
|
||||
borderStyle: 'dotted',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
7
components/divider/demo/variant.md
Normal file
7
components/divider/demo/variant.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
分隔线默认为 `solid`(实线)变体。您可以将其更改为 `dashed`(虚线)或 `dotted`(点线)。
|
||||
|
||||
## en-US
|
||||
|
||||
Divider is of `solid` variant by default. You can change that to either `dashed` or `dotted`.
|
28
components/divider/demo/variant.tsx
Normal file
28
components/divider/demo/variant.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Divider } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider style={{ borderColor: '#7cb305' }}>Solid</Divider>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider variant="dotted" style={{ borderColor: '#7cb305' }}>Dotted</Divider>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
<Divider variant="dashed" style={{ borderColor: '#7cb305' }} dashed >Dashed</Divider>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
|
||||
probare, quae sunt a te dicta? Refert tamen, quo modo.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -25,6 +25,7 @@ group:
|
||||
<code src="./demo/vertical.tsx">Vertical</code>
|
||||
<code src="./demo/customize-style.tsx" debug>Style Customization</code>
|
||||
<code src="./demo/component-token.tsx" debug>Component Token</code>
|
||||
<code src="./demo/variant.tsx">Variant</code>
|
||||
|
||||
## API
|
||||
|
||||
@ -35,6 +36,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| children | The wrapped title | ReactNode | - | |
|
||||
| className | The className of container | string | - | |
|
||||
| dashed | Whether line is dashed | boolean | false | |
|
||||
| variant | Whether line is dashed, dotted or solid | `dashed` \| `dotted` \| `solid` | solid | |
|
||||
| orientation | The position of title inside divider | `left` \| `right` \| `center` | `center` | |
|
||||
| orientationMargin | The margin-left/right between the title and its closest border, while the `orientation` must be `left` or `right`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \| number | - | |
|
||||
| plain | Divider text show as plain style | boolean | true | 4.2.0 |
|
||||
|
@ -14,6 +14,7 @@ export interface DividerProps {
|
||||
rootClassName?: string;
|
||||
children?: React.ReactNode;
|
||||
dashed?: boolean;
|
||||
variant?: 'dashed' | 'dotted' | 'solid'
|
||||
style?: React.CSSProperties;
|
||||
plain?: boolean;
|
||||
}
|
||||
@ -30,6 +31,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
rootClassName,
|
||||
children,
|
||||
dashed,
|
||||
variant = 'solid',
|
||||
plain,
|
||||
style,
|
||||
...restProps
|
||||
@ -51,6 +53,7 @@ const Divider: React.FC<DividerProps> = (props) => {
|
||||
[`${prefixCls}-with-text`]: hasChildren,
|
||||
[`${prefixCls}-with-text-${orientation}`]: hasChildren,
|
||||
[`${prefixCls}-dashed`]: !!dashed,
|
||||
[`${prefixCls}-${variant}`]: variant !== 'solid',
|
||||
[`${prefixCls}-plain`]: !!plain,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-no-default-orientation-margin-left`]: hasCustomMarginLeft,
|
||||
|
@ -26,6 +26,7 @@ group:
|
||||
<code src="./demo/vertical.tsx">垂直分割线</code>
|
||||
<code src="./demo/customize-style.tsx" debug>样式自定义</code>
|
||||
<code src="./demo/component-token.tsx" debug>组件 Token</code>
|
||||
<code src="./demo/variant.tsx">变体</code>
|
||||
|
||||
## API
|
||||
|
||||
@ -36,6 +37,7 @@ group:
|
||||
| children | 嵌套的标题 | ReactNode | - | |
|
||||
| className | 分割线样式类 | string | - | |
|
||||
| dashed | 是否虚线 | boolean | false | |
|
||||
| variant | 分割线是虚线、点线还是实线 | `dashed` \| `dotted` \| `solid` | solid | |
|
||||
| orientation | 分割线标题的位置 | `left` \| `right` \| `center` | `center` | |
|
||||
| orientationMargin | 标题和最近 left/right 边框之间的距离,去除了分割线,同时 `orientation` 必须为 `left` 或 `right`。如果传入 `string` 类型的数字且不带单位,默认单位是 px | string \| number | - | |
|
||||
| plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 |
|
||||
|
@ -136,6 +136,26 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
|
||||
borderBlockEnd: 0,
|
||||
},
|
||||
|
||||
'&-dotted': {
|
||||
background: 'none',
|
||||
borderColor: colorSplit,
|
||||
borderStyle: 'dotted',
|
||||
borderWidth: `${unit(lineWidth)} 0 0`,
|
||||
},
|
||||
|
||||
[`&-horizontal${componentCls}-with-text${componentCls}-dotted`]: {
|
||||
'&::before, &::after': {
|
||||
borderStyle: 'dotted none none',
|
||||
},
|
||||
},
|
||||
|
||||
[`&-vertical${componentCls}-dotted`]: {
|
||||
borderInlineStartWidth: lineWidth,
|
||||
borderInlineEnd: 0,
|
||||
borderBlockStart: 0,
|
||||
borderBlockEnd: 0,
|
||||
},
|
||||
|
||||
[`&-plain${componentCls}-with-text`]: {
|
||||
color: token.colorText,
|
||||
fontWeight: 'normal',
|
||||
|
@ -11,7 +11,7 @@ interface EmptyToken extends FullToken<'Empty'> {
|
||||
emptyImgCls: string;
|
||||
emptyImgHeight: number | string;
|
||||
emptyImgHeightSM: number | string;
|
||||
emptyImgHeightMD: number;
|
||||
emptyImgHeightMD: number | string;
|
||||
}
|
||||
|
||||
// ============================== Shared ==============================
|
||||
|
@ -5309,7 +5309,7 @@ Array [
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
style="position: absolute; left: 0%; top: 100%; z-index: 1; transform: translate(-50%, -50%);"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler"
|
||||
@ -5329,46 +5329,46 @@ Array [
|
||||
class="ant-color-picker-slider-group"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
>
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="359"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgb(255, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgba(0, 0, 0, 0);"
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgba(0, 0, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -22742,7 +22742,7 @@ exports[`renders components/form/demo/validate-other.tsx extend context correctl
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
style="position: absolute; left: 0%; top: 100%; z-index: 1; transform: translate(-50%, -50%);"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler"
|
||||
@ -22762,46 +22762,46 @@ exports[`renders components/form/demo/validate-other.tsx extend context correctl
|
||||
class="ant-color-picker-slider-group"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
>
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgb(255, 0, 0);"
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="359"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgb(255, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
||||
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-palette"
|
||||
style="position: relative;"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
||||
>
|
||||
<div
|
||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
||||
style="background-color: rgba(0, 0, 0, 0);"
|
||||
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-color-picker-gradient"
|
||||
style="position: absolute; inset: 0;"
|
||||
class="ant-slider-step"
|
||||
/>
|
||||
<div
|
||||
aria-disabled="false"
|
||||
aria-orientation="horizontal"
|
||||
aria-valuemax="100"
|
||||
aria-valuemin="0"
|
||||
aria-valuenow="0"
|
||||
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||
role="slider"
|
||||
style="left: 0%; transform: translateX(-50%); background: rgba(0, 0, 0, 0);"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -34,7 +34,7 @@ export interface ComponentToken {
|
||||
* @desc 标签高度
|
||||
* @descEN Label height
|
||||
*/
|
||||
labelHeight: number;
|
||||
labelHeight: number | string;
|
||||
/**
|
||||
* @desc 标签冒号前间距
|
||||
* @descEN Label colon margin-inline-start
|
||||
|
@ -3203,7 +3203,7 @@ exports[`renders components/input-number/demo/out-of-range.tsx extend context co
|
||||
|
||||
exports[`renders components/input-number/demo/out-of-range.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/input-number/demo/prefix.tsx extend context correctly 1`] = `
|
||||
exports[`renders components/input-number/demo/presuffix.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-outlined"
|
||||
@ -3490,10 +3490,95 @@ Array [
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-outlined"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-input-number-suffix"
|
||||
>
|
||||
RMB
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders components/input-number/demo/prefix.tsx extend context correctly 2`] = `[]`;
|
||||
exports[`renders components/input-number/demo/presuffix.tsx extend context correctly 2`] = `[]`;
|
||||
|
||||
exports[`renders components/input-number/demo/render-panel.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
|
@ -2911,7 +2911,7 @@ exports[`renders components/input-number/demo/out-of-range.tsx correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders components/input-number/demo/prefix.tsx correctly 1`] = `
|
||||
exports[`renders components/input-number/demo/presuffix.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-outlined"
|
||||
@ -3198,6 +3198,91 @@ Array [
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<br />,
|
||||
<br />,
|
||||
<div
|
||||
class="ant-input-number-affix-wrapper ant-input-number-outlined"
|
||||
style="width:100%"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number"
|
||||
>
|
||||
<div
|
||||
class="ant-input-number-handler-wrap"
|
||||
>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Increase Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-up"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="up"
|
||||
class="anticon anticon-up ant-input-number-handler-up-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="up"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-disabled="false"
|
||||
aria-label="Decrease Value"
|
||||
class="ant-input-number-handler ant-input-number-handler-down"
|
||||
role="button"
|
||||
unselectable="on"
|
||||
>
|
||||
<span
|
||||
aria-label="down"
|
||||
class="anticon anticon-down ant-input-number-handler-down-inner"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="down"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="ant-input-number-input-wrap"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="ant-input-number-input"
|
||||
role="spinbutton"
|
||||
step="1"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="ant-input-number-suffix"
|
||||
>
|
||||
RMB
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
|
19
components/input-number/__tests__/suffix.test.tsx
Normal file
19
components/input-number/__tests__/suffix.test.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
import InputNumber from '..';
|
||||
import { fireEvent, render } from '../../../tests/utils';
|
||||
|
||||
describe('suffix', () => {
|
||||
it('should support suffix prop', () => {
|
||||
const { container } = render(<InputNumber suffix={<i>hello</i>} />);
|
||||
expect(container.querySelector('.ant-input-number-suffix')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should trigger focus when suffix is clicked', () => {
|
||||
const { container } = render(<InputNumber suffix={<i>antd</i>} />);
|
||||
|
||||
const mockFocus = jest.spyOn(container.querySelector('input')!, 'focus');
|
||||
fireEvent.click(container.querySelector('i')!);
|
||||
expect(mockFocus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
## zh-CN
|
||||
|
||||
在输入框上添加前缀图标。
|
||||
|
||||
## en-US
|
||||
|
||||
Add a prefix inside input.
|
7
components/input-number/demo/presuffix.md
Normal file
7
components/input-number/demo/presuffix.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
在输入框上添加前缀或后缀图标。
|
||||
|
||||
## en-US
|
||||
|
||||
Add a prefix or suffix inside input.
|
@ -11,6 +11,9 @@ const App: React.FC = () => (
|
||||
<br />
|
||||
<br />
|
||||
<InputNumber prefix="¥" disabled style={{ width: '100%' }} />
|
||||
<br />
|
||||
<br />
|
||||
<InputNumber suffix="RMB" style={{ width: '100%' }} />
|
||||
</>
|
||||
);
|
||||
|
@ -27,7 +27,7 @@ When a numeric value needs to be provided.
|
||||
<code src="./demo/variant.tsx" version="5.13.0">Variants</code>
|
||||
<code src="./demo/filled-debug.tsx" debug>Filled Debug</code>
|
||||
<code src="./demo/out-of-range.tsx">Out of range</code>
|
||||
<code src="./demo/prefix.tsx">Prefix</code>
|
||||
<code src="./demo/presuffix.tsx">Prefix / Suffix</code>
|
||||
<code src="./demo/status.tsx">Status</code>
|
||||
<code src="./demo/controls.tsx" debug>Icon</code>
|
||||
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||
@ -58,6 +58,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| readOnly | If readonly the input | boolean | false | - |
|
||||
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 |
|
||||
| suffix | The suffix icon for the Input | ReactNode | - | 5.20.0 |
|
||||
| size | The height of input box | `large` \| `middle` \| `small` | - | - |
|
||||
| step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \| string | 1 | - |
|
||||
| stringMode | Set value as string to support high precision decimals. Will return string value by `onChange` | boolean | false | 4.13.0 |
|
||||
|
@ -27,6 +27,7 @@ export interface InputNumberProps<T extends ValueType = ValueType>
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
prefix?: React.ReactNode;
|
||||
suffix?: React.ReactNode;
|
||||
size?: SizeType;
|
||||
disabled?: boolean;
|
||||
/** @deprecated Use `variant` instead. */
|
||||
@ -66,6 +67,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
addonBefore,
|
||||
addonAfter,
|
||||
prefix,
|
||||
suffix,
|
||||
bordered,
|
||||
readOnly,
|
||||
status: customStatus,
|
||||
@ -141,7 +143,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
|
||||
readOnly={readOnly}
|
||||
controls={controlsTemp}
|
||||
prefix={prefix}
|
||||
suffix={suffixNode}
|
||||
suffix={suffixNode || suffix}
|
||||
addonBefore={
|
||||
addonBefore && (
|
||||
<ContextIsolator form space>
|
||||
|
@ -28,7 +28,7 @@ demo:
|
||||
<code src="./demo/variant.tsx" version="5.13.0">形态变体</code>
|
||||
<code src="./demo/filled-debug.tsx" debug>Filled Debug</code>
|
||||
<code src="./demo/out-of-range.tsx">超出边界</code>
|
||||
<code src="./demo/prefix.tsx">前缀</code>
|
||||
<code src="./demo/presuffix.tsx">前缀/后缀</code>
|
||||
<code src="./demo/status.tsx">自定义状态</code>
|
||||
<code src="./demo/controls.tsx" debug>图标按钮</code>
|
||||
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||
@ -59,6 +59,7 @@ demo:
|
||||
| readOnly | 只读 | boolean | false | - |
|
||||
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
|
||||
| prefix | 带有前缀图标的 input | ReactNode | - | 4.17.0 |
|
||||
| suffix | 带有后缀图标的 input | ReactNode | - | 5.20.0 |
|
||||
| size | 输入框大小 | `large` \| `middle` \| `small` | - | - |
|
||||
| step | 每次改变步数,可以为小数 | number \| string | 1 | - |
|
||||
| stringMode | 字符值模式,开启后支持高精度小数。同时 `onChange` 将返回 string 类型 | boolean | false | 4.13.0 |
|
||||
|
@ -61,6 +61,7 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
|
||||
colorTextDescription,
|
||||
motionDurationMid,
|
||||
handleHoverColor,
|
||||
handleOpacity,
|
||||
paddingInline,
|
||||
paddingBlock,
|
||||
handleBg,
|
||||
@ -69,7 +70,6 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
|
||||
borderRadiusSM,
|
||||
borderRadiusLG,
|
||||
controlWidth,
|
||||
handleOpacity,
|
||||
handleBorderColor,
|
||||
filledHandleBg,
|
||||
lineHeightLG,
|
||||
@ -236,31 +236,32 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
|
||||
},
|
||||
},
|
||||
},
|
||||
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
|
||||
width: token.handleWidth,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Handler
|
||||
{
|
||||
[componentCls]: {
|
||||
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
[`${componentCls}-handler-wrap`]: {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineEnd: 0,
|
||||
width: token.handleWidth,
|
||||
width: 0,
|
||||
opacity: handleOpacity,
|
||||
height: '100%',
|
||||
borderStartStartRadius: 0,
|
||||
borderStartEndRadius: borderRadius,
|
||||
borderEndEndRadius: borderRadius,
|
||||
borderEndStartRadius: 0,
|
||||
opacity: handleOpacity,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'stretch',
|
||||
transition: `opacity ${motionDurationMid} linear ${motionDurationMid}`,
|
||||
transition: `all ${motionDurationMid}`,
|
||||
overflow: 'hidden',
|
||||
|
||||
// Fix input number inside Menu makes icon too large
|
||||
// We arise the selector priority by nest selector here
|
||||
@ -370,6 +371,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
|
||||
paddingInlineSM,
|
||||
paddingBlockLG,
|
||||
paddingBlockSM,
|
||||
motionDurationMid,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
@ -382,6 +384,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
|
||||
// or number handler will cover form status
|
||||
position: 'relative',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
width: controlWidth,
|
||||
padding: 0,
|
||||
paddingInlineStart: paddingInline,
|
||||
@ -438,6 +441,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
|
||||
},
|
||||
|
||||
[componentCls]: {
|
||||
position: 'static',
|
||||
color: 'inherit',
|
||||
|
||||
'&-prefix, &-suffix': {
|
||||
@ -452,15 +456,22 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
|
||||
},
|
||||
|
||||
'&-suffix': {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineEnd: 0,
|
||||
zIndex: 1,
|
||||
height: '100%',
|
||||
marginInlineEnd: paddingInline,
|
||||
marginInlineStart: inputAffixPadding,
|
||||
transition: `margin ${motionDurationMid}`,
|
||||
},
|
||||
},
|
||||
|
||||
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
|
||||
width: token.handleWidth,
|
||||
opacity: 1,
|
||||
},
|
||||
[`&:hover ${componentCls}-suffix`]: {
|
||||
marginInlineEnd: token.calc(token.handleWidth).add(paddingInline).equal(),
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -124,10 +124,12 @@ describe('Input.Search', () => {
|
||||
const { container } = render(<Search defaultValue="search text" onSearch={onSearch} />);
|
||||
fireEvent.compositionStart(container.querySelector('input')!);
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.keyUp(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.compositionEnd(container.querySelector('input')!);
|
||||
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
fireEvent.keyUp(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith('search text', expect.anything(), { source: 'input' });
|
||||
});
|
||||
|
@ -71,6 +71,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| variant | Variants of Input | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| onChange | Callback when user input | function(e) | - | |
|
||||
| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | |
|
||||
| onClear | Callback when click the clear button | () => void | - | 5.20.0 |
|
||||
|
||||
> When `Input` is used in a `Form.Item` context, if the `Form.Item` has the `id` props defined then `value`, `defaultValue`, and `id` props of `Input` are automatically set.
|
||||
|
||||
|
@ -72,6 +72,7 @@ demo:
|
||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| onChange | 输入框内容变化时的回调 | function(e) | - | |
|
||||
| onPressEnter | 按下回车的回调 | function(e) | - | |
|
||||
| onClear | 按下清除按钮的回调 | () => void | - | 5.20.0 |
|
||||
|
||||
> 如果 `Input` 在 `Form.Item` 内,并且 `Form.Item` 设置了 `id` 属性,则 `value` `defaultValue` 和 `id` 属性会被自动设置。
|
||||
|
||||
|
@ -58,7 +58,7 @@ export interface ComponentToken {
|
||||
* @desc 侧边栏开关高度
|
||||
* @descEN Height of sider trigger
|
||||
*/
|
||||
triggerHeight: number;
|
||||
triggerHeight: number | string;
|
||||
/**
|
||||
* @desc 侧边栏开关背景色
|
||||
* @descEN Background Color of sider trigger
|
||||
|
@ -11,7 +11,7 @@ export interface ComponentToken {
|
||||
* @desc 内容宽度
|
||||
* @descEN Width of content
|
||||
*/
|
||||
contentWidth: number;
|
||||
contentWidth: number | string;
|
||||
/**
|
||||
* @desc 大号列表项内间距
|
||||
* @descEN Padding of large item
|
||||
@ -66,7 +66,7 @@ export interface ComponentToken {
|
||||
|
||||
interface ListToken extends FullToken<'List'> {
|
||||
listBorderedCls: string;
|
||||
minHeight: number;
|
||||
minHeight: number | string;
|
||||
}
|
||||
|
||||
const genBorderedStyle = (token: ListToken): CSSObject => {
|
||||
|
@ -144,6 +144,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Boşdur',
|
||||
transparent: 'Şəffaf',
|
||||
singleColor: 'Tək rəng',
|
||||
gradientColor: 'Gradient rəng',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Empty',
|
||||
transparent: 'Transparent',
|
||||
singleColor: 'Single',
|
||||
gradientColor: 'Gradient',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Hustu',
|
||||
transparent: 'Gardena',
|
||||
singleColor: 'Kolore bakarra',
|
||||
gradientColor: 'Gradiente kolorea',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Kosong',
|
||||
transparent: 'Transparan',
|
||||
singleColor: 'Warna tunggal',
|
||||
gradientColor: 'Warna gradien',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -56,6 +56,9 @@ export interface Locale {
|
||||
};
|
||||
ColorPicker?: {
|
||||
presetEmpty: string;
|
||||
transparent: string;
|
||||
singleColor: string;
|
||||
gradientColor: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: '空の',
|
||||
transparent: '透明',
|
||||
singleColor: '単色',
|
||||
gradientColor: 'グラデーション',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: '미정',
|
||||
transparent: '투명',
|
||||
singleColor: '단색',
|
||||
gradientColor: '그라데이션',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Tuščia',
|
||||
transparent: 'Permatomas',
|
||||
singleColor: 'Vieno spalvos',
|
||||
gradientColor: 'Gradientas',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -141,6 +141,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'Tiada',
|
||||
transparent: 'Tidak tembus cahaya',
|
||||
singleColor: 'Warna tunggal',
|
||||
gradientColor: 'Warna gradien',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -142,6 +142,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'अहिलेसम्म कुनै पनि छैन',
|
||||
transparent: 'पारदर्शी',
|
||||
singleColor: 'एक रंग',
|
||||
gradientColor: 'ग्रेडिएण्ट',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: 'ไม่มีข้อมูล',
|
||||
transparent: 'โปร่งใส',
|
||||
singleColor: 'สีเดียว',
|
||||
gradientColor: 'สีไล่ระดับ',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -144,6 +144,9 @@ const localeValues: Locale = {
|
||||
},
|
||||
ColorPicker: {
|
||||
presetEmpty: '暂无',
|
||||
transparent: '无色',
|
||||
singleColor: '单色',
|
||||
gradientColor: '渐变色',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -74,6 +74,7 @@ Common props ref:[Common props](/docs/react/common-props)
|
||||
| variant | Variants of Input | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| onBlur | Trigger when mentions lose focus | () => void | - | |
|
||||
| onChange | Trigger when value changed | (text: string) => void | - | |
|
||||
| onClear | Callback when click the clear button | () => void | - | 5.20.0 |
|
||||
| onFocus | Trigger when mentions get focus | () => void | - | |
|
||||
| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - | |
|
||||
| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - | |
|
||||
|
@ -75,6 +75,7 @@ return (
|
||||
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
|
||||
| onBlur | 失去焦点时触发 | () => void | - | |
|
||||
| onChange | 值改变时触发 | (text: string) => void | - | |
|
||||
| onClear | 按下清除按钮的回调 | () => void | - | 5.20.0 |
|
||||
| onFocus | 获得焦点时触发 | () => void | - | |
|
||||
| onResize | resize 回调 | function({ width, height }) | - | |
|
||||
| onSearch | 搜索时触发 | (text: string, prefix: string) => void | - | |
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user