Merge branch 'next' into next-merge-feature

This commit is contained in:
MadCcc 2022-03-25 20:19:42 +08:00
commit 9f3dc50df6
50 changed files with 1843 additions and 674 deletions

View File

@ -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!'

View File

@ -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,

View File

@ -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<DerivativeToken, OmitDerivativeKey> {
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<DerivativeToken, OmitDerivativeKey> {
paddingXXS: number;
paddingLG: number;
marginXS: number;
marginSM: number;
marginLG: number;
marginXXS: number;
}

View File

@ -194,6 +194,7 @@ const seedToken: SeedToken = {
// zIndex
zIndexBase: 0,
zIndexPopover: 1030,
zIndexPopup: 1000,
};

View File

@ -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),

View File

@ -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,

View File

@ -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')`,
},
};
};

View File

@ -123,7 +123,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
[`&-active:not(${cascaderMenuItemCls}-disabled)`]: {
[`&, &:hover`]: {
fontWeight: 600, // FIXME: hardcode
fontWeight: token.fontWeightStrong,
backgroundColor: token.controlItemBgActive,
},
},

View File

@ -184,7 +184,7 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (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}`,
},
},

View File

@ -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';

View File

@ -60,7 +60,7 @@ const Cell: React.FC<CellProps> = ({
colSpan={span}
>
<div className={`${itemPrefixCls}-item-container`}>
{label && (
{(label || label === 0) && (
<span
className={classNames(`${itemPrefixCls}-item-label`, {
[`${itemPrefixCls}-item-no-colon`]: !colon,
@ -70,7 +70,7 @@ const Cell: React.FC<CellProps> = ({
{label}
</span>
)}
{content && (
{(content || content === 0) && (
<span className={classNames(`${itemPrefixCls}-item-content`)} style={contentStyle}>
{content}
</span>

View File

@ -209,6 +209,46 @@ exports[`Descriptions column is number 1`] = `
</div>
`;
exports[`Descriptions number 0 should render correct 1`] = `
<div
class="ant-descriptions"
>
<div
class="ant-descriptions-view"
>
<table>
<tbody>
<tr
class="ant-descriptions-row"
>
<td
class="ant-descriptions-item"
colspan="1"
>
<div
class="ant-descriptions-item-container"
>
<span
class="ant-descriptions-item-label"
style="color: red;"
>
0
</span>
<span
class="ant-descriptions-item-content"
style="color: red;"
>
0
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
exports[`Descriptions should work with React Fragment 1`] = `
<div
class="ant-descriptions"

View File

@ -238,4 +238,15 @@ describe('Descriptions', () => {
wrapper.setProps({ extra: undefined });
expect(wrapper.find('.ant-descriptions-extra').exists()).toBe(false);
});
it('number 0 should render correct', () => {
const wrapper = mount(
<Descriptions>
<Descriptions.Item label={0} labelStyle={{ color: 'red' }} contentStyle={{ color: 'red' }}>
{0}
</Descriptions.Item>
</Descriptions>,
);
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -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<DividerToken> = (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<DividerToken> = (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<DividerToken> = (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<DividerToken> = (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<DividerToken> = (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<DividerToken> = (token): CSSObject =>
width: '100%',
},
'.ant-divider-inner-text': {
paddingInlineStart: `${token.dividerNotDefaultTextPadding}px`,
[`${dividerCls}-inner-text`]: {
paddingInlineStart: sizePaddingEdgeHorizontal,
},
},
@ -150,8 +150,8 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
width: 0,
},
'.ant-divider-inner-text': {
paddingInlineEnd: `${token.dividerNotDefaultTextPadding}px`,
[`${dividerCls}-inner-text`]: {
paddingInlineEnd: sizePaddingEdgeHorizontal,
},
},
},
@ -161,35 +161,31 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (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,
];
}

View File

@ -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<DrawerRef, DrawerProps>(
const parentDrawer = React.useContext(DrawerContext);
const destroyClose = React.useRef<boolean>(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<DrawerRef, DrawerProps>(
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
hashId,
);
const offsetStyle = mask ? getOffsetStyle() : {};
return (
return wrapSSR(
<DrawerContext.Provider value={operations}>
<RcDrawer
handler={false}
@ -323,7 +332,7 @@ const Drawer = React.forwardRef<DrawerRef, DrawerProps>(
>
{renderBody()}
</RcDrawer>
</DrawerContext.Provider>
</DrawerContext.Provider>,
);
},
);

View File

@ -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);

View File

@ -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<DrawerToken> = (
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<DrawerToken> = (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,
];
}

View File

@ -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,
});

View File

@ -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';

View File

@ -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';

View File

@ -188,7 +188,7 @@ Array [
`;
exports[`renders ./components/notification/demo/placement.md extend context correctly 1`] = `
<div>
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -255,11 +255,11 @@ exports[`renders ./components/notification/demo/placement.md extend context corr
</span>
</button>
</div>
</div>
</div>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>
/>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -326,11 +326,11 @@ exports[`renders ./components/notification/demo/placement.md extend context corr
</span>
</button>
</div>
</div>
</div>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>
/>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -397,8 +397,8 @@ exports[`renders ./components/notification/demo/placement.md extend context corr
</span>
</button>
</div>
</div>
</div>
</div>,
]
`;
exports[`renders ./components/notification/demo/update.md extend context correctly 1`] = `

View File

@ -188,7 +188,7 @@ Array [
`;
exports[`renders ./components/notification/demo/placement.md correctly 1`] = `
<div>
Array [
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -255,11 +255,11 @@ exports[`renders ./components/notification/demo/placement.md correctly 1`] = `
</span>
</button>
</div>
</div>
</div>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>
/>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -326,11 +326,11 @@ exports[`renders ./components/notification/demo/placement.md correctly 1`] = `
</span>
</button>
</div>
</div>
</div>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>
/>,
<div
class="ant-space ant-space-horizontal ant-space-align-center"
>
@ -397,8 +397,8 @@ exports[`renders ./components/notification/demo/placement.md correctly 1`] = `
</span>
</button>
</div>
</div>
</div>
</div>,
]
`;
exports[`renders ./components/notification/demo/update.md correctly 1`] = `

View File

@ -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({

View File

@ -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(
<div>
<>
<Space>
<Button type="primary" onClick={() => openNotification('top')}>
<BorderTopOutlined />
<Button type="primary" onClick={() => openNotification('top')} icon={<BorderTopOutlined />}>
top
</Button>
<Button type="primary" onClick={() => openNotification('bottom')}>
<BorderBottomOutlined />
<Button
type="primary"
onClick={() => openNotification('bottom')}
icon={<BorderBottomOutlined />}
>
bottom
</Button>
</Space>
<Divider />
<Space>
<Button type="primary" onClick={() => openNotification('topLeft')}>
<RadiusUpleftOutlined />
<Button
type="primary"
onClick={() => openNotification('topLeft')}
icon={<RadiusUpleftOutlined />}
>
topLeft
</Button>
<Button type="primary" onClick={() => openNotification('topRight')}>
<RadiusUprightOutlined />
<Button
type="primary"
onClick={() => openNotification('topRight')}
icon={<RadiusUprightOutlined />}
>
topRight
</Button>
</Space>
<Divider />
<Space>
<Button type="primary" onClick={() => openNotification('bottomLeft')}>
<RadiusBottomleftOutlined />
<Button
type="primary"
onClick={() => openNotification('bottomLeft')}
icon={<RadiusBottomleftOutlined />}
>
bottomLeft
</Button>
<Button type="primary" onClick={() => openNotification('bottomRight')}>
<RadiusBottomrightOutlined />
<Button
type="primary"
onClick={() => openNotification('bottomRight')}
icon={<RadiusBottomrightOutlined />}
>
bottomRight
</Button>
</Space>
</div>,
</>,
mountNode,
);
```

View File

@ -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,
};

View File

@ -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';

View File

@ -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;
}
}

View File

@ -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<unknown, PopoverProps>(
({ 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<unknown, PopoverProps>(
};
const prefixCls = getPrefixCls('popover', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
const rootPrefixCls = getPrefixCls();
return (
const overlayCls = classNames(overlayClassName, hashId);
return wrapSSR(
<Tooltip
{...otherProps}
prefixCls={prefixCls}
overlayClassName={overlayCls}
ref={ref as any}
overlay={getOverlay(prefixCls)}
transitionName={getTransitionName(rootPrefixCls, 'zoom-big', otherProps.transitionName)}
/>
/>,
);
},
);

View File

@ -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';

View File

@ -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<PopoverToken> = 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<PopoverToken> = 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<PopoverToken> = 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<PopoverToken> = (
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,
];
}

View File

@ -123,7 +123,7 @@ const genSingleStyle: GenerateStyle<SelectToken> = (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`]: {

View File

@ -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<SelectToken> = token => {
'&:hover': {
color: token.colorTextSecondary,
},
},
[`${selectCls}:hover &`]: {
'&:hover': {
[`${selectCls}-clear`]: {
opacity: 1,
},
},
@ -251,7 +252,7 @@ const genBaseStyle: GenerateStyle<SelectToken> = token => {
// ========================= Feedback ==========================
[`${selectCls}-has-feedback`]: {
[`${selectCls}-clear`]: {
insetInlineEnd: token.padding * 2,
insetInlineEnd: inputPaddingHorizontalBase + token.fontSize + token.paddingXXS,
},
},
};

View File

@ -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,
[`

View File

@ -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<SkeletonElementProps, 'shape'> {
@ -9,25 +9,24 @@ export interface AvatarProps extends Omit<SkeletonElementProps, 'shape'> {
}
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-avatar`} {...otherProps} />
</div>
);
};
return <ConfigConsumer>{renderSkeletonAvatar}</ConfigConsumer>;
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-avatar`} {...otherProps} />
</div>
);
};
SkeletonAvatar.defaultProps = {

View File

@ -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<SkeletonElementProps, 'size'> {
size?: 'large' | 'small' | 'default';
@ -10,26 +10,25 @@ export interface SkeletonButtonProps extends Omit<SkeletonElementProps, 'size'>
}
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-button`} {...otherProps} />
</div>
);
};
return <ConfigConsumer>{renderSkeletonButton}</ConfigConsumer>;
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-button`} {...otherProps} />
</div>
);
};
SkeletonButton.defaultProps = {

View File

@ -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<SkeletonElementProps, 'size' | 'shape' | 'active'> {}
@ -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 (
<div className={cls}>
<div className={classNames(`${prefixCls}-image`, className)} style={style}>
<svg
viewBox="0 0 1098 1024"
xmlns="http://www.w3.org/2000/svg"
className={`${prefixCls}-image-svg`}
>
<path d={path} className={`${prefixCls}-image-path`} />
</svg>
</div>
return (
<div className={cls}>
<div className={classNames(`${prefixCls}-image`, className)} style={style}>
<svg
viewBox="0 0 1098 1024"
xmlns="http://www.w3.org/2000/svg"
className={`${prefixCls}-image-svg`}
>
<path d={path} className={`${prefixCls}-image-path`} />
</svg>
</div>
);
};
return <ConfigConsumer>{renderSkeletonImage}</ConfigConsumer>;
</div>
);
};
export default SkeletonImage;

View File

@ -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<SkeletonElementProps, 'size' | 'shape'> {
size?: 'large' | 'small' | 'default';
@ -10,26 +10,25 @@ export interface SkeletonInputProps extends Omit<SkeletonElementProps, 'size' |
}
const SkeletonInput = (props: SkeletonInputProps) => {
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-input`} {...otherProps} />
</div>
);
};
return <ConfigConsumer>{renderSkeletonInput}</ConfigConsumer>;
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 (
<div className={cls}>
<Element prefixCls={`${prefixCls}-input`} {...otherProps} />
</div>
);
};
SkeletonInput.defaultProps = {

View File

@ -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 = (
<div className={`${prefixCls}-header`}>
<Element {...avatarProps} />
</div>
);
}
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}
// 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 = {

View File

@ -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>
`;

View File

@ -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();

View File

@ -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);
});

View File

@ -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;

View File

@ -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,
];
}

View File

@ -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';

View File

@ -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 | &lt;EllipsisOutlined /> | 4.14.0 |
| moreIcon | The custom icon of ellipsis | ReactNode | &lt;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` | |

View File

@ -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 | - |

View File

@ -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: {

View File

@ -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,
];
}

View File

@ -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,