diff --git a/src/BaseSelect/index.tsx b/src/BaseSelect/index.tsx index 12b8bf8d4..4c04f713e 100644 --- a/src/BaseSelect/index.tsx +++ b/src/BaseSelect/index.tsx @@ -30,6 +30,7 @@ import { getSeparatedContent, isValidCount } from '../utils/valueUtil'; import SelectContext from '../SelectContext'; import type { SelectContextProps } from '../SelectContext'; import Polite from './Polite'; +export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input'; export type { DisplayInfoType, @@ -131,6 +132,8 @@ export type BaseSelectPropsWithoutPrivate = Omit>; + styles?: Partial>; title?: string; showSearch?: boolean; tagRender?: (props: CustomTagProps) => React.ReactElement; @@ -405,7 +408,12 @@ const BaseSelect = React.forwardRef((props, ref) [tokenSeparators], ); - const { maxCount, rawValues } = React.useContext(SelectContext) || {}; + const { + maxCount, + rawValues, + classNames: selectClassNames, + styles, + } = React.useContext(SelectContext) || {}; const onInternalSearch = (searchText: string, fromTyping: boolean, isCompositing: boolean) => { if (multiple && isValidCount(maxCount) && rawValues?.size >= maxCount) { @@ -720,9 +728,10 @@ const BaseSelect = React.forwardRef((props, ref) if (showSuffixIcon) { arrowNode = ( ((props, ref) ) : ( = (_, r listHeight, listItemHeight, optionRender, + classNames: contextClassNames, + styles: contextStyles, } = React.useContext(SelectContext); const itemPrefixCls = `${prefixCls}-item`; @@ -327,6 +329,8 @@ const OptionList: React.ForwardRefRenderFunction = (_, r direction={direction} innerProps={virtual ? null : a11yProps} showScrollBar={showScrollBar} + className={contextClassNames?.list} + style={contextStyles?.list} > {(item, itemIndex) => { const { group, groupOption, data, label, value } = item; @@ -355,12 +359,18 @@ const OptionList: React.ForwardRefRenderFunction = (_, r const mergedDisabled = disabled || (!selected && overMaxCount); const optionPrefixCls = `${itemPrefixCls}-option`; - const optionClassName = classNames(itemPrefixCls, optionPrefixCls, className, { - [`${optionPrefixCls}-grouped`]: groupOption, - [`${optionPrefixCls}-active`]: activeIndex === itemIndex && !mergedDisabled, - [`${optionPrefixCls}-disabled`]: mergedDisabled, - [`${optionPrefixCls}-selected`]: selected, - }); + const optionClassName = classNames( + itemPrefixCls, + optionPrefixCls, + className, + contextClassNames?.listItem, + { + [`${optionPrefixCls}-grouped`]: groupOption, + [`${optionPrefixCls}-active`]: activeIndex === itemIndex && !mergedDisabled, + [`${optionPrefixCls}-disabled`]: mergedDisabled, + [`${optionPrefixCls}-selected`]: selected, + }, + ); const mergedLabel = getLabel(item); @@ -393,7 +403,7 @@ const OptionList: React.ForwardRefRenderFunction = (_, r onSelectValue(value); } }} - style={style} + style={{ ...contextStyles?.listItem, ...style }} >
{typeof optionRender === 'function' diff --git a/src/Select.tsx b/src/Select.tsx index a4e3dcf2c..e741aa900 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -36,6 +36,7 @@ import type { BaseSelectProps, BaseSelectPropsWithoutPrivate, BaseSelectRef, + BaseSelectSemanticName, DisplayInfoType, DisplayValueType, RenderNode, @@ -107,6 +108,7 @@ export type SelectHandler = T extends (infer E)[] ? E : T; +export type SemanticName = BaseSelectSemanticName | 'listItem' | 'list'; export interface SelectProps extends BaseSelectPropsWithoutPrivate { prefixCls?: string; @@ -157,6 +159,8 @@ export interface SelectProps void; + classNames?: Partial>; + styles?: Partial>; } function isRawValue(value: DraftValueType): value is RawValueType { @@ -204,7 +208,8 @@ const Select = React.forwardRef>; + styles?: Partial>; options: BaseOptionType[]; optionRender?: SelectProps['optionRender']; flattenOptions: FlattenOptionData[]; diff --git a/src/Selector/Input.tsx b/src/Selector/Input.tsx index a3929e367..ab71314b3 100644 --- a/src/Selector/Input.tsx +++ b/src/Selector/Input.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import classNames from 'classnames'; import { composeRef } from '@rc-component/util/lib/ref'; import { warning } from '@rc-component/util/lib/warning'; - +import SelectContext from '../SelectContext'; type InputRef = HTMLInputElement | HTMLTextAreaElement; interface InputProps { @@ -57,6 +57,8 @@ const Input: React.ForwardRefRenderFunction = (props, ref) open, attrs, } = props; + const { classNames: contextClassNames, styles: contextStyles } = + React.useContext(SelectContext) || {}; let inputNode: React.ComponentElement = inputElement || ; @@ -80,7 +82,6 @@ const Input: React.ForwardRefRenderFunction = (props, ref) inputNode = React.cloneElement(inputNode, { type: 'search', ...originProps, - // Override over origin props id, ref: composeRef(ref, originRef as any), @@ -89,7 +90,11 @@ const Input: React.ForwardRefRenderFunction = (props, ref) autoComplete: autoComplete || 'off', autoFocus, - className: classNames(`${prefixCls}-selection-search-input`, inputNode?.props?.className), + className: classNames( + `${prefixCls}-selection-search-input`, + inputNode?.props?.className, + contextClassNames?.input, + ), role: 'combobox', 'aria-expanded': open || false, @@ -104,7 +109,7 @@ const Input: React.ForwardRefRenderFunction = (props, ref) readOnly: !editable, unselectable: !editable ? 'on' : null, - style: { ...style, opacity: editable ? null : 0 }, + style: { ...style, opacity: editable ? null : 0, ...contextStyles?.input }, onKeyDown: (event: React.KeyboardEvent) => { onKeyDown(event); diff --git a/src/Selector/index.tsx b/src/Selector/index.tsx index deff5d31d..af0062405 100644 --- a/src/Selector/index.tsx +++ b/src/Selector/index.tsx @@ -17,6 +17,7 @@ import useLock from '../hooks/useLock'; import { isValidateOpenKey } from '../utils/keyUtil'; import MultipleSelector from './MultipleSelector'; import SingleSelector from './SingleSelector'; +import classNames from 'classnames'; export interface InnerSelectorProps { prefixCls: string; @@ -54,6 +55,8 @@ export interface RefSelectorProps { } export interface SelectorProps { + prefixClassName: string; + prefixStyle: React.CSSProperties; id: string; prefixCls: string; showSearch?: boolean; @@ -107,6 +110,8 @@ const Selector: React.ForwardRefRenderFunction const compositionStatusRef = useRef(false); const { + prefixClassName, + prefixStyle, prefixCls, open, mode, @@ -290,7 +295,11 @@ const Selector: React.ForwardRefRenderFunction onClick={onClick} onMouseDown={onMouseDown} > - {prefix &&
{prefix}
} + {prefix && ( +
+ {prefix} +
+ )} {selectNode}
); diff --git a/src/TransBtn.tsx b/src/TransBtn.tsx index a8c05ee16..33d43a335 100644 --- a/src/TransBtn.tsx +++ b/src/TransBtn.tsx @@ -4,6 +4,7 @@ import type { RenderNode } from './BaseSelect'; export interface TransBtnProps { className: string; + style?: React.CSSProperties; customizeIcon: RenderNode; customizeIconProps?: any; onMouseDown?: React.MouseEventHandler; @@ -12,7 +13,8 @@ export interface TransBtnProps { } const TransBtn: React.FC = (props) => { - const { className, customizeIcon, customizeIconProps, children, onMouseDown, onClick } = props; + const { className, style, customizeIcon, customizeIconProps, children, onMouseDown, onClick } = + props; const icon = typeof customizeIcon === 'function' ? customizeIcon(customizeIconProps) : customizeIcon; @@ -24,7 +26,7 @@ const TransBtn: React.FC = (props) => { event.preventDefault(); onMouseDown?.(event); }} - style={{ userSelect: 'none', WebkitUserSelect: 'none' }} + style={{ userSelect: 'none', WebkitUserSelect: 'none', ...style }} unselectable="on" onClick={onClick} aria-hidden diff --git a/tests/Select.test.tsx b/tests/Select.test.tsx index 7e01870d9..d1e0cf70c 100644 --- a/tests/Select.test.tsx +++ b/tests/Select.test.tsx @@ -2417,4 +2417,51 @@ describe('Select.Basic', () => { expect(onBlur).toHaveBeenCalledTimes(2); expect(inputElem.value).toEqual('bb'); }); + it('support classnames and styles', () => { + const customClassNames = { + prefix: 'cutsom-prefix', + suffix: 'custom-suffix', + list: 'custom-list', + listItem: 'custom-item', + input: 'custom-input', + }; + const customStyle = { + prefix: { color: 'red' }, + suffix: { color: 'green' }, + list: { color: 'yellow' }, + listItem: { color: 'blue' }, + input: { color: 'black' }, + }; + const { container } = render( +