diff --git a/docs/examples/basic.tsx b/docs/examples/basic.tsx index c641b2775..08d747111 100644 --- a/docs/examples/basic.tsx +++ b/docs/examples/basic.tsx @@ -51,7 +51,16 @@ export default () => {

Basic

- {...sharedProps} locale={zhCN} suffixIcon="SUFFIX" /> + + {...sharedProps} + locale={zhCN} + suffixIcon="SUFFIX" + rootClassName="bamboo" + className="little" + classNames={{ + root: 'light', + }} + /> {...sharedProps} locale={enUS} />
diff --git a/src/PickerInput/Popup/Footer.tsx b/src/PickerInput/Popup/Footer.tsx index df9e800a9..453205a51 100644 --- a/src/PickerInput/Popup/Footer.tsx +++ b/src/PickerInput/Popup/Footer.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import * as React from 'react'; import type { GenerateConfig } from '../../generate'; import useTimeInfo from '../../hooks/useTimeInfo'; @@ -42,7 +42,13 @@ export default function Footer(props: FooterProps) { disabledDate, } = props; - const { prefixCls, locale, button: Button = 'button' } = React.useContext(PickerContext); + const { + prefixCls, + locale, + button: Button = 'button', + classNames, + styles, + } = React.useContext(PickerContext); // >>> Now const now = generateConfig.getNow(); @@ -70,7 +76,7 @@ export default function Footer(props: FooterProps) { const presetNode = showNow && (
  • @@ -101,7 +107,10 @@ export default function Footer(props: FooterProps) { } return ( -
    +
    {extraNode &&
    {extraNode}
    } {rangeNode}
    diff --git a/src/PickerInput/RangePicker.tsx b/src/PickerInput/RangePicker.tsx index db6d66fbe..7a0b57839 100644 --- a/src/PickerInput/RangePicker.tsx +++ b/src/PickerInput/RangePicker.tsx @@ -1,4 +1,5 @@ import { useEvent, useMergedState } from '@rc-component/util'; +import cls from 'classnames'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import omit from '@rc-component/util/lib/omit'; import pickAttrs from '@rc-component/util/lib/pickAttrs'; @@ -35,6 +36,7 @@ import useRangeValue, { useInnerValue } from './hooks/useRangeValue'; import useShowNow from './hooks/useShowNow'; import Popup, { type PopupShowTimeConfig } from './Popup'; import RangeSelector, { type SelectorIdType } from './Selector/RangeSelector'; +import useSemantic from '../hooks/useSemantic'; function separateConfig(config: T | [T, T] | null | undefined, defaultConfig: T): [T, T] { const singleConfig = config ?? defaultConfig; @@ -158,8 +160,9 @@ function RangePicker( const { // Style prefixCls, - styles, - classNames, + rootClassName, + styles: propStyles, + classNames: propClassNames, // Value defaultValue, @@ -224,6 +227,9 @@ function RangePicker( // ========================= Refs ========================= const selectorRef = usePickerRef(ref); + // ======================= Semantic ======================= + const [mergedClassNames, mergedStyles] = useSemantic(propClassNames, propStyles); + // ========================= Open ========================= const [mergedOpen, setMergeOpen] = useOpen(open, defaultOpen, disabled, onOpenChange); @@ -562,6 +568,8 @@ function RangePicker( 'className', 'onPanelChange', 'disabledTime', + 'classNames', + 'styles', ]); return restProps; }, [filledProps]); @@ -693,10 +701,18 @@ function RangePicker( generateConfig, button: components.button, input: components.input, - styles, - classNames, + classNames: mergedClassNames, + styles: mergedStyles, }), - [prefixCls, locale, generateConfig, components.button, components.input, classNames, styles], + [ + prefixCls, + locale, + generateConfig, + components.button, + components.input, + mergedClassNames, + mergedStyles, + ], ); // ======================== Effect ======================== @@ -755,8 +771,8 @@ function RangePicker( ( {...filledProps} // Ref ref={selectorRef} + // Style + className={cls(filledProps.className, rootClassName, mergedClassNames.root)} + style={{ + ...mergedStyles.root, + ...filledProps.style, + }} // Icon suffixIcon={suffixIcon} // Active diff --git a/src/PickerInput/Selector/Icon.tsx b/src/PickerInput/Selector/Icon.tsx index d32faf4bc..23628f4c2 100644 --- a/src/PickerInput/Selector/Icon.tsx +++ b/src/PickerInput/Selector/Icon.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import PickerContext from '../context'; -import classNames from 'classnames'; +import cls from 'classnames'; export interface IconProps extends React.HtmlHTMLAttributes { icon?: React.ReactNode; @@ -9,12 +9,12 @@ export interface IconProps extends React.HtmlHTMLAttributes { export default function Icon(props: IconProps) { const { icon, type, ...restProps } = props; - const { prefixCls, classNames: iconClassNames, styles } = React.useContext(PickerContext); + const { prefixCls, classNames, styles } = React.useContext(PickerContext); return icon ? ( {icon} diff --git a/src/PickerInput/Selector/Input.tsx b/src/PickerInput/Selector/Input.tsx index 6d1f36d0e..b849792e7 100644 --- a/src/PickerInput/Selector/Input.tsx +++ b/src/PickerInput/Selector/Input.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import { useEvent } from '@rc-component/util'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import raf from '@rc-component/util/lib/raf'; @@ -75,7 +75,7 @@ const Input = React.forwardRef((props, ref) => { const { prefixCls, input: Component = 'input', - classNames: inputClassNames, + classNames, styles, } = React.useContext(PickerContext); const inputPrefixCls = `${prefixCls}-input`; @@ -383,16 +383,14 @@ const Input = React.forwardRef((props, ref) => { return (
    ((props, ref) => { // Value value={inputValue} onChange={onInternalChange} + className={classNames.input} + style={styles.input} /> {clearIcon} diff --git a/src/PickerInput/Selector/RangeSelector.tsx b/src/PickerInput/Selector/RangeSelector.tsx index 7d664eb6d..e08ca2c28 100644 --- a/src/PickerInput/Selector/RangeSelector.tsx +++ b/src/PickerInput/Selector/RangeSelector.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import ResizeObserver from '@rc-component/resize-observer'; import { useEvent } from '@rc-component/util'; import * as React from 'react'; @@ -121,7 +121,7 @@ function RangeSelector( const rtl = direction === 'rtl'; // ======================== Prefix ======================== - const { prefixCls, classNames: selectorClassNames, styles } = React.useContext(PickerContext); + const { prefixCls, classNames, styles } = React.useContext(PickerContext); // ========================== Id ========================== const ids = React.useMemo(() => { @@ -211,7 +211,7 @@ function RangeSelector(
    ( }} > {prefix && ( -
    +
    {prefix}
    )} diff --git a/src/PickerInput/Selector/SingleSelector/index.tsx b/src/PickerInput/Selector/SingleSelector/index.tsx index 0d859136a..f8b56a832 100644 --- a/src/PickerInput/Selector/SingleSelector/index.tsx +++ b/src/PickerInput/Selector/SingleSelector/index.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import * as React from 'react'; import type { InternalMode, PickerRef, SelectorProps } from '../../../interface'; import { isSame } from '../../../utils/dateUtil'; @@ -110,7 +110,7 @@ function SingleSelector( const rtl = direction === 'rtl'; // ======================== Prefix ======================== - const { prefixCls, classNames: selectorClassNames, styles } = React.useContext(PickerContext); + const { prefixCls, classNames, styles } = React.useContext(PickerContext); // ========================= Refs ========================= const rootRef = React.useRef(); @@ -201,7 +201,7 @@ function SingleSelector( return (
    ( }} > {prefix && ( -
    +
    {prefix}
    )} diff --git a/src/PickerInput/SinglePicker.tsx b/src/PickerInput/SinglePicker.tsx index 652fbb1b7..c773fbd09 100644 --- a/src/PickerInput/SinglePicker.tsx +++ b/src/PickerInput/SinglePicker.tsx @@ -1,4 +1,5 @@ import { useEvent, useMergedState } from '@rc-component/util'; +import cls from 'classnames'; import useLayoutEffect from '@rc-component/util/lib/hooks/useLayoutEffect'; import omit from '@rc-component/util/lib/omit'; import pickAttrs from '@rc-component/util/lib/pickAttrs'; @@ -31,6 +32,7 @@ import useRangeValue, { useInnerValue } from './hooks/useRangeValue'; import useShowNow from './hooks/useShowNow'; import Popup from './Popup'; import SingleSelector from './Selector/SingleSelector'; +import useSemantic from '../hooks/useSemantic'; // TODO: isInvalidateDate with showTime.disabledTime should not provide `range` prop @@ -122,8 +124,9 @@ function Picker( const { // Style prefixCls, - styles, - classNames, + rootClassName, + styles: propStyles, + classNames: propClassNames, // Value order, @@ -202,6 +205,9 @@ function Picker( const toggleDates = useToggleDates(generateConfig, locale, internalPicker); + // ======================= Semantic ======================= + const [mergedClassNames, mergedStyles] = useSemantic(propClassNames, propStyles); + // ========================= Open ========================= const [mergedOpen, triggerOpen] = useOpen(open, defaultOpen, [disabled], onOpenChange); @@ -478,6 +484,8 @@ function Picker( 'style', 'className', 'onPanelChange', + 'classNames', + 'styles', ]); return { ...restProps, @@ -577,10 +585,18 @@ function Picker( generateConfig, button: components.button, input: components.input, - styles, - classNames, + classNames: mergedClassNames, + styles: mergedStyles, }), - [prefixCls, locale, generateConfig, components.button, components.input, styles, classNames], + [ + prefixCls, + locale, + generateConfig, + components.button, + components.input, + mergedClassNames, + mergedStyles, + ], ); // ======================== Effect ======================== @@ -615,8 +631,8 @@ function Picker( ( {...filledProps} // Ref ref={selectorRef} + // Style + className={cls(filledProps.className, rootClassName, mergedClassNames.root)} + style={{ + ...mergedStyles.root, + ...filledProps.style, + }} // Icon suffixIcon={suffixIcon} removeIcon={removeIcon} diff --git a/src/PickerInput/context.tsx b/src/PickerInput/context.tsx index 97cf09f51..6f3c66aab 100644 --- a/src/PickerInput/context.tsx +++ b/src/PickerInput/context.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import type { GenerateConfig } from '../generate'; -import type { Components, Locale, SemanticStructure } from '../interface'; +import type { Components, Locale } from '../interface'; +import type { FilledClassNames, FilledStyles } from '../hooks/useSemantic'; export interface PickerContextProps { prefixCls: string; @@ -9,8 +10,8 @@ export interface PickerContextProps { /** Customize button component */ button?: Components['button']; input?: Components['input']; - styles?: Partial>; - classNames?: Partial>; + classNames: FilledClassNames; + styles: FilledStyles; } const PickerContext = React.createContext(null!); diff --git a/src/PickerPanel/PanelBody.tsx b/src/PickerPanel/PanelBody.tsx index 395cdde1f..f202c5a85 100644 --- a/src/PickerPanel/PanelBody.tsx +++ b/src/PickerPanel/PanelBody.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import * as React from 'react'; import type { DisabledDate } from '../interface'; import { formatValue, isInRange, isSame } from '../utils/dateUtil'; @@ -45,6 +45,8 @@ export default function PanelBody(props: PanelBod const { prefixCls, + classNames, + styles, panelType: type, now, disabledDate: contextDisabledDate, @@ -63,11 +65,7 @@ export default function PanelBody(props: PanelBod const cellPrefixCls = `${prefixCls}-cell`; // ============================= Context ============================== - const { - onCellDblClick, - classNames: pickerClassNames, - styles, - } = React.useContext(PickerHackContext); + const { onCellDblClick } = React.useContext(PickerHackContext); // ============================== Value =============================== const matchValues = (date: DateType) => @@ -127,7 +125,7 @@ export default function PanelBody(props: PanelBod isSame(generateConfig, locale, currentDate, date, type), @@ -142,7 +140,7 @@ export default function PanelBody(props: PanelBod matchValues(currentDate), ...getCellClassName(currentDate), })} - style={styles?.popupItem} + style={styles.item} onClick={() => { if (!disabled) { onSelect(currentDate); @@ -186,14 +184,8 @@ export default function PanelBody(props: PanelBod // ============================== Render ============================== return ( -
    - +
    +
    {headerCells && ( {headerCells} diff --git a/src/PickerPanel/PanelHeader.tsx b/src/PickerPanel/PanelHeader.tsx index 28dbb3179..1ff81e07b 100644 --- a/src/PickerPanel/PanelHeader.tsx +++ b/src/PickerPanel/PanelHeader.tsx @@ -1,4 +1,4 @@ -import classNames from 'classnames'; +import cls from 'classnames'; import * as React from 'react'; import { isSameOrAfter } from '../utils/dateUtil'; import { PickerHackContext, usePanelContext } from './context'; @@ -33,6 +33,8 @@ function PanelHeader(props: HeaderProps) { const { prefixCls, + classNames, + styles, // Icons prevIcon = '\u2039', @@ -118,17 +120,14 @@ function PanelHeader(props: HeaderProps) { const superNextBtnCls = `${headerPrefixCls}-super-next-btn`; return ( -
    +
    {superOffset && (