mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 22:36:31 +08:00
feat: ColorPicker support gradient color (#50050)
* refactor: support type update * chore: update clear style * chore: bump color picker * chore: use slider * chore: bump color picker * chore: range slider * chore: layout * chore: useModeColor * chore: simplify * chore: bump color picker * refactor: event * chore: tmp lock check * chore: of it * chore: update ts def * chore: update ts def * chore: remove useless ts * chore: linear * chore: adjust style * chore: rm useless code * chore: fill color * chore: basic linear * chore: support toStr * chore: limit minCount * chore: use cache * chore: drag support: * chore: yes * chore: update demo * chore: useLayoutEffect instead * chore: fix click to add point * chore: add smmoth * chore: support of locale * chore: add locale * chore: fix lint * chore: adjust style * chore: fix lint * chore: fix style * test: fix test case * chore: fix popover * test: fix test case * chore: fix test * test: clean up * chore: fix lint * chore: fix lint * chore: fix lint * test: coverage * test: coverage * test: coverage * test: coverage * test: coverage * test: coverage * chore: fix docs * docs: update demo desc * chore: enhance hover range * fix: delete not working * chore: fix lint * test: coverage * test: coverage * chore: clean up * chore: adjust * chore: highlight * chore: adjust style * chore: fix lint * chore: update demo * chore: memo perf * refactor: up to down colors * test: update snapshot
This commit is contained in:
parent
aed4665047
commit
832cffcdf9
@ -7,7 +7,7 @@ import classNames from 'classnames';
|
|||||||
|
|
||||||
import { PRESET_COLORS } from './colorUtil';
|
import { PRESET_COLORS } from './colorUtil';
|
||||||
|
|
||||||
type Color = GetProp<ColorPickerProps, 'value'>;
|
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||||
|
|
||||||
const useStyle = createStyles(({ token, css }) => ({
|
const useStyle = createStyles(({ token, css }) => ({
|
||||||
color: css`
|
color: css`
|
||||||
|
@ -40,7 +40,7 @@ import RadiusPicker from './RadiusPicker';
|
|||||||
import type { THEME } from './ThemePicker';
|
import type { THEME } from './ThemePicker';
|
||||||
import ThemePicker 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;
|
const { Header, Content, Sider } = Layout;
|
||||||
|
|
||||||
|
@ -18,13 +18,8 @@ export type CustomComponent<P = AnyObject> = React.ComponentType<P> | string;
|
|||||||
* ```
|
* ```
|
||||||
* @since 5.13.0
|
* @since 5.13.0
|
||||||
*/
|
*/
|
||||||
export type GetProps<T extends React.ComponentType<any> | object> = T extends React.ComponentType<
|
export type GetProps<T extends React.ComponentType<any> | object> =
|
||||||
infer P
|
T extends React.ComponentType<infer P> ? P : T extends object ? T : never;
|
||||||
>
|
|
||||||
? P
|
|
||||||
: T extends object
|
|
||||||
? T
|
|
||||||
: never;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get component props by component name
|
* 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>
|
: T extends React.ComponentType<infer P>
|
||||||
? ExtractRefAttributesRef<P>
|
? ExtractRefAttributesRef<P>
|
||||||
: never;
|
: 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 { Keyframes, unit } from '@ant-design/cssinjs';
|
||||||
|
|
||||||
import { resetComponent } from '../../style';
|
import { resetComponent } from '../../style';
|
||||||
import type {
|
import type { FullToken, GenerateStyle, GenStyleFn, GetDefaultToken } from '../../theme/internal';
|
||||||
FullToken,
|
|
||||||
GenerateStyle,
|
|
||||||
GenStyleFn,
|
|
||||||
GetDefaultToken,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genPresetColor, genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genPresetColor, genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
/** Component only token. Which will handle additional calculation of alias token */
|
/** Component only token. Which will handle additional calculation of alias token */
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
import type {
|
import type { FullToken, GetDefaultToken, GenStyleFn } from '../../theme/internal';
|
||||||
FullToken,
|
|
||||||
GetDefaultToken,
|
|
||||||
GenStyleFn,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { getLineHeight, mergeToken } from '../../theme/internal';
|
import { getLineHeight, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
/** Component only token. Which will handle additional calculation of alias token */
|
/** Component only token. Which will handle additional calculation of alias token */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useMemo, useRef } from 'react';
|
import React, { useContext, useMemo } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
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 { FormItemInputContext } from '../form/context';
|
||||||
import type { PopoverProps } from '../popover';
|
import type { PopoverProps } from '../popover';
|
||||||
import Popover from '../popover';
|
import Popover from '../popover';
|
||||||
import type { AggregationColor } from './color';
|
import { AggregationColor } from './color';
|
||||||
import type { ColorPickerPanelProps } from './ColorPickerPanel';
|
import type { ColorPickerPanelProps } from './ColorPickerPanel';
|
||||||
import ColorPickerPanel from './ColorPickerPanel';
|
import ColorPickerPanel from './ColorPickerPanel';
|
||||||
import ColorTrigger from './components/ColorTrigger';
|
import ColorTrigger from './components/ColorTrigger';
|
||||||
import useColorState from './hooks/useColorState';
|
import useModeColor from './hooks/useModeColor';
|
||||||
import type { ColorPickerBaseProps, ColorPickerProps, TriggerPlacement } from './interface';
|
import type { ColorPickerProps, ModeType, TriggerPlacement } from './interface';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
import { genAlphaColor, generateColor, getAlphaColor } from './util';
|
import { genAlphaColor, generateColor, getColorAlpha } from './util';
|
||||||
|
|
||||||
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
type CompoundedComponent = React.FC<ColorPickerProps> & {
|
||||||
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
_InternalPanelDoNotUseOrYouWillBeFired: typeof PurePanel;
|
||||||
@ -29,6 +29,7 @@ type CompoundedComponent = React.FC<ColorPickerProps> & {
|
|||||||
|
|
||||||
const ColorPicker: CompoundedComponent = (props) => {
|
const ColorPicker: CompoundedComponent = (props) => {
|
||||||
const {
|
const {
|
||||||
|
mode,
|
||||||
value,
|
value,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
format,
|
format,
|
||||||
@ -65,10 +66,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
const contextDisabled = useContext(DisabledContext);
|
const contextDisabled = useContext(DisabledContext);
|
||||||
const mergedDisabled = disabled ?? contextDisabled;
|
const mergedDisabled = disabled ?? contextDisabled;
|
||||||
|
|
||||||
const [colorValue, setColorValue, prevValue] = useColorState('', {
|
|
||||||
value,
|
|
||||||
defaultValue,
|
|
||||||
});
|
|
||||||
const [popupOpen, setPopupOpen] = useMergedState(false, {
|
const [popupOpen, setPopupOpen] = useMergedState(false, {
|
||||||
value: open,
|
value: open,
|
||||||
postState: (openData) => !mergedDisabled && openData,
|
postState: (openData) => !mergedDisabled && openData,
|
||||||
@ -82,9 +79,91 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
|
|
||||||
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
|
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);
|
const { status: contextStatus } = React.useContext(FormItemInputContext);
|
||||||
|
|
||||||
// ===================== Style =====================
|
// ===================== Style =====================
|
||||||
@ -106,8 +185,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
);
|
);
|
||||||
const mergedPopupCls = classNames(prefixCls, mergedRootCls);
|
const mergedPopupCls = classNames(prefixCls, mergedRootCls);
|
||||||
|
|
||||||
const popupAllowCloseRef = useRef(true);
|
|
||||||
|
|
||||||
// ===================== Warning ======================
|
// ===================== Warning ======================
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const warning = devUseWarning('ColorPicker');
|
const warning = devUseWarning('ColorPicker');
|
||||||
@ -119,48 +196,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange: ColorPickerPanelProps['onChange'] = (data, type, pickColor) => {
|
|
||||||
let color: AggregationColor = generateColor(data as AggregationColor);
|
|
||||||
|
|
||||||
// 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 = {
|
const popoverProps: PopoverProps = {
|
||||||
open: popupOpen,
|
open: popupOpen,
|
||||||
trigger,
|
trigger,
|
||||||
@ -172,19 +207,6 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
destroyTooltipOnHide,
|
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 };
|
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };
|
||||||
|
|
||||||
// ============================ zIndex ============================
|
// ============================ zIndex ============================
|
||||||
@ -194,17 +216,32 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
style={styles?.popup}
|
style={styles?.popup}
|
||||||
overlayInnerStyle={styles?.popupOverlayInner}
|
overlayInnerStyle={styles?.popupOverlayInner}
|
||||||
onOpenChange={(visible) => {
|
onOpenChange={(visible) => {
|
||||||
if (popupAllowCloseRef.current && !mergedDisabled) {
|
if (!visible || !mergedDisabled) {
|
||||||
setPopupOpen(visible);
|
setPopupOpen(visible);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
content={
|
content={
|
||||||
<ContextIsolator form>
|
<ContextIsolator form>
|
||||||
<ColorPickerPanel
|
<ColorPickerPanel
|
||||||
{...colorBaseProps}
|
mode={modeState}
|
||||||
onChange={handleChange}
|
onModeChange={onInternalModeChange}
|
||||||
onChangeComplete={handleChangeComplete}
|
modeOptions={modeOptions}
|
||||||
onClear={handleClear}
|
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>
|
</ContextIsolator>
|
||||||
}
|
}
|
||||||
@ -213,6 +250,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
>
|
>
|
||||||
{children || (
|
{children || (
|
||||||
<ColorTrigger
|
<ColorTrigger
|
||||||
|
activeIndex={popupOpen ? activeIndex : -1}
|
||||||
open={popupOpen}
|
open={popupOpen}
|
||||||
className={mergedCls}
|
className={mergedCls}
|
||||||
style={mergedStyle}
|
style={mergedStyle}
|
||||||
@ -221,7 +259,7 @@ const ColorPicker: CompoundedComponent = (props) => {
|
|||||||
showText={showText}
|
showText={showText}
|
||||||
format={formatValue}
|
format={formatValue}
|
||||||
{...rest}
|
{...rest}
|
||||||
color={colorValue}
|
color={mergedColor}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Popover>,
|
</Popover>,
|
||||||
|
@ -1,40 +1,91 @@
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { HsbaColorType } from '@rc-component/color-picker';
|
|
||||||
|
|
||||||
import Divider from '../divider';
|
import Divider from '../divider';
|
||||||
import type { AggregationColor } from './color';
|
|
||||||
import PanelPicker from './components/PanelPicker';
|
import PanelPicker from './components/PanelPicker';
|
||||||
import PanelPresets from './components/PanelPresets';
|
import PanelPresets from './components/PanelPresets';
|
||||||
import { PanelPickerProvider, PanelPresetsProvider } from './context';
|
import { PanelPickerContext, PanelPresetsContext } from './context';
|
||||||
import type { ColorPickerBaseProps } from './interface';
|
import type { PanelPickerContextProps, PanelPresetsContextProps } from './context';
|
||||||
|
import type { ColorPickerProps } from './interface';
|
||||||
|
|
||||||
export interface ColorPickerPanelProps extends ColorPickerBaseProps {
|
export interface ColorPickerPanelProps
|
||||||
onChange?: (value?: AggregationColor, type?: HsbaColorType, pickColor?: boolean) => void;
|
extends PanelPickerContextProps,
|
||||||
|
Omit<PanelPresetsContextProps, 'onChange'> {
|
||||||
onClear?: () => void;
|
onClear?: () => void;
|
||||||
|
panelRender?: ColorPickerProps['panelRender'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
||||||
const { prefixCls, presets, panelRender, color, onChange, onClear, ...injectProps } = props;
|
const {
|
||||||
const colorPickerPanelPrefixCls = `${prefixCls}-inner`;
|
|
||||||
|
|
||||||
// ==== Inject props ===
|
|
||||||
const panelPickerProps = {
|
|
||||||
prefixCls,
|
prefixCls,
|
||||||
value: color,
|
presets,
|
||||||
|
panelRender,
|
||||||
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
onClear,
|
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,
|
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,
|
presets,
|
||||||
onChange,
|
onChange,
|
||||||
}),
|
}),
|
||||||
[prefixCls, color, presets, onChange],
|
[prefixCls, value, presets, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
// ====================== Render ======================
|
// ====================== Render ======================
|
||||||
@ -47,8 +98,8 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelPickerProvider value={panelPickerProps}>
|
<PanelPickerContext.Provider value={panelContext}>
|
||||||
<PanelPresetsProvider value={panelPresetsProps}>
|
<PanelPresetsContext.Provider value={presetContext}>
|
||||||
<div className={colorPickerPanelPrefixCls}>
|
<div className={colorPickerPanelPrefixCls}>
|
||||||
{typeof panelRender === 'function'
|
{typeof panelRender === 'function'
|
||||||
? panelRender(innerPanel, {
|
? panelRender(innerPanel, {
|
||||||
@ -59,8 +110,8 @@ const ColorPickerPanel: FC<ColorPickerPanelProps> = (props) => {
|
|||||||
})
|
})
|
||||||
: innerPanel}
|
: innerPanel}
|
||||||
</div>
|
</div>
|
||||||
</PanelPresetsProvider>
|
</PanelPresetsContext.Provider>
|
||||||
</PanelPickerProvider>
|
</PanelPickerContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -206,6 +206,93 @@ exports[`renders components/color-picker/demo/format.tsx correctly 1`] = `
|
|||||||
</div>
|
</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`] = `
|
exports[`renders components/color-picker/demo/panel-render.tsx correctly 1`] = `
|
||||||
<div
|
<div
|
||||||
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
|
||||||
|
@ -59,7 +59,7 @@ exports[`ColorPicker Should panelRender work 1`] = `
|
|||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<div
|
||||||
class="ant-color-picker-handler"
|
class="ant-color-picker-handler"
|
||||||
@ -79,46 +79,46 @@ exports[`ColorPicker Should panelRender work 1`] = `
|
|||||||
class="ant-color-picker-slider-group"
|
class="ant-color-picker-slider-group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
style="position: relative;"
|
/>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
class="ant-slider-step"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
aria-disabled="false"
|
||||||
style="background-color: rgb(255, 0, 0);"
|
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>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
style="position: absolute; inset: 0;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
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);"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider-step"
|
||||||
style="position: absolute; inset: 0;"
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -343,7 +343,7 @@ exports[`ColorPicker Should panelRender work 2`] = `
|
|||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<div
|
||||||
class="ant-color-picker-handler"
|
class="ant-color-picker-handler"
|
||||||
@ -363,46 +363,46 @@ exports[`ColorPicker Should panelRender work 2`] = `
|
|||||||
class="ant-color-picker-slider-group"
|
class="ant-color-picker-slider-group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
style="position: relative;"
|
/>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style="position: absolute; left: -50px; top: -16.666666666666668px; z-index: 1;"
|
class="ant-slider-step"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
aria-disabled="false"
|
||||||
style="background-color: rgb(255, 0, 0);"
|
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>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
style="position: absolute; inset: 0;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
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);"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider-step"
|
||||||
style="position: absolute; inset: 0;"
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
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 ConfigProvider from '../../config-provider';
|
||||||
import Form from '../../form';
|
import Form from '../../form';
|
||||||
import theme from '../../theme';
|
import theme from '../../theme';
|
||||||
import type { AggregationColor } from '../color';
|
import { AggregationColor } from '../color';
|
||||||
import ColorPicker from '../ColorPicker';
|
import ColorPicker from '../ColorPicker';
|
||||||
import type { ColorPickerProps, ColorValueType } from '../interface';
|
import type { ColorPickerProps, ColorValueType } from '../interface';
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
@ -25,6 +25,11 @@ function doMouseMove(
|
|||||||
pageX: start,
|
pageX: start,
|
||||||
pageY: start,
|
pageY: start,
|
||||||
});
|
});
|
||||||
|
Object.defineProperties(mouseDown, {
|
||||||
|
pageX: { get: () => start },
|
||||||
|
pageY: { get: () => start },
|
||||||
|
});
|
||||||
|
|
||||||
fireEvent(container.getElementsByClassName(element)[0], mouseDown);
|
fireEvent(container.getElementsByClassName(element)[0], mouseDown);
|
||||||
// Drag
|
// Drag
|
||||||
const mouseMove: any = new Event('mousemove');
|
const mouseMove: any = new Event('mousemove');
|
||||||
@ -340,7 +345,7 @@ describe('ColorPicker', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should fix hover boundary issues', async () => {
|
it('Should fix hover boundary issues', async () => {
|
||||||
spyElementPrototypes(HTMLElement, {
|
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||||
getBoundingClientRect: () => ({
|
getBoundingClientRect: () => ({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 100,
|
y: 100,
|
||||||
@ -356,6 +361,8 @@ describe('ColorPicker', () => {
|
|||||||
fireEvent.mouseLeave(container.querySelector('.ant-color-picker-trigger')!);
|
fireEvent.mouseLeave(container.querySelector('.ant-color-picker-trigger')!);
|
||||||
await waitFakeTimer();
|
await waitFakeTimer();
|
||||||
expect(container.querySelector('.ant-popover-hidden')).toBeTruthy();
|
expect(container.querySelector('.ant-popover-hidden')).toBeTruthy();
|
||||||
|
|
||||||
|
spyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should work at dark mode', async () => {
|
it('Should work at dark mode', async () => {
|
||||||
@ -385,6 +392,12 @@ describe('ColorPicker', () => {
|
|||||||
expect(targetEle?.innerHTML).toBe('#1677ff');
|
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 () => {
|
it('Should showText work', async () => {
|
||||||
const { container } = render(<ColorPicker defaultValue="#1677ff" open showText />);
|
const { container } = render(<ColorPicker defaultValue="#1677ff" open showText />);
|
||||||
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
const targetEle = container.querySelector('.ant-color-picker-trigger-text');
|
||||||
@ -448,7 +461,7 @@ describe('ColorPicker', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should null work as expect', async () => {
|
it('Should null work as expect', async () => {
|
||||||
spyElementPrototypes(HTMLElement, {
|
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||||
getBoundingClientRect: () => ({
|
getBoundingClientRect: () => ({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 100,
|
y: 100,
|
||||||
@ -456,7 +469,8 @@ describe('ColorPicker', () => {
|
|||||||
height: 100,
|
height: 100,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const { container } = render(<ColorPicker value={null} open />);
|
|
||||||
|
const { container } = render(<ColorPicker defaultValue={null} open />);
|
||||||
expect(
|
expect(
|
||||||
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
||||||
).toEqual('0%');
|
).toEqual('0%');
|
||||||
@ -467,6 +481,8 @@ describe('ColorPicker', () => {
|
|||||||
expect(
|
expect(
|
||||||
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
container.querySelector('.ant-color-picker-alpha-input input')?.getAttribute('value'),
|
||||||
).toEqual('100%');
|
).toEqual('100%');
|
||||||
|
|
||||||
|
spyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support valid in form', async () => {
|
it('should support valid in form', async () => {
|
||||||
@ -501,17 +517,37 @@ describe('ColorPicker', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should onChangeComplete work', async () => {
|
it('Should onChangeComplete work', async () => {
|
||||||
|
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||||
|
getBoundingClientRect: () => ({
|
||||||
|
x: 0,
|
||||||
|
y: 100,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const handleChangeComplete = jest.fn();
|
const handleChangeComplete = jest.fn();
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<ColorPicker open onChangeComplete={handleChangeComplete} allowClear />,
|
<ColorPicker open onChangeComplete={handleChangeComplete} allowClear />,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Move
|
||||||
doMouseMove(container, 0, 999);
|
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')!, {
|
fireEvent.change(container.querySelector('.ant-color-picker-hex-input input')!, {
|
||||||
target: { value: '#273B57' },
|
target: { value: '#273B57' },
|
||||||
});
|
});
|
||||||
expect(handleChangeComplete).toHaveBeenCalledTimes(3);
|
expect(handleChangeComplete).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
spyRect.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should disabledAlpha work', async () => {
|
it('Should disabledAlpha work', async () => {
|
||||||
@ -522,7 +558,7 @@ describe('ColorPicker', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should disabledAlpha work with value', async () => {
|
it('Should disabledAlpha work with value', async () => {
|
||||||
spyElementPrototypes(HTMLElement, {
|
const spyRect = spyElementPrototypes(HTMLElement, {
|
||||||
getBoundingClientRect: () => ({
|
getBoundingClientRect: () => ({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 100,
|
y: 100,
|
||||||
@ -542,10 +578,12 @@ describe('ColorPicker', () => {
|
|||||||
onChangeComplete={setChangedValue}
|
onChangeComplete={setChangedValue}
|
||||||
>
|
>
|
||||||
<div className="color-value">
|
<div className="color-value">
|
||||||
{typeof value === 'string' ? value : value?.toHexString()}
|
{value instanceof AggregationColor ? value.toHexString() : String(value)}
|
||||||
</div>
|
</div>
|
||||||
<div className="color-value-changed">
|
<div className="color-value-changed">
|
||||||
{typeof changedValue === 'string' ? changedValue : changedValue?.toHexString()}
|
{changedValue instanceof AggregationColor
|
||||||
|
? changedValue.toHexString()
|
||||||
|
: String(changedValue)}
|
||||||
</div>
|
</div>
|
||||||
</ColorPicker>
|
</ColorPicker>
|
||||||
);
|
);
|
||||||
@ -555,6 +593,8 @@ describe('ColorPicker', () => {
|
|||||||
doMouseMove(container, 0, 999);
|
doMouseMove(container, 0, 999);
|
||||||
expect(container.querySelector('.color-value')?.innerHTML).toEqual('#000000');
|
expect(container.querySelector('.color-value')?.innerHTML).toEqual('#000000');
|
||||||
expect(container.querySelector('.color-value-changed')?.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', () => {
|
it('Should warning work when set disabledAlpha true and color is alpha color', () => {
|
||||||
@ -645,7 +685,7 @@ describe('ColorPicker', () => {
|
|||||||
|
|
||||||
it('Controlled string value should work with allowClear correctly', async () => {
|
it('Controlled string value should work with allowClear correctly', async () => {
|
||||||
const Demo = (props: any) => {
|
const Demo = (props: any) => {
|
||||||
const [color, setColor] = useState<ColorValueType>(generateColor('red'));
|
const [color, setColor] = useState<ColorValueType>(generateColor('#FF0000'));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof props.value !== 'undefined') {
|
if (typeof props.value !== 'undefined') {
|
||||||
@ -654,7 +694,14 @@ describe('ColorPicker', () => {
|
|||||||
}, [props.value]);
|
}, [props.value]);
|
||||||
|
|
||||||
return (
|
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 />);
|
const { container, rerender } = render(<Demo />);
|
||||||
@ -662,10 +709,13 @@ describe('ColorPicker', () => {
|
|||||||
expect(
|
expect(
|
||||||
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
|
// Clear
|
||||||
fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
|
fireEvent.click(container.querySelector('.ant-color-picker-clear')!);
|
||||||
expect(
|
expect(
|
||||||
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
rerender(<Demo value="#1677ff" />);
|
rerender(<Demo value="#1677ff" />);
|
||||||
expect(
|
expect(
|
||||||
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
container.querySelector('.ant-color-picker-trigger .ant-color-picker-clear'),
|
||||||
|
@ -1,23 +1,52 @@
|
|||||||
/* eslint-disable class-methods-use-this */
|
/* eslint-disable class-methods-use-this */
|
||||||
import type { ColorGenInput } from '@rc-component/color-picker';
|
|
||||||
import { Color as RcColor } 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) =>
|
export const toHexFormat = (value?: string, alpha?: boolean) =>
|
||||||
value?.replace(/[^\w/]/gi, '').slice(0, alpha ? 8 : 6) || '';
|
value?.replace(/[^\w/]/gi, '').slice(0, alpha ? 8 : 6) || '';
|
||||||
|
|
||||||
export const getHex = (value?: string, alpha?: boolean) => (value ? toHexFormat(value, alpha) : '');
|
export const getHex = (value?: string, alpha?: boolean) => (value ? toHexFormat(value, alpha) : '');
|
||||||
|
|
||||||
|
export type GradientColor = {
|
||||||
|
color: AggregationColor;
|
||||||
|
percent: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
export class AggregationColor {
|
export class AggregationColor {
|
||||||
/** Original Color object */
|
/** Original Color object */
|
||||||
private metaColor: RcColor;
|
private metaColor: RcColor;
|
||||||
|
|
||||||
public cleared: boolean | 'controlled' = false;
|
private colors: GradientColor | undefined;
|
||||||
|
|
||||||
constructor(color: ColorGenInput<AggregationColor>) {
|
public cleared = false;
|
||||||
this.metaColor = new RcColor(color instanceof AggregationColor ? color.metaColor : color);
|
|
||||||
|
|
||||||
if (!color) {
|
constructor(color: ColorGenInput<AggregationColor> | Colors<AggregationColor>) {
|
||||||
this.metaColor.setAlpha(0);
|
// 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;
|
this.cleared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,7 +60,7 @@ export class AggregationColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toHex() {
|
toHex() {
|
||||||
return getHex(this.toHexString(), this.metaColor.getAlpha() < 1);
|
return getHex(this.toHexString(), this.metaColor.a < 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHexString() {
|
toHexString() {
|
||||||
@ -45,4 +74,42 @@ export class AggregationColor {
|
|||||||
toRgbString() {
|
toRgbString() {
|
||||||
return this.metaColor.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);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ import type { FC } from 'react';
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
import { generateColor, getColorAlpha } from '../util';
|
||||||
import { generateColor, getAlphaColor } from '../util';
|
|
||||||
import ColorSteppers from './ColorSteppers';
|
import ColorSteppers from './ColorSteppers';
|
||||||
|
|
||||||
interface ColorAlphaInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorAlphaInputProps {
|
||||||
|
prefixCls: string;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ const ColorAlphaInput: FC<ColorAlphaInputProps> = ({ prefixCls, value, onChange
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ColorSteppers
|
<ColorSteppers
|
||||||
value={getAlphaColor(alphaValue)}
|
value={getColorAlpha(alphaValue)}
|
||||||
prefixCls={prefixCls}
|
prefixCls={prefixCls}
|
||||||
formatter={(step) => `${step}%`}
|
formatter={(step) => `${step}%`}
|
||||||
className={colorAlphaInputPrefixCls}
|
className={colorAlphaInputPrefixCls}
|
||||||
|
@ -2,22 +2,23 @@ import type { FC } from 'react';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
interface ColorClearProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorClearProps {
|
||||||
|
prefixCls: string;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorClear: FC<ColorClearProps> = ({ prefixCls, value, onChange }) => {
|
const ColorClear: FC<ColorClearProps> = ({ prefixCls, value, onChange }) => {
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (value && !value.cleared) {
|
if (onChange && value && !value.cleared) {
|
||||||
const hsba = value.toHsb();
|
const hsba = value.toHsb();
|
||||||
hsba.a = 0;
|
hsba.a = 0;
|
||||||
const genColor = generateColor(hsba);
|
const genColor = generateColor(hsba);
|
||||||
genColor.cleared = true;
|
genColor.cleared = true;
|
||||||
onChange?.(genColor);
|
|
||||||
|
onChange(genColor);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return <div className={`${prefixCls}-clear`} onClick={handleClick} />;
|
return <div className={`${prefixCls}-clear`} onClick={handleClick} />;
|
||||||
|
@ -4,10 +4,10 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import Input from '../../input';
|
import Input from '../../input';
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import { toHexFormat } from '../color';
|
import { toHexFormat } from '../color';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
interface ColorHexInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorHexInputProps {
|
||||||
|
prefixCls: string;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import type { HSB } from '@rc-component/color-picker';
|
import type { HSB } from '@rc-component/color-picker';
|
||||||
|
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import { generateColor, getRoundNumber } from '../util';
|
import { generateColor, getRoundNumber } from '../util';
|
||||||
import ColorSteppers from './ColorSteppers';
|
import ColorSteppers from './ColorSteppers';
|
||||||
|
|
||||||
interface ColorHsbInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorHsbInputProps {
|
||||||
|
prefixCls: string;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,18 @@ import useMergedState from 'rc-util/lib/hooks/useMergedState';
|
|||||||
|
|
||||||
import Select from '../../select';
|
import Select from '../../select';
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorFormatType, ColorPickerBaseProps } from '../interface';
|
import type { ColorFormatType } from '../interface';
|
||||||
import { ColorFormat } from '../interface';
|
import { ColorFormat } from '../interface';
|
||||||
import ColorAlphaInput from './ColorAlphaInput';
|
import ColorAlphaInput from './ColorAlphaInput';
|
||||||
import ColorHexInput from './ColorHexInput';
|
import ColorHexInput from './ColorHexInput';
|
||||||
import ColorHsbInput from './ColorHsbInput';
|
import ColorHsbInput from './ColorHsbInput';
|
||||||
import ColorRgbInput from './ColorRgbInput';
|
import ColorRgbInput from './ColorRgbInput';
|
||||||
|
|
||||||
interface ColorInputProps
|
interface ColorInputProps {
|
||||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'format' | 'onFormatChange' | 'disabledAlpha'> {
|
prefixCls: string;
|
||||||
|
format?: ColorFormatType;
|
||||||
|
onFormatChange?: (format: ColorFormatType) => void;
|
||||||
|
disabledAlpha?: boolean;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,11 @@ import Collapse from '../../collapse';
|
|||||||
import { useLocale } from '../../locale';
|
import { useLocale } from '../../locale';
|
||||||
import { useToken } from '../../theme/internal';
|
import { useToken } from '../../theme/internal';
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorPickerBaseProps, PresetsItem } from '../interface';
|
import type { PresetsItem } from '../interface';
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
|
|
||||||
interface ColorPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorPresetsProps {
|
||||||
|
prefixCls: string;
|
||||||
presets: PresetsItem[];
|
presets: PresetsItem[];
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
|
@ -3,11 +3,11 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import type { RGB } from '@rc-component/color-picker';
|
import type { RGB } from '@rc-component/color-picker';
|
||||||
|
|
||||||
import type { AggregationColor } from '../color';
|
import type { AggregationColor } from '../color';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import { generateColor } from '../util';
|
import { generateColor } from '../util';
|
||||||
import ColorSteppers from './ColorSteppers';
|
import ColorSteppers from './ColorSteppers';
|
||||||
|
|
||||||
interface ColorRgbInputProps extends Pick<ColorPickerBaseProps, 'prefixCls'> {
|
interface ColorRgbInputProps {
|
||||||
|
prefixCls: string;
|
||||||
value?: AggregationColor;
|
value?: AggregationColor;
|
||||||
onChange?: (value: AggregationColor) => void;
|
onChange?: (value: AggregationColor) => void;
|
||||||
}
|
}
|
||||||
|
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 type { InputNumberProps } from '../../input-number';
|
||||||
import InputNumber 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;
|
value?: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
|
/* eslint-disable react/no-array-index-key */
|
||||||
import type { CSSProperties, MouseEventHandler } from 'react';
|
import type { CSSProperties, MouseEventHandler } from 'react';
|
||||||
import React, { forwardRef, useMemo } from 'react';
|
import React, { forwardRef, useMemo } from 'react';
|
||||||
import { ColorBlock } from '@rc-component/color-picker';
|
import { ColorBlock } from '@rc-component/color-picker';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import pickAttrs from 'rc-util/lib/pickAttrs';
|
import pickAttrs from 'rc-util/lib/pickAttrs';
|
||||||
|
|
||||||
import type { ColorPickerBaseProps, ColorPickerProps } from '../interface';
|
import { useLocale } from '../../locale';
|
||||||
import { getAlphaColor } from '../util';
|
import type { AggregationColor } from '../color';
|
||||||
|
import type { ColorFormatType, ColorPickerProps } from '../interface';
|
||||||
|
import { getColorAlpha } from '../util';
|
||||||
import ColorClear from './ColorClear';
|
import ColorClear from './ColorClear';
|
||||||
|
|
||||||
export interface ColorTriggerProps
|
export interface ColorTriggerProps {
|
||||||
extends Pick<ColorPickerBaseProps, 'prefixCls' | 'disabled' | 'format'> {
|
prefixCls: string;
|
||||||
color: NonNullable<ColorPickerBaseProps['color']>;
|
disabled?: boolean;
|
||||||
|
format?: ColorFormatType;
|
||||||
|
color: AggregationColor;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
showText?: ColorPickerProps['showText'];
|
showText?: ColorPickerProps['showText'];
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -18,25 +23,57 @@ export interface ColorTriggerProps
|
|||||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
onMouseEnter?: MouseEventHandler<HTMLDivElement>;
|
onMouseEnter?: MouseEventHandler<HTMLDivElement>;
|
||||||
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
onMouseLeave?: MouseEventHandler<HTMLDivElement>;
|
||||||
|
activeIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref) => {
|
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 colorTriggerPrefixCls = `${prefixCls}-trigger`;
|
||||||
|
const colorTextPrefixCls = `${colorTriggerPrefixCls}-text`;
|
||||||
|
const colorTextCellPrefixCls = `${colorTextPrefixCls}-cell`;
|
||||||
|
|
||||||
const containerNode = useMemo<React.ReactNode>(
|
const [locale] = useLocale('ColorPicker');
|
||||||
() =>
|
|
||||||
color.cleared ? (
|
// ============================== Text ==============================
|
||||||
<ColorClear prefixCls={prefixCls} />
|
const desc: React.ReactNode = React.useMemo(() => {
|
||||||
) : (
|
if (!showText) {
|
||||||
<ColorBlock prefixCls={prefixCls} color={color.toRgbString()} />
|
return '';
|
||||||
),
|
}
|
||||||
[color, prefixCls],
|
|
||||||
|
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 hexString = color.toHexString().toUpperCase();
|
||||||
const alpha = getAlphaColor(color);
|
const alpha = getColorAlpha(color);
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'rgb':
|
case 'rgb':
|
||||||
return color.toRgbString();
|
return color.toRgbString();
|
||||||
@ -46,16 +83,18 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
|
|||||||
default:
|
default:
|
||||||
return alpha < 100 ? `${hexString.slice(0, 7)},${alpha}%` : hexString;
|
return alpha < 100 ? `${hexString.slice(0, 7)},${alpha}%` : hexString;
|
||||||
}
|
}
|
||||||
};
|
}, [color, format, showText, activeIndex]);
|
||||||
|
|
||||||
const renderText = () => {
|
// ============================= Render =============================
|
||||||
if (typeof showText === 'function') {
|
const containerNode = useMemo<React.ReactNode>(
|
||||||
return showText(color);
|
() =>
|
||||||
}
|
color.cleared ? (
|
||||||
if (showText) {
|
<ColorClear prefixCls={prefixCls} />
|
||||||
return genColorString();
|
) : (
|
||||||
}
|
<ColorBlock prefixCls={prefixCls} color={color.toCssString()} />
|
||||||
};
|
),
|
||||||
|
[color, prefixCls],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -67,7 +106,7 @@ const ColorTrigger = forwardRef<HTMLDivElement, ColorTriggerProps>((props, ref)
|
|||||||
{...pickAttrs(rest)}
|
{...pickAttrs(rest)}
|
||||||
>
|
>
|
||||||
{containerNode}
|
{containerNode}
|
||||||
{showText && <div className={`${colorTriggerPrefixCls}-text`}>{renderText()}</div>}
|
{showText && <div className={colorTextPrefixCls}>{desc}</div>}
|
||||||
</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 { AggregationColor } 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?: AggregationColor;
|
|
||||||
onChange?: (value?: AggregationColor, 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);
|
161
components/color-picker/components/PanelPicker/index.tsx
Normal file
161
components/color-picker/components/PanelPicker/index.tsx
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
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) => {
|
||||||
|
const nextColor = generateColor(colorValue);
|
||||||
|
|
||||||
|
onChange(fillColor(value.cleared ? genAlphaColor(nextColor) : nextColor), 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) => {
|
||||||
|
onInternalChange(colorValue, true);
|
||||||
|
}}
|
||||||
|
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 type { FC } from 'react';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
import type { AggregationColor } from '../color';
|
|
||||||
import { PanelPresetsContext } from '../context';
|
import { PanelPresetsContext } from '../context';
|
||||||
import type { ColorPickerBaseProps } from '../interface';
|
|
||||||
import ColorPresets from './ColorPresets';
|
import ColorPresets from './ColorPresets';
|
||||||
|
|
||||||
export interface PanelPresetsProps extends Pick<ColorPickerBaseProps, 'prefixCls' | 'presets'> {
|
|
||||||
value?: AggregationColor;
|
|
||||||
onChange?: (value: AggregationColor) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PanelPresets: FC = () => {
|
const PanelPresets: FC = () => {
|
||||||
const { prefixCls, value, presets, onChange } = useContext(PanelPresetsContext);
|
const { prefixCls, value, presets, onChange } = useContext(PanelPresetsContext);
|
||||||
return Array.isArray(presets) ? (
|
return Array.isArray(presets) ? (
|
||||||
|
@ -1,11 +1,50 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type { PanelPickerProps } from './components/PanelPicker';
|
import type { GetProp } from '../_util/type';
|
||||||
import type { PanelPresetsProps } from './components/PanelPresets';
|
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;
|
format?: ColorFormatType;
|
||||||
export const { Provider: PanelPresetsProvider } = PanelPresetsContext;
|
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 { ColorPicker, Space } from 'antd';
|
||||||
import type { ColorPickerProps, GetProp } 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'>;
|
type Format = GetProp<ColorPickerProps, 'format'>;
|
||||||
|
|
||||||
const HexCase: React.FC = () => {
|
const HexCase: React.FC = () => {
|
||||||
@ -28,7 +28,7 @@ const HexCase: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const HsbCase: 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 [formatHsb, setFormatHsb] = useState<ColorPickerProps['format']>('hsb');
|
||||||
|
|
||||||
const hsbString = React.useMemo(
|
const hsbString = React.useMemo(
|
||||||
@ -50,7 +50,7 @@ const HsbCase: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RgbCase: 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 [formatRgb, setFormatRgb] = useState<ColorPickerProps['format']>('rgb');
|
||||||
|
|
||||||
const rgbString = React.useMemo(
|
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);
|
const [open, setOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<ColorPicker defaultValue="#1677ff" showText />
|
<ColorPicker defaultValue="#1677ff" showText allowClear />
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
defaultValue="#1677ff"
|
defaultValue="#1677ff"
|
||||||
showText={(color) => <span>Custom Text ({color.toHexString()})</span>}
|
showText={(color) => <span>Custom Text ({color.toHexString()})</span>}
|
||||||
|
@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
|
|||||||
import { Button, ColorPicker } from 'antd';
|
import { Button, ColorPicker } from 'antd';
|
||||||
import type { ColorPickerProps, GetProp } 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 Demo: React.FC = () => {
|
||||||
const [color, setColor] = useState<Color>('#1677ff');
|
const [color, setColor] = useState<Color>('#1677ff');
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
|
||||||
|
|
||||||
import type { AggregationColor } 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<AggregationColor>(generateColor(''));
|
|
||||||
const [colorValue, _setColorValue] = useState<AggregationColor>(() => {
|
|
||||||
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: AggregationColor) => {
|
|
||||||
_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/size.tsx">Trigger size</code>
|
||||||
<code src="./demo/controlled.tsx">controlled mode</code>
|
<code src="./demo/controlled.tsx">controlled mode</code>
|
||||||
<code src="./demo/change-completed.tsx">Color change completed</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/text-render.tsx">Rendering Trigger Text</code>
|
||||||
<code src="./demo/disabled.tsx">Disable</code>
|
<code src="./demo/disabled.tsx">Disable</code>
|
||||||
<code src="./demo/disabled-alpha.tsx">Disabled Alpha</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 |
|
| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |
|
||||||
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
|
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
|
||||||
| format | Format of color | `rgb` \| `hex` \| `hsb` | `hex` | |
|
| 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 | - | |
|
| open | Whether to show popup | boolean | - | |
|
||||||
| presets | Preset colors | `{ label: ReactNode, colors: Array<string \| Color>, defaultOpen?: boolean }[]` | - | `defaultOpen: 5.11.0` |
|
| 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` | |
|
| 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
|
### Color
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
<!-- 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` | - |
|
| 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` | - |
|
| 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 })` | - |
|
| toHsb | Convert to `hsb` object | `() => ({ h: number, s: number, b: number, a number })` | - |
|
||||||
|
@ -23,6 +23,7 @@ group:
|
|||||||
<code src="./demo/size.tsx">触发器尺寸大小</code>
|
<code src="./demo/size.tsx">触发器尺寸大小</code>
|
||||||
<code src="./demo/controlled.tsx">受控模式</code>
|
<code src="./demo/controlled.tsx">受控模式</code>
|
||||||
<code src="./demo/change-completed.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/text-render.tsx">渲染触发器文本</code>
|
||||||
<code src="./demo/disabled.tsx">禁用</code>
|
<code src="./demo/disabled.tsx">禁用</code>
|
||||||
<code src="./demo/disabled-alpha.tsx">禁用透明度</code>
|
<code src="./demo/disabled-alpha.tsx">禁用透明度</code>
|
||||||
@ -52,6 +53,7 @@ group:
|
|||||||
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
|
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
|
||||||
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
|
||||||
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | |
|
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | `hex` | |
|
||||||
|
| mode | 选择器模式,用于配置单色与渐变 | `('single' \| 'gradient')[]` | `single` | 5.20.0 |
|
||||||
| open | 是否显示弹出窗口 | boolean | - | |
|
| open | 是否显示弹出窗口 | boolean | - | |
|
||||||
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color>, defaultOpen?: boolean }[]` | - | `defaultOpen: 5.11.0` |
|
| presets | 预设的颜色 | `{ label: ReactNode, colors: Array<string \| Color>, defaultOpen?: boolean }[]` | - | `defaultOpen: 5.11.0` |
|
||||||
| placement | 弹出窗口的位置 | 同 `Tooltips` 组件的 [placement](/components/tooltip-cn/#api) 参数设计 | `bottomLeft` | |
|
| placement | 弹出窗口的位置 | 同 `Tooltips` 组件的 [placement](/components/tooltip-cn/#api) 参数设计 | `bottomLeft` | |
|
||||||
@ -69,8 +71,9 @@ group:
|
|||||||
### Color
|
### Color
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 版本 |
|
||||||
| :-- | :-- | :-- | :-- |
|
| :-- | :-- | :-- | :-- |
|
||||||
|
| toCssString | 转换成 CSS 支持的格式 | `() => string` | 5.20.0 |
|
||||||
| toHex | 转换成 `hex` 格式字符,返回格式如:`1677ff` | `() => string` | - |
|
| toHex | 转换成 `hex` 格式字符,返回格式如:`1677ff` | `() => string` | - |
|
||||||
| toHexString | 转换成 `hex` 格式颜色字符串,返回格式如:`#1677ff` | `() => string` | - |
|
| toHexString | 转换成 `hex` 格式颜色字符串,返回格式如:`#1677ff` | `() => string` | - |
|
||||||
| toHsb | 转换成 `hsb` 对象 | `() => ({ h: number, s: number, b: number, a number })` | - |
|
| toHsb | 转换成 `hsb` 对象 | `() => ({ h: number, s: number, b: number, a number })` | - |
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import type { CSSProperties, FC, ReactNode } from 'react';
|
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 { SizeType } from '../config-provider/SizeContext';
|
||||||
import type { PopoverProps } from '../popover';
|
import type { PopoverProps } from '../popover';
|
||||||
import type { TooltipPlacement } from '../tooltip';
|
import type { TooltipPlacement } from '../tooltip';
|
||||||
import type { AggregationColor } from './color';
|
import type { AggregationColor } from './color';
|
||||||
|
|
||||||
|
export type { ColorGenInput };
|
||||||
|
|
||||||
|
export type Colors<T> = {
|
||||||
|
color: ColorGenInput<T>;
|
||||||
|
percent: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
export enum ColorFormat {
|
export enum ColorFormat {
|
||||||
hex = 'hex',
|
hex = 'hex',
|
||||||
rgb = 'rgb',
|
rgb = 'rgb',
|
||||||
@ -28,25 +38,29 @@ export type TriggerType = 'click' | 'hover';
|
|||||||
|
|
||||||
export type TriggerPlacement = TooltipPlacement; // Alias, to prevent breaking changes.
|
export type TriggerPlacement = TooltipPlacement; // Alias, to prevent breaking changes.
|
||||||
|
|
||||||
export interface ColorPickerBaseProps {
|
export type SingleValueType = AggregationColor | string;
|
||||||
color?: AggregationColor;
|
|
||||||
prefixCls: string;
|
|
||||||
format?: ColorFormatType;
|
|
||||||
allowClear?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
disabledAlpha?: boolean;
|
|
||||||
presets?: PresetsItem[];
|
|
||||||
panelRender?: ColorPickerProps['panelRender'];
|
|
||||||
onFormatChange?: ColorPickerProps['onFormatChange'];
|
|
||||||
onChangeComplete?: ColorPickerProps['onChangeComplete'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ColorValueType = AggregationColor | string | null;
|
export type ColorValueType =
|
||||||
|
| SingleValueType
|
||||||
|
| null
|
||||||
|
| {
|
||||||
|
color: SingleValueType;
|
||||||
|
percent: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
export type ModeType = 'single' | 'gradient';
|
||||||
|
|
||||||
export type ColorPickerProps = Omit<
|
export type ColorPickerProps = Omit<
|
||||||
RcColorPickerProps,
|
RcColorPickerProps,
|
||||||
'onChange' | 'value' | 'defaultValue' | 'panelRender' | 'disabledAlpha' | 'onChangeComplete'
|
| 'onChange'
|
||||||
|
| 'value'
|
||||||
|
| 'defaultValue'
|
||||||
|
| 'panelRender'
|
||||||
|
| 'disabledAlpha'
|
||||||
|
| 'onChangeComplete'
|
||||||
|
| 'components'
|
||||||
> & {
|
> & {
|
||||||
|
mode?: ModeType | ModeType[];
|
||||||
value?: ColorValueType;
|
value?: ColorValueType;
|
||||||
defaultValue?: ColorValueType;
|
defaultValue?: ColorValueType;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
@ -21,11 +21,13 @@ const genColorBlockStyle = (token: ColorPickerToken, size: number): CSSObject =>
|
|||||||
width: size,
|
width: size,
|
||||||
height: size,
|
height: size,
|
||||||
boxShadow: colorPickerInsetShadow,
|
boxShadow: colorPickerInsetShadow,
|
||||||
|
flex: 'none',
|
||||||
|
|
||||||
...getTransBg('50%', token.colorFillSecondary),
|
...getTransBg('50%', token.colorFillSecondary),
|
||||||
[`${componentCls}-color-block-inner`]: {
|
[`${componentCls}-color-block-inner`]: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
border: `${unit(lineWidth)} solid ${colorFillSecondary}`,
|
boxShadow: `inset 0 0 0 ${unit(lineWidth)} ${colorFillSecondary}`,
|
||||||
borderRadius: 'inherit',
|
borderRadius: 'inherit',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ import genColorBlockStyle from './color-block';
|
|||||||
import genInputStyle from './input';
|
import genInputStyle from './input';
|
||||||
import genPickerStyle from './picker';
|
import genPickerStyle from './picker';
|
||||||
import genPresetsStyle from './presets';
|
import genPresetsStyle from './presets';
|
||||||
|
import genSliderStyle from './slider';
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noEmptyInterface: ComponentToken need to be empty by default
|
// biome-ignore lint/suspicious/noEmptyInterface: ComponentToken need to be empty by default
|
||||||
export interface ComponentToken {}
|
export interface ComponentToken {}
|
||||||
@ -74,12 +75,12 @@ const genClearStyle = (
|
|||||||
'&::after': {
|
'&::after': {
|
||||||
content: '""',
|
content: '""',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
insetInlineEnd: lineWidth,
|
insetInlineEnd: token.calc(lineWidth).mul(-1).equal(),
|
||||||
top: 0,
|
top: token.calc(lineWidth).mul(-1).equal(),
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: 40, // maximum
|
width: 40, // maximum
|
||||||
height: 2, // fixed
|
height: 2, // fixed
|
||||||
transformOrigin: 'right',
|
transformOrigin: `calc(100% - 1px) 1px`,
|
||||||
transform: 'rotate(-45deg)',
|
transform: 'rotate(-45deg)',
|
||||||
backgroundColor: red6,
|
backgroundColor: red6,
|
||||||
},
|
},
|
||||||
@ -138,7 +139,7 @@ const genSizeStyle = (token: ColorPickerToken): CSSObject => {
|
|||||||
return {
|
return {
|
||||||
[`&${componentCls}-lg`]: {
|
[`&${componentCls}-lg`]: {
|
||||||
minWidth: controlHeightLG,
|
minWidth: controlHeightLG,
|
||||||
height: controlHeightLG,
|
minHeight: controlHeightLG,
|
||||||
borderRadius: borderRadiusLG,
|
borderRadius: borderRadiusLG,
|
||||||
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
||||||
width: controlHeight,
|
width: controlHeight,
|
||||||
@ -151,13 +152,17 @@ const genSizeStyle = (token: ColorPickerToken): CSSObject => {
|
|||||||
},
|
},
|
||||||
[`&${componentCls}-sm`]: {
|
[`&${componentCls}-sm`]: {
|
||||||
minWidth: controlHeightSM,
|
minWidth: controlHeightSM,
|
||||||
height: controlHeightSM,
|
minHeight: controlHeightSM,
|
||||||
borderRadius: borderRadiusSM,
|
borderRadius: borderRadiusSM,
|
||||||
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
[`${componentCls}-color-block, ${componentCls}-clear`]: {
|
||||||
width: controlHeightXS,
|
width: controlHeightXS,
|
||||||
height: controlHeightXS,
|
height: controlHeightXS,
|
||||||
borderRadius: borderRadiusXS,
|
borderRadius: borderRadiusXS,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[`${componentCls}-trigger-text`]: {
|
||||||
|
lineHeight: controlHeightXS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -206,23 +211,30 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
|||||||
[`${componentCls}-panel`]: {
|
[`${componentCls}-panel`]: {
|
||||||
...genPickerStyle(token),
|
...genPickerStyle(token),
|
||||||
},
|
},
|
||||||
|
...genSliderStyle(token),
|
||||||
...genColorBlockStyle(token, colorPickerPreviewSize),
|
...genColorBlockStyle(token, colorPickerPreviewSize),
|
||||||
...genInputStyle(token),
|
...genInputStyle(token),
|
||||||
...genPresetsStyle(token),
|
...genPresetsStyle(token),
|
||||||
...genClearStyle(token, colorPickerPresetColorSize, {
|
...genClearStyle(token, colorPickerPresetColorSize, {
|
||||||
marginInlineStart: 'auto',
|
marginInlineStart: 'auto',
|
||||||
marginBottom: marginXS,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Operation bar
|
||||||
|
[`${componentCls}-operation`]: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: marginXS,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'&-trigger': {
|
'&-trigger': {
|
||||||
minWidth: controlHeight,
|
minWidth: controlHeight,
|
||||||
height: controlHeight,
|
minHeight: controlHeight,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
border: `${unit(lineWidth)} solid ${colorBorder}`,
|
border: `${unit(lineWidth)} solid ${colorBorder}`,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'flex-start',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
transition: `all ${motionDurationMid}`,
|
transition: `all ${motionDurationMid}`,
|
||||||
background: colorBgElevated,
|
background: colorBgElevated,
|
||||||
@ -235,6 +247,17 @@ const genColorPickerStyle: GenerateStyle<ColorPickerToken> = (token) => {
|
|||||||
.equal(),
|
.equal(),
|
||||||
fontSize,
|
fontSize,
|
||||||
color: colorText,
|
color: colorText,
|
||||||
|
alignSelf: 'center',
|
||||||
|
|
||||||
|
'&-cell': {
|
||||||
|
'&:not(:last-child):after': {
|
||||||
|
content: '", "',
|
||||||
|
},
|
||||||
|
|
||||||
|
'&-inactive': {
|
||||||
|
color: colorTextDisabled,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
borderColor: colorPrimaryHover,
|
borderColor: colorPrimaryHover,
|
||||||
@ -275,7 +298,7 @@ export default genStyleHooks('ColorPicker', (token) => {
|
|||||||
colorPickerHandlerSizeSM: 12,
|
colorPickerHandlerSizeSM: 12,
|
||||||
colorPickerAlphaInputWidth: 44,
|
colorPickerAlphaInputWidth: 44,
|
||||||
colorPickerInputNumberHandleWidth: 16,
|
colorPickerInputNumberHandleWidth: 16,
|
||||||
colorPickerPresetColorSize: 18,
|
colorPickerPresetColorSize: 24,
|
||||||
colorPickerInsetShadow: `inset 0 0 1px 0 ${colorTextQuaternary}`,
|
colorPickerInsetShadow: `inset 0 0 1px 0 ${colorTextQuaternary}`,
|
||||||
colorPickerSliderHeight,
|
colorPickerSliderHeight,
|
||||||
colorPickerPreviewSize: token
|
colorPickerPreviewSize: token
|
||||||
|
@ -2,7 +2,6 @@ import { unit } from '@ant-design/cssinjs';
|
|||||||
import type { CSSObject } from '@ant-design/cssinjs';
|
import type { CSSObject } from '@ant-design/cssinjs';
|
||||||
|
|
||||||
import type { GenerateStyle } from '../../theme/internal';
|
import type { GenerateStyle } from '../../theme/internal';
|
||||||
import { getTransBg } from './color-block';
|
|
||||||
import type { ColorPickerToken } from './index';
|
import type { ColorPickerToken } from './index';
|
||||||
|
|
||||||
const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
||||||
@ -16,11 +15,11 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
|||||||
colorFillSecondary,
|
colorFillSecondary,
|
||||||
lineWidthBold,
|
lineWidthBold,
|
||||||
colorPickerHandlerSize,
|
colorPickerHandlerSize,
|
||||||
colorPickerHandlerSizeSM,
|
|
||||||
colorPickerSliderHeight,
|
|
||||||
} = token;
|
} = token;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
userSelect: 'none',
|
||||||
|
|
||||||
[`${componentCls}-select`]: {
|
[`${componentCls}-select`]: {
|
||||||
[`${componentCls}-palette`]: {
|
[`${componentCls}-palette`]: {
|
||||||
minHeight: token.calc(controlHeightLG).mul(4).equal(),
|
minHeight: token.calc(controlHeightLG).mul(4).equal(),
|
||||||
@ -36,6 +35,7 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
|||||||
marginBottom: marginSM,
|
marginBottom: marginSM,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ======================== Panel =========================
|
||||||
[`${componentCls}-handler`]: {
|
[`${componentCls}-handler`]: {
|
||||||
width: colorPickerHandlerSize,
|
width: colorPickerHandlerSize,
|
||||||
height: colorPickerHandlerSize,
|
height: colorPickerHandlerSize,
|
||||||
@ -44,40 +44,6 @@ const genPickerStyle: GenerateStyle<ColorPickerToken, CSSObject> = (token) => {
|
|||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
boxShadow: `${colorPickerInsetShadow}, 0 0 0 1px ${colorFillSecondary}`,
|
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,8 +1,12 @@
|
|||||||
import type { ColorGenInput } from '@rc-component/color-picker';
|
import type { ColorGenInput } from '@rc-component/color-picker';
|
||||||
|
import { Color as RcColor } from '@rc-component/color-picker';
|
||||||
|
|
||||||
import { AggregationColor } from './color';
|
import { AggregationColor } from './color';
|
||||||
|
import type { ColorValueType } from './interface';
|
||||||
|
|
||||||
export const generateColor = (color: ColorGenInput<AggregationColor>): AggregationColor => {
|
export const generateColor = (
|
||||||
|
color: ColorGenInput<AggregationColor> | Exclude<ColorValueType, null>,
|
||||||
|
): AggregationColor => {
|
||||||
if (color instanceof AggregationColor) {
|
if (color instanceof AggregationColor) {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
@ -11,10 +15,55 @@ export const generateColor = (color: ColorGenInput<AggregationColor>): Aggregati
|
|||||||
|
|
||||||
export const getRoundNumber = (value: number) => Math.round(Number(value || 0));
|
export const getRoundNumber = (value: number) => Math.round(Number(value || 0));
|
||||||
|
|
||||||
export const getAlphaColor = (color: AggregationColor) => getRoundNumber(color.toHsb().a * 100);
|
export const getColorAlpha = (color: AggregationColor) => getRoundNumber(color.toHsb().a * 100);
|
||||||
|
|
||||||
|
/** Return the color whose `alpha` is 1 */
|
||||||
export const genAlphaColor = (color: AggregationColor, alpha?: number) => {
|
export const genAlphaColor = (color: AggregationColor, alpha?: number) => {
|
||||||
const hsba = color.toHsb();
|
const hsba = color.toHsb();
|
||||||
hsba.a = alpha || 1;
|
hsba.a = alpha || 1;
|
||||||
return generateColor(hsba);
|
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 '';
|
||||||
|
};
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColorPickerProps, GetProp } 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 = {
|
type ThemeData = {
|
||||||
borderRadius: number;
|
borderRadius: number;
|
||||||
|
@ -5309,7 +5309,7 @@ Array [
|
|||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<div
|
||||||
class="ant-color-picker-handler"
|
class="ant-color-picker-handler"
|
||||||
@ -5329,46 +5329,46 @@ Array [
|
|||||||
class="ant-color-picker-slider-group"
|
class="ant-color-picker-slider-group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
style="position: relative;"
|
/>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
class="ant-slider-step"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
aria-disabled="false"
|
||||||
style="background-color: rgb(255, 0, 0);"
|
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>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
style="position: absolute; inset: 0;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
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);"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider-step"
|
||||||
style="position: absolute; inset: 0;"
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -22742,7 +22742,7 @@ exports[`renders components/form/demo/validate-other.tsx extend context correctl
|
|||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<div
|
||||||
class="ant-color-picker-handler"
|
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"
|
class="ant-color-picker-slider-group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
style="position: relative;"
|
/>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
class="ant-slider-step"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
aria-disabled="false"
|
||||||
style="background-color: rgb(255, 0, 0);"
|
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>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
style="position: absolute; inset: 0;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
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);"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider-step"
|
||||||
style="position: absolute; inset: 0;"
|
/>
|
||||||
|
<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>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -144,6 +144,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Boşdur',
|
presetEmpty: 'Boşdur',
|
||||||
|
transparent: 'Şəffaf',
|
||||||
|
singleColor: 'Tək rəng',
|
||||||
|
gradientColor: 'Gradient rəng',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Empty',
|
presetEmpty: 'Empty',
|
||||||
|
transparent: 'Transparent',
|
||||||
|
singleColor: 'Single',
|
||||||
|
gradientColor: 'Gradient',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Hustu',
|
presetEmpty: 'Hustu',
|
||||||
|
transparent: 'Gardena',
|
||||||
|
singleColor: 'Kolore bakarra',
|
||||||
|
gradientColor: 'Gradiente kolorea',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Kosong',
|
presetEmpty: 'Kosong',
|
||||||
|
transparent: 'Transparan',
|
||||||
|
singleColor: 'Warna tunggal',
|
||||||
|
gradientColor: 'Warna gradien',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@ export interface Locale {
|
|||||||
};
|
};
|
||||||
ColorPicker?: {
|
ColorPicker?: {
|
||||||
presetEmpty: string;
|
presetEmpty: string;
|
||||||
|
transparent: string;
|
||||||
|
singleColor: string;
|
||||||
|
gradientColor: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: '空の',
|
presetEmpty: '空の',
|
||||||
|
transparent: '透明',
|
||||||
|
singleColor: '単色',
|
||||||
|
gradientColor: 'グラデーション',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: '미정',
|
presetEmpty: '미정',
|
||||||
|
transparent: '투명',
|
||||||
|
singleColor: '단색',
|
||||||
|
gradientColor: '그라데이션',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,6 +140,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Tuščia',
|
presetEmpty: 'Tuščia',
|
||||||
|
transparent: 'Permatomas',
|
||||||
|
singleColor: 'Vieno spalvos',
|
||||||
|
gradientColor: 'Gradientas',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,6 +141,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'Tiada',
|
presetEmpty: 'Tiada',
|
||||||
|
transparent: 'Tidak tembus cahaya',
|
||||||
|
singleColor: 'Warna tunggal',
|
||||||
|
gradientColor: 'Warna gradien',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,6 +142,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'अहिलेसम्म कुनै पनि छैन',
|
presetEmpty: 'अहिलेसम्म कुनै पनि छैन',
|
||||||
|
transparent: 'पारदर्शी',
|
||||||
|
singleColor: 'एक रंग',
|
||||||
|
gradientColor: 'ग्रेडिएण्ट',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,6 +143,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: 'ไม่มีข้อมูล',
|
presetEmpty: 'ไม่มีข้อมูล',
|
||||||
|
transparent: 'โปร่งใส',
|
||||||
|
singleColor: 'สีเดียว',
|
||||||
|
gradientColor: 'สีไล่ระดับ',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,6 +144,9 @@ const localeValues: Locale = {
|
|||||||
},
|
},
|
||||||
ColorPicker: {
|
ColorPicker: {
|
||||||
presetEmpty: '暂无',
|
presetEmpty: '暂无',
|
||||||
|
transparent: '无色',
|
||||||
|
singleColor: '单色',
|
||||||
|
gradientColor: '渐变色',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -457,11 +457,7 @@ export default () => {
|
|||||||
<Anchor>
|
<Anchor>
|
||||||
<Link href="#anchor-demo-basic" title="Basic demo" />
|
<Link href="#anchor-demo-basic" title="Basic demo" />
|
||||||
<Link href="#anchor-demo-static" title="Static demo" />
|
<Link href="#anchor-demo-static" title="Static demo" />
|
||||||
<Link
|
<Link href="#anchor-demo-basic" title="Basic demo with Target" target="_blank" />
|
||||||
href="#anchor-demo-basic"
|
|
||||||
title="Basic demo with Target"
|
|
||||||
target="_blank"
|
|
||||||
/>
|
|
||||||
<Link href="#API" title="API">
|
<Link href="#API" title="API">
|
||||||
<Link href="#Anchor-Props" title="Anchor Props" />
|
<Link href="#Anchor-Props" title="Anchor Props" />
|
||||||
<Link href="#Link-Props" title="Link Props" />
|
<Link href="#Link-Props" title="Link Props" />
|
||||||
|
@ -3,12 +3,7 @@ import { Keyframes, unit } from '@ant-design/cssinjs';
|
|||||||
|
|
||||||
import { CONTAINER_MAX_OFFSET } from '../../_util/hooks/useZIndex';
|
import { CONTAINER_MAX_OFFSET } from '../../_util/hooks/useZIndex';
|
||||||
import { genFocusStyle, resetComponent } from '../../style';
|
import { genFocusStyle, resetComponent } from '../../style';
|
||||||
import type {
|
import type { AliasToken, FullToken, GenerateStyle, GenStyleFn } from '../../theme/internal';
|
||||||
AliasToken,
|
|
||||||
FullToken,
|
|
||||||
GenerateStyle,
|
|
||||||
GenStyleFn,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
import genNotificationPlacementStyle from './placement';
|
import genNotificationPlacementStyle from './placement';
|
||||||
import genStackStyle from './stack';
|
import genStackStyle from './stack';
|
||||||
|
@ -10,12 +10,7 @@ import {
|
|||||||
import type { SharedComponentToken, SharedInputToken } from '../../input/style/token';
|
import type { SharedComponentToken, SharedInputToken } from '../../input/style/token';
|
||||||
import { genBaseOutlinedStyle, genDisabledStyle } from '../../input/style/variants';
|
import { genBaseOutlinedStyle, genDisabledStyle } from '../../input/style/variants';
|
||||||
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
|
import { genFocusOutline, genFocusStyle, resetComponent } from '../../style';
|
||||||
import type {
|
import type { FullToken, GenerateStyle, GetDefaultToken, GenStyleFn } from '../../theme/internal';
|
||||||
FullToken,
|
|
||||||
GenerateStyle,
|
|
||||||
GetDefaultToken,
|
|
||||||
GenStyleFn,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
export interface ComponentToken {
|
export interface ComponentToken {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
import type { CSSObject } from '@ant-design/cssinjs';
|
import type { CSSObject } from '@ant-design/cssinjs';
|
||||||
import { Keyframes, unit } from '@ant-design/cssinjs';
|
import { Keyframes, unit } from '@ant-design/cssinjs';
|
||||||
|
|
||||||
import type {
|
import type { FullToken, GenerateStyle, GetDefaultToken, CSSUtil } from '../../theme/internal';
|
||||||
FullToken,
|
|
||||||
GenerateStyle,
|
|
||||||
GetDefaultToken,
|
|
||||||
CSSUtil,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
export type ComponentToken = {
|
export type ComponentToken = {
|
||||||
|
14
components/slider/Context.ts
Normal file
14
components/slider/Context.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import type { SliderProps as RcSliderProps } from 'rc-slider';
|
||||||
|
|
||||||
|
import type { DirectionType } from '../config-provider';
|
||||||
|
|
||||||
|
export interface SliderInternalContextProps {
|
||||||
|
handleRender?: RcSliderProps['handleRender'];
|
||||||
|
direction?: DirectionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @private Internal context. Do not use in your production. */
|
||||||
|
const SliderInternalContext = createContext<SliderInternalContextProps>({});
|
||||||
|
|
||||||
|
export default SliderInternalContext;
|
@ -12,6 +12,7 @@ import DisabledContext from '../config-provider/DisabledContext';
|
|||||||
import type { AbstractTooltipProps, TooltipPlacement } from '../tooltip';
|
import type { AbstractTooltipProps, TooltipPlacement } from '../tooltip';
|
||||||
import SliderTooltip from './SliderTooltip';
|
import SliderTooltip from './SliderTooltip';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
|
import SliderInternalContext from './Context';
|
||||||
import useRafLock from './useRafLock';
|
import useRafLock from './useRafLock';
|
||||||
|
|
||||||
export type SliderMarks = RcSliderProps['marks'];
|
export type SliderMarks = RcSliderProps['marks'];
|
||||||
@ -146,10 +147,22 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
|
|
||||||
const { vertical } = props;
|
const { vertical } = props;
|
||||||
|
|
||||||
const { direction, slider, getPrefixCls, getPopupContainer } = React.useContext(ConfigContext);
|
const {
|
||||||
|
direction: contextDirection,
|
||||||
|
slider,
|
||||||
|
getPrefixCls,
|
||||||
|
getPopupContainer,
|
||||||
|
} = React.useContext(ConfigContext);
|
||||||
const contextDisabled = React.useContext(DisabledContext);
|
const contextDisabled = React.useContext(DisabledContext);
|
||||||
const mergedDisabled = disabled ?? contextDisabled;
|
const mergedDisabled = disabled ?? contextDisabled;
|
||||||
|
|
||||||
|
// ============================= Context ==============================
|
||||||
|
const { handleRender: contextHandleRender, direction: internalContextDirection } =
|
||||||
|
React.useContext(SliderInternalContext);
|
||||||
|
|
||||||
|
const mergedDirection = internalContextDirection || contextDirection;
|
||||||
|
const isRTL = mergedDirection === 'rtl';
|
||||||
|
|
||||||
// =============================== Open ===============================
|
// =============================== Open ===============================
|
||||||
const [hoverOpen, setHoverOpen] = useRafLock();
|
const [hoverOpen, setHoverOpen] = useRafLock();
|
||||||
const [focusOpen, setFocusOpen] = useRafLock();
|
const [focusOpen, setFocusOpen] = useRafLock();
|
||||||
@ -186,7 +199,7 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
if (!vert) {
|
if (!vert) {
|
||||||
return 'top';
|
return 'top';
|
||||||
}
|
}
|
||||||
return direction === 'rtl' ? 'left' : 'right';
|
return isRTL ? 'left' : 'right';
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================== Style ===============================
|
// ============================== Style ===============================
|
||||||
@ -199,7 +212,7 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
slider?.className,
|
slider?.className,
|
||||||
rootClassName,
|
rootClassName,
|
||||||
{
|
{
|
||||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
[`${prefixCls}-rtl`]: isRTL,
|
||||||
[`${prefixCls}-lock`]: dragging,
|
[`${prefixCls}-lock`]: dragging,
|
||||||
},
|
},
|
||||||
hashId,
|
hashId,
|
||||||
@ -207,7 +220,7 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
);
|
);
|
||||||
|
|
||||||
// make reverse default on rtl direction
|
// make reverse default on rtl direction
|
||||||
if (direction === 'rtl' && !restProps.vertical) {
|
if (isRTL && !restProps.vertical) {
|
||||||
restProps.reverse = !restProps.reverse;
|
restProps.reverse = !restProps.reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,35 +259,49 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
|
|
||||||
const useActiveTooltipHandle = range && !lockOpen;
|
const useActiveTooltipHandle = range && !lockOpen;
|
||||||
|
|
||||||
const handleRender: RcSliderProps['handleRender'] = (node, info) => {
|
const handleRender: RcSliderProps['handleRender'] =
|
||||||
|
contextHandleRender ||
|
||||||
|
((node, info) => {
|
||||||
const { index } = info;
|
const { index } = info;
|
||||||
|
|
||||||
const nodeProps = node.props;
|
const nodeProps = node.props;
|
||||||
|
|
||||||
|
function proxyEvent(
|
||||||
|
eventName: string,
|
||||||
|
event: React.SyntheticEvent,
|
||||||
|
triggerRestPropsEvent?: boolean,
|
||||||
|
) {
|
||||||
|
if (triggerRestPropsEvent) {
|
||||||
|
(restProps as any)[eventName]?.(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodeProps as any)[eventName]?.(event);
|
||||||
|
}
|
||||||
|
|
||||||
const passedProps: typeof nodeProps = {
|
const passedProps: typeof nodeProps = {
|
||||||
...nodeProps,
|
...nodeProps,
|
||||||
onMouseEnter: (e) => {
|
onMouseEnter: (e) => {
|
||||||
setHoverOpen(true);
|
setHoverOpen(true);
|
||||||
nodeProps.onMouseEnter?.(e);
|
proxyEvent('onMouseEnter', e);
|
||||||
},
|
},
|
||||||
onMouseLeave: (e) => {
|
onMouseLeave: (e) => {
|
||||||
setHoverOpen(false);
|
setHoverOpen(false);
|
||||||
nodeProps.onMouseLeave?.(e);
|
proxyEvent('onMouseLeave', e);
|
||||||
},
|
},
|
||||||
onMouseDown: (e) => {
|
onMouseDown: (e) => {
|
||||||
setFocusOpen(true);
|
setFocusOpen(true);
|
||||||
setDragging(true);
|
setDragging(true);
|
||||||
nodeProps.onMouseDown?.(e);
|
proxyEvent('onMouseDown', e);
|
||||||
},
|
},
|
||||||
onFocus: (e) => {
|
onFocus: (e) => {
|
||||||
setFocusOpen(true);
|
setFocusOpen(true);
|
||||||
restProps.onFocus?.(e);
|
restProps.onFocus?.(e);
|
||||||
nodeProps.onFocus?.(e);
|
proxyEvent('onFocus', e, true);
|
||||||
},
|
},
|
||||||
onBlur: (e) => {
|
onBlur: (e) => {
|
||||||
setFocusOpen(false);
|
setFocusOpen(false);
|
||||||
restProps.onBlur?.(e);
|
restProps.onBlur?.(e);
|
||||||
nodeProps.onBlur?.(e);
|
proxyEvent('onBlur', e, true);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -303,7 +330,7 @@ const Slider = React.forwardRef<SliderRef, SliderSingleProps | SliderRangeProps>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return cloneNode;
|
return cloneNode;
|
||||||
};
|
});
|
||||||
|
|
||||||
// ========================== Active Handle ===========================
|
// ========================== Active Handle ===========================
|
||||||
const activeHandleRender: SliderProps['activeHandleRender'] = useActiveTooltipHandle
|
const activeHandleRender: SliderProps['activeHandleRender'] = useActiveTooltipHandle
|
||||||
|
@ -198,6 +198,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = (token) => {
|
|||||||
width: handleSize,
|
width: handleSize,
|
||||||
height: handleSize,
|
height: handleSize,
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
userSelect: 'none',
|
||||||
|
|
||||||
// Dragging status
|
// Dragging status
|
||||||
'&-dragging-delete': {
|
'&-dragging-delete': {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||||
|
|
||||||
import type {
|
import type { AliasToken, FullToken, OverrideComponent, CSSUtil } from '../theme/internal';
|
||||||
AliasToken,
|
|
||||||
FullToken,
|
|
||||||
OverrideComponent,
|
|
||||||
CSSUtil,
|
|
||||||
} from '../theme/internal';
|
|
||||||
|
|
||||||
function compactItemVerticalBorder(token: AliasToken & CSSUtil, parentCls: string): CSSObject {
|
function compactItemVerticalBorder(token: AliasToken & CSSUtil, parentCls: string): CSSObject {
|
||||||
return {
|
return {
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
/* eslint-disable import/prefer-default-export */
|
/* eslint-disable import/prefer-default-export */
|
||||||
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
|
||||||
|
|
||||||
import type {
|
import type { AliasToken, FullToken, OverrideComponent, CSSUtil } from '../theme/internal';
|
||||||
AliasToken,
|
|
||||||
FullToken,
|
|
||||||
OverrideComponent,
|
|
||||||
CSSUtil,
|
|
||||||
} from '../theme/internal';
|
|
||||||
|
|
||||||
interface CompactItemOptions {
|
interface CompactItemOptions {
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
|
@ -12,10 +12,7 @@ export const textEllipsis: CSSObject = {
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetComponent = (
|
export const resetComponent = (token: AliasToken, needInheritFontFamily = false): CSSObject => ({
|
||||||
token: AliasToken,
|
|
||||||
needInheritFontFamily = false,
|
|
||||||
): CSSObject => ({
|
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
@ -176,11 +176,7 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form
|
<Form layout="inline" className="table-demo-control-bar" style={{ marginBottom: 16 }}>
|
||||||
layout="inline"
|
|
||||||
className="table-demo-control-bar"
|
|
||||||
style={{ marginBottom: 16 }}
|
|
||||||
>
|
|
||||||
<Form.Item label="Bordered">
|
<Form.Item label="Bordered">
|
||||||
<Switch checked={bordered} onChange={handleBorderChange} />
|
<Switch checked={bordered} onChange={handleBorderChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -176,11 +176,7 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form
|
<Form layout="inline" className="table-demo-control-bar" style={{ marginBottom: 16 }}>
|
||||||
layout="inline"
|
|
||||||
className="table-demo-control-bar"
|
|
||||||
style={{ marginBottom: 16 }}
|
|
||||||
>
|
|
||||||
<Form.Item label="Bordered">
|
<Form.Item label="Bordered">
|
||||||
<Switch checked={bordered} onChange={handleBorderChange} />
|
<Switch checked={bordered} onChange={handleBorderChange} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -99,11 +99,7 @@ const App: React.FC = () => {
|
|||||||
const [childTableBordered, setChildTableBordered] = useState(true);
|
const [childTableBordered, setChildTableBordered] = useState(true);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form
|
<Form layout="inline" className="table-demo-control-bar" style={{ marginBottom: 16 }}>
|
||||||
layout="inline"
|
|
||||||
className="table-demo-control-bar"
|
|
||||||
style={{ marginBottom: 16 }}
|
|
||||||
>
|
|
||||||
<Form.Item label="Root Table Bordered">
|
<Form.Item label="Root Table Bordered">
|
||||||
<Switch checked={rootTableBordered} onChange={(v) => setRootTableBordered(v)} />
|
<Switch checked={rootTableBordered} onChange={(v) => setRootTableBordered(v)} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@ -4,11 +4,7 @@ import type { CSSInterpolation } from '@ant-design/cssinjs';
|
|||||||
import { TinyColor } from '@ctrl/tinycolor';
|
import { TinyColor } from '@ctrl/tinycolor';
|
||||||
|
|
||||||
import { resetComponent } from '../../style';
|
import { resetComponent } from '../../style';
|
||||||
import type {
|
import type { FullToken, GetDefaultToken, GenStyleFn } from '../../theme/internal';
|
||||||
FullToken,
|
|
||||||
GetDefaultToken,
|
|
||||||
GenStyleFn,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
export interface ComponentToken {
|
export interface ComponentToken {
|
||||||
|
@ -10,7 +10,6 @@ import type {
|
|||||||
import type { AliasToken } from './alias';
|
import type { AliasToken } from './alias';
|
||||||
import type { ComponentTokenMap } from './components';
|
import type { ComponentTokenMap } from './components';
|
||||||
|
|
||||||
|
|
||||||
/** Final token which contains the components level override */
|
/** Final token which contains the components level override */
|
||||||
export type GlobalToken = GlobalTokenTypeUtil<ComponentTokenMap, AliasToken>;
|
export type GlobalToken = GlobalTokenTypeUtil<ComponentTokenMap, AliasToken>;
|
||||||
|
|
||||||
@ -18,8 +17,20 @@ export type OverrideToken = OverrideTokenTypeUtil<ComponentTokenMap, AliasToken>
|
|||||||
|
|
||||||
export type OverrideComponent = TokenMapKey<ComponentTokenMap>;
|
export type OverrideComponent = TokenMapKey<ComponentTokenMap>;
|
||||||
|
|
||||||
export type FullToken<C extends TokenMapKey<ComponentTokenMap>> = FullTokenTypeUtil<ComponentTokenMap, AliasToken, C>;
|
export type FullToken<C extends TokenMapKey<ComponentTokenMap>> = FullTokenTypeUtil<
|
||||||
|
ComponentTokenMap,
|
||||||
|
AliasToken,
|
||||||
|
C
|
||||||
|
>;
|
||||||
|
|
||||||
export type GetDefaultToken<C extends TokenMapKey<ComponentTokenMap>> = GetDefaultTokenTypeUtil<ComponentTokenMap, AliasToken, C>;
|
export type GetDefaultToken<C extends TokenMapKey<ComponentTokenMap>> = GetDefaultTokenTypeUtil<
|
||||||
|
ComponentTokenMap,
|
||||||
|
AliasToken,
|
||||||
|
C
|
||||||
|
>;
|
||||||
|
|
||||||
export type GenStyleFn<C extends TokenMapKey<ComponentTokenMap>> = GenStyleFnTypeUtil<ComponentTokenMap, AliasToken, C>;
|
export type GenStyleFn<C extends TokenMapKey<ComponentTokenMap>> = GenStyleFnTypeUtil<
|
||||||
|
ComponentTokenMap,
|
||||||
|
AliasToken,
|
||||||
|
C
|
||||||
|
>;
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import { useStyleRegister } from '@ant-design/cssinjs';
|
import { useStyleRegister } from '@ant-design/cssinjs';
|
||||||
import {
|
import { genCalc as calc, mergeToken, statisticToken, statistic } from '@ant-design/cssinjs-utils';
|
||||||
genCalc as calc,
|
|
||||||
mergeToken,
|
|
||||||
statisticToken,
|
|
||||||
statistic,
|
|
||||||
} from '@ant-design/cssinjs-utils';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AliasToken,
|
AliasToken,
|
||||||
@ -22,11 +17,7 @@ import type {
|
|||||||
import { PresetColors } from './interface';
|
import { PresetColors } from './interface';
|
||||||
import { getLineHeight } from './themes/shared/genFontSizes';
|
import { getLineHeight } from './themes/shared/genFontSizes';
|
||||||
import useToken from './useToken';
|
import useToken from './useToken';
|
||||||
import {
|
import { genComponentStyleHook, genStyleHooks, genSubStyleComponent } from './util/genStyleUtils';
|
||||||
genComponentStyleHook,
|
|
||||||
genStyleHooks,
|
|
||||||
genSubStyleComponent,
|
|
||||||
} from './util/genStyleUtils';
|
|
||||||
import genPresetColor from './util/genPresetColor';
|
import genPresetColor from './util/genPresetColor';
|
||||||
import useResetIconStyle from './util/useResetIconStyle';
|
import useResetIconStyle from './util/useResetIconStyle';
|
||||||
|
|
||||||
|
@ -4,12 +4,7 @@ import { Keyframes, unit } from '@ant-design/cssinjs';
|
|||||||
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
|
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
|
||||||
import { genFocusOutline, resetComponent } from '../../style';
|
import { genFocusOutline, resetComponent } from '../../style';
|
||||||
import { genCollapseMotion } from '../../style/motion';
|
import { genCollapseMotion } from '../../style/motion';
|
||||||
import type {
|
import type { AliasToken, FullToken, GetDefaultToken, CSSUtil } from '../../theme/internal';
|
||||||
AliasToken,
|
|
||||||
FullToken,
|
|
||||||
GetDefaultToken,
|
|
||||||
CSSUtil,
|
|
||||||
} from '../../theme/internal';
|
|
||||||
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
import { genStyleHooks, mergeToken } from '../../theme/internal';
|
||||||
|
|
||||||
export interface TreeSharedToken {
|
export interface TreeSharedToken {
|
||||||
|
@ -176,11 +176,12 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const extraContent = typeof customExtra === 'function' ? customExtra(file) : customExtra;
|
const extraContent = typeof customExtra === 'function' ? customExtra(file) : customExtra;
|
||||||
const extra = extraContent && <span className={`${prefixCls}-list-item-extra`}>{extraContent}</span>
|
const extra = extraContent && (
|
||||||
|
<span className={`${prefixCls}-list-item-extra`}>{extraContent}</span>
|
||||||
|
);
|
||||||
|
|
||||||
const listItemNameClass = classNames(`${prefixCls}-list-item-name`);
|
const listItemNameClass = classNames(`${prefixCls}-list-item-name`);
|
||||||
const fileName = file.url
|
const fileName = file.url ? (
|
||||||
?
|
|
||||||
<a
|
<a
|
||||||
key="view"
|
key="view"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -194,7 +195,7 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
|
|||||||
{file.name}
|
{file.name}
|
||||||
{extra}
|
{extra}
|
||||||
</a>
|
</a>
|
||||||
:
|
) : (
|
||||||
<span
|
<span
|
||||||
key="view"
|
key="view"
|
||||||
className={listItemNameClass}
|
className={listItemNameClass}
|
||||||
@ -204,7 +205,7 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
|
|||||||
{file.name}
|
{file.name}
|
||||||
{extra}
|
{extra}
|
||||||
</span>
|
</span>
|
||||||
;
|
);
|
||||||
|
|
||||||
const previewIcon =
|
const previewIcon =
|
||||||
showPreviewIcon && (file.url || file.thumbUrl) ? (
|
showPreviewIcon && (file.url || file.thumbUrl) ? (
|
||||||
|
@ -168,7 +168,7 @@ exports[`renders components/watermark/demo/custom.tsx extend context correctly 1
|
|||||||
style="position: relative;"
|
style="position: relative;"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<div
|
||||||
class="ant-color-picker-handler"
|
class="ant-color-picker-handler"
|
||||||
@ -188,46 +188,46 @@ exports[`renders components/watermark/demo/custom.tsx extend context correctly 1
|
|||||||
class="ant-color-picker-slider-group"
|
class="ant-color-picker-slider-group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-hue"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
style="position: relative;"
|
/>
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
style="position: absolute; left: 0px; top: 0px; z-index: 1;"
|
class="ant-slider-step"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-handler ant-color-picker-handler-sm"
|
aria-disabled="false"
|
||||||
style="background-color: rgb(255, 0, 0);"
|
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>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider ant-color-picker-slider ant-slider-horizontal"
|
||||||
style="position: absolute; inset: 0;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="ant-color-picker-slider ant-color-picker-slider-alpha"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-palette"
|
class="ant-slider-rail ant-color-picker-slider-rail"
|
||||||
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.15);"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="ant-color-picker-gradient"
|
class="ant-slider-step"
|
||||||
style="position: absolute; inset: 0;"
|
/>
|
||||||
|
<div
|
||||||
|
aria-disabled="false"
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
aria-valuemax="100"
|
||||||
|
aria-valuemin="0"
|
||||||
|
aria-valuenow="15"
|
||||||
|
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
|
||||||
|
role="slider"
|
||||||
|
style="left: 15%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.15);"
|
||||||
|
tabindex="0"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|||||||
import { ColorPicker, Flex, Form, Input, InputNumber, Slider, Typography, Watermark } from 'antd';
|
import { ColorPicker, Flex, Form, Input, InputNumber, Slider, Typography, Watermark } from 'antd';
|
||||||
import type { ColorPickerProps, GetProp, WatermarkProps } from 'antd';
|
import type { ColorPickerProps, GetProp, WatermarkProps } from 'antd';
|
||||||
|
|
||||||
type Color = GetProp<ColorPickerProps, 'color'>;
|
type Color = Extract<GetProp<ColorPickerProps, 'value'>, string | { cleared: any }>;
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
|
@ -5,5 +5,8 @@
|
|||||||
},
|
},
|
||||||
"codeSplitting": {
|
"codeSplitting": {
|
||||||
"strategy": "auto"
|
"strategy": "auto"
|
||||||
|
},
|
||||||
|
"watch": {
|
||||||
|
"_nodeModulesRegexes": ["rc-.*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
"@ant-design/react-slick": "~1.1.2",
|
"@ant-design/react-slick": "~1.1.2",
|
||||||
"@babel/runtime": "^7.24.8",
|
"@babel/runtime": "^7.24.8",
|
||||||
"@ctrl/tinycolor": "^3.6.1",
|
"@ctrl/tinycolor": "^3.6.1",
|
||||||
"@rc-component/color-picker": "~1.6.1",
|
"@rc-component/color-picker": "~1.9.0",
|
||||||
"@rc-component/mutate-observer": "^1.1.0",
|
"@rc-component/mutate-observer": "^1.1.0",
|
||||||
"@rc-component/qrcode": "~1.0.0",
|
"@rc-component/qrcode": "~1.0.0",
|
||||||
"@rc-component/tour": "~1.15.0",
|
"@rc-component/tour": "~1.15.0",
|
||||||
@ -135,7 +135,7 @@
|
|||||||
"rc-resize-observer": "^1.4.0",
|
"rc-resize-observer": "^1.4.0",
|
||||||
"rc-segmented": "~2.3.0",
|
"rc-segmented": "~2.3.0",
|
||||||
"rc-select": "~14.15.1",
|
"rc-select": "~14.15.1",
|
||||||
"rc-slider": "~11.1.0",
|
"rc-slider": "~11.1.3",
|
||||||
"rc-steps": "~6.0.1",
|
"rc-steps": "~6.0.1",
|
||||||
"rc-switch": "~4.1.0",
|
"rc-switch": "~4.1.0",
|
||||||
"rc-table": "~7.45.7",
|
"rc-table": "~7.45.7",
|
||||||
|
@ -138,7 +138,12 @@ async function boot() {
|
|||||||
try {
|
try {
|
||||||
await retry(doUpload, 3, 1000);
|
await retry(doUpload, 3, 1000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Uploading file `%s` failed after retry %s, error: %s', fileOrFolderName, 3, err);
|
console.error(
|
||||||
|
'Uploading file `%s` failed after retry %s, error: %s',
|
||||||
|
fileOrFolderName,
|
||||||
|
3,
|
||||||
|
err,
|
||||||
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -152,7 +157,13 @@ async function boot() {
|
|||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await retry(doUpload, 3, 1000);
|
await retry(doUpload, 3, 1000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Skip uploading file `%s` in folder `%s` failed after retry %s, error: %s', path.relative(workspacePath, file), fileOrFolderName, 3, err);
|
console.warn(
|
||||||
|
'Skip uploading file `%s` in folder `%s` failed after retry %s, error: %s',
|
||||||
|
path.relative(workspacePath, file),
|
||||||
|
fileOrFolderName,
|
||||||
|
3,
|
||||||
|
err,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user