feat(color-picker): Support better customization with semantic classNames/styles as function (#54962)

* feat(color-picker): Support better customization with semantic classNames/styles as function

* update demo

* update demo

---------

Co-authored-by: thinkasany <480968828@qq.com>
This commit is contained in:
遇见同学
2025-10-01 00:25:52 +08:00
committed by GitHub
parent e7148d3300
commit 4afd480fba
17 changed files with 1057 additions and 34 deletions

View File

@@ -75,7 +75,33 @@ const ColorPicker: CompoundedComponent = (props) => {
styles: contextStyles,
} = useComponentConfig('colorPicker');
const [mergedClassNames, mergedStyles] = useMergeSemantic(
const contextDisabled = useContext(DisabledContext);
const mergedDisabled = disabled ?? contextDisabled;
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
// ================== Size ==================
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const mergedSize = useSize((ctx) => customizeSize ?? compactSize ?? ctx);
// =========== Merged Props for Semantic ===========
const mergedProps: ColorPickerProps = {
...props,
trigger,
allowClear,
autoAdjustOverflow,
disabledAlpha,
arrow,
placement,
disabled: mergedDisabled,
size: mergedSize,
};
const [mergedClassNames, mergedStyles] = useMergeSemantic<
NonNullable<ColorPickerProps['classNames']>,
NonNullable<ColorPickerProps['styles']>,
ColorPickerProps
>(
[contextClassNames, classNames],
[contextStyles, styles],
{
@@ -83,11 +109,11 @@ const ColorPicker: CompoundedComponent = (props) => {
_default: 'root',
},
},
{
props: mergedProps,
},
);
const contextDisabled = useContext(DisabledContext);
const mergedDisabled = disabled ?? contextDisabled;
const [internalPopupOpen, setPopupOpen] = useControlledState(false, open);
const popupOpen = !mergedDisabled && internalPopupOpen;
@@ -107,8 +133,6 @@ const ColorPicker: CompoundedComponent = (props) => {
}
};
const prefixCls = getPrefixCls('color-picker', customizePrefixCls);
// ================== Value & Mode =================
const [mergedColor, setColor, modeState, setModeState, modeOptions] = useModeColor(
defaultValue,
@@ -196,11 +220,7 @@ const ColorPicker: CompoundedComponent = (props) => {
// ================== Form Status ==================
const { status: contextStatus } = React.useContext(FormItemInputContext);
// ==================== Compact ====================
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
// ===================== Style =====================
const mergedSize = useSize((ctx) => customizeSize ?? compactSize ?? ctx);
const rootCls = useCSSVarCls(prefixCls);
const [hashId, cssVarCls] = useStyle(prefixCls, rootCls);

View File

@@ -9853,6 +9853,794 @@ exports[`renders components/color-picker/demo/size.tsx extend context correctly
exports[`renders components/color-picker/demo/size.tsx extend context correctly 2`] = `[]`;
exports[`renders components/color-picker/demo/style-class.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center css-var-test-id"
style="flex-wrap: wrap; column-gap: 8px; row-gap: 16px;"
>
<div
class="ant-space-item"
>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
>
<div
aria-describedby="test-id"
class="ant-color-picker-trigger acss-pbemrr css-var-test-id ant-color-picker-css-var"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
</div>
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popover-css-var css-var-test-id css-var-test-id ant-color-picker acss-pbemrr css-var-test-id ant-color-picker-css-var ant-popover-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; right: auto; bottom: auto; box-sizing: border-box; border: 1px solid rgb(255, 255, 255);"
>
<div
class="ant-popover-arrow"
style="position: absolute;"
>
<span
class="ant-popover-arrow-content"
/>
</div>
<div
class="ant-popover-container"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-content"
>
<div
class="ant-color-picker-inner"
>
<div
class="ant-color-picker-inner-content"
>
<div
class="ant-color-picker-panel"
>
<div
class="ant-color-picker-select"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 91.37254901960785%; top: 0%; z-index: 1; transform: translate(-50%, -50%);"
>
<div
class="ant-color-picker-handler"
style="background-color: rgb(22, 119, 255);"
/>
</div>
<div
class="ant-color-picker-saturation"
style="background-color: rgb(0, 106, 255); background-image: linear-gradient(0deg, #000, transparent), linear-gradient(90deg, #fff, hsla(0, 0%, 100%, 0));"
/>
</div>
</div>
<div
class="ant-color-picker-slider-container"
>
<div
class="ant-color-picker-slider-group"
>
<div
class="ant-slider ant-color-picker-slider css-var-test-id ant-slider-horizontal"
>
<div
class="ant-slider-rail ant-color-picker-slider-rail"
style="background: linear-gradient(90deg, rgb(255, 0, 0) 0%, rgb(255, 255, 0) 17%, rgb(0, 255, 0) 33%, rgb(0, 255, 255) 50%, rgb(0, 0, 255) 67%, rgb(255, 0, 255) 83%, rgb(255, 0, 0) 100%);"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-orientation="horizontal"
aria-valuemax="359"
aria-valuemin="0"
aria-valuenow="215"
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
role="slider"
style="left: 59.888579387186624%; transform: translateX(-50%); background: rgb(0, 106, 255);"
tabindex="0"
/>
</div>
<div
class="ant-slider ant-color-picker-slider css-var-test-id ant-slider-horizontal"
>
<div
class="ant-slider-rail ant-color-picker-slider-rail"
style="background: linear-gradient(90deg, rgba(255, 0, 4, 0) 0%, rgb(22,119,255) 100%);"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-orientation="horizontal"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="100"
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
role="slider"
style="left: 100%; transform: translateX(-50%); background: rgb(22, 119, 255);"
tabindex="0"
/>
</div>
</div>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(22, 119, 255);"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-input-container"
>
<div
class="ant-select ant-select-sm ant-select-borderless ant-color-picker-format-select css-var-test-id ant-select-css-var ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-wrap"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="test-id_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="test-id_list"
autocomplete="off"
class="ant-select-selection-search-input"
id="test-id"
readonly=""
role="combobox"
style="opacity: 0;"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-item"
title="HEX"
>
HEX
</span>
</span>
</div>
<div
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up css-var-test-id ant-select-css-var ant-select-dropdown-placement-bottomRight"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; right: auto; bottom: auto; box-sizing: border-box; z-index: 1150; width: 68px;"
>
<div>
<div
id="test-id_list"
role="listbox"
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="HEX"
aria-selected="true"
id="test-id_list_0"
role="option"
>
hex
</div>
<div
aria-label="HSB"
aria-selected="false"
id="test-id_list_1"
role="option"
>
hsb
</div>
</div>
<div
class="rc-virtual-list"
style="position: relative;"
>
<div
class="rc-virtual-list-holder"
style="max-height: 256px; overflow-y: auto; overflow-anchor: none;"
>
<div>
<div
class="rc-virtual-list-holder-inner"
style="display: flex; flex-direction: column;"
>
<div
class="ant-select-item ant-select-item-option ant-select-item-option-active ant-select-item-option-selected"
title="HEX"
>
<div
class="ant-select-item-option-content"
>
HEX
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
class="ant-select-item ant-select-item-option"
title="HSB"
>
<div
class="ant-select-item-option-content"
>
HSB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
class="ant-select-item ant-select-item-option"
title="RGB"
>
<div
class="ant-select-item-option-content"
>
RGB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select: none;"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-color-picker-input"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-sm ant-input-outlined ant-color-picker-hex-input css-var-test-id ant-input-css-var"
>
<span
class="ant-input-prefix"
>
#
</span>
<input
class="ant-input ant-input-sm"
type="text"
value="1677ff"
/>
</span>
</div>
<div
class="ant-input-number ant-input-number-sm ant-input-number-outlined css-var-test-id ant-input-number-css-var ant-color-picker-steppers ant-color-picker-alpha-input"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="true"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="100"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value="100%"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
>
<div
aria-describedby="test-id"
class="ant-color-picker-trigger ant-color-picker-lg acss-pbemrr css-var-test-id ant-color-picker-css-var"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(114, 46, 209);"
/>
</div>
</div>
<div
class="ant-popover ant-zoom-big-appear ant-zoom-big-appear-prepare ant-zoom-big ant-popover-css-var css-var-test-id css-var-test-id ant-color-picker acss-pbemrr css-var-test-id ant-color-picker-css-var ant-popover-placement-bottomLeft"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; right: auto; bottom: auto; box-sizing: border-box; border: 1px solid rgb(114, 46, 209);"
>
<div
class="ant-popover-container"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-content"
>
<div
class="ant-color-picker-inner"
>
<div
class="ant-color-picker-inner-content"
>
<div
class="ant-color-picker-panel"
>
<div
class="ant-color-picker-select"
>
<div
class="ant-color-picker-palette"
style="position: relative;"
>
<div
style="position: absolute; left: 77.99043062200957%; top: 18.039215686274513%; z-index: 1; transform: translate(-50%, -50%);"
>
<div
class="ant-color-picker-handler"
style="background-color: rgb(114, 46, 209);"
/>
</div>
<div
class="ant-color-picker-saturation"
style="background-color: rgb(106, 0, 255); background-image: linear-gradient(0deg, #000, transparent), linear-gradient(90deg, #fff, hsla(0, 0%, 100%, 0));"
/>
</div>
</div>
<div
class="ant-color-picker-slider-container"
>
<div
class="ant-color-picker-slider-group"
>
<div
class="ant-slider ant-color-picker-slider css-var-test-id ant-slider-horizontal"
>
<div
class="ant-slider-rail ant-color-picker-slider-rail"
style="background: linear-gradient(90deg, rgb(255, 0, 0) 0%, rgb(255, 255, 0) 17%, rgb(0, 255, 0) 33%, rgb(0, 255, 255) 50%, rgb(0, 0, 255) 67%, rgb(255, 0, 255) 83%, rgb(255, 0, 0) 100%);"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-orientation="horizontal"
aria-valuemax="359"
aria-valuemin="0"
aria-valuenow="265"
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
role="slider"
style="left: 73.81615598885793%; transform: translateX(-50%); background: rgb(106, 0, 255);"
tabindex="0"
/>
</div>
<div
class="ant-slider ant-color-picker-slider css-var-test-id ant-slider-horizontal"
>
<div
class="ant-slider-rail ant-color-picker-slider-rail"
style="background: linear-gradient(90deg, rgba(255, 0, 4, 0) 0%, rgb(114,46,209) 100%);"
/>
<div
class="ant-slider-step"
/>
<div
aria-disabled="false"
aria-orientation="horizontal"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="100"
class="ant-slider-handle ant-slider-handle-1 ant-color-picker-slider-handle"
role="slider"
style="left: 100%; transform: translateX(-50%); background: rgb(114, 46, 209);"
tabindex="0"
/>
</div>
</div>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background: rgb(114, 46, 209);"
/>
</div>
</div>
</div>
<div
class="ant-color-picker-input-container"
>
<div
class="ant-select ant-select-sm ant-select-borderless ant-color-picker-format-select css-var-test-id ant-select-css-var ant-select-single ant-select-show-arrow"
>
<div
class="ant-select-selector"
>
<span
class="ant-select-selection-wrap"
>
<span
class="ant-select-selection-search"
>
<input
aria-autocomplete="list"
aria-controls="test-id_list"
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="test-id_list"
autocomplete="off"
class="ant-select-selection-search-input"
id="test-id"
readonly=""
role="combobox"
style="opacity: 0;"
type="search"
unselectable="on"
value=""
/>
</span>
<span
class="ant-select-selection-item"
title="HEX"
>
HEX
</span>
</span>
</div>
<div
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up css-var-test-id ant-select-css-var ant-select-dropdown-placement-bottomRight"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; right: auto; bottom: auto; box-sizing: border-box; z-index: 1150; width: 68px;"
>
<div>
<div
id="test-id_list"
role="listbox"
style="height: 0px; width: 0px; overflow: hidden;"
>
<div
aria-label="HEX"
aria-selected="true"
id="test-id_list_0"
role="option"
>
hex
</div>
<div
aria-label="HSB"
aria-selected="false"
id="test-id_list_1"
role="option"
>
hsb
</div>
</div>
<div
class="rc-virtual-list"
style="position: relative;"
>
<div
class="rc-virtual-list-holder"
style="max-height: 256px; overflow-y: auto; overflow-anchor: none;"
>
<div>
<div
class="rc-virtual-list-holder-inner"
style="display: flex; flex-direction: column;"
>
<div
class="ant-select-item ant-select-item-option ant-select-item-option-active ant-select-item-option-selected"
title="HEX"
>
<div
class="ant-select-item-option-content"
>
HEX
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
class="ant-select-item ant-select-item-option"
title="HSB"
>
<div
class="ant-select-item-option-content"
>
HSB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
<div
class="ant-select-item ant-select-item-option"
title="RGB"
>
<div
class="ant-select-item-option-content"
>
RGB
</div>
<span
aria-hidden="true"
class="ant-select-item-option-state"
style="user-select: none;"
unselectable="on"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<span
aria-hidden="true"
class="ant-select-arrow"
style="user-select: none;"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-suffix"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-color-picker-input"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-sm ant-input-outlined ant-color-picker-hex-input css-var-test-id ant-input-css-var"
>
<span
class="ant-input-prefix"
>
#
</span>
<input
class="ant-input ant-input-sm"
type="text"
value="722ed1"
/>
</span>
</div>
<div
class="ant-input-number ant-input-number-sm ant-input-number-outlined css-var-test-id ant-input-number-css-var ant-color-picker-steppers ant-color-picker-alpha-input"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="true"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up ant-input-number-handler-up-disabled"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="100"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value="100%"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders components/color-picker/demo/style-class.tsx extend context correctly 2`] = `[]`;
exports[`renders components/color-picker/demo/text-render.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"

View File

@@ -561,6 +561,56 @@ exports[`renders components/color-picker/demo/size.tsx correctly 1`] = `
</div>
`;
exports[`renders components/color-picker/demo/style-class.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center css-var-test-id"
style="flex-wrap:wrap;column-gap:8px;row-gap:16px"
>
<div
class="ant-space-item"
>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
>
<div
aria-describedby="test-id"
class="ant-color-picker-trigger acss-pbemrr css-var-test-id ant-color-picker-css-var"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(22,119,255)"
/>
</div>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-flex css-var-test-id ant-flex-gap-small"
>
<div
aria-describedby="test-id"
class="ant-color-picker-trigger ant-color-picker-lg acss-pbemrr css-var-test-id ant-color-picker-css-var"
>
<div
class="ant-color-picker-color-block"
>
<div
class="ant-color-picker-color-block-inner"
style="background:rgb(114,46,209)"
/>
</div>
</div>
</div>
</div>
</div>
`;
exports[`renders components/color-picker/demo/text-render.tsx correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small css-var-test-id"

View File

@@ -0,0 +1,12 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`ColorPicker.Semantic rtl render component should be rendered correctly in RTL direction 1`] = `
<div
aria-describedby="test-id"
class="ant-color-picker-trigger css-var-root ant-color-picker-css-var ant-color-picker-rtl"
>
<div
class="ant-color-picker-clear"
/>
</div>
`;

View File

@@ -0,0 +1,80 @@
import React from 'react';
import { render } from '@testing-library/react';
import { resetWarned } from '../../_util/warning';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import ColorPicker from '../ColorPicker';
describe('ColorPicker.Semantic', () => {
mountTest(ColorPicker);
rtlTest(ColorPicker);
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
beforeEach(() => {
resetWarned();
jest.useFakeTimers();
});
afterEach(() => {
errorSpy.mockReset();
jest.useRealTimers();
});
it('support classNames and styles', () => {
const testClassNames = {
root: 'test-root',
popup: {
root: 'test-popup',
},
};
const testStyles = {
root: { color: 'rgb(255, 0, 0)' },
popup: {
root: { color: 'rgb(0, 255, 0)' },
},
};
const { container } = render(
<ColorPicker defaultValue="red" open classNames={testClassNames} styles={testStyles} />,
);
const root = container.querySelector('.ant-color-picker-trigger');
const popup = container.querySelector('.ant-color-picker');
expect(root).toHaveClass(testClassNames.root);
expect(popup).toHaveClass(testClassNames.popup.root);
expect(root).toHaveStyle(testStyles.root);
expect(popup).toHaveStyle(testStyles.popup.root);
});
it('support classNames and styles as functions', () => {
const classNamesFn = (info: { props: any }) => {
if (info.props.disabled) {
return { root: 'test-disabled' };
}
return { root: 'test-enabled' };
};
const stylesFn = (info: { props: any }) => {
if (info.props.size === 'large') {
return { root: { fontSize: '16px' } };
}
return { root: { fontSize: '14px' } };
};
const { container, rerender } = render(
<ColorPicker defaultValue="red" classNames={classNamesFn} styles={stylesFn} />,
);
let root = container.querySelector('.ant-color-picker-trigger');
expect(root).toHaveClass('test-enabled');
expect(root).toHaveStyle({ fontSize: '14px' });
rerender(
<ColorPicker defaultValue="red" disabled classNames={classNamesFn} styles={stylesFn} />,
);
root = container.querySelector('.ant-color-picker-trigger');
expect(root).toHaveClass('test-disabled');
rerender(
<ColorPicker defaultValue="red" size="large" classNames={classNamesFn} styles={stylesFn} />,
);
root = container.querySelector('.ant-color-picker-trigger');
expect(root).toHaveStyle({ fontSize: '16px' });
});
});

View File

@@ -0,0 +1,7 @@
## zh-CN
通过 `classNames``styles` 传入对象/函数可以自定义 ColorPicker 的[语义化结构](#semantic-dom)样式。
## en-US
You can customize the [semantic dom](#semantic-dom) style of ColorPicker by passing objects/functions through `classNames` and `styles`.

View File

@@ -0,0 +1,53 @@
import React from 'react';
import { ColorPicker, Flex, Space } from 'antd';
import type { ColorPickerProps } from 'antd';
import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => ({
root: {
borderRadius: token.borderRadius,
},
}));
const App: React.FC = () => {
const { styles: classNames } = useStyles();
const stylesObject: ColorPickerProps['styles'] = {
popup: {
root: {
border: '1px solid #fff',
},
},
};
const stylesFn: ColorPickerProps['styles'] = (info) => {
if (info.props.size === 'large') {
return {
popup: {
root: {
border: '1px solid #722ed1',
},
},
};
}
return {};
};
return (
<Space size={[8, 16]} wrap>
<Flex gap="small">
<ColorPicker defaultValue="#1677ff" styles={stylesObject} classNames={classNames} />
</Flex>
<Flex gap="small">
<ColorPicker
defaultValue="#722ed1"
size="large"
styles={stylesFn}
arrow={false}
classNames={classNames}
/>
</Flex>
</Space>
);
};
export default App;

View File

@@ -32,6 +32,7 @@ Used when the user needs to make a customized color selection.
<code src="./demo/presets.tsx">Preset Colors</code>
<code src="./demo/presets-line-gradient.tsx" debug>Preset Line Gradient</code>
<code src="./demo/panel-render.tsx">Custom Render Panel</code>
<code src="./demo/style-class.tsx">Custom semantic dom styling</code>
<code src="./demo/pure-panel.tsx" debug>Pure Render</code>
## API
@@ -46,6 +47,7 @@ Common props ref[Common props](/docs/react/common-props)
| allowClear | Allow clearing color selected | boolean | false | |
| arrow | Configuration for popup arrow | `boolean \| { pointAtCenter: boolean }` | true | |
| children | Trigger of ColorPicker | React.ReactNode | - | |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| defaultValue | Default value of color | [ColorType](#colortype) | - | |
| defaultFormat | Default format of color | `rgb` \| `hex` \| `hsb` | `hex` | 5.9.0 |
| disabled | Disable ColorPicker | boolean | - | |
@@ -61,6 +63,7 @@ Common props ref[Common props](/docs/react/common-props)
| panelRender | Custom Render Panel | `(panel: React.ReactNode, extra: { components: { Picker: FC; Presets: FC } }) => React.ReactNode` | - | 5.7.0 |
| showText | Show color text | boolean \| `(color: Color) => React.ReactNode` | - | 5.7.0 |
| size | Setting the trigger size | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| trigger | ColorPicker trigger mode | `hover` \| `click` | `click` | |
| value | Value of color | [ColorType](#colortype) | - | |
| onChange | Callback when `value` is changed | `(value: Color, css: string) => void` | - | |

View File

@@ -33,6 +33,7 @@ group:
<code src="./demo/presets.tsx">预设颜色</code>
<code src="./demo/presets-line-gradient.tsx" debug>预设渐变色</code>
<code src="./demo/panel-render.tsx">自定义面板</code>
<code src="./demo/style-class.tsx">自定义各种语义结构的样式和类</code>
<code src="./demo/pure-panel.tsx" debug>Pure Render</code>
## API
@@ -47,6 +48,7 @@ group:
| allowClear | 允许清除选择的颜色 | boolean | false | |
| arrow | 配置弹出的箭头 | `boolean \| { pointAtCenter: boolean }` | true | |
| children | 颜色选择器的触发器 | React.ReactNode | - | |
| classNames | 用于自定义组件内部各语义化结构的 class支持对象或函数 | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| defaultValue | 颜色默认的值 | [ColorType](#colortype) | - | |
| defaultFormat | 颜色格式默认的值 | `rgb` \| `hex` \| `hsb` | `hex` | 5.9.0 |
| disabled | 禁用颜色选择器 | boolean | - | |
@@ -62,6 +64,7 @@ group:
| panelRender | 自定义渲染面板 | `(panel: React.ReactNode, extra: { components: { Picker: FC; Presets: FC } }) => React.ReactNode` | - | 5.7.0 |
| showText | 显示颜色文本 | boolean \| `(color: Color) => React.ReactNode` | - | 5.7.0 |
| size | 设置触发器大小 | `large` \| `middle` \| `small` | `middle` | 5.7.0 |
| styles | 用于自定义组件内部各语义化结构的行内 style支持对象或函数 | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| trigger | 颜色选择器的触发模式 | `hover` \| `click` | `click` | |
| value | 颜色的值 | [ColorType](#colortype) | - | |
| onChange | 颜色变化的回调 | `(value: Color, css: string) => void` | - | |

View File

@@ -4,6 +4,7 @@ import type {
ColorPickerProps as RcColorPickerProps,
} from '@rc-component/color-picker';
import type { SemanticClassNamesType, SemanticStylesType } from '../_util/hooks/useMergeSemantic';
import type { SizeType } from '../config-provider/SizeContext';
import type { PopoverProps } from '../popover';
import type { TooltipPlacement } from '../tooltip';
@@ -55,6 +56,22 @@ export type ModeType = 'single' | 'gradient';
type SemanticName = 'root';
type PopupSemantic = 'root';
export type ColorPickerClassNamesType = SemanticClassNamesType<
ColorPickerProps,
SemanticName,
{
popup?: Partial<Record<PopupSemantic, string>>;
}
>;
export type ColorPickerStylesType = SemanticStylesType<
ColorPickerProps,
SemanticName,
{
popup?: Partial<Record<PopupSemantic, React.CSSProperties>>;
popupOverlayInner?: React.CSSProperties;
}
>;
export type ColorPickerProps = Omit<
RcColorPickerProps,
| 'onChange'
@@ -84,13 +101,8 @@ export type ColorPickerProps = Omit<
) => React.ReactNode;
showText?: boolean | ((color: AggregationColor) => React.ReactNode);
size?: SizeType;
classNames?: Partial<Record<SemanticName, string>> & {
popup?: Partial<Record<PopupSemantic, string>>;
};
styles?: Partial<Record<SemanticName, React.CSSProperties>> & {
popup?: Partial<Record<PopupSemantic, React.CSSProperties>>;
popupOverlayInner?: React.CSSProperties;
};
classNames?: ColorPickerClassNamesType;
styles?: ColorPickerStylesType;
rootClassName?: string;
disabledAlpha?: boolean;
[key: `data-${string}`]: string;

View File

@@ -92,7 +92,7 @@ Common props ref[Common props](/docs/react/common-props)
| ~~labelStyle~~ | Customize label style | CSSProperties, Please use `styles.label` instead | - | 4.10.0 |
| layout | Define description layout | `horizontal` \| `vertical` | `horizontal` | |
| size | Set the size of the list. Can be set to `middle`,`small`, or not filled | `default` \| `middle` \| `small` | - | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| title | The title of the description list, placed at the top | ReactNode | - | |
### DescriptionItem

View File

@@ -55,7 +55,7 @@ The properties of config are as follows:
| actions | Customized button group | ReactNode | - | 5.24.0 |
| ~~btn~~ | Customized close button group, please use `actions` instead | ReactNode | - | - |
| className | Customized CSS class | string | - | - |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | [Record<SemanticDOM, string> \| (info: { props })=> Record<SemanticDOM, string>](#semantic-dom) | - | |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | [Record<SemanticDOM, string> \| (info: { props })=> Record<SemanticDOM, string>](#semantic-dom) | - | |
| closeIcon | Custom close icon | ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |
| description | The content of notification box (required) | ReactNode | - | - |
| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | - |
@@ -68,7 +68,7 @@ The properties of config are as follows:
| placement | Position of Notification, can be one of `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | string | `topRight` | - |
| role | The semantics of notification content recognized by screen readers. The default value is `alert`. When set as the default value, the screen reader will promptly interrupt any ongoing content reading and prioritize the notification content for immediate attention. | `alert \| status` | `alert` | 5.6.0 |
| style | Customized inline style | [CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - | - |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | [Record<SemanticDOM, CSSProperties> \| (info: { props })=> Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | [Record<SemanticDOM, CSSProperties> \| (info: { props })=> Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | |
| onClick | Specify a function that will be called when the notification is clicked | function | - | - |
| onClose | Trigger when notification closed | function | - | - |
| props | An object that can contain `data-*`, `aria-*`, or `role` props, to be put on the notification `div`. This currently only allows `data-testid` instead of `data-*` in TypeScript. See https://github.com/microsoft/TypeScript/issues/28960. | Object | - | - |

View File

@@ -56,7 +56,7 @@ config 参数如下:
| actions | 自定义按钮组 | ReactNode | - | 5.24.0 |
| ~~btn~~ | 自定义按钮组,请使用 `actions` 替换 | ReactNode | - | - |
| className | 自定义 CSS class | string | - | - |
| classNames | 用于自定义组件内部各语义化结构的 class支持对象或函数 | [Record<SemanticDOM, string> \| (info: { props })=> Record<SemanticDOM, string>](#semantic-dom) | - | |
| classNames | 用于自定义组件内部各语义化结构的 class支持对象或函数 | [Record<SemanticDOM, string> \| (info: { props })=> Record<SemanticDOM, string>](#semantic-dom) | - | |
| closable | 是否显示右上角的关闭按钮 | boolean \| [ClosableType](#closabletype) | true | - |
| closeIcon | 自定义关闭图标 | ReactNode | true | 5.7.0:设置为 null 或 false 时隐藏关闭按钮 |
| description | 通知提醒内容,必选 | ReactNode | - | - |
@@ -70,7 +70,7 @@ config 参数如下:
| placement | 弹出位置,可选 `top` \| `topLeft` \| `topRight` \| `bottom` \| `bottomLeft` \| `bottomRight` | string | `topRight` | - |
| role | 供屏幕阅读器识别的通知内容语义,默认为 `alert`。此情况下屏幕阅读器会立即打断当前正在阅读的其他内容,转而阅读通知内容 | `alert \| status` | `alert` | 5.6.0 |
| style | 自定义内联样式 | [CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - | - |
| styles | 用于自定义组件内部各语义化结构的行内 style支持对象或函数 | [Record<SemanticDOM, CSSProperties> \| (info: { props })=> Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | |
| styles | 用于自定义组件内部各语义化结构的行内 style支持对象或函数 | [Record<SemanticDOM, CSSProperties> \| (info: { props })=> Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | |
| onClick | 点击通知时触发的回调函数 | function | - | - |
| onClose | 当通知关闭时触发 | function | - | - |
| props | 透传至通知 `div` 上的 props 对象,支持传入 `data-*` `aria-*``role` 作为对象的属性。需要注意的是,虽然在 TypeScript 类型中声明的类型支持传入 `data-*` 作为对象的属性,但目前只允许传入 `data-testid` 作为对象的属性。 详见 https://github.com/microsoft/TypeScript/issues/28960 | Object | - | - |

View File

@@ -45,14 +45,14 @@ Common props ref[Common props](/docs/react/common-props)
| size | QRCode size | number | 160 |
| iconSize | include image size | number \| { width: number; height: number } | 40 | 5.19.0 |
| color | QRCode Color | string | `#000` |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| bgColor | QRCode Background Color | string | `transparent` | 5.5.0 |
| bordered | Whether has border style | boolean | `true` |
| errorLevel | Error Code Level | `'L' \| 'M' \| 'Q' \| 'H' ` | `M` |
| boostLevel | If enabled, the Error Correction Level of the result may be higher than the specified Error Correction Level | `boolean` | true | 5.28.0 |
| status | QRCode status | `active \| expired \| loading \| scanned` | `active` | scanned: 5.13.0 |
| statusRender | custom status render | `(info: [StatusRenderInfo](/components/qr-code-cn#statusrenderinfo)) => React.ReactNode` | - | 5.20.0 |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| type | render type | `canvas \| svg` | `canvas` | 5.6.0 |
| value | scanned text | string | - | |

View File

@@ -9,12 +9,7 @@ import { devUseWarning } from '../_util/warning';
import { useComponentConfig } from '../config-provider/context';
import { useLocale } from '../locale';
import { useToken } from '../theme/internal';
import type {
QRCodeClassNamesType,
QRCodeProps,
QRCodeStylesType,
QRProps,
} from './interface';
import type { QRCodeClassNamesType, QRCodeProps, QRCodeStylesType, QRProps } from './interface';
import QRcodeStatus from './QrcodeStatus';
import useStyle from './style/index';
@@ -54,7 +49,7 @@ const QRCode: React.FC<QRCodeProps> = (props) => {
} = useComponentConfig('qrcode');
// =========== Merged Props for Semantic ===========
const mergedProps : QRCodeProps = {
const mergedProps: QRCodeProps = {
...props,
bgColor,
type,

View File

@@ -46,14 +46,14 @@ tag: 5.1.0
| size | 二维码大小 | number | 160 |
| iconSize | 二维码中图片的大小 | number \| { width: number; height: number } | 40 | 5.19.0 |
| color | 二维码颜色 | string | `#000` |
| classNames | 用于自定义组件内部各语义化结构的 class支持对象或函数 | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| classNames | 用于自定义组件内部各语义化结构的 class支持对象或函数 | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| bgColor | 二维码背景颜色 | string | `transparent` | 5.5.0 |
| bordered | 是否有边框 | boolean | `true` |
| errorLevel | 二维码纠错等级 | `'L' \| 'M' \| 'Q' \| 'H' ` | `M` |
| boostLevel | 如果启用,自动提升纠错等级,结果的纠错级别可能会高于指定的纠错级别 | `boolean` | true | 5.28.0 |
| status | 二维码状态 | `active \| expired \| loading \| scanned` | `active` | scanned: 5.13.0 |
| statusRender | 自定义状态渲染器 | (info: [StatusRenderInfo](/components/qr-code-cn#statusrenderinfo)) => React.ReactNode | - | 5.20.0 |
| styles | 用于自定义组件内部各语义化结构的行内 style支持对象或函数 | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| styles | 用于自定义组件内部各语义化结构的行内 style支持对象或函数 | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| type | 渲染类型 | `canvas \| svg` | `canvas` | 5.6.0 |
| value | 扫描后的文本 | string | - | |

View File

@@ -142,7 +142,7 @@ const Result: ResultType = (props) => {
styles,
classNames: resultClassNames,
} = props;
const {
getPrefixCls,
direction,