diff --git a/.github/workflows/pr-check-ci.yml b/.github/workflows/pr-check-ci.yml index d9599590a5..e3e608b4b5 100644 --- a/.github/workflows/pr-check-ci.yml +++ b/.github/workflows/pr-check-ci.yml @@ -13,7 +13,7 @@ jobs: with: filter-label: 'BranchAutoMerge' filter-creator-authority: 'write' - filter-head-ref: 'master, feature, next, master-merge-feature, feature-merge-master, next-merge-master' + filter-head-ref: 'master, feature, next, master-merge-feature, feature-merge-master, next-merge-master, next-merge-feature' filter-support-fork: false skip-run-names: 'deploy preview, pr-check-ci, build preview failed, suggest-related-links' conflict-review-body: '😅 This branch has conflicts that must be resolved!' diff --git a/components/_util/theme/index.tsx b/components/_util/theme/index.tsx index 77869031da..f4758bdb7b 100644 --- a/components/_util/theme/index.tsx +++ b/components/_util/theme/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { CSSInterpolation, Theme, useCacheToken, useStyleRegister } from '@ant-design/cssinjs'; import defaultSeedToken, { derivative as defaultDerivative } from './themes/default'; import version from '../../version'; -import { resetComponent, resetIcon, clearFix } from './util'; +import { resetComponent, resetIcon, clearFix, roundedArrow } from './util'; import formatToken from './util/alias'; import { initSlideMotion, @@ -29,6 +29,7 @@ export { resetComponent, resetIcon, clearFix, + roundedArrow, initSlideMotion, slideUpIn, slideUpOut, diff --git a/components/_util/theme/interface.ts b/components/_util/theme/interface.ts index c0ed72ba8a..e17233285e 100644 --- a/components/_util/theme/interface.ts +++ b/components/_util/theme/interface.ts @@ -1,5 +1,7 @@ import * as React from 'react'; import type { ComponentToken as ButtonComponentToken } from '../../button/style'; +import type { ComponentToken as DividerComponentToken } from '../../divider/style'; +import type { ComponentToken as TypographyComponentToken } from '../../typography/style'; export const PresetColors = [ 'blue', @@ -32,6 +34,8 @@ export interface OverrideToken { // Customize component button?: ButtonComponentToken; + divider?: DividerComponentToken; + typography?: TypographyComponentToken; } /** Final token which contains the components level override */ @@ -87,6 +91,7 @@ export interface SeedToken extends PresetColorType { // zIndex /** Base zIndex of component like BackTop, Affix which can be cover by large popup */ zIndexBase: number; + zIndexPopover: number; /** Base popup component zIndex */ zIndexPopup: number; } @@ -195,6 +200,9 @@ export interface AliasToken extends Omit { fontSizeHeading4: number; fontSizeHeading5: number; + /** For heading like h1, h2, h3 or option selected item */ + fontWeightStrong: number; + // LineHeight lineHeight: number; lineHeightLG: number; @@ -253,6 +261,7 @@ export interface AliasToken extends Omit { paddingXXS: number; paddingLG: number; marginXS: number; + marginSM: number; marginLG: number; marginXXS: number; } diff --git a/components/_util/theme/themes/default.ts b/components/_util/theme/themes/default.ts index 9d0249b6da..320eee5a0f 100644 --- a/components/_util/theme/themes/default.ts +++ b/components/_util/theme/themes/default.ts @@ -194,6 +194,7 @@ const seedToken: SeedToken = { // zIndex zIndexBase: 0, + zIndexPopover: 1030, zIndexPopup: 1000, }; diff --git a/components/_util/theme/util/alias.ts b/components/_util/theme/util/alias.ts index f3aa9edc56..6ef0c37559 100644 --- a/components/_util/theme/util/alias.ts +++ b/components/_util/theme/util/alias.ts @@ -79,8 +79,9 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken controlLineType: 'solid', controlRadius: mergedToken.radiusBase, colorBorder: new TinyColor({ h: 0, s: 0, v: 85 }).toHexString(), - colorSplit: new TinyColor({ h: 0, s: 0, v: 94 }).toHexString(), + colorSplit: 'rgba(0, 0, 0, 0.06)', controlItemBgActive: primaryColors[0], + fontWeightStrong: 600, // 🔥🔥🔥🔥🔥🔥🔥🔥🔥 All TMP Token leaves here 🔥🔥🔥🔥🔥🔥🔥🔥🔥 // FIXME: Handle this when derivative is ready @@ -97,21 +98,21 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken linkHoverDecoration: 'none', linkFocusDecoration: 'none', - controlPaddingHorizontal: 16, - controlPaddingHorizontalSM: 12, + controlPaddingHorizontal: 12, + controlPaddingHorizontalSM: 8, padding: 16, margin: 16, - paddingXXS: 2, - paddingXS: 4, - paddingSM: 8, - paddingLG: 32, + paddingXXS: 4, + paddingXS: 8, + paddingSM: 12, + paddingLG: 24, - marginXXS: 2, - marginXS: 4, - // marginSM: 8, - marginLG: 32, + marginXXS: 4, + marginXS: 8, + marginSM: 12, + marginLG: 24, boxShadow: ` 0 3px 6px -4px rgba(0, 0, 0, 0.12), diff --git a/components/_util/theme/util/index.tsx b/components/_util/theme/util/index.tsx index c69f4a99b7..374d3421c4 100644 --- a/components/_util/theme/util/index.tsx +++ b/components/_util/theme/util/index.tsx @@ -2,6 +2,8 @@ import { CSSObject } from '@ant-design/cssinjs'; import type { DerivativeToken } from '..'; +export { roundedArrow } from './roundedArrow'; + export const resetComponent = (token: DerivativeToken): CSSObject => ({ boxSizing: 'border-box', margin: 0, @@ -15,7 +17,8 @@ export const resetComponent = (token: DerivativeToken): CSSObject => ({ }); export const resetIcon = (): CSSObject => ({ - display: 'inline-block', + display: 'inline-flex', + alignItems: 'center', color: 'inherit', fontStyle: 'normal', lineHeight: 0, diff --git a/components/_util/theme/util/roundedArrow.tsx b/components/_util/theme/util/roundedArrow.tsx new file mode 100644 index 0000000000..4777876665 --- /dev/null +++ b/components/_util/theme/util/roundedArrow.tsx @@ -0,0 +1,39 @@ +/* eslint-disable import/prefer-default-export */ +import { CSSObject } from '@ant-design/cssinjs'; +import seedToken from '../themes/default'; + +export const roundedArrow = (width: number, outerRadius: number, bgColor: string): CSSObject => { + const cornerHeight = outerRadius * (1 - 1 / Math.sqrt(2)); + const { radiusBase } = seedToken; + + const ax = width - cornerHeight; + const ay = 2 * width + cornerHeight; + const bx = ax + outerRadius * (1 / Math.sqrt(2)); + const by = 2 * width; + const cx = 2 * width - radiusBase; + const cy = 2 * width; + const dx = 2 * width; + const dy = 2 * width - radiusBase; + const fx = 2 * width + cornerHeight; + const fy = width - cornerHeight; + const ex = 2 * width; + const ey = fy + outerRadius * (1 / Math.sqrt(2)); + + return { + borderRadius: `0 0 ${radiusBase}px 0`, + pointerEvents: 'none', + + '&::before': { + position: 'absolute', + top: -width, + insetInlineStart: -width, + width: width * 3, + height: width * 3, + background: `linear-gradient(to left, ${bgColor} 50%, ${bgColor} 50%) no-repeat ${Math.ceil( + -width + 1, + )}px ${Math.ceil(-width + 1)}px`, + content: '""', + clipPath: `path('M ${ax} ${ay} A ${outerRadius} ${outerRadius} 0 0 1 ${bx} ${by} L ${cx} ${cy} A ${radiusBase} ${radiusBase} 0 0 0 ${dx} ${dy} L ${ex} ${ey} A ${outerRadius} ${outerRadius} 0 0 1 ${fx} ${fy} Z')`, + }, + }; +}; diff --git a/components/cascader/style/index.tsx b/components/cascader/style/index.tsx index 40ad4a3e3c..fabb095743 100644 --- a/components/cascader/style/index.tsx +++ b/components/cascader/style/index.tsx @@ -123,7 +123,7 @@ const genBaseStyle: GenerateStyle = (token, hashId) => { [`&-active:not(${cascaderMenuItemCls}-disabled)`]: { [`&, &:hover`]: { - fontWeight: 600, // FIXME: hardcode + fontWeight: token.fontWeightStrong, backgroundColor: token.controlItemBgActive, }, }, diff --git a/components/checkbox/style/index.tsx b/components/checkbox/style/index.tsx index de843d1be3..b39d451e45 100644 --- a/components/checkbox/style/index.tsx +++ b/components/checkbox/style/index.tsx @@ -184,7 +184,7 @@ export const genCheckboxStyle: GenerateStyle = (token, hashId) => '&:after': { opacity: 1, transform: 'rotate(45deg) scale(1) translate(-50%,-50%)', - transition: `all ${token.motionDurationSlow} ${token.motionEaseOutBack} 0.1s`, + transition: `all ${token.motionDurationSlow} ${token.motionEaseOutBack} ${token.motionDurationFast}`, }, }, diff --git a/components/config-provider/__tests__/components.test.js b/components/config-provider/__tests__/components.test.js index 0a8ef2fa77..ec6718d9be 100644 --- a/components/config-provider/__tests__/components.test.js +++ b/components/config-provider/__tests__/components.test.js @@ -40,6 +40,7 @@ import Rate from '../../rate'; import Select from '../../select'; import Skeleton from '../../skeleton'; import Slider from '../../slider'; +// eslint-disable-next-line import/no-named-as-default import Spin from '../../spin'; import Statistic from '../../statistic'; import Steps from '../../steps'; diff --git a/components/descriptions/Cell.tsx b/components/descriptions/Cell.tsx index 928f43aeaa..0fbdb66caa 100644 --- a/components/descriptions/Cell.tsx +++ b/components/descriptions/Cell.tsx @@ -60,7 +60,7 @@ const Cell: React.FC = ({ colSpan={span} >
- {label && ( + {(label || label === 0) && ( = ({ {label} )} - {content && ( + {(content || content === 0) && ( {content} diff --git a/components/descriptions/__tests__/__snapshots__/index.test.js.snap b/components/descriptions/__tests__/__snapshots__/index.test.js.snap index 52c052dbcd..5142da10b0 100644 --- a/components/descriptions/__tests__/__snapshots__/index.test.js.snap +++ b/components/descriptions/__tests__/__snapshots__/index.test.js.snap @@ -209,6 +209,46 @@ exports[`Descriptions column is number 1`] = `
`; +exports[`Descriptions number 0 should render correct 1`] = ` +
+
+ + + + + + +
+
+ + 0 + + + 0 + +
+
+
+
+`; + exports[`Descriptions should work with React Fragment 1`] = `
{ wrapper.setProps({ extra: undefined }); expect(wrapper.find('.ant-descriptions-extra').exists()).toBe(false); }); + + it('number 0 should render correct', () => { + const wrapper = mount( + + + {0} + + , + ); + expect(wrapper.render()).toMatchSnapshot(); + }); }); diff --git a/components/divider/style/index.tsx b/components/divider/style/index.tsx index a289a17288..e7a1f61759 100644 --- a/components/divider/style/index.tsx +++ b/components/divider/style/index.tsx @@ -9,14 +9,14 @@ import { GenerateStyle, } from '../../_util/theme'; -interface DividerToken extends DerivativeToken { +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + sizePaddingEdgeHorizontal: number; +} + +interface DividerToken extends DerivativeToken, ComponentToken { dividerCls: string; - dividerBorderColor: string; - - dividerBorderWidth: number; - - dividerNotDefaultTextPadding: number; dividerVerticalGutterMargin: number; dividerHorizontalWithTextGutterMargin: number; dividerHorizontalGutterMargin: number; @@ -24,12 +24,12 @@ interface DividerToken extends DerivativeToken { // ============================== Shared ============================== const genSharedDividerStyle: GenerateStyle = (token): CSSObject => { - const { dividerCls } = token; + const { dividerCls, sizePaddingEdgeHorizontal, colorSplit, controlLineWidth } = token; return { [dividerCls]: { ...resetComponent(token), - borderBlockStart: `${token.dividerBorderWidth}px solid ${token.dividerBorderColor}`, + borderBlockStart: `${controlLineWidth}px solid ${colorSplit}`, // vertical '&-vertical': { @@ -40,7 +40,7 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => margin: `0 ${token.dividerVerticalGutterMargin}px`, verticalAlign: 'middle', borderTop: 0, - borderInlineStart: `${token.dividerBorderWidth}px solid ${token.dividerBorderColor}`, + borderInlineStart: `${controlLineWidth}px solid ${colorSplit}`, }, '&-horizontal': { @@ -59,13 +59,13 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => fontSize: token.fontSizeLG, whiteSpace: 'nowrap', textAlign: 'center', - borderBlockStart: `0 ${token.dividerBorderColor}`, + borderBlockStart: `0 ${colorSplit}`, '&::before, &::after': { position: 'relative', top: '50%', width: '50%', - borderBlockStart: `${token.dividerBorderWidth}px solid transparent`, + borderBlockStart: `${controlLineWidth}px solid transparent`, // Chrome not accept `inherit` in `border-top` borderBlockStartColor: 'inherit', borderBlockEnd: 0, @@ -105,10 +105,10 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => '&-dashed': { background: 'none', - borderColor: token.dividerBorderColor, + borderColor: colorSplit, borderStyle: 'dashed', borderWidth: 0, - borderBlockStart: `${token.dividerBorderWidth}px`, + borderBlockStart: `${controlLineWidth}px`, }, '&-horizontal&-with-text&-dashed': { @@ -118,7 +118,7 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => }, '&-vertical&-dashed': { - borderWidth: `0 0 0 ${token.dividerBorderWidth}px`, + borderWidth: `0 0 0 ${controlLineWidth}px`, }, '&-plain&-with-text': { @@ -136,8 +136,8 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => width: '100%', }, - '.ant-divider-inner-text': { - paddingInlineStart: `${token.dividerNotDefaultTextPadding}px`, + [`${dividerCls}-inner-text`]: { + paddingInlineStart: sizePaddingEdgeHorizontal, }, }, @@ -150,8 +150,8 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => width: 0, }, - '.ant-divider-inner-text': { - paddingInlineEnd: `${token.dividerNotDefaultTextPadding}px`, + [`${dividerCls}-inner-text`]: { + paddingInlineEnd: sizePaddingEdgeHorizontal, }, }, }, @@ -161,35 +161,31 @@ const genSharedDividerStyle: GenerateStyle = (token): CSSObject => // ============================== Export ============================== export default function useStyle(prefixCls: string): UseComponentStyleResult { const [theme, token, hashId] = useToken(); - // FIXME - const dividerBorderColor = 'rgba(0, 0, 0, 6%)'; - - const dividerBorderWidth = token.controlLineWidth; - - const dividerNotDefaultTextPadding = 0; - const dividerVerticalGutterMargin = token.marginXS; - const dividerHorizontalWithTextGutterMargin = token.margin; - const dividerHorizontalGutterMargin = token.marginLG; - - const dividerToken: DividerToken = { - ...token, - - dividerCls: `.${prefixCls}`, - - dividerBorderColor, - - dividerBorderWidth, - - dividerNotDefaultTextPadding, - dividerVerticalGutterMargin, - dividerHorizontalWithTextGutterMargin, - dividerHorizontalGutterMargin, - }; return [ - useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [ - genSharedDividerStyle(dividerToken), - ]), + useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => { + const { divider } = token; + + const dividerVerticalGutterMargin = token.marginSM; + const dividerHorizontalWithTextGutterMargin = token.margin; + const dividerHorizontalGutterMargin = token.marginLG; + + const dividerToken: DividerToken = { + ...token, + + dividerCls: `.${prefixCls}`, + + sizePaddingEdgeHorizontal: 0, + + dividerVerticalGutterMargin, + dividerHorizontalWithTextGutterMargin, + dividerHorizontalGutterMargin, + + ...divider, + }; + + return [genSharedDividerStyle(dividerToken)]; + }), hashId, ]; } diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx index d5d8755a25..5aae5fde37 100644 --- a/components/drawer/index.tsx +++ b/components/drawer/index.tsx @@ -4,6 +4,9 @@ import CloseOutlined from '@ant-design/icons/CloseOutlined'; import classNames from 'classnames'; import { ConfigContext } from '../config-provider'; import { tuple } from '../_util/type'; +// CSSINJS +import useStyle from './style'; + import useForceUpdate from '../_util/hooks/useForceUpdate'; type DrawerRef = { @@ -109,8 +112,13 @@ const Drawer = React.forwardRef( const parentDrawer = React.useContext(DrawerContext); const destroyClose = React.useRef(false); - const { getPopupContainer, getPrefixCls, direction } = React.useContext(ConfigContext); + const { getPopupContainer, getPrefixCls, direction, iconPrefixCls } = + React.useContext(ConfigContext); const prefixCls = getPrefixCls('drawer', customizePrefixCls); + + // Style + const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls); + const getContainer = // 有可能为 false,所以不能直接判断 customizeGetContainer === undefined && getPopupContainer @@ -297,10 +305,11 @@ const Drawer = React.forwardRef( [`${prefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); const offsetStyle = mask ? getOffsetStyle() : {}; - return ( + return wrapSSR( ( > {renderBody()} - + , ); }, ); diff --git a/components/drawer/style/index.less b/components/drawer/style/index.less index 77efacf49d..3acac2551b 100644 --- a/components/drawer/style/index.less +++ b/components/drawer/style/index.less @@ -1,6 +1,6 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; -@import './drawer'; -@import './rtl'; - -.popover-customize-bg(@drawer-prefix-cls, @popover-background); +//@import '../../style/themes/index'; +//@import '../../style/mixins/index'; +//@import './drawer'; +//@import './rtl'; +// +//.popover-customize-bg(@drawer-prefix-cls, @popover-background); diff --git a/components/drawer/style/index.tsx b/components/drawer/style/index.tsx index 1f18f88462..0d71d25a46 100644 --- a/components/drawer/style/index.tsx +++ b/components/drawer/style/index.tsx @@ -1,3 +1,338 @@ -// deps-lint-skip: empty -import '../../style/index.less'; -import './index.less'; +// deps-lint-skip-all +import { CSSObject, Keyframes } from '@ant-design/cssinjs'; +import { TinyColor } from '@ctrl/tinycolor'; +import { + DerivativeToken, + GenerateStyle, + UseComponentStyleResult, + useStyleRegister, + useToken, +} from '../../_util/theme'; + +export interface DrawerToken extends DerivativeToken { + prefixCls: string; + iconPrefixCls: string; + drawerHeaderCloseSize: number; + shadow1Right: string; + shadow1Left: string; + shadow1Up: string; + shadow1Down: string; + drawerTitleLineHeight: number; + closeRight: number; + white: string; + black: string; + paddingMd: number; + modalFooterPaddingVertical: number; + modalFooterPaddingHorizontal: number; + hoverColor: string; + borderColorSplit: string; + borderStyle: string; + textColorSecondary: string; + motionEaseOut: string; + drawerPrefixCls: string; +} + +const antdDrawerFadeIn = new Keyframes('antNoWrapperZoomBadgeIn', { + '0%': { opacity: 0 }, + '100%': { opacity: 1 }, +}); + +// =============================== Base =============================== +const genBaseStyle: GenerateStyle = ( + token: DrawerToken, + hashId: string, +): CSSObject => { + const { + drawerPrefixCls, + motionEaseOut, + motionDurationSlow, + fontSizeLG, + drawerTitleLineHeight, + white, + closeRight, + paddingLG, + paddingMd, + lineWidth, + borderStyle, + radiusBase, + fontSize, + lineHeight, + modalFooterPaddingVertical, + modalFooterPaddingHorizontal, + borderColorSplit, + zIndexPopup, + colorText, + textColorSecondary, + hoverColor, + } = token; + + return { + [`${drawerPrefixCls}`]: { + // FIXME: Seems useless? + // @drawer-header-close-padding: ceil(((drawerHeaderCloseSize - @font-size-lg) / 2)); + position: 'fixed', + zIndex: zIndexPopup, + width: 0, + height: '100%', + transition: `width 0s ease ${motionDurationSlow}, height 0s ease ${motionDurationSlow}`, + [`${drawerPrefixCls}-content-wrapper`]: { + position: 'absolute', + width: '100%', + height: '100%', + transition: `transform ${motionDurationSlow} ${motionEaseOut},box-shadow ${motionDurationSlow} ${motionEaseOut}`, + [`${drawerPrefixCls}-content`]: { + width: '100%', + height: '100%', + position: 'relative', + zIndex: 1, + overflow: 'auto', + backgroundColor: white, + backgroundClip: `padding-box`, + border: 0, + [`${drawerPrefixCls}-wrapper-body`]: { + display: 'flex', + flexFlow: 'column nowrap', + width: '100%', + height: '100%', + [`${drawerPrefixCls}-header`]: { + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: `${paddingMd}px ${paddingLG}px`, // FIXME px + color: colorText, + background: white, + borderBottom: `${lineWidth}px ${borderStyle} ${borderColorSplit}`, // FIXME px + borderRadius: `${radiusBase}px ${radiusBase}px 0 0`, // FIXME px + + [`${drawerPrefixCls}-header-title`]: { + display: 'flex', + flex: 1, + alignItems: 'center', + justifyContent: 'space-between', + [`${drawerPrefixCls}-title`]: { + flex: 1, + margin: 0, + color: colorText, + fontWeight: 500, + fontSize: fontSizeLG, + lineHeight: drawerTitleLineHeight, + }, + [`${drawerPrefixCls}-close`]: { + display: 'inline-block', + marginRight: closeRight, + color: textColorSecondary, + fontWeight: 700, + fontSize: fontSizeLG, + fontStyle: 'normal', + lineHeight: 1, + textAlign: 'center', + textTransform: 'none', + textDecoration: 'none', + background: 'transparent', + border: 0, + outline: 0, + cursor: 'pointer', + transition: `color ${motionDurationSlow}`, + textRendering: 'auto', + + [`${drawerPrefixCls}:focus,${drawerPrefixCls}:hover`]: { + color: hoverColor, + textDecoration: 'none', + }, + }, + }, + [`${drawerPrefixCls}-header-close-only`]: { + paddingBottom: 0, + border: 'none', + }, + }, + [`${drawerPrefixCls}-body`]: { + flexGrow: 1, + padding: paddingLG, + overflow: 'auto', + fontSize, + lineHeight, + wordWrap: 'break-word', + }, + [`${drawerPrefixCls}-footer`]: { + flexShrink: 0, + padding: `${modalFooterPaddingVertical}px ${modalFooterPaddingHorizontal}px`, // FIXME px + borderTop: `${lineWidth}px ${borderStyle} ${borderColorSplit}`, // FIXME px + }, + }, + }, + }, + [`${drawerPrefixCls}-mask`]: { + position: 'absolute', + insetBlockStart: 0, + insetInlineStart: 0, + width: '100%', + height: 0, + backgroundColor: textColorSecondary, + opacity: 0, + transition: `opacity ${motionDurationSlow} linear, height 0s ease ${motionDurationSlow}`, + pointerEvents: 'none', + }, + }, + [`${drawerPrefixCls}${drawerPrefixCls}-open ${drawerPrefixCls}-mask`]: { + height: '100%', + opacity: 1, + transition: 'none', + animation: `${antdDrawerFadeIn.getName(hashId)} ${motionDurationSlow} ${motionEaseOut}`, + pointerEvents: 'auto', + }, + }; +}; + +const genDrawerStyle: GenerateStyle = (token: DrawerToken) => { + const { + drawerPrefixCls, + motionDurationSlow, + shadow1Right, + shadow1Left, + shadow1Down, + shadow1Up, + lineWidth, + motionEaseOut, + } = token; + + return { + // =================== left,right =================== + [`${drawerPrefixCls}-left`]: { + insetInlineStart: 0, + insetBlockStart: 0, + width: 0, + height: '100%', + [`${drawerPrefixCls}-content-wrapper`]: { + height: '100%', + insetInlineStart: 0, + }, + }, + [`${drawerPrefixCls}-left${drawerPrefixCls}-open`]: { + width: '100%', + transition: `transform ${motionDurationSlow} ${motionEaseOut}`, + [`${drawerPrefixCls}-content-wrapper`]: { + boxShadow: shadow1Right, + }, + }, + [`${drawerPrefixCls}-right`]: { + insetInlineEnd: 0, + insetBlockStart: 0, + width: 0, + height: '100%', + [`${drawerPrefixCls}-content-wrapper`]: { + height: '100%', + insetInlineEnd: 0, + }, + }, + [`${drawerPrefixCls}-right${drawerPrefixCls}-open`]: { + width: '100%', + transition: `transform ${motionDurationSlow} ${motionEaseOut}`, + [`${drawerPrefixCls}-content-wrapper`]: { + boxShadow: shadow1Left, + }, + }, + // https://github.com/ant-design/ant-design/issues/18607, Avoid edge alignment bug. + [`${drawerPrefixCls}-right${drawerPrefixCls}-open.no-mask`]: { + insetInlineEnd: lineWidth, + transform: `translateX(${lineWidth})`, + }, + + // =================== top,bottom =================== + [`${drawerPrefixCls}-top,${drawerPrefixCls}-bottom`]: { + insetInlineStart: 0, + width: '100%', + height: 0, + [`${drawerPrefixCls}-content-wrapper`]: { + width: '100%', + }, + }, + + [`${drawerPrefixCls}-top${drawerPrefixCls}-open,${drawerPrefixCls}-bottom${drawerPrefixCls}-open`]: + { + height: '100%', + transition: `transform ${motionDurationSlow} ${motionEaseOut}`, + }, + + [`${drawerPrefixCls}-top`]: { + insetBlockStart: 0, + }, + + [`${drawerPrefixCls}-top${drawerPrefixCls}-open`]: { + [`${drawerPrefixCls}-content-wrapper`]: { + boxShadow: shadow1Down, + }, + }, + + [`${drawerPrefixCls}-bottom`]: { + bottom: 0, + [`${drawerPrefixCls}-content-wrapper`]: { + bottom: 0, + }, + }, + + [`${drawerPrefixCls}-bottom${drawerPrefixCls}-bottom-open`]: { + [`${drawerPrefixCls}-content-wrapper`]: { + boxShadow: shadow1Up, + }, + }, + + [`${drawerPrefixCls}-bottom${drawerPrefixCls}-bottom-open.no-mask`]: { + insetBlockEnd: lineWidth, + transform: `translateY(${lineWidth})`, + }, + + // ==================== Hook Components =================== + // FIXME: Seems useless? + // .@{picker-prefix-cls} { + // &-clear { + // background: @popover-background, + // } + // } + }; +}; + +// ============================== Export ============================== +export default function useStyle( + prefixCls: string, + iconPrefixCls: string, +): UseComponentStyleResult { + const [theme, token, hashId] = useToken(); + const drawerToken: DrawerToken = { + ...token, + prefixCls, + iconPrefixCls, + drawerPrefixCls: `.${prefixCls}`, + + black: '#000', // FIXME: hard code + white: '#fff', // FIXME: hard code + drawerHeaderCloseSize: 56, // FIXME: hard code + shadow1Right: + '6px 0 16px -8px rgba(0, 0, 0, 0.08), 9px 0 28px 0 rgba(0, 0, 0, 0.05),12px 0 48px 16px rgba(0, 0, 0, 0.03)', // FIXME: hard code in v4 + shadow1Left: + '-6px 0 16px -8px rgba(0, 0, 0, 0.08), -9px 0 28px 0 rgba(0, 0, 0, 0.05), -12px 0 48px 16px rgba(0, 0, 0, 0.03)', // FIXME: hard code in v4 + shadow1Up: + '0 -6px 16px -8px rgba(0, 0, 0, 0.32), 0 -9px 28px 0 rgba(0, 0, 0, 0.2),0 -12px 48px 16px rgba(0, 0, 0, 0.12)', // FIXME: hard code in v4 + shadow1Down: + '0 6px 16px -8px rgba(0, 0, 0, 0.32), 0 9px 28px 0 rgba(0, 0, 0, 0.2), 0 12px 48px 16px rgba(0, 0, 0, 0.12)', // FIXME: hard code in v4 + drawerTitleLineHeight: 1.375, // FIXME: hard code + closeRight: 22, // FIXME: hard code + paddingMd: 16, // FIXME: hard code + modalFooterPaddingVertical: 10, // FIXME: hard code + modalFooterPaddingHorizontal: 16, // FIXME: hard code + borderColorSplit: new TinyColor({ h: 0, s: 0, v: 94 }).toHexString(), // FIXME: hard code + hoverColor: new TinyColor('#000').setAlpha(0.75).toRgbString(), // FIXME: hard code + textColorSecondary: new TinyColor('#000').setAlpha(0.45).toRgbString(), // FIXME: hard code + borderStyle: 'solid', // FIXME: hard code + motionEaseOut: 'cubic-bezier(0.215, 0.61, 0.355, 1)', // FIXME: hard code + }; + + return [ + useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [ + genBaseStyle(drawerToken, hashId), + genDrawerStyle(drawerToken), + ]), + hashId, + ]; +} diff --git a/components/input/style/index.tsx b/components/input/style/index.tsx index 077c7d1d41..d8234c624a 100644 --- a/components/input/style/index.tsx +++ b/components/input/style/index.tsx @@ -75,7 +75,7 @@ const genInputLargeStyle = (token: InputToken): CSSObject => { }; const genInputSmallStyle = (token: InputToken): CSSObject => ({ - padding: `${token.inputPaddingVerticalSM}px ${token.paddingXS - 1}px`, + padding: `${token.inputPaddingVerticalSM}px ${token.controlPaddingHorizontalSM - 1}px`, }); export const genStatusStyle = (token: InputToken): CSSObject => { @@ -785,7 +785,7 @@ export const initInputToken = ( ...token, prefixCls, iconPrefixCls, - inputAffixMargin: token.marginXXS, + inputAffixMargin: token.marginXS, inputPaddingVertical: Math.max( Math.round(((token.controlHeight - token.fontSize * token.lineHeight) / 2) * 10) / 10 - token.controlLineWidth, @@ -799,7 +799,7 @@ export const initInputToken = ( token.controlLineWidth, 0, ), - inputPaddingHorizontal: token.paddingSM - token.controlLineWidth, + inputPaddingHorizontal: token.controlPaddingHorizontal - token.controlLineWidth, inputBorderHoverColor: token.colorPrimaryHover, inputBorderActiveColor: token.colorPrimaryHover, }); diff --git a/components/list/index.tsx b/components/list/index.tsx index 0f4ba9718d..ad59d67300 100644 --- a/components/list/index.tsx +++ b/components/list/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import classNames from 'classnames'; +// eslint-disable-next-line import/no-named-as-default import Spin, { SpinProps } from '../spin'; import useBreakpoint from '../grid/hooks/useBreakpoint'; import { Breakpoint, responsiveArray } from '../_util/responsiveObserve'; diff --git a/components/mentions/index.tsx b/components/mentions/index.tsx index 8bfc327671..97cea296f4 100644 --- a/components/mentions/index.tsx +++ b/components/mentions/index.tsx @@ -3,6 +3,7 @@ import classNames from 'classnames'; import RcMentions from 'rc-mentions'; import { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions'; import { composeRef } from 'rc-util/lib/ref'; +// eslint-disable-next-line import/no-named-as-default import Spin from '../spin'; import { ConfigContext } from '../config-provider'; import { FormItemInputContext } from '../form/context'; diff --git a/components/notification/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/notification/__tests__/__snapshots__/demo-extend.test.ts.snap index 586575b02d..70d1afe20d 100644 --- a/components/notification/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/notification/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -188,7 +188,7 @@ Array [ `; exports[`renders ./components/notification/demo/placement.md extend context correctly 1`] = ` -
+Array [
@@ -255,11 +255,11 @@ exports[`renders ./components/notification/demo/placement.md extend context corr
-
+
, + , - + , +] `; exports[`renders ./components/notification/demo/update.md extend context correctly 1`] = ` diff --git a/components/notification/__tests__/__snapshots__/demo.test.js.snap b/components/notification/__tests__/__snapshots__/demo.test.js.snap index 419ebc8e60..bbb7b6fca1 100644 --- a/components/notification/__tests__/__snapshots__/demo.test.js.snap +++ b/components/notification/__tests__/__snapshots__/demo.test.js.snap @@ -188,7 +188,7 @@ Array [ `; exports[`renders ./components/notification/demo/placement.md correctly 1`] = ` -
+Array [
@@ -255,11 +255,11 @@ exports[`renders ./components/notification/demo/placement.md correctly 1`] = `
-
+ , + , - + , +] `; exports[`renders ./components/notification/demo/update.md correctly 1`] = ` diff --git a/components/notification/__tests__/placement.test.js b/components/notification/__tests__/placement.test.js index e7fed3a720..054b109e80 100644 --- a/components/notification/__tests__/placement.test.js +++ b/components/notification/__tests__/placement.test.js @@ -41,8 +41,9 @@ describe('Notification.placement', () => { }); style = getStyle($$('.ant-notification-top')[0]); expect(style.top).toBe('50px'); - expect(style.left).toBe('0px'); - expect(style.right).toBe('0px'); + expect(style.left).toBe('50%'); + expect(style.transform).toBe('translateX(-50%)'); + expect(style.right).toBe(''); expect(style.bottom).toBe(''); open({ @@ -86,8 +87,9 @@ describe('Notification.placement', () => { }); style = getStyle($$('.ant-notification-bottom')[0]); expect(style.top).toBe(''); - expect(style.left).toBe('0px'); - expect(style.right).toBe('0px'); + expect(style.left).toBe('50%'); + expect(style.transform).toBe('translateX(-50%)'); + expect(style.right).toBe(''); expect(style.bottom).toBe('100px'); open({ diff --git a/components/notification/demo/placement.md b/components/notification/demo/placement.md index 73531c20ab..874366e943 100755 --- a/components/notification/demo/placement.md +++ b/components/notification/demo/placement.md @@ -7,11 +7,11 @@ title: ## zh-CN -通知从右上角、右下角、左下角、左上角弹出。 +使用 `placement` 可以配置通知从右上角、右下角、左下角、左上角弹出。 ## en-US -A notification box can appear from the `topRight`, `bottomRight`, `bottomLeft` or `topLeft` of the viewport. +A notification box can appear from the `topRight`, `bottomRight`, `bottomLeft` or `topLeft` of the viewport via `placement`. ```jsx import { Button, notification, Divider, Space } from 'antd'; @@ -34,40 +34,54 @@ const openNotification = placement => { }; ReactDOM.render( -
+ <> - - - - - - -
, + , mountNode, ); ``` diff --git a/components/notification/index.tsx b/components/notification/index.tsx index 620a192405..15fb147454 100755 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -87,8 +87,9 @@ function getPlacementStyle( switch (placement) { case 'top': style = { - left: 0, - right: 0, + left: '50%', + transform: 'translateX(-50%)', + right: 'auto', top, bottom: 'auto', }; @@ -109,8 +110,9 @@ function getPlacementStyle( break; case 'bottom': style = { - left: 0, - right: 0, + left: '50%', + transform: 'translateX(-50%)', + right: 'auto', top: 'auto', bottom, }; diff --git a/components/notification/style/index.less b/components/notification/style/index.less index fd457e392a..ae0d0a6744 100644 --- a/components/notification/style/index.less +++ b/components/notification/style/index.less @@ -16,25 +16,6 @@ z-index: @zindex-notification; margin-right: @notification-margin-edge; - &-top, - &-bottom, - &-topLeft, - &-bottomLeft { - margin-right: 0; - margin-left: @notification-margin-edge; - - .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, - .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { - animation-name: NotificationLeftFadeIn; - } - } - - &-top, - &-bottom { - margin-right: auto; - margin-left: auto; - } - &-close-icon { font-size: @font-size-base; cursor: pointer; @@ -206,18 +187,6 @@ } } -@keyframes NotificationLeftFadeIn { - 0% { - right: @notification-width; - opacity: 0; - } - - 100% { - right: 0; - opacity: 1; - } -} - @keyframes NotificationFadeOut { 0% { max-height: 150px; @@ -235,3 +204,4 @@ } @import './rtl'; +@import './placement'; diff --git a/components/notification/style/placement.less b/components/notification/style/placement.less new file mode 100644 index 0000000000..9a60fecac4 --- /dev/null +++ b/components/notification/style/placement.less @@ -0,0 +1,68 @@ +.@{notification-prefix-cls} { + &-top, + &-bottom { + margin-right: 0; + margin-left: 0; + } + + &-top { + .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, + .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { + animation-name: NotificationTopFadeIn; + } + } + + &-bottom { + .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, + .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { + animation-name: NotificationBottomFadeIn; + } + } + + &-topLeft, + &-bottomLeft { + margin-right: 0; + margin-left: @notification-margin-edge; + + .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, + .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { + animation-name: NotificationLeftFadeIn; + } + } +} + +@keyframes NotificationTopFadeIn { + 0% { + margin-top: -100%; + opacity: 0; + } + + 100% { + margin-top: 0; + opacity: 1; + } +} + +@keyframes NotificationBottomFadeIn { + 0% { + margin-bottom: -100%; + opacity: 0; + } + + 100% { + margin-bottom: 0; + opacity: 1; + } +} + +@keyframes NotificationLeftFadeIn { + 0% { + right: @notification-width; + opacity: 0; + } + + 100% { + right: 0; + opacity: 1; + } +} diff --git a/components/popover/index.tsx b/components/popover/index.tsx index a02a1b635a..be7839ba18 100644 --- a/components/popover/index.tsx +++ b/components/popover/index.tsx @@ -1,17 +1,21 @@ import * as React from 'react'; +import classNames from 'classnames'; import Tooltip, { AbstractTooltipProps, TooltipPlacement } from '../tooltip'; import { ConfigContext } from '../config-provider'; import { getRenderPropValue, RenderFunction } from '../_util/getRenderPropValue'; import { getTransitionName } from '../_util/motion'; +// CSSINJS +import useStyle from './style'; + export interface PopoverProps extends AbstractTooltipProps { title?: React.ReactNode | RenderFunction; content?: React.ReactNode | RenderFunction; } const Popover = React.forwardRef( - ({ prefixCls: customizePrefixCls, title, content, ...otherProps }, ref) => { - const { getPrefixCls } = React.useContext(ConfigContext); + ({ prefixCls: customizePrefixCls, title, content, overlayClassName, ...otherProps }, ref) => { + const { getPrefixCls, iconPrefixCls } = React.useContext(ConfigContext); const getOverlay = (prefixCls: string) => { if (!title && !content) return undefined; @@ -24,16 +28,20 @@ const Popover = React.forwardRef( }; const prefixCls = getPrefixCls('popover', customizePrefixCls); + const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls); const rootPrefixCls = getPrefixCls(); - return ( + const overlayCls = classNames(overlayClassName, hashId); + + return wrapSSR( + />, ); }, ); diff --git a/components/popover/style/index.less b/components/popover/style/index.less index 6cc7c294be..d0da7cc28a 100644 --- a/components/popover/style/index.less +++ b/components/popover/style/index.less @@ -1,258 +1,258 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; +// @import '../../style/themes/index'; +// @import '../../style/mixins/index'; -@popover-prefix-cls: ~'@{ant-prefix}-popover'; +// @popover-prefix-cls: ~'@{ant-prefix}-popover'; -@popover-arrow-rotate-width: sqrt(@popover-arrow-width * @popover-arrow-width * 2); +// @popover-arrow-rotate-width: sqrt(@popover-arrow-width * @popover-arrow-width * 2); -@popover-arrow-offset-vertical: 12px; -@popover-arrow-offset-horizontal: 16px; +// @popover-arrow-offset-vertical: 12px; +// @popover-arrow-offset-horizontal: 16px; -.@{popover-prefix-cls} { - .reset-component(); +// .@{popover-prefix-cls} { +// .reset-component(); - position: absolute; - top: 0; - left: 0; - z-index: @zindex-popover; - font-weight: normal; - white-space: normal; - text-align: left; - cursor: auto; - user-select: text; +// position: absolute; +// top: 0; +// left: 0; +// z-index: @zindex-popover; +// font-weight: normal; +// white-space: normal; +// text-align: left; +// cursor: auto; +// user-select: text; - &::after { - position: absolute; - background: fade(@white, 1%); - content: ''; - } +// &::after { +// position: absolute; +// background: fade(@white, 1%); +// content: ''; +// } - &-hidden { - display: none; - } +// &-hidden { +// display: none; +// } - // Offset the popover to account for the popover arrow - &-placement-top, - &-placement-topLeft, - &-placement-topRight { - padding-bottom: @popover-distance; - } +// // Offset the popover to account for the popover arrow +// &-placement-top, +// &-placement-topLeft, +// &-placement-topRight { +// padding-bottom: @popover-distance; +// } - &-placement-right, - &-placement-rightTop, - &-placement-rightBottom { - padding-left: @popover-distance; - } +// &-placement-right, +// &-placement-rightTop, +// &-placement-rightBottom { +// padding-left: @popover-distance; +// } - &-placement-bottom, - &-placement-bottomLeft, - &-placement-bottomRight { - padding-top: @popover-distance; - } +// &-placement-bottom, +// &-placement-bottomLeft, +// &-placement-bottomRight { +// padding-top: @popover-distance; +// } - &-placement-left, - &-placement-leftTop, - &-placement-leftBottom { - padding-right: @popover-distance; - } +// &-placement-left, +// &-placement-leftTop, +// &-placement-leftBottom { +// padding-right: @popover-distance; +// } - &-inner { - background-color: @popover-bg; - background-clip: padding-box; - border-radius: @border-radius-base; - box-shadow: @box-shadow-base; - box-shadow: ~'0 0 8px @{shadow-color} \9'; - } +// &-inner { +// background-color: @popover-bg; +// background-clip: padding-box; +// border-radius: @border-radius-base; +// box-shadow: @box-shadow-base; +// box-shadow: ~'0 0 8px @{shadow-color} \9'; +// } - @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - /* IE10+ */ - &-inner { - box-shadow: @box-shadow-base; - } - } +// @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { +// /* IE10+ */ +// &-inner { +// box-shadow: @box-shadow-base; +// } +// } - &-title { - min-width: @popover-min-width; - min-height: @popover-min-height; - margin: 0; // reset heading margin - padding: 5px @popover-padding-horizontal 4px; - color: @heading-color; - font-weight: 500; - border-bottom: 1px solid @border-color-split; - } +// &-title { +// min-width: @popover-min-width; +// min-height: @popover-min-height; +// margin: 0; // reset heading margin +// padding: 5px @popover-padding-horizontal 4px; +// color: @heading-color; +// font-weight: 500; +// border-bottom: 1px solid @border-color-split; +// } - &-inner-content { - padding: @padding-sm @popover-padding-horizontal; - color: @popover-color; - } +// &-inner-content { +// padding: @padding-sm @popover-padding-horizontal; +// color: @popover-color; +// } - &-message { - position: relative; - padding: 4px 0 12px; - color: @popover-color; - font-size: @font-size-base; - > .@{iconfont-css-prefix} { - position: absolute; - top: ( - 4px + ((@line-height-base * @font-size-base - @font-size-base) / 2) - ); // 4px for padding-top, 4px for vertical middle - color: @warning-color; - font-size: @font-size-base; - } +// &-message { +// position: relative; +// padding: 4px 0 12px; +// color: @popover-color; +// font-size: @font-size-base; +// > .@{iconfont-css-prefix} { +// position: absolute; +// top: ( +// 4px + ((@line-height-base * @font-size-base - @font-size-base) / 2) +// ); // 4px for padding-top, 4px for vertical middle +// color: @warning-color; +// font-size: @font-size-base; +// } - &-title { - padding-left: @font-size-base + 8px; - } - } +// &-title { +// padding-left: @font-size-base + 8px; +// } +// } - &-buttons { - margin-bottom: 4px; - text-align: right; +// &-buttons { +// margin-bottom: 4px; +// text-align: right; - button { - margin-left: 8px; - } - } +// button { +// margin-left: 8px; +// } +// } - // Arrows - &-arrow { - position: absolute; - display: block; - width: @popover-arrow-rotate-width; - height: @popover-arrow-rotate-width; - overflow: hidden; - background: transparent; - pointer-events: none; +// // Arrows +// &-arrow { +// position: absolute; +// display: block; +// width: @popover-arrow-rotate-width; +// height: @popover-arrow-rotate-width; +// overflow: hidden; +// background: transparent; +// pointer-events: none; - &-content { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - display: block; - width: @popover-arrow-width; - height: @popover-arrow-width; - margin: auto; - background-color: @popover-bg; - content: ''; - pointer-events: auto; - .roundedArrow(@popover-arrow-width, 5px, @popover-bg); - } - } +// &-content { +// position: absolute; +// top: 0; +// right: 0; +// bottom: 0; +// left: 0; +// display: block; +// width: @popover-arrow-width; +// height: @popover-arrow-width; +// margin: auto; +// background-color: @popover-bg; +// content: ''; +// pointer-events: auto; +// .roundedArrow(@popover-arrow-width, 5px, @popover-bg); +// } +// } - &-placement-top &-arrow, - &-placement-topLeft &-arrow, - &-placement-topRight &-arrow { - bottom: @popover-distance - @popover-arrow-rotate-width; +// &-placement-top &-arrow, +// &-placement-topLeft &-arrow, +// &-placement-topRight &-arrow { +// bottom: @popover-distance - @popover-arrow-rotate-width; - &-content { - box-shadow: 3px 3px 7px fade(@black, 7%); - transform: translateY((-@popover-arrow-rotate-width / 2)) rotate(45deg); - } - } +// &-content { +// box-shadow: 3px 3px 7px fade(@black, 7%); +// transform: translateY((-@popover-arrow-rotate-width / 2)) rotate(45deg); +// } +// } - &-placement-top &-arrow { - left: 50%; - transform: translateX(-50%); - } +// &-placement-top &-arrow { +// left: 50%; +// transform: translateX(-50%); +// } - &-placement-topLeft &-arrow { - left: @popover-arrow-offset-horizontal; - } +// &-placement-topLeft &-arrow { +// left: @popover-arrow-offset-horizontal; +// } - &-placement-topRight &-arrow { - right: @popover-arrow-offset-horizontal; - } +// &-placement-topRight &-arrow { +// right: @popover-arrow-offset-horizontal; +// } - &-placement-right &-arrow, - &-placement-rightTop &-arrow, - &-placement-rightBottom &-arrow { - left: @popover-distance - @popover-arrow-rotate-width; +// &-placement-right &-arrow, +// &-placement-rightTop &-arrow, +// &-placement-rightBottom &-arrow { +// left: @popover-distance - @popover-arrow-rotate-width; - &-content { - box-shadow: 3px 3px 7px fade(@black, 7%); - transform: translateX((@popover-arrow-rotate-width / 2)) rotate(135deg); - } - } +// &-content { +// box-shadow: 3px 3px 7px fade(@black, 7%); +// transform: translateX((@popover-arrow-rotate-width / 2)) rotate(135deg); +// } +// } - &-placement-right &-arrow { - top: 50%; - transform: translateY(-50%); - } +// &-placement-right &-arrow { +// top: 50%; +// transform: translateY(-50%); +// } - &-placement-rightTop &-arrow { - top: @popover-arrow-offset-vertical; - } +// &-placement-rightTop &-arrow { +// top: @popover-arrow-offset-vertical; +// } - &-placement-rightBottom &-arrow { - bottom: @popover-arrow-offset-vertical; - } +// &-placement-rightBottom &-arrow { +// bottom: @popover-arrow-offset-vertical; +// } - &-placement-bottom &-arrow, - &-placement-bottomLeft &-arrow, - &-placement-bottomRight &-arrow { - top: @popover-distance - @popover-arrow-rotate-width; +// &-placement-bottom &-arrow, +// &-placement-bottomLeft &-arrow, +// &-placement-bottomRight &-arrow { +// top: @popover-distance - @popover-arrow-rotate-width; - &-content { - box-shadow: 2px 2px 5px fade(@black, 6%); - transform: translateY((@popover-arrow-rotate-width / 2)) rotate(-135deg); - } - } +// &-content { +// box-shadow: 2px 2px 5px fade(@black, 6%); +// transform: translateY((@popover-arrow-rotate-width / 2)) rotate(-135deg); +// } +// } - &-placement-bottom &-arrow { - left: 50%; - transform: translateX(-50%); - } +// &-placement-bottom &-arrow { +// left: 50%; +// transform: translateX(-50%); +// } - &-placement-bottomLeft &-arrow { - left: @popover-arrow-offset-horizontal; - } +// &-placement-bottomLeft &-arrow { +// left: @popover-arrow-offset-horizontal; +// } - &-placement-bottomRight &-arrow { - right: @popover-arrow-offset-horizontal; - } +// &-placement-bottomRight &-arrow { +// right: @popover-arrow-offset-horizontal; +// } - &-placement-left &-arrow, - &-placement-leftTop &-arrow, - &-placement-leftBottom &-arrow { - right: @popover-distance - @popover-arrow-rotate-width; +// &-placement-left &-arrow, +// &-placement-leftTop &-arrow, +// &-placement-leftBottom &-arrow { +// right: @popover-distance - @popover-arrow-rotate-width; - &-content { - box-shadow: 3px 3px 7px fade(@black, 7%); - transform: translateX((-@popover-arrow-rotate-width / 2)) rotate(-45deg); - } - } +// &-content { +// box-shadow: 3px 3px 7px fade(@black, 7%); +// transform: translateX((-@popover-arrow-rotate-width / 2)) rotate(-45deg); +// } +// } - &-placement-left &-arrow { - top: 50%; - transform: translateY(-50%); - } +// &-placement-left &-arrow { +// top: 50%; +// transform: translateY(-50%); +// } - &-placement-leftTop &-arrow { - top: @popover-arrow-offset-vertical; - } +// &-placement-leftTop &-arrow { +// top: @popover-arrow-offset-vertical; +// } - &-placement-leftBottom &-arrow { - bottom: @popover-arrow-offset-vertical; - } -} +// &-placement-leftBottom &-arrow { +// bottom: @popover-arrow-offset-vertical; +// } +// } -.generator-popover-preset-color(@i: length(@preset-colors)) when (@i > 0) { - .generator-popover-preset-color(@i - 1); - @color: extract(@preset-colors, @i); - @lightColor: '@{color}-6'; - .@{popover-prefix-cls}-@{color} { - .@{popover-prefix-cls}-inner { - background-color: @@lightColor; - } - .@{popover-prefix-cls}-arrow { - &-content { - background-color: @@lightColor; - } - } - } -} -.generator-popover-preset-color(); +// .generator-popover-preset-color(@i: length(@preset-colors)) when (@i > 0) { +// .generator-popover-preset-color(@i - 1); +// @color: extract(@preset-colors, @i); +// @lightColor: '@{color}-6'; +// .@{popover-prefix-cls}-@{color} { +// .@{popover-prefix-cls}-inner { +// background-color: @@lightColor; +// } +// .@{popover-prefix-cls}-arrow { +// &-content { +// background-color: @@lightColor; +// } +// } +// } +// } +// .generator-popover-preset-color(); -@import './rtl'; +// @import './rtl'; diff --git a/components/popover/style/index.tsx b/components/popover/style/index.tsx index 9f2a34a79c..0cd682652a 100644 --- a/components/popover/style/index.tsx +++ b/components/popover/style/index.tsx @@ -1,5 +1,394 @@ -import '../../style/index.less'; -import './index.less'; +// import '../../style/index.less'; +// import './index.less'; // style dependencies // deps-lint-skip: tooltip + +// deps-lint-skip-all +import { TinyColor } from '@ctrl/tinycolor'; +import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs'; +import { + PresetColors, + PresetColorType, + DerivativeToken, + useStyleRegister, + useToken, + UseComponentStyleResult, + GenerateStyle, + resetComponent, + roundedArrow, +} from '../../_util/theme'; + +// FIXME +type PopoverToken = DerivativeToken & { + popoverCls: string; + iconPrefixCls: string; + popoverBg: string; + popoverColor: string; + popoverMinWidth: number; + popoverMinHeight: number; + popoverArrowWidth: number; + popoverArrowColor: string; + popoverArrowOuterColor: string; + popoverDistance: number; + popoverPaddingHorizonta: number; + popoverArrowRotateWidth: number; + popoverArrowOffsetVertical: number; + popoverArrowOffsetHorizontal: number; +}; + +const genBaseStyle: GenerateStyle = token => { + const { + popoverCls, + iconPrefixCls, + popoverBg, + popoverColor, + popoverMinWidth, + popoverMinHeight, + popoverArrowWidth, + popoverPaddingHorizonta, + popoverArrowRotateWidth, + + boxShadow, + colorSplit, + colorTextHeading, + colorWarning, + fontSize, + marginSM, + marginXS, + lineHeight, + radiusBase: borderRadius, + paddingSM, + zIndexPopover, + } = token; + + return { + [popoverCls]: { + ...resetComponent(token), + position: 'absolute', + top: 0, + insetInlineStart: 0, + zIndex: zIndexPopover, + fontWeight: 'normal', + whiteSpace: 'normal', + textAlign: 'start', + cursor: 'auto', + userSelect: 'text', + + '&::after': { + position: 'absolute', + // FIXME + background: new TinyColor('#fff').setAlpha(0.01).toRgbString(), + content: '""', + }, + + '&-rtl': { + direction: 'rtl', + }, + + '&-hidden': { + display: 'none', + }, + + [`${popoverCls}-inner`]: { + backgroundColor: popoverBg, + backgroundClip: 'padding-box', + borderRadius, + boxShadow, + }, + + [`${popoverCls}-title`]: { + minWidth: popoverMinWidth, + minHeight: popoverMinHeight, + margin: 0, + // FIXME + padding: `5px ${popoverPaddingHorizonta}px 4px`, + color: colorTextHeading, + fontWeight: 500, + // FIXME + borderBottom: `1px solid ${colorSplit}`, + }, + + [`${popoverCls}-inner-content`]: { + padding: `${paddingSM}px ${popoverPaddingHorizonta}px`, + color: popoverColor, + }, + + // FIXME 没找到使用地方,先保留 + '&-message': { + position: 'relative', + // FIXME + padding: '4px 0 12px', + color: popoverColor, + fontSize, + + [`> .${iconPrefixCls}`]: { + position: 'absolute', + // FIXME + top: 4 + (lineHeight * fontSize - fontSize) / 2, + color: colorWarning, + fontSize, + }, + + '&-title': { + // FIXME + paddingInlineStart: fontSize + 8, + }, + }, + + // FIXME 没找到使用地方,先保留 + '&-buttons': { + marginBottom: marginXS, + textAlign: 'end', + + button: { + marginInlineStart: marginSM, + }, + }, + + [`${popoverCls}-arrow`]: { + position: 'absolute', + display: 'block', + width: popoverArrowRotateWidth, + height: popoverArrowRotateWidth, + overflow: 'hidden', + background: 'transparent', + pointerEvents: 'none', + + '&-content': { + position: 'absolute', + top: 0, + insetInlineEnd: 0, + bottom: 0, + insetInlineStart: 0, + display: 'block', + width: popoverArrowWidth, + height: popoverArrowWidth, + margin: 'auto', + backgroundColor: popoverBg, + content: '""', + pointerEvents: 'auto', + ...roundedArrow(popoverArrowWidth, 5, popoverBg), + }, + }, + }, + }; +}; + +const genPlacementStyle: GenerateStyle = token => { + const { + popoverCls, + popoverDistance, + popoverArrowRotateWidth, + popoverArrowOffsetHorizontal, + popoverArrowOffsetVertical, + } = token; + + return { + [popoverCls]: { + [` + &-placement-top, + &-placement-topLeft, + &-placement-topRight + `]: { + paddingBottom: popoverDistance, + }, + + [` + &-placement-right, + &-placement-rightTop, + &-placement-rightBottom + `]: { + paddingLeft: popoverDistance, + }, + + [` + &-placement-bottom, + &-placement-bottomLeft, + &-placement-bottomRight + `]: { + paddingTop: popoverDistance, + }, + + [` + &-placement-left, + &-placement-leftTop, + &-placement-leftBottom + `]: { + paddingRight: popoverDistance, + }, + + [` + &-placement-top ${popoverCls}-arrow, + &-placement-topLeft ${popoverCls}-arrow, + &-placement-topRight ${popoverCls}-arrow + `]: { + bottom: popoverDistance - popoverArrowRotateWidth, + + '&-content': { + // FIXME + boxShadow: `3px 3px 7px ${new TinyColor('#000').setAlpha(0.07).toRgbString()}`, + transform: `translateY(-${popoverArrowRotateWidth / 2}px) rotate(45deg)`, + }, + }, + + [`&-placement-top ${popoverCls}-arrow`]: { + insetInlineStart: '50%', + transform: 'translateX(-50%)', + }, + + [`&-placement-topLeft ${popoverCls}-arrow`]: { + insetInlineStart: popoverArrowOffsetHorizontal, + }, + + [`&-placement-topRight ${popoverCls}-arrow`]: { + insetInlineEnd: popoverArrowOffsetHorizontal, + }, + + [` + &-placement-right ${popoverCls}-arrow, + &-placement-rightTop ${popoverCls}-arrow, + &-placement-rightBottom ${popoverCls}-arrow + `]: { + insetInlineStart: popoverDistance - popoverArrowRotateWidth, + + '&-content': { + // FIXME + boxShadow: `3px 3px 7px ${new TinyColor('#000').setAlpha(0.07).toRgbString()}`, + transform: `translateX(${popoverArrowRotateWidth / 2}px) rotate(135deg)`, + }, + }, + + [`&-placement-right ${popoverCls}-arrow`]: { + top: '50%', + transform: 'translateY(-50%)', + }, + + [`&-placement-rightTop ${popoverCls}-arrow`]: { + top: popoverArrowOffsetVertical, + }, + + [`&-placement-rightBottom ${popoverCls}-arrow`]: { + bottom: popoverArrowOffsetVertical, + }, + + [` + &-placement-bottom ${popoverCls}-arrow, + &-placement-bottomLeft ${popoverCls}-arrow, + &-placement-bottomRight ${popoverCls}-arrow + `]: { + top: popoverDistance - popoverArrowRotateWidth, + + '&-content': { + // FIXME + boxShadow: `2px 2px 5px ${new TinyColor('#000').setAlpha(0.06).toRgbString()}`, + transform: `translateY(${popoverArrowRotateWidth / 2}px) rotate(-135deg)`, + }, + }, + + [`&-placement-bottom ${popoverCls}-arrow`]: { + insetInlineStart: '50%', + transform: 'translateX(-50%)', + }, + + [`&-placement-bottomLeft ${popoverCls}-arrow`]: { + insetInlineStart: popoverArrowOffsetHorizontal, + }, + + [`&-placement-bottomRight ${popoverCls}-arrow`]: { + insetInlineEnd: popoverArrowOffsetHorizontal, + }, + + [` + &-placement-left ${popoverCls}-arrow, + &-placement-leftTop ${popoverCls}-arrow, + &-placement-leftBottom ${popoverCls}-arrow + `]: { + insetInlineEnd: popoverDistance - popoverArrowRotateWidth, + + '&-content': { + // FIXME + boxShadow: `3px 3px 7px ${new TinyColor('#000').setAlpha(0.07).toRgbString()}`, + transform: `translateX(-${popoverArrowRotateWidth / 2}px) rotate(-45deg)`, + }, + }, + + [`&-placement-left ${popoverCls}-arrow`]: { + top: '50%', + transform: 'translateY(-50%)', + }, + + [`&-placement-leftTop ${popoverCls}-arrow`]: { + top: popoverArrowOffsetVertical, + }, + + [`&-placement-leftBottom ${popoverCls}-arrow`]: { + bottom: popoverArrowOffsetVertical, + }, + }, + }; +}; + +// FIXME: special preset colors +const genColorStyle: GenerateStyle = token => { + const { popoverCls } = token; + + return PresetColors.reduce((prev: CSSObject, colorKey: keyof PresetColorType) => { + const lightColor = token[`${colorKey}-6`]; + return { + ...prev, + [`${popoverCls}-${colorKey}`]: { + [`${popoverCls}-inner`]: { + backgroundColor: lightColor, + }, + [`${popoverCls}-arrow`]: { + '&-content': { + backgroundColor: lightColor, + }, + }, + }, + }; + }, {} as CSSObject); +}; + +export const genPopoverStyle: GenerateStyle = ( + token: PopoverToken, +): CSSInterpolation => [genBaseStyle(token), genPlacementStyle(token), genColorStyle(token)]; + +export default function useStyle( + prefixCls: string, + iconPrefixCls: string, +): UseComponentStyleResult { + const [theme, token, hashId] = useToken(); + + const popoverBg = token.colorBg; + // FIXME + const popoverArrowWidth = 8 * Math.sqrt(2); + + const popoverToken = { + ...token, + popoverCls: `.${prefixCls}`, + iconPrefixCls, + popoverBg, + popoverColor: token.colorText, + // FIXME + popoverMinWidth: 177, + popoverMinHeight: 32, + popoverArrowWidth, + popoverArrowColor: popoverBg, + popoverArrowOuterColor: popoverBg, + popoverDistance: popoverArrowWidth + 4, + popoverPaddingHorizonta: token.padding, + popoverArrowRotateWidth: Math.sqrt(popoverArrowWidth * popoverArrowWidth * 2), + // FIXME + popoverArrowOffsetVertical: 12, + popoverArrowOffsetHorizontal: 16, + }; + + return [ + useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [ + genPopoverStyle(popoverToken), + ]), + hashId, + ]; +} diff --git a/components/select/style/dropdown.tsx b/components/select/style/dropdown.tsx index 302ccf6f97..f3a950b132 100644 --- a/components/select/style/dropdown.tsx +++ b/components/select/style/dropdown.tsx @@ -123,7 +123,7 @@ const genSingleStyle: GenerateStyle = (token, hashId) => { [`&-selected:not(${selectItemCls}-option-disabled)`]: { color: token.colorText, - fontWeight: 600, // FIXME: Need design token? + fontWeight: token.fontWeightStrong, backgroundColor: token.controlItemBgActive, [`${selectItemCls}-option-state`]: { diff --git a/components/select/style/index.tsx b/components/select/style/index.tsx index 0c41dc38d9..bc64ca2d09 100644 --- a/components/select/style/index.tsx +++ b/components/select/style/index.tsx @@ -96,7 +96,6 @@ const genStatusStyle = ( [`${selectCls}-focused& ${selectCls}-selector`]: { borderColor: borderHoverColor, - // FIXME: missing variable of `@input-outline-offset` boxShadow: `0 0 0 ${token.controlOutlineWidth}px ${outlineColor}`, borderInlineEndWidth: `${token.controlLineWidth}px !important`, outline: 0, @@ -241,8 +240,10 @@ const genBaseStyle: GenerateStyle = token => { '&:hover': { color: token.colorTextSecondary, }, + }, - [`${selectCls}:hover &`]: { + '&:hover': { + [`${selectCls}-clear`]: { opacity: 1, }, }, @@ -251,7 +252,7 @@ const genBaseStyle: GenerateStyle = token => { // ========================= Feedback ========================== [`${selectCls}-has-feedback`]: { [`${selectCls}-clear`]: { - insetInlineEnd: token.padding * 2, + insetInlineEnd: inputPaddingHorizontalBase + token.fontSize + token.paddingXXS, }, }, }; diff --git a/components/select/style/multiple.tsx b/components/select/style/multiple.tsx index 071412f976..d4a5ff8795 100644 --- a/components/select/style/multiple.tsx +++ b/components/select/style/multiple.tsx @@ -148,7 +148,6 @@ function genSizeStyle(token: SelectToken, suffix?: string): CSSObject { display: 'inline-flex', position: 'relative', maxWidth: '100%', - // FIXME: no sure this style marginInlineStart: token.inputPaddingHorizontalBase - selectItemDist, [` diff --git a/components/skeleton/Avatar.tsx b/components/skeleton/Avatar.tsx index 59fdb79ee1..c708829d91 100644 --- a/components/skeleton/Avatar.tsx +++ b/components/skeleton/Avatar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import omit from 'rc-util/lib/omit'; import classNames from 'classnames'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; import Element, { SkeletonElementProps } from './Element'; export interface AvatarProps extends Omit { @@ -9,25 +9,24 @@ export interface AvatarProps extends Omit { } const SkeletonAvatar = (props: AvatarProps) => { - const renderSkeletonAvatar = ({ getPrefixCls }: ConfigConsumerProps) => { - const { prefixCls: customizePrefixCls, className, active } = props; - const prefixCls = getPrefixCls('skeleton', customizePrefixCls); - const otherProps = omit(props, ['prefixCls', 'className']); - const cls = classNames( - prefixCls, - `${prefixCls}-element`, - { - [`${prefixCls}-active`]: active, - }, - className, - ); - return ( -
- -
- ); - }; - return {renderSkeletonAvatar}; + const { prefixCls: customizePrefixCls, className, active } = props; + const { getPrefixCls } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('skeleton', customizePrefixCls); + + const otherProps = omit(props, ['prefixCls', 'className']); + const cls = classNames( + prefixCls, + `${prefixCls}-element`, + { + [`${prefixCls}-active`]: active, + }, + className, + ); + return ( +
+ +
+ ); }; SkeletonAvatar.defaultProps = { diff --git a/components/skeleton/Button.tsx b/components/skeleton/Button.tsx index 4f51d08ab9..5ca1872257 100644 --- a/components/skeleton/Button.tsx +++ b/components/skeleton/Button.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import omit from 'rc-util/lib/omit'; import classNames from 'classnames'; import Element, { SkeletonElementProps } from './Element'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; export interface SkeletonButtonProps extends Omit { size?: 'large' | 'small' | 'default'; @@ -10,26 +10,25 @@ export interface SkeletonButtonProps extends Omit } const SkeletonButton = (props: SkeletonButtonProps) => { - const renderSkeletonButton = ({ getPrefixCls }: ConfigConsumerProps) => { - const { prefixCls: customizePrefixCls, className, active, block = false } = props; - const prefixCls = getPrefixCls('skeleton', customizePrefixCls); - const otherProps = omit(props, ['prefixCls']); - const cls = classNames( - prefixCls, - `${prefixCls}-element`, - { - [`${prefixCls}-active`]: active, - [`${prefixCls}-block`]: block, - }, - className, - ); - return ( -
- -
- ); - }; - return {renderSkeletonButton}; + const { prefixCls: customizePrefixCls, className, active, block = false } = props; + const { getPrefixCls } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('skeleton', customizePrefixCls); + + const otherProps = omit(props, ['prefixCls']); + const cls = classNames( + prefixCls, + `${prefixCls}-element`, + { + [`${prefixCls}-active`]: active, + [`${prefixCls}-block`]: block, + }, + className, + ); + return ( +
+ +
+ ); }; SkeletonButton.defaultProps = { diff --git a/components/skeleton/Image.tsx b/components/skeleton/Image.tsx index ccde31bc18..3f31a7179b 100644 --- a/components/skeleton/Image.tsx +++ b/components/skeleton/Image.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import classNames from 'classnames'; import { SkeletonElementProps } from './Element'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; export interface SkeletonImageProps extends Omit {} @@ -10,26 +10,24 @@ const path = 'M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z'; const SkeletonImage = (props: SkeletonImageProps) => { - const renderSkeletonImage = ({ getPrefixCls }: ConfigConsumerProps) => { - const { prefixCls: customizePrefixCls, className, style } = props; - const prefixCls = getPrefixCls('skeleton', customizePrefixCls); - const cls = classNames(prefixCls, `${prefixCls}-element`, className); + const { prefixCls: customizePrefixCls, className, style } = props; + const { getPrefixCls } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('skeleton', customizePrefixCls); + const cls = classNames(prefixCls, `${prefixCls}-element`, className); - return ( -
-
- - - -
+ return ( +
+
+ + +
- ); - }; - return {renderSkeletonImage}; +
+ ); }; export default SkeletonImage; diff --git a/components/skeleton/Input.tsx b/components/skeleton/Input.tsx index 1cb5e31f85..61532c4756 100644 --- a/components/skeleton/Input.tsx +++ b/components/skeleton/Input.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import omit from 'rc-util/lib/omit'; import classNames from 'classnames'; import Element, { SkeletonElementProps } from './Element'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; export interface SkeletonInputProps extends Omit { size?: 'large' | 'small' | 'default'; @@ -10,26 +10,25 @@ export interface SkeletonInputProps extends Omit { - const renderSkeletonInput = ({ getPrefixCls }: ConfigConsumerProps) => { - const { prefixCls: customizePrefixCls, className, active, block } = props; - const prefixCls = getPrefixCls('skeleton', customizePrefixCls); - const otherProps = omit(props, ['prefixCls']); - const cls = classNames( - prefixCls, - `${prefixCls}-element`, - { - [`${prefixCls}-active`]: active, - [`${prefixCls}-block`]: block, - }, - className, - ); - return ( -
- -
- ); - }; - return {renderSkeletonInput}; + const { prefixCls: customizePrefixCls, className, active, block } = props; + const { getPrefixCls } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('skeleton', customizePrefixCls); + + const otherProps = omit(props, ['prefixCls']); + const cls = classNames( + prefixCls, + `${prefixCls}-element`, + { + [`${prefixCls}-active`]: active, + [`${prefixCls}-block`]: block, + }, + className, + ); + return ( +
+ +
+ ); }; SkeletonInput.defaultProps = { diff --git a/components/skeleton/Skeleton.tsx b/components/skeleton/Skeleton.tsx index 41b64629b7..a809371ba1 100644 --- a/components/skeleton/Skeleton.tsx +++ b/components/skeleton/Skeleton.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import classNames from 'classnames'; import Title, { SkeletonTitleProps } from './Title'; import Paragraph, { SkeletonParagraphProps } from './Paragraph'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; import Element from './Element'; import SkeletonAvatar, { AvatarProps } from './Avatar'; import SkeletonButton from './Button'; @@ -72,99 +72,96 @@ function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): Skeleton } const Skeleton = (props: SkeletonProps) => { - const renderSkeleton = ({ getPrefixCls, direction }: ConfigConsumerProps) => { - const { - prefixCls: customizePrefixCls, - loading, - className, - style, - children, - avatar, - title, - paragraph, - active, - round, - } = props; + const { + prefixCls: customizePrefixCls, + loading, + className, + style, + children, + avatar, + title, + paragraph, + active, + round, + } = props; - const prefixCls = getPrefixCls('skeleton', customizePrefixCls); + const { getPrefixCls, direction } = React.useContext(ConfigContext); + const prefixCls = getPrefixCls('skeleton', customizePrefixCls); - if (loading || !('loading' in props)) { - const hasAvatar = !!avatar; - const hasTitle = !!title; - const hasParagraph = !!paragraph; + if (loading || !('loading' in props)) { + const hasAvatar = !!avatar; + const hasTitle = !!title; + const hasParagraph = !!paragraph; - // Avatar - let avatarNode; - if (hasAvatar) { - const avatarProps: SkeletonAvatarProps = { - prefixCls: `${prefixCls}-avatar`, - ...getAvatarBasicProps(hasTitle, hasParagraph), - ...getComponentProps(avatar), - }; - // We direct use SkeletonElement as avatar in skeleton internal. - avatarNode = ( -
- -
- ); - } - - let contentNode; - if (hasTitle || hasParagraph) { - // Title - let $title; - if (hasTitle) { - const titleProps: SkeletonTitleProps = { - prefixCls: `${prefixCls}-title`, - ...getTitleBasicProps(hasAvatar, hasParagraph), - ...getComponentProps(title), - }; - - $title = ; - } - - // Paragraph - let paragraphNode; - if (hasParagraph) { - const paragraphProps: SkeletonParagraphProps = { - prefixCls: `${prefixCls}-paragraph`, - ...getParagraphBasicProps(hasAvatar, hasTitle), - ...getComponentProps(paragraph), - }; - - paragraphNode = <Paragraph {...paragraphProps} />; - } - - contentNode = ( - <div className={`${prefixCls}-content`}> - {$title} - {paragraphNode} - </div> - ); - } - - const cls = classNames( - prefixCls, - { - [`${prefixCls}-with-avatar`]: hasAvatar, - [`${prefixCls}-active`]: active, - [`${prefixCls}-rtl`]: direction === 'rtl', - [`${prefixCls}-round`]: round, - }, - className, - ); - - return ( - <div className={cls} style={style}> - {avatarNode} - {contentNode} + // Avatar + let avatarNode; + if (hasAvatar) { + const avatarProps: SkeletonAvatarProps = { + prefixCls: `${prefixCls}-avatar`, + ...getAvatarBasicProps(hasTitle, hasParagraph), + ...getComponentProps(avatar), + }; + // We direct use SkeletonElement as avatar in skeleton internal. + avatarNode = ( + <div className={`${prefixCls}-header`}> + <Element {...avatarProps} /> </div> ); } - return children; - }; - return <ConfigConsumer>{renderSkeleton}</ConfigConsumer>; + let contentNode; + if (hasTitle || hasParagraph) { + // Title + let $title; + if (hasTitle) { + const titleProps: SkeletonTitleProps = { + prefixCls: `${prefixCls}-title`, + ...getTitleBasicProps(hasAvatar, hasParagraph), + ...getComponentProps(title), + }; + + $title = <Title {...titleProps} />; + } + + // Paragraph + let paragraphNode; + if (hasParagraph) { + const paragraphProps: SkeletonParagraphProps = { + prefixCls: `${prefixCls}-paragraph`, + ...getParagraphBasicProps(hasAvatar, hasTitle), + ...getComponentProps(paragraph), + }; + + paragraphNode = <Paragraph {...paragraphProps} />; + } + + contentNode = ( + <div className={`${prefixCls}-content`}> + {$title} + {paragraphNode} + </div> + ); + } + + const cls = classNames( + prefixCls, + { + [`${prefixCls}-with-avatar`]: hasAvatar, + [`${prefixCls}-active`]: active, + [`${prefixCls}-rtl`]: direction === 'rtl', + [`${prefixCls}-round`]: round, + }, + className, + ); + + return ( + <div className={cls} style={style}> + {avatarNode} + {contentNode} + </div> + ); + } + return React.isValidElement(children) ? children : null; }; Skeleton.defaultProps = { diff --git a/components/spin/__tests__/__snapshots__/index.test.js.snap b/components/spin/__tests__/__snapshots__/index.test.js.snap index e77c6a1689..18de381464 100644 --- a/components/spin/__tests__/__snapshots__/index.test.js.snap +++ b/components/spin/__tests__/__snapshots__/index.test.js.snap @@ -1,18 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Spin if indicator set null should not be render default indicator 1`] = ` -<Spin - indicator={null} - size="default" - spinning={true} - wrapperClassName="" -> - <div - aria-busy={true} - aria-live="polite" - className="ant-spin ant-spin-spinning" - /> -</Spin> +<div + aria-busy="true" + aria-live="polite" + class="ant-spin ant-spin-spinning" +/> `; exports[`Spin rtl render component should be rendered correctly in RTL direction 1`] = ` @@ -53,19 +46,13 @@ exports[`Spin should render custom indicator when it's set 1`] = ` `; exports[`Spin should support static method Spin.setDefaultIndicator 1`] = ` -<Spin - size="default" - spinning={true} - wrapperClassName="" +<div + aria-busy="true" + aria-live="polite" + class="ant-spin ant-spin-spinning" > - <div - aria-busy={true} - aria-live="polite" - className="ant-spin ant-spin-spinning" - > - <em - className="custom-spinner ant-spin-dot" - /> - </div> -</Spin> + <em + class="custom-spinner ant-spin-dot" + /> +</div> `; diff --git a/components/spin/__tests__/delay.test.js b/components/spin/__tests__/delay.test.js index fbee446e7c..61f345746d 100644 --- a/components/spin/__tests__/delay.test.js +++ b/components/spin/__tests__/delay.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import Spin from '..'; +// eslint-disable-next-line import/no-named-as-default +import Spin, { Spin as SpinClass } from '..'; import { sleep } from '../../../tests/utils'; describe('delay spinning', () => { @@ -24,8 +25,8 @@ describe('delay spinning', () => { it('should cancel debounce function when unmount', async () => { const wrapper = mount(<Spin spinning delay={100} />); - const spy = jest.spyOn(wrapper.instance().updateSpinning, 'cancel'); - expect(wrapper.instance().updateSpinning.cancel).toEqual(expect.any(Function)); + const spy = jest.spyOn(wrapper.find(SpinClass).instance().updateSpinning, 'cancel'); + expect(wrapper.find(SpinClass).instance().updateSpinning.cancel).toEqual(expect.any(Function)); expect(spy).not.toHaveBeenCalled(); wrapper.unmount(); expect(spy).toHaveBeenCalled(); diff --git a/components/spin/__tests__/index.test.js b/components/spin/__tests__/index.test.js index 641adc7eb5..8460c5b94c 100644 --- a/components/spin/__tests__/index.test.js +++ b/components/spin/__tests__/index.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { render, mount } from 'enzyme'; -import Spin from '..'; +// eslint-disable-next-line import/no-named-as-default +import Spin, { Spin as SpinClass } from '..'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; @@ -26,20 +27,20 @@ describe('Spin', () => { it('should be controlled by spinning', () => { const wrapper = mount(<Spin spinning={false} />); - expect(wrapper.instance().state.spinning).toBe(false); + expect(wrapper.find(SpinClass).instance().state.spinning).toBe(false); wrapper.setProps({ spinning: true }); - expect(wrapper.instance().state.spinning).toBe(true); + expect(wrapper.find(SpinClass).instance().state.spinning).toBe(true); }); it('if indicator set null should not be render default indicator', () => { const wrapper = mount(<Spin indicator={null} />); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.render()).toMatchSnapshot(); }); it('should support static method Spin.setDefaultIndicator', () => { Spin.setDefaultIndicator(<em className="custom-spinner" />); const wrapper = mount(<Spin />); - expect(wrapper).toMatchSnapshot(); + expect(wrapper.render()).toMatchSnapshot(); Spin.setDefaultIndicator(null); }); diff --git a/components/spin/index.tsx b/components/spin/index.tsx index 4c1b1163e0..1b45d51d54 100644 --- a/components/spin/index.tsx +++ b/components/spin/index.tsx @@ -2,9 +2,10 @@ import * as React from 'react'; import classNames from 'classnames'; import omit from 'rc-util/lib/omit'; import debounce from 'lodash/debounce'; -import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; +import { ConfigConsumer, ConfigConsumerProps, ConfigContext } from '../config-provider'; import { tuple } from '../_util/type'; import { isValidElement, cloneElement } from '../_util/reactNode'; +import useStyle from './style/index'; const SpinSizes = tuple('small', 'default', 'large'); export type SpinSize = typeof SpinSizes[number]; @@ -22,6 +23,15 @@ export interface SpinProps { indicator?: SpinIndicator; } +export interface SpinClassProps extends SpinProps { + hashId: string; + spinPrefixCls: string; +} + +export type SpinFCType = React.FC<SpinProps> & { + setDefaultIndicator: (indicator: React.ReactNode) => void; +}; + export interface SpinState { spinning?: boolean; notCssAnimationSupported?: boolean; @@ -30,7 +40,7 @@ export interface SpinState { // Render indicator let defaultIndicator: React.ReactNode = null; -function renderIndicator(prefixCls: string, props: SpinProps): React.ReactNode { +function renderIndicator(prefixCls: string, props: SpinClassProps): React.ReactNode { const { indicator } = props; const dotClassName = `${prefixCls}-dot`; @@ -65,20 +75,16 @@ function shouldDelay(spinning?: boolean, delay?: number): boolean { return !!spinning && !!delay && !isNaN(Number(delay)); } -class Spin extends React.Component<SpinProps, SpinState> { +export class Spin extends React.Component<SpinClassProps, SpinState> { static defaultProps = { spinning: true, size: 'default' as SpinSize, wrapperClassName: '', }; - static setDefaultIndicator(indicator: React.ReactNode) { - defaultIndicator = indicator; - } - originalUpdateSpinning: () => void; - constructor(props: SpinProps) { + constructor(props: SpinClassProps) { super(props); const { spinning, delay } = props; @@ -103,7 +109,7 @@ class Spin extends React.Component<SpinProps, SpinState> { this.cancelExistingSpin(); } - debouncifyUpdateSpinning = (props?: SpinProps) => { + debouncifyUpdateSpinning = (props?: SpinClassProps) => { const { delay } = props || this.props; if (delay) { this.cancelExistingSpin(); @@ -130,9 +136,10 @@ class Spin extends React.Component<SpinProps, SpinState> { return !!(this.props && typeof this.props.children !== 'undefined'); } - renderSpin = ({ getPrefixCls, direction }: ConfigConsumerProps) => { + renderSpin = ({ direction }: ConfigConsumerProps) => { const { - prefixCls: customizePrefixCls, + spinPrefixCls: prefixCls, + hashId, className, size, tip, @@ -142,7 +149,6 @@ class Spin extends React.Component<SpinProps, SpinState> { } = this.props; const { spinning } = this.state; - const prefixCls = getPrefixCls('spin', customizePrefixCls); const spinClassName = classNames( prefixCls, { @@ -153,10 +159,11 @@ class Spin extends React.Component<SpinProps, SpinState> { [`${prefixCls}-rtl`]: direction === 'rtl', }, className, + hashId, ); // fix https://fb.me/react-unknown-prop - const divProps = omit(restProps, ['spinning', 'delay', 'indicator']); + const divProps = omit(restProps, ['spinning', 'delay', 'indicator', 'prefixCls']); const spinElement = ( <div @@ -175,7 +182,10 @@ class Spin extends React.Component<SpinProps, SpinState> { [`${prefixCls}-blur`]: spinning, }); return ( - <div {...divProps} className={classNames(`${prefixCls}-nested-loading`, wrapperClassName)}> + <div + {...divProps} + className={classNames(`${prefixCls}-nested-loading`, wrapperClassName, hashId)} + > {spinning && <div key="loading">{spinElement}</div>} <div className={containerClassName} key="container"> {this.props.children} @@ -191,4 +201,28 @@ class Spin extends React.Component<SpinProps, SpinState> { } } -export default Spin; +const SpinFC: SpinFCType = (props: SpinProps) => { + const { prefixCls: customizePrefixCls } = props; + const { getPrefixCls } = React.useContext(ConfigContext); + + const spinPrefixCls = getPrefixCls('spin', customizePrefixCls); + + const [wrapSSR, hashId] = useStyle(spinPrefixCls); + + const spinClassProps: SpinClassProps = { + ...props, + spinPrefixCls, + hashId, + }; + return wrapSSR(<Spin {...spinClassProps} />); +}; + +SpinFC.setDefaultIndicator = (indicator: React.ReactNode) => { + defaultIndicator = indicator; +}; + +if (process.env.NODE_ENV !== 'production') { + SpinFC.displayName = 'Spin'; +} + +export default SpinFC; diff --git a/components/spin/style/index.less b/components/spin/style/_index.less similarity index 100% rename from components/spin/style/index.less rename to components/spin/style/_index.less diff --git a/components/spin/style/index.tsx b/components/spin/style/index.tsx index 3a3ab0de59..330d5246a6 100644 --- a/components/spin/style/index.tsx +++ b/components/spin/style/index.tsx @@ -1,2 +1,244 @@ -import '../../style/index.less'; -import './index.less'; +// deps-lint-skip-all +import { CSSObject, Keyframes } from '@ant-design/cssinjs'; +import { + useStyleRegister, + useToken, + resetComponent, + GenerateStyle, + UseComponentStyleResult, +} from '../../_util/theme'; +import type { DerivativeToken } from '../../_util/theme'; + +interface SpinToken extends DerivativeToken { + spinCls: string; + spinDotDefault: string; + spinDotSize: number; + spinDotSizeSM: number; + spinDotSizeLG: number; +} + +const antSpinMove = new Keyframes('antSpinMove', { + to: { opacity: 1 }, +}); + +const antRotate = new Keyframes('antRotate', { + to: { transform: 'rotate(405deg)' }, +}); + +const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken, hashId: string): CSSObject => ({ + [`${token.spinCls}`]: { + ...resetComponent(token), + position: 'absolute', + display: 'none', + color: token.colorPrimary, + textAlign: 'center', + verticalAlign: 'middle', + opacity: 0, + transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOutCirc}`, + + '&-spinning': { + position: 'static', + display: 'inline-block', + opacity: 1, + }, + + '&-nested-loading': { + position: 'relative', + [`> div > ${token.spinCls}`]: { + position: 'absolute', + top: 0, + insetInlineStart: 0, + zIndex: 4, + display: 'block', + width: '100%', + height: '100%', + maxHeight: 400, // FIXME: hard code in v4 + + [`${token.spinCls}-dot`]: { + position: 'absolute', + top: '50%', + insetInlineStart: '50%', + margin: -token.spinDotSize / 2, + }, + + [`${token.spinCls}-text`]: { + position: 'absolute', + top: '50%', + width: '100%', + paddingTop: (token.spinDotSize - token.fontSize) / 2 + 2, + textShadow: `0 1px 2px ${token.colorBgComponent}`, + }, + + [`&${token.spinCls}-show-text ${token.spinCls}-dot`]: { + marginTop: -(token.spinDotSize / 2) - 10, + }, + + [`> div > ${token.spinCls}-sm`]: { + [`${token.spinCls}-dot`]: { + margin: -token.spinDotSizeSM / 2, + }, + [`${token.spinCls}-text`]: { + paddingTop: (token.spinDotSizeSM - token.fontSize) / 2 + 2, + }, + [`&${token.spinCls}-show-text ${token.spinCls}-dot`]: { + marginTop: -(token.spinDotSizeSM / 2) - 10, + }, + }, + + [`> div > ${token.spinCls}-lg`]: { + [`${token.spinCls}-dot`]: { + margin: -(token.spinDotSizeLG / 2), + }, + [`${token.spinCls}-text`]: { + paddingTop: (token.spinDotSizeLG - token.fontSize) / 2 + 2, + }, + [`&${token.spinCls}-show-text ${token.spinCls}-dot`]: { + marginTop: -(token.spinDotSizeLG / 2) - 10, + }, + }, + }, + + [`${token.spinCls}-container`]: { + position: 'relative', + transition: `opacity ${token.motionDurationSlow}`, + + '&::after': { + position: 'absolute', + top: 0, + insetInlineEnd: 0, + bottom: 0, + insetInlineStart: 0, + zIndex: 10, + width: '100%', + height: '100%', + background: token.colorBgComponent, + opacity: 0, + transition: `all ${token.motionDurationSlow}`, + content: '""', + pointerEvents: 'none', + }, + }, + + [`${token.spinCls}-blur`]: { + clear: 'both', + opacity: 0.5, + userSelect: 'none', + pointerEvents: 'none', + + [`&::after`]: { + opacity: 0.4, + pointerEvents: 'auto', + }, + }, + }, + + // tip + // ------------------------------ + [`&-tip`]: { + color: token.spinDotDefault, + }, + + // dots + // ------------------------------ + [`${token.spinCls}-dot`]: { + position: 'relative', + display: 'inline-block', + fontSize: token.spinDotSize, + width: '1em', + height: '1em', + + '&-item': { + position: 'absolute', + display: 'block', + width: 9, // FIXME: hard code in v4 + height: 9, // FIXME: hard code in v4 + backgroundColor: token.colorPrimary, + borderRadius: '100%', + transform: 'scale(0.75)', + transformOrigin: '50% 50%', + opacity: 0.3, + animation: `${antSpinMove.getName(hashId)} 1s infinite linear alternate`, + + '&:nth-child(1)': { + top: 0, + insetInlineStart: 0, + }, + + '&:nth-child(2)': { + top: 0, + insetInlineEnd: 0, + animationDelay: '0.4s', + }, + + '&:nth-child(3)': { + insetInlineEnd: 0, + bottom: 0, + animationDelay: '0.8s', + }, + + '&:nth-child(4)': { + bottom: 0, + insetInlineStart: 0, + animationDelay: '1.2s', + }, + }, + + '&-spin': { + transform: 'rotate(45deg)', + animation: `${antRotate.getName(hashId)} 1.2s infinite linear`, + }, + }, + + // Sizes + // ------------------------------ + + // small + [`&-sm ${token.spinCls}-dot`]: { + fontSize: token.spinDotSizeSM, + + i: { + width: 6, // FIXME: hard code in v4 + height: 6, // FIXME: hard code in v4 + }, + }, + + // large + [`&-lg ${token.spinCls}-dot`]: { + fontSize: token.spinDotSizeLG, + + i: { + width: 14, // FIXME: hard code in v4 + height: 14, // FIXME: hard code in v4 + }, + }, + + [`&${token.spinCls}-show-text ${token.spinCls}-text`]: { + display: 'block', + }, + + // animation + antSpinMove, + antRotate, + }, +}); + +// ============================== Export ============================== +export default function useStyle(prefixCls: string): UseComponentStyleResult { + const [theme, token, hashId] = useToken(); + + const spinToken: SpinToken = { + ...token, + spinCls: `.${prefixCls}`, + spinDotDefault: token.colorTextSecondary, + spinDotSize: 20, // FIXME: hard code in v4 + spinDotSizeSM: 14, // FIXME: hard code in v4 + spinDotSizeLG: 32, // FIXME: hard code in v4 + }; + + return [ + useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [ + genSpinStyle(spinToken, hashId), + ]), + hashId, + ]; +} diff --git a/components/table/Table.tsx b/components/table/Table.tsx index aed6bb309e..cec6668e27 100644 --- a/components/table/Table.tsx +++ b/components/table/Table.tsx @@ -4,6 +4,7 @@ import omit from 'rc-util/lib/omit'; import RcTable, { Summary } from 'rc-table'; import { TableProps as RcTableProps, INTERNAL_HOOKS } from 'rc-table/lib/Table'; import { convertChildrenToColumns } from 'rc-table/lib/hooks/useColumns'; +// eslint-disable-next-line import/no-named-as-default import Spin, { SpinProps } from '../spin'; import Pagination from '../pagination'; import { TooltipProps } from '../tooltip'; diff --git a/components/tabs/index.en-US.md b/components/tabs/index.en-US.md index c9b2559063..fcda32f140 100644 --- a/components/tabs/index.en-US.md +++ b/components/tabs/index.en-US.md @@ -28,12 +28,12 @@ Ant Design has 3 types of Tabs for different situations. | centered | Centers tabs | boolean | false | 4.4.0 | | defaultActiveKey | Initial active TabPane's key, if `activeKey` is not set | string | - | | | hideAdd | Hide plus icon or not. Only works while `type="editable-card"` | boolean | false | | -| moreIcon | The custom icon of ellipsis | ReactNode | <EllipsisOutlined /> | 4.14.0 | +| moreIcon | The custom icon of ellipsis | ReactNode | <EllipsisOutlined /> | 4.14.0 | | renderTabBar | Replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | | | size | Preset tab bar size | `large` \| `default` \| `small` | `default` | | | tabBarExtraContent | Extra content in tab bar | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 | | tabBarGutter | The gap between tabs | number | - | | -| tabBarStyle | Tab bar style object | object | - | | +| tabBarStyle | Tab bar style object | CSSProperties | - | | | tabPosition | Position of tabs | `top` \| `right` \| `bottom` \| `left` | `top` | | | destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false | | | type | Basic style of tabs | `line` \| `card` \| `editable-card` | `line` | | diff --git a/components/tabs/index.zh-CN.md b/components/tabs/index.zh-CN.md index 0e54eb999d..24dd77d57b 100644 --- a/components/tabs/index.zh-CN.md +++ b/components/tabs/index.zh-CN.md @@ -36,7 +36,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。 | size | 大小,提供 `large` `default` 和 `small` 三种大小 | string | `default` | | | tabBarExtraContent | tab bar 上额外的元素 | ReactNode \| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 | | tabBarGutter | tabs 之间的间隙 | number | - | | -| tabBarStyle | tab bar 的样式对象 | object | - | | +| tabBarStyle | tab bar 的样式对象 | CSSProperties | - | | | tabPosition | 页签位置,可选值有 `top` `right` `bottom` `left` | string | `top` | | | destroyInactiveTabPane | 被隐藏时是否销毁 DOM 结构 | boolean | false | | | type | 页签的基本样式,可选 `line`、`card` `editable-card` 类型 | string | `line` | | @@ -47,9 +47,9 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。 ### Tabs.TabPane -| 参数 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| closeIcon | 自定义关闭图标,`在 type="editable-card"`时有效 | ReactNode | - | -| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | -| key | 对应 activeKey | string | - | -| tab | 选项卡头显示文字 | ReactNode | - | +| 参数 | 说明 | 类型 | 默认值 | +| ----------- | ----------------------------------------------- | --------- | ------ | +| closeIcon | 自定义关闭图标,`在 type="editable-card"`时有效 | ReactNode | - | +| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false | +| key | 对应 activeKey | string | - | +| tab | 选项卡头显示文字 | ReactNode | - | diff --git a/components/tree/style/index.tsx b/components/tree/style/index.tsx index b2a1c96926..7a236c39a1 100644 --- a/components/tree/style/index.tsx +++ b/components/tree/style/index.tsx @@ -26,7 +26,7 @@ const treeNodeFX = new Keyframes('ant-tree-node-fx-do-not-use', { const getSwitchStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({ [`.${prefixCls}-switcher-icon`]: { display: 'inline-block', - fontSize: 10, // FIXME: missing token + fontSize: 10, verticalAlign: 'baseline', svg: { diff --git a/components/typography/style/index.tsx b/components/typography/style/index.tsx index a4f1ed2fbd..bdfe2a0e0e 100644 --- a/components/typography/style/index.tsx +++ b/components/typography/style/index.tsx @@ -1,6 +1,6 @@ // deps-lint-skip-all import { useStyleRegister, useToken } from '../../_util/theme'; -import type { UseComponentStyleResult, GenerateStyle } from '../../_util/theme'; +import type { UseComponentStyleResult, GenerateStyle, AliasToken } from '../../_util/theme'; import { operationUnit } from '../../_util/theme/util/operationUnit'; import { getTitleStyles, @@ -10,12 +10,22 @@ import { getCopiableStyles, getEllipsisStyles, } from './mixins'; -import type { TypographyToken } from './mixins'; + +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + sizeMarginHeadingVerticalStart: number | string; + sizeMarginHeadingVerticalEnd: number | string; +} + +export interface TypographyToken extends AliasToken, ComponentToken { + typographyCls: string; +} const genTypographyStyle: GenerateStyle<TypographyToken> = token => { - const { prefixCls, titleMarginTop } = token.typography; + const { typographyCls, sizeMarginHeadingVerticalStart } = token; + return { - [`.${prefixCls}`]: { + [typographyCls]: { color: token.colorText, overflowWrap: 'break-word', '&&-secondary': { @@ -59,7 +69,7 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => { & + h4&, & + h5& `]: { - marginTop: titleMarginTop, + marginTop: sizeMarginHeadingVerticalStart, }, [` @@ -79,7 +89,7 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => { + h4, + h5 `]: { - marginTop: titleMarginTop, + marginTop: sizeMarginHeadingVerticalStart, }, }, @@ -89,9 +99,9 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => { // Operation [` - .${prefixCls}-expand, - .${prefixCls}-edit, - .${prefixCls}-copy + ${typographyCls}-expand, + ${typographyCls}-edit, + ${typographyCls}-copy `]: { ...operationUnit(token), marginInlineStart: token.marginXXS, @@ -114,20 +124,23 @@ const genTypographyStyle: GenerateStyle<TypographyToken> = token => { export default function useStyle(prefixCls: string): UseComponentStyleResult { const [theme, token, hashId] = useToken(); - const typographyToken: TypographyToken = { - ...token, - typography: { - prefixCls, - titleMarginTop: '1.2em', - titleMarginBottom: '0.5em', - titleFontWeight: 600, - }, - }; - return [ - useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [ - genTypographyStyle(typographyToken), - ]), + useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => { + const { typography } = token; + + const typographyToken: TypographyToken = { + ...token, + + typographyCls: `.${prefixCls}`, + + sizeMarginHeadingVerticalStart: '1.2em', + sizeMarginHeadingVerticalEnd: '0.5em', + + ...typography, + }; + + return [genTypographyStyle(typographyToken)]; + }), hashId, ]; } diff --git a/components/typography/style/mixins.tsx b/components/typography/style/mixins.tsx index 73c16129b9..dc401f06a3 100644 --- a/components/typography/style/mixins.tsx +++ b/components/typography/style/mixins.tsx @@ -9,37 +9,28 @@ */ import { gold } from '@ant-design/colors'; import type { CSSObject } from '@ant-design/cssinjs'; -import type { DerivativeToken, GenerateStyle } from '../../_util/theme'; +import type { GenerateStyle } from '../../_util/theme'; import { operationUnit } from '../../_util/theme/util/operationUnit'; import { initInputToken } from '../../input/style'; - -export interface TypographyToken extends DerivativeToken { - typography: { - prefixCls: string; - titleMarginTop: string; - titleMarginBottom: string; - titleFontWeight: number; - }; -} +import type { TypographyToken } from '.'; // eslint-disable-next-line import/prefer-default-export -const getTitleStyle = ({ - fontSize, - lineHeight, - color, - typographyToken, -}: { - fontSize: number; - lineHeight: number; - color: string; - typographyToken: TypographyToken['typography']; -}) => ({ - marginBottom: typographyToken.titleMarginBottom, - color, - fontWeight: typographyToken.titleFontWeight, - fontSize, - lineHeight, -}); +const getTitleStyle = ( + fontSize: number, + lineHeight: number, + color: string, + token: TypographyToken, +) => { + const { sizeMarginHeadingVerticalEnd, fontWeightStrong } = token; + + return { + marginBottom: sizeMarginHeadingVerticalEnd, + color, + fontWeight: fontWeightStrong, + fontSize, + lineHeight, + }; +}; // eslint-disable-next-line import/prefer-default-export export const getTitleStyles: GenerateStyle<TypographyToken, CSSObject> = token => { @@ -54,39 +45,43 @@ export const getTitleStyles: GenerateStyle<TypographyToken, CSSObject> = token = div&-h${headingLevel} > textarea, h${headingLevel} ` - ] = getTitleStyle({ - fontSize: token[`fontSizeHeading${headingLevel}`], - lineHeight: token[`lineHeightHeading${headingLevel}`], - color: token.colorTextHeading, - typographyToken: token.typography, - }); + ] = getTitleStyle( + token[`fontSizeHeading${headingLevel}`], + token[`lineHeightHeading${headingLevel}`], + token.colorTextHeading, + token, + ); }); return styles; }; -export const getLinkStyles: GenerateStyle<TypographyToken, CSSObject> = token => ({ - 'a&, a': { - ...operationUnit(token), - textDecoration: token.linkDecoration, +export const getLinkStyles: GenerateStyle<TypographyToken, CSSObject> = token => { + const { typographyCls } = token; - '&:active, &:hover': { - textDecoration: token.linkHoverDecoration, - }, - - [`&[disabled], &.${token.typography.prefixCls}-disabled`]: { - color: token.colorTextDisabled, - cursor: 'not-allowed', + return { + 'a&, a': { + ...operationUnit(token), + textDecoration: token.linkDecoration, '&:active, &:hover': { - color: '@disabled-color', + textDecoration: token.linkHoverDecoration, }, - '&:active': { - pointerEvents: 'none', + [`&[disabled], &${typographyCls}-disabled`]: { + color: token.colorTextDisabled, + cursor: 'not-allowed', + + '&:active, &:hover': { + color: token.colorTextDisabled, + }, + + '&:active': { + pointerEvents: 'none', + }, }, }, - }, -}); + }; +}; export const getResetStyles = (): CSSObject => ({ code: { @@ -188,6 +183,8 @@ export const getResetStyles = (): CSSObject => ({ }); export const getEditableStyles: GenerateStyle<TypographyToken, CSSObject> = token => { + const { typographyCls } = token; + const inputToken = initInputToken(token, '', ''); const inputShift = inputToken.inputPaddingVertical + 1; return { @@ -200,7 +197,7 @@ export const getEditableStyles: GenerateStyle<TypographyToken, CSSObject> = toke marginBottom: `calc(1em - ${inputShift}px)`, }, - [`.${token.typography.prefixCls}-edit-content-confirm`]: { + [`${typographyCls}-edit-content-confirm`]: { position: 'absolute', insetInlineEnd: token.marginXS + 2, insetBlockEnd: token.marginXS,