From 5d66b0ba3f177edf6693931a66c6d0063bf73c76 Mon Sep 17 00:00:00 2001 From: vagusX Date: Wed, 11 May 2022 19:35:00 +0800 Subject: [PATCH] refactor: cssinjs for segmented (#35134) * refactor: cssinjs for segmented * fix: disabled styles * feat: sync code from master --- components/_util/theme/interface.ts | 3 + components/segmented/index.tsx | 22 ++- components/segmented/style/index.tsx | 217 ++++++++++++++++++++++++++- 3 files changed, 233 insertions(+), 9 deletions(-) diff --git a/components/_util/theme/interface.ts b/components/_util/theme/interface.ts index 22b62429e6..3ddc53eeda 100644 --- a/components/_util/theme/interface.ts +++ b/components/_util/theme/interface.ts @@ -7,6 +7,7 @@ import type { ComponentToken as EmptyComponentToken } from '../../empty/style'; import type { ComponentToken as CascaderComponentToken } from '../../cascader/style'; import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style'; import type { ComponentToken as MentionsComponentToken } from '../../mentions/style'; +import type { ComponentToken as SegmentedComponentToken } from '../../segmented/style'; import type { ComponentToken as SelectComponentToken } from '../../select/style'; import type { ComponentToken as SliderComponentToken } from '../../slider/style'; import type { ComponentToken as TypographyComponentToken } from '../../typography/style'; @@ -79,6 +80,7 @@ export interface OverrideToken { Popover?: {}; Rate?: {}; Result?: {}; + Segmented?: SegmentedComponentToken; Select?: SelectComponentToken; Skeleton?: {}; Slider?: SliderComponentToken; @@ -326,6 +328,7 @@ export interface AliasToken extends Omit { controlPaddingHorizontal: number; controlPaddingHorizontalSM: number; + paddingSM: number; paddingXS: number; paddingXXS: number; diff --git a/components/segmented/index.tsx b/components/segmented/index.tsx index 144809d9ab..9eda716a08 100644 --- a/components/segmented/index.tsx +++ b/components/segmented/index.tsx @@ -11,6 +11,8 @@ import { ConfigContext } from '../config-provider'; import type { SizeType } from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext'; +import useStyle from './style'; + export type { SegmentedValue } from 'rc-segmented'; interface SegmentedLabeledOptionWithoutIcon extends RcSegmentedLabeledOption { @@ -53,6 +55,8 @@ const Segmented = React.forwardRef((props, ref) const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('segmented', customizePrefixCls); + // Style + const [wrapSSR, hashId] = useStyle(prefixCls); // ===================== Size ===================== const size = React.useContext(SizeContext); @@ -79,19 +83,23 @@ const Segmented = React.forwardRef((props, ref) [options, prefixCls], ); - return ( + return wrapSSR( + />, ); }); diff --git a/components/segmented/style/index.tsx b/components/segmented/style/index.tsx index 3a3ab0de59..dcf0b056a8 100644 --- a/components/segmented/style/index.tsx +++ b/components/segmented/style/index.tsx @@ -1,2 +1,215 @@ -import '../../style/index.less'; -import './index.less'; +// deps-lint-skip-all +import type { CSSObject } from '@ant-design/cssinjs'; +import { TinyColor } from '@ctrl/tinycolor'; +import type { GenerateStyle, FullToken } from '../../_util/theme'; +import { resetComponent, genComponentStyleHook, mergeToken } from '../../_util/theme'; + +export interface ComponentToken {} + +interface SegmentedToken extends FullToken<'Segmented'> { + segmentedBg: string; + segmentedHoverBg: string; + segmentedSelectedBg: string; + segmentedLabelColor: string; + segmentedLabelHoverColor: string; + segmentedPaddingVertical: number; + segmentedPaddingVerticalLG: number; + segmentedPaddingVerticalSM: number; + segmentedPaddingHorizontal: number; + segmentedPaddingHorizontalSM: number; + segmentedContainerPadding: number; +} + +// ============================== Mixins ============================== +function segmentedDisabledItem(cls: string, token: SegmentedToken): CSSObject { + return { + [`${cls}, ${cls}:hover, ${cls}:focus`]: { + color: token.colorTextDisabled, + cursor: 'not-allowed', + }, + }; +} + +// FIXME: hard code +const segmentedSelectedItemBoxShadow = [ + `0 2px 8px -2px ${new TinyColor('#000').setAlpha(0.05).toRgbString()}`, + `0 1px 4px -1px ${new TinyColor('#000').setAlpha(0.07).toRgbString()}`, + `0 0 1px 0 ${new TinyColor('#000').setAlpha(0.08).toRgbString()}`, +].join(','); + +function getSegmentedItemSelectedStyle(token: SegmentedToken): CSSObject { + return { + backgroundColor: token.segmentedSelectedBg, + borderRadius: token.controlRadius, + boxShadow: segmentedSelectedItemBoxShadow, + }; +} + +const segmentedTextEllipsisCss: CSSObject = { + overflow: 'hidden', + // handle text ellipsis + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + wordBreak: 'keep-all', +}; + +// ============================== Styles ============================== +const genSegmentedStyle: GenerateStyle = (token: SegmentedToken) => { + const { componentCls } = token; + + return { + [componentCls]: { + ...resetComponent(token), + + display: 'inline-block', + padding: token.segmentedContainerPadding, + color: token.segmentedLabelColor, + backgroundColor: token.segmentedBg, + borderRadius: token.radiusBase, + transition: `all ${token.motionDurationSlow} ${token.motionEaseInOut}`, + + '&-group': { + position: 'relative', + display: 'flex', + alignItems: 'stretch', + justifyItems: 'flex-start', + width: '100%', + }, + + // RTL styles + '&&-rtl': { + direction: 'rtl', + }, + + // hover/focus styles + [`&:not(${componentCls}-disabled)`]: { + '&:hover, &:focus': { + backgroundColor: token.segmentedHoverBg, + }, + }, + + // block styles + '&&-block': { + display: 'flex', + }, + + '&&-block &-item': { + flex: 1, + minWidth: 0, + }, + + // item styles + [`${componentCls}-item`]: { + position: 'relative', + textAlign: 'center', + cursor: 'pointer', + transition: `color ${token.motionDurationSlow} ${token.motionEaseInOut}`, + + '&-selected': { + ...getSegmentedItemSelectedStyle(token), + color: token.segmentedLabelHoverColor, + }, + + '&:hover, &:focus': { + color: token.segmentedLabelHoverColor, + }, + + '&-label': { + // FIXME: hard code + minHeight: token.controlHeight - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeight - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontal}px`, + ...segmentedTextEllipsisCss, + }, + + // syntactic sugar to add `icon` for Segmented Item + '&-icon + *': { + // FIXME: hard code + marginInlineEnd: token.marginSM / 2, + }, + + '&-input': { + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: 0, + height: 0, + opacity: 0, + pointerEvents: 'none', + }, + }, + + // size styles + [`&&-lg ${componentCls}-item-label`]: { + // FIXME: hard code + minHeight: token.controlHeightLG - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeightLG - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontal}px`, + fontSize: token.fontSizeLG, + }, + + [`&&-sm ${componentCls}-item-label`]: { + // FIXME: hard code + minHeight: token.controlHeightSM - token.segmentedContainerPadding * 2, + lineHeight: `${token.controlHeightSM - token.segmentedContainerPadding * 2}px`, + padding: `0 ${token.segmentedPaddingHorizontalSM}px`, + }, + + // disabled styles + ...segmentedDisabledItem(`&-disabled ${componentCls}-item`, token), + ...segmentedDisabledItem(`${componentCls}-item-disabled`, token), + + // thumb styles + [`${componentCls}-thumb`]: { + ...getSegmentedItemSelectedStyle(token), + + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: 0, + height: '100%', + padding: `${token.paddingXXS}px 0`, + }, + + // transition effect when `appear-active` + [`${componentCls}-thumb-motion-appear-active`]: { + transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOut}, width ${token.motionDurationSlow} ${token.motionEaseInOut}`, + willChange: 'transform, width', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Segmented', token => { + const segmentedToken = mergeToken(token, { + // FIXME: hard code + segmentedBg: new TinyColor('#000').setAlpha(0.04).toRgbString(), + // FIXME: hard code + segmentedHoverBg: new TinyColor('#000').setAlpha(0.06).toRgbString(), + // FIXME: hard code + segmentedSelectedBg: '#fff', + // FIXME: hard code + segmentedLabelColor: new TinyColor('#000').setAlpha(0.65).toRgbString(), + // FIXME: hard code + segmentedLabelHoverColor: '#262626', + segmentedPaddingVertical: Math.max( + Math.round(((token.controlHeight - token.fontSize * token.lineHeight) / 2) * 10) / 10 - + token.controlLineWidth, + 3, + ), + segmentedPaddingVerticalLG: + Math.ceil(((token.controlHeightLG - token.fontSizeLG * token.lineHeight) / 2) * 10) / 10 - + token.controlLineWidth, + segmentedPaddingVerticalSM: Math.max( + Math.round(((token.controlHeightSM - token.fontSize * token.lineHeight) / 2) * 10) / 10 - + token.controlLineWidth, + 0, + ), + segmentedPaddingHorizontal: token.controlPaddingHorizontal - token.controlLineWidth, + segmentedPaddingHorizontalSM: token.controlPaddingHorizontalSM - token.controlLineWidth, + // FIXME: hard code + segmentedContainerPadding: token.paddingXXS / 2, + }); + return [genSegmentedStyle(segmentedToken)]; +});