diff --git a/components/skeleton/Avatar.tsx b/components/skeleton/Avatar.tsx index 2b47aa6ede..aa65f30f54 100644 --- a/components/skeleton/Avatar.tsx +++ b/components/skeleton/Avatar.tsx @@ -5,6 +5,7 @@ import initDefaultProps from '../_util/props-util/initDefaultProps'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import Element, { skeletonElementProps } from './Element'; import useStyle from './style'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; export const avatarProps = () => { return { @@ -24,7 +25,8 @@ const SkeletonAvatar = defineComponent({ }), setup(props) { const { prefixCls } = useConfigInject('skeleton', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const cls = computed(() => classNames( prefixCls.value, @@ -32,6 +34,8 @@ const SkeletonAvatar = defineComponent({ { [`${prefixCls.value}-active`]: props.active, }, + rootCls.value, + cssVarCls.value, hashId.value, ), ); diff --git a/components/skeleton/Button.tsx b/components/skeleton/Button.tsx index 8e32ad00d6..4c7b3a5c1e 100644 --- a/components/skeleton/Button.tsx +++ b/components/skeleton/Button.tsx @@ -5,6 +5,7 @@ import useConfigInject from '../config-provider/hooks/useConfigInject'; import { initDefaultProps } from '../_util/props-util'; import Element, { skeletonElementProps } from './Element'; import useStyle from './style'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; export const skeletonButtonProps = () => { return { @@ -24,7 +25,8 @@ const SkeletonButton = defineComponent({ }), setup(props) { const { prefixCls } = useConfigInject('skeleton', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const cls = computed(() => classNames( prefixCls.value, @@ -33,6 +35,8 @@ const SkeletonButton = defineComponent({ [`${prefixCls.value}-active`]: props.active, [`${prefixCls.value}-block`]: props.block, }, + rootCls.value, + cssVarCls.value, hashId.value, ), ); diff --git a/components/skeleton/Image.tsx b/components/skeleton/Image.tsx index 6bfe8c009a..5d0293b30f 100644 --- a/components/skeleton/Image.tsx +++ b/components/skeleton/Image.tsx @@ -5,6 +5,7 @@ import omit from '../_util/omit'; import type { SkeletonElementProps } from './Element'; import { skeletonElementProps } from './Element'; import useStyle from './style'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; export type SkeletonImageProps = Omit; @@ -17,9 +18,16 @@ const SkeletonImage = defineComponent({ props: omit(skeletonElementProps(), ['size', 'shape', 'active']), setup(props) { const { prefixCls } = useConfigInject('skeleton', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const cls = computed(() => - classNames(prefixCls.value, `${prefixCls.value}-element`, hashId.value), + classNames( + prefixCls.value, + `${prefixCls.value}-element`, + rootCls.value, + hashId.value, + cssVarCls.value, + ), ); return () => { return wrapSSR( diff --git a/components/skeleton/Input.tsx b/components/skeleton/Input.tsx index a1a4e9ca94..e3c1209e2f 100644 --- a/components/skeleton/Input.tsx +++ b/components/skeleton/Input.tsx @@ -6,6 +6,7 @@ import type { SkeletonElementProps } from './Element'; import Element, { skeletonElementProps } from './Element'; import omit from '../_util/omit'; import useStyle from './style'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; export interface SkeletonInputProps extends Omit { size?: 'large' | 'small' | 'default'; @@ -22,7 +23,8 @@ const SkeletonInput = defineComponent({ }, setup(props) { const { prefixCls } = useConfigInject('skeleton', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); const cls = computed(() => classNames( prefixCls.value, @@ -31,7 +33,9 @@ const SkeletonInput = defineComponent({ [`${prefixCls.value}-active`]: props.active, [`${prefixCls.value}-block`]: props.block, }, + rootCls.value, hashId.value, + cssVarCls.value, ), ); return () => { diff --git a/components/skeleton/Skeleton.tsx b/components/skeleton/Skeleton.tsx index 90dfc07760..f1deaa286c 100644 --- a/components/skeleton/Skeleton.tsx +++ b/components/skeleton/Skeleton.tsx @@ -10,6 +10,7 @@ import Paragraph from './Paragraph'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import Element from './Element'; import useStyle from './style'; +import useCSSVarCls from '../config-provider/hooks/useCssVarCls'; /* This only for skeleton internal. */ type SkeletonAvatarProps = Omit; @@ -90,7 +91,8 @@ const Skeleton = defineComponent({ }), setup(props, { slots }) { const { prefixCls, direction } = useConfigInject('skeleton', props); - const [wrapSSR, hashId] = useStyle(prefixCls); + const rootCls = useCSSVarCls(prefixCls); + const [wrapSSR, hashId, cssVarCls] = useStyle(prefixCls, rootCls); return () => { const { loading, avatar, title, paragraph, active, round } = props; @@ -155,7 +157,9 @@ const Skeleton = defineComponent({ [`${pre}-active`]: active, [`${pre}-rtl`]: direction.value === 'rtl', [`${pre}-round`]: round, + [rootCls.value]: true, [hashId.value]: true, + [cssVarCls.value]: true, }); return wrapSSR( diff --git a/components/skeleton/style/index.ts b/components/skeleton/style/index.ts index 07cbfd48b4..80a6f3f754 100644 --- a/components/skeleton/style/index.ts +++ b/components/skeleton/style/index.ts @@ -1,19 +1,52 @@ import type { CSSObject } from '../../_util/cssinjs'; -import { Keyframes } from '../../_util/cssinjs'; -import type { FullToken, GenerateStyle } from '../../theme/internal'; -import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { Keyframes, unit } from '../../_util/cssinjs'; -export type ComponentToken = { +import type { CSSUtil, FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal'; +import { genStyleHooks, mergeToken } from '../../theme/internal'; + +export interface ComponentToken { + /** @deprecated use gradientFromColor instead. */ color: string; + /** @deprecated use gradientToColor instead. */ colorGradientEnd: string; -}; + /** + * @desc 渐变色起点颜色 + * @descEN Start color of gradient + */ + gradientFromColor: string; + /** + * @desc 渐变色终点颜色 + * @descEN End color of gradient + */ + gradientToColor: string; + /** + * @desc 标题骨架屏高度 + * @descEN Height of title skeleton + */ + titleHeight: number | string; + /** + * @desc 骨架屏圆角 + * @descEN Border radius of skeleton + */ + blockRadius: number; + /** + * @desc 段落骨架屏上间距 + * @descEN Margin top of paragraph skeleton + */ + paragraphMarginTop: number; + /** + * @desc 段落骨架屏单行高度 + * @descEN Line height of paragraph skeleton + */ + paragraphLiHeight: number; +} const skeletonClsLoading = new Keyframes(`ant-skeleton-loading`, { '0%': { - transform: 'translateX(-37.5%)', + backgroundPosition: '100% 50%', }, '100%': { - transform: 'translateX(37.5%)', + backgroundPosition: '0 50%', }, }); @@ -24,62 +57,44 @@ interface SkeletonToken extends FullToken<'Skeleton'> { skeletonButtonCls: string; skeletonInputCls: string; skeletonImageCls: string; - imageSizeBase: number; - skeletonTitleHeight: number; - skeletonBlockRadius: number; - skeletonParagraphLineHeight: number; - skeletonParagraphMarginTop: number; + imageSizeBase: number | string; skeletonLoadingBackground: string; skeletonLoadingMotionDuration: string; borderRadius: number; } -const genSkeletonElementCommonSize = (size: number): CSSObject => ({ +const genSkeletonElementCommonSize = (size: number | string): CSSObject => ({ height: size, - lineHeight: `${size}px`, + lineHeight: unit(size), }); -const genSkeletonElementAvatarSize = (size: number): CSSObject => ({ +const genSkeletonElementAvatarSize = (size: number | string): CSSObject => ({ width: size, ...genSkeletonElementCommonSize(size), }); const genSkeletonColor = (token: SkeletonToken): CSSObject => ({ - position: 'relative', - // fix https://github.com/ant-design/ant-design/issues/36444 - // https://monshin.github.io/202109/css/safari-border-radius-overflow-hidden/ - /* stylelint-disable-next-line property-no-vendor-prefix,value-no-vendor-prefix */ - zIndex: 0, - overflow: 'hidden', - background: 'transparent', - '&::after': { - position: 'absolute', - top: 0, - insetInlineEnd: '-150%', - bottom: 0, - insetInlineStart: '-150%', - background: token.skeletonLoadingBackground, - animationName: skeletonClsLoading, - animationDuration: token.skeletonLoadingMotionDuration, - animationTimingFunction: 'ease', - animationIterationCount: 'infinite', - content: '""', - }, + background: token.skeletonLoadingBackground, + backgroundSize: '400% 100%', + animationName: skeletonClsLoading, + animationDuration: token.skeletonLoadingMotionDuration, + animationTimingFunction: 'ease', + animationIterationCount: 'infinite', }); - -const genSkeletonElementInputSize = (size: number): CSSObject => ({ - width: size * 5, - minWidth: size * 5, +const genSkeletonElementInputSize = (size: number, calc: CSSUtil['calc']): CSSObject => ({ + width: calc(size).mul(5).equal(), + minWidth: calc(size).mul(5).equal(), ...genSkeletonElementCommonSize(size), }); const genSkeletonElementAvatar = (token: SkeletonToken): CSSObject => { - const { skeletonAvatarCls, color, controlHeight, controlHeightLG, controlHeightSM } = token; + const { skeletonAvatarCls, gradientFromColor, controlHeight, controlHeightLG, controlHeightSM } = + token; return { - [`${skeletonAvatarCls}`]: { + [skeletonAvatarCls]: { display: 'inline-block', verticalAlign: 'top', - background: color, + background: gradientFromColor, ...genSkeletonElementAvatarSize(controlHeight), }, [`${skeletonAvatarCls}${skeletonAvatarCls}-circle`]: { @@ -101,50 +116,51 @@ const genSkeletonElementInput = (token: SkeletonToken): CSSObject => { skeletonInputCls, controlHeightLG, controlHeightSM, - color, + gradientFromColor, + calc, } = token; return { - [`${skeletonInputCls}`]: { + [skeletonInputCls]: { display: 'inline-block', verticalAlign: 'top', - background: color, + background: gradientFromColor, borderRadius: borderRadiusSM, - ...genSkeletonElementInputSize(controlHeight), + ...genSkeletonElementInputSize(controlHeight, calc), }, [`${skeletonInputCls}-lg`]: { - ...genSkeletonElementInputSize(controlHeightLG), + ...genSkeletonElementInputSize(controlHeightLG, calc), }, [`${skeletonInputCls}-sm`]: { - ...genSkeletonElementInputSize(controlHeightSM), + ...genSkeletonElementInputSize(controlHeightSM, calc), }, }; }; -const genSkeletonElementImageSize = (size: number): CSSObject => ({ +const genSkeletonElementImageSize = (size: number | string): CSSObject => ({ width: size, ...genSkeletonElementCommonSize(size), }); const genSkeletonElementImage = (token: SkeletonToken): CSSObject => { - const { skeletonImageCls, imageSizeBase, color, borderRadiusSM } = token; + const { skeletonImageCls, imageSizeBase, gradientFromColor, borderRadiusSM, calc } = token; return { - [`${skeletonImageCls}`]: { - display: 'flex', + [skeletonImageCls]: { + display: 'inline-flex', alignItems: 'center', justifyContent: 'center', - verticalAlign: 'top', - background: color, + verticalAlign: 'middle', + background: gradientFromColor, borderRadius: borderRadiusSM, - ...genSkeletonElementImageSize(imageSizeBase * 2), + ...genSkeletonElementImageSize(calc(imageSizeBase).mul(2).equal()), [`${skeletonImageCls}-path`]: { fill: '#bfbfbf', }, [`${skeletonImageCls}-svg`]: { ...genSkeletonElementImageSize(imageSizeBase), - maxWidth: imageSizeBase * 4, - maxHeight: imageSizeBase * 4, + maxWidth: calc(imageSizeBase).mul(4).equal(), + maxHeight: calc(imageSizeBase).mul(4).equal(), }, [`${skeletonImageCls}-svg${skeletonImageCls}-svg-circle`]: { borderRadius: '50%', @@ -173,9 +189,9 @@ const genSkeletonElementButtonShape = ( }; }; -const genSkeletonElementButtonSize = (size: number): CSSObject => ({ - width: size * 2, - minWidth: size * 2, +const genSkeletonElementButtonSize = (size: number, calc: CSSUtil['calc']): CSSObject => ({ + width: calc(size).mul(2).equal(), + minWidth: calc(size).mul(2).equal(), ...genSkeletonElementCommonSize(size), }); @@ -186,27 +202,28 @@ const genSkeletonElementButton = (token: SkeletonToken): CSSObject => { controlHeight, controlHeightLG, controlHeightSM, - color, + gradientFromColor, + calc, } = token; return { - [`${skeletonButtonCls}`]: { + [skeletonButtonCls]: { display: 'inline-block', verticalAlign: 'top', - background: color, + background: gradientFromColor, borderRadius: borderRadiusSM, - width: controlHeight * 2, - minWidth: controlHeight * 2, - ...genSkeletonElementButtonSize(controlHeight), + width: calc(controlHeight).mul(2).equal(), + minWidth: calc(controlHeight).mul(2).equal(), + ...genSkeletonElementButtonSize(controlHeight, calc), }, ...genSkeletonElementButtonShape(token, controlHeight, skeletonButtonCls), [`${skeletonButtonCls}-lg`]: { - ...genSkeletonElementButtonSize(controlHeightLG), + ...genSkeletonElementButtonSize(controlHeightLG, calc), }, ...genSkeletonElementButtonShape(token, controlHeightLG, `${skeletonButtonCls}-lg`), [`${skeletonButtonCls}-sm`]: { - ...genSkeletonElementButtonSize(controlHeightSM), + ...genSkeletonElementButtonSize(controlHeightSM, calc), }, ...genSkeletonElementButtonShape(token, controlHeightSM, `${skeletonButtonCls}-sm`), }; @@ -225,19 +242,19 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { controlHeight, controlHeightLG, controlHeightSM, - color, + gradientFromColor, padding, marginSM, borderRadius, - skeletonTitleHeight, - skeletonBlockRadius, - skeletonParagraphLineHeight, + titleHeight, + blockRadius, + paragraphLiHeight, controlHeightXS, - skeletonParagraphMarginTop, + paragraphMarginTop, } = token; return { - [`${componentCls}`]: { + [componentCls]: { display: 'table', width: '100%', @@ -247,10 +264,10 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { verticalAlign: 'top', // Avatar - [`${skeletonAvatarCls}`]: { + [skeletonAvatarCls]: { display: 'inline-block', verticalAlign: 'top', - background: color, + background: gradientFromColor, ...genSkeletonElementAvatarSize(controlHeight), }, [`${skeletonAvatarCls}-circle`]: { @@ -269,25 +286,25 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { verticalAlign: 'top', // Title - [`${skeletonTitleCls}`]: { + [skeletonTitleCls]: { width: '100%', - height: skeletonTitleHeight, - background: color, - borderRadius: skeletonBlockRadius, + height: titleHeight, + background: gradientFromColor, + borderRadius: blockRadius, [`+ ${skeletonParagraphCls}`]: { marginBlockStart: controlHeightSM, }, }, // paragraph - [`${skeletonParagraphCls}`]: { + [skeletonParagraphCls]: { padding: 0, '> li': { width: '100%', - height: skeletonParagraphLineHeight, + height: paragraphLiHeight, listStyle: 'none', - background: color, - borderRadius: skeletonBlockRadius, + background: gradientFromColor, + borderRadius: blockRadius, '+ li': { marginBlockStart: controlHeightXS, }, @@ -307,11 +324,11 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { }, [`${componentCls}-with-avatar ${componentCls}-content`]: { // Title - [`${skeletonTitleCls}`]: { + [skeletonTitleCls]: { marginBlockStart: marginSM, [`+ ${skeletonParagraphCls}`]: { - marginBlockStart: skeletonParagraphMarginTop, + marginBlockStart: paragraphMarginTop, }, }, }, @@ -329,11 +346,11 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { [`${componentCls}${componentCls}-block`]: { width: '100%', - [`${skeletonButtonCls}`]: { + [skeletonButtonCls]: { width: '100%', }, - [`${skeletonInputCls}`]: { + [skeletonInputCls]: { width: '100%', }, }, @@ -354,10 +371,26 @@ const genBaseStyle: GenerateStyle = (token: SkeletonToken) => { }; // ============================== Export ============================== -export default genComponentStyleHook( +export const prepareComponentToken: GetDefaultToken<'Skeleton'> = token => { + const { colorFillContent, colorFill } = token; + const gradientFromColor = colorFillContent; + const gradientToColor = colorFill; + return { + color: gradientFromColor, + colorGradientEnd: gradientToColor, + gradientFromColor, + gradientToColor, + titleHeight: token.controlHeight / 2, + blockRadius: token.borderRadiusSM, + paragraphMarginTop: token.marginLG + token.marginXXS, + paragraphLiHeight: token.controlHeight / 2, + }; +}; + +export default genStyleHooks( 'Skeleton', token => { - const { componentCls } = token; + const { componentCls, calc } = token; const skeletonToken = mergeToken(token, { skeletonAvatarCls: `${componentCls}-avatar`, @@ -366,23 +399,18 @@ export default genComponentStyleHook( skeletonButtonCls: `${componentCls}-button`, skeletonInputCls: `${componentCls}-input`, skeletonImageCls: `${componentCls}-image`, - imageSizeBase: token.controlHeight * 1.5, - skeletonTitleHeight: token.controlHeight / 2, - skeletonBlockRadius: token.borderRadiusSM, - skeletonParagraphLineHeight: token.controlHeight / 2, - skeletonParagraphMarginTop: token.marginLG + token.marginXXS, + imageSizeBase: calc(token.controlHeight).mul(1.5).equal(), borderRadius: 100, // Large number to make capsule shape - skeletonLoadingBackground: `linear-gradient(90deg, ${token.color} 25%, ${token.colorGradientEnd} 37%, ${token.color} 63%)`, + skeletonLoadingBackground: `linear-gradient(90deg, ${token.gradientFromColor} 25%, ${token.gradientToColor} 37%, ${token.gradientFromColor} 63%)`, skeletonLoadingMotionDuration: '1.4s', }); return [genBaseStyle(skeletonToken)]; }, - token => { - const { colorFillContent, colorFill } = token; - - return { - color: colorFillContent, - colorGradientEnd: colorFill, - }; + prepareComponentToken, + { + deprecatedTokens: [ + ['color', 'gradientFromColor'], + ['colorGradientEnd', 'gradientToColor'], + ], }, );