From a205615af9fe057cad2d44620d1cf19f6ae641fe Mon Sep 17 00:00:00 2001 From: bqy_fe <1743369777@qq.com> Date: Fri, 3 Feb 2023 09:50:40 +0800 Subject: [PATCH] refactor(tag): less to cssinjs (#6227) --- components/style.ts | 2 +- components/tag/CheckableTag.tsx | 15 +- components/tag/demo/controlled.vue | 33 ----- components/tag/demo/index.vue | 3 - components/tag/index.en-US.md | 13 +- components/tag/index.tsx | 22 ++- components/tag/index.zh-CN.md | 13 +- components/tag/style/index.less | 129 ----------------- components/tag/style/index.ts | 171 +++++++++++++++++++++++ components/tag/style/index.tsx | 2 - components/tag/style/rtl.less | 28 ---- components/theme/interface/components.ts | 4 +- 12 files changed, 215 insertions(+), 220 deletions(-) delete mode 100644 components/tag/demo/controlled.vue delete mode 100644 components/tag/style/index.less create mode 100644 components/tag/style/index.ts delete mode 100644 components/tag/style/index.tsx delete mode 100644 components/tag/style/rtl.less diff --git a/components/style.ts b/components/style.ts index a360f5f65..338d6438b 100644 --- a/components/style.ts +++ b/components/style.ts @@ -3,7 +3,7 @@ import './radio/style'; import './checkbox/style'; // import './grid/style'; -import './tag/style'; +// import './tag/style'; import './rate/style'; import './pagination/style'; // import './avatar/style'; diff --git a/components/tag/CheckableTag.tsx b/components/tag/CheckableTag.tsx index 38e856a5e..7baca6bf0 100644 --- a/components/tag/CheckableTag.tsx +++ b/components/tag/CheckableTag.tsx @@ -2,6 +2,7 @@ import type { ExtractPropTypes, PropType } from 'vue'; import { defineComponent, computed } from 'vue'; import classNames from '../_util/classNames'; import useConfigInject from '../config-provider/hooks/useConfigInject'; +import useStyle from './style'; const checkableTagProps = () => ({ prefixCls: String, @@ -19,10 +20,14 @@ export type CheckableTagProps = Partial { const { checked } = props; emit('update:checked', !checked); @@ -31,17 +36,17 @@ const CheckableTag = defineComponent({ }; const cls = computed(() => - classNames(prefixCls.value, { + classNames(prefixCls.value, hashId.value, { [`${prefixCls.value}-checkable`]: true, [`${prefixCls.value}-checkable-checked`]: props.checked, }), ); return () => { - return ( - + return wrapSSR( + {slots.default?.()} - + , ); }; }, diff --git a/components/tag/demo/controlled.vue b/components/tag/demo/controlled.vue deleted file mode 100644 index 3fd062bf9..000000000 --- a/components/tag/demo/controlled.vue +++ /dev/null @@ -1,33 +0,0 @@ - ---- -order: 4 -title: - zh-CN: 控制关闭状态 - en-US: Controlled ---- - -## zh-CN - -通过 `visible` 属性控制关闭状态。 - -## en-US - -By using the `visible` prop, you can control the close state of Tag. - - - - - diff --git a/components/tag/demo/index.vue b/components/tag/demo/index.vue index 2eed9b914..99c393865 100644 --- a/components/tag/demo/index.vue +++ b/components/tag/demo/index.vue @@ -4,7 +4,6 @@ - @@ -16,7 +15,6 @@ import Basic from './basic.vue'; import Checkable from './checkable.vue'; import Colorful from './colorful.vue'; import Control from './control.vue'; -import Controlled from './controlled.vue'; import HotTags from './hot-tags.vue'; import Icon from './icon.vue'; import Status from './status.vue'; @@ -32,7 +30,6 @@ export default defineComponent({ Checkable, Colorful, Control, - Controlled, HotTags, Icon, Status, diff --git a/components/tag/index.en-US.md b/components/tag/index.en-US.md index 1a9861eca..588e00065 100644 --- a/components/tag/index.en-US.md +++ b/components/tag/index.en-US.md @@ -16,13 +16,12 @@ Tag for categorizing or markup. ### Tag -| Property | Description | Type | Default | Version | -| ---------------- | -------------------------------- | ------------- | ------- | ------- | -| closable | Whether the Tag can be closed | boolean | `false` | | -| closeIcon | Custom close icon | VNode \| slot | - | 2.0.0 | -| color | Color of the Tag | string | - | | -| icon | Set the icon of tag | VNode \| slot | - | 2.0.0 | -| visible(v-model) | Whether the Tag is closed or not | boolean | `true` | | +| Property | Description | Type | Default | Version | +| --------- | ----------------------------- | ------------- | ------- | ------- | +| closable | Whether the Tag can be closed | boolean | `false` | | +| closeIcon | Custom close icon | VNode \| slot | - | 2.0.0 | +| color | Color of the Tag | string | - | | +| icon | Set the icon of tag | VNode \| slot | - | 2.0.0 | ### Tag Events diff --git a/components/tag/index.tsx b/components/tag/index.tsx index 718372e62..ef201c68d 100644 --- a/components/tag/index.tsx +++ b/components/tag/index.tsx @@ -9,6 +9,9 @@ import { isPresetColor, isPresetStatusColor } from '../_util/colors'; import type { LiteralUnion } from '../_util/type'; import CheckableTag from './CheckableTag'; import useConfigInject from '../config-provider/hooks/useConfigInject'; +import warning from '../_util/warning'; + +import useStyle from './style'; export const tagProps = () => ({ prefixCls: String, @@ -17,6 +20,7 @@ export const tagProps = () => ({ }, closable: { type: Boolean, default: false }, closeIcon: PropTypes.any, + /** @deprecated `visible` will be removed in next major version. */ visible: { type: Boolean, default: undefined }, onClose: { type: Function as PropType<(e: MouseEvent) => void>, @@ -30,14 +34,26 @@ export type TagProps = HTMLAttributes & Partial` instead.', + ); + } + watchEffect(() => { if (props.visible !== undefined) { visible.value = props.visible!; @@ -70,7 +86,7 @@ const Tag = defineComponent({ ); const tagClassName = computed(() => - classNames(prefixCls.value, { + classNames(prefixCls.value, hashId.value, { [`${prefixCls.value}-${props.color}`]: isInternalColor.value, [`${prefixCls.value}-has-color`]: props.color && !isInternalColor.value, [`${prefixCls.value}-hidden`]: !visible.value, @@ -117,13 +133,13 @@ const Tag = defineComponent({ const isNeedWave = 'onClick' in attrs; const tagNode = ( - + {kids} {renderCloseIcon()} ); - return isNeedWave ? {tagNode} : tagNode; + return wrapSSR(isNeedWave ? {tagNode} : tagNode); }; }, }); diff --git a/components/tag/index.zh-CN.md b/components/tag/index.zh-CN.md index 52aea2b02..2130e37a5 100644 --- a/components/tag/index.zh-CN.md +++ b/components/tag/index.zh-CN.md @@ -17,13 +17,12 @@ cover: https://gw.alipayobjects.com/zos/alicdn/cH1BOLfxC/Tag.svg ### Tag -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| ---------------- | ---------------- | ------------- | ------ | ----- | -| closable | 标签是否可以关闭 | boolean | false | | -| closeIcon | 自定义关闭按钮 | VNode \| slot | - | 2.0.0 | -| color | 标签色 | string | - | | -| icon | 设置图标 | VNode \| slot | - | 2.0.0 | -| visible(v-model) | 是否显示标签 | boolean | `true` | | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --------- | ---------------- | ------------- | ------ | ----- | +| closable | 标签是否可以关闭 | boolean | false | | +| closeIcon | 自定义关闭按钮 | VNode \| slot | - | 2.0.0 | +| color | 标签色 | string | - | | +| icon | 设置图标 | VNode \| slot | - | 2.0.0 | ### 事件 diff --git a/components/tag/style/index.less b/components/tag/style/index.less deleted file mode 100644 index f65fd369d..000000000 --- a/components/tag/style/index.less +++ /dev/null @@ -1,129 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@tag-prefix-cls: ~'@{ant-prefix}-tag'; - -.@{tag-prefix-cls} { - .reset-component(); - - display: inline-block; - height: auto; - margin-right: 8px; - padding: 0 7px; - font-size: @tag-font-size; - line-height: @tag-line-height; - white-space: nowrap; - background: @tag-default-bg; - border: @border-width-base @border-style-base @border-color-base; - border-radius: @tag-border-radius; - opacity: 1; - transition: all 0.3s; - - &, - a, - a:hover { - color: @tag-default-color; - } - - > a:first-child:last-child { - display: inline-block; - margin: 0 -8px; - padding: 0 8px; - } - - &-close-icon { - margin-left: 3px; - color: @text-color-secondary; - font-size: 10px; - cursor: pointer; - transition: all 0.3s; - - &:hover { - color: @heading-color; - } - } - - &-has-color { - border-color: transparent; - &, - a, - a:hover, - .@{iconfont-css-prefix}-close, - .@{iconfont-css-prefix}-close:hover { - color: @text-color-inverse; - } - } - - &-checkable { - background-color: transparent; - border-color: transparent; - cursor: pointer; - - &:not(&-checked):hover { - color: @primary-color; - } - - &:active, - &-checked { - color: @text-color-inverse; - } - - &-checked { - background-color: @primary-6; - } - - &:active { - background-color: @primary-7; - } - } - - &-hidden { - display: none; - } - - // mixin to iterate over colors and create CSS class for each one - .make-color-classes(@i: length(@preset-colors)) when (@i > 0) { - .make-color-classes(@i - 1); - @color: extract(@preset-colors, @i); - @lightColor: '@{color}-1'; - @lightBorderColor: '@{color}-3'; - @darkColor: '@{color}-6'; - @textColor: '@{color}-7'; - &-@{color} { - color: @@textColor; - background: @@lightColor; - border-color: @@lightBorderColor; - } - &-@{color}-inverse { - color: @text-color-inverse; - background: @@darkColor; - border-color: @@darkColor; - } - } - - .make-status-color-classes(@status, @cssVariableType) { - @bgColor: '@{cssVariableType}-color-deprecated-bg'; - @borderColor: '@{cssVariableType}-color-deprecated-border'; - @textColor: '@{cssVariableType}-color'; - &-@{status} { - color: @@textColor; - background: @@bgColor; - border-color: @@borderColor; - } - } - - .make-color-classes(); - - .make-status-color-classes(success, success); - .make-status-color-classes(processing, info); - .make-status-color-classes(error, error); - .make-status-color-classes(warning, warning); - - // To ensure that a space will be placed between character and `Icon`. - > .@{iconfont-css-prefix} + span, - > span + .@{iconfont-css-prefix} { - margin-left: 7px; - } -} - -@import './rtl'; diff --git a/components/tag/style/index.ts b/components/tag/style/index.ts new file mode 100644 index 000000000..e3b3c1147 --- /dev/null +++ b/components/tag/style/index.ts @@ -0,0 +1,171 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { FullToken } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { genPresetColor, resetComponent } from '../../_style'; +import type { CSSProperties } from 'vue'; +import { capitalize } from '../../_util/util'; + +export interface ComponentToken {} + +interface TagToken extends FullToken<'Tag'> { + tagFontSize: number; + tagLineHeight: CSSProperties['lineHeight']; + tagDefaultBg: string; + tagDefaultColor: string; + tagIconSize: number; + tagPaddingHorizontal: number; +} + +// ============================== Styles ============================== + +type CssVariableType = 'Success' | 'Info' | 'Error' | 'Warning'; + +const genTagStatusStyle = ( + token: TagToken, + status: 'success' | 'processing' | 'error' | 'warning', + cssVariableType: CssVariableType, +): CSSObject => { + const capitalizedCssVariableType = capitalize(cssVariableType); + return { + [`${token.componentCls}-${status}`]: { + color: token[`color${cssVariableType}`], + background: token[`color${capitalizedCssVariableType}Bg`], + borderColor: token[`color${capitalizedCssVariableType}Border`], + }, + }; +}; + +const genPresetStyle = (token: TagToken) => + genPresetColor(token, (colorKey, { textColor, lightBorderColor, lightColor, darkColor }) => ({ + [`${token.componentCls}-${colorKey}`]: { + color: textColor, + background: lightColor, + borderColor: lightBorderColor, + + // Inverse color + '&-inverse': { + color: token.colorTextLightSolid, + background: darkColor, + borderColor: darkColor, + }, + }, + })); + +const genBaseStyle = (token: TagToken): CSSObject => { + const { paddingXXS, lineWidth, tagPaddingHorizontal, componentCls } = token; + const paddingInline = tagPaddingHorizontal - lineWidth; + const iconMarginInline = paddingXXS - lineWidth; + + return { + // Result + [componentCls]: { + ...resetComponent(token), + display: 'inline-block', + height: 'auto', + marginInlineEnd: token.marginXS, + paddingInline, + fontSize: token.tagFontSize, + lineHeight: `${token.tagLineHeight}px`, + whiteSpace: 'nowrap', + background: token.tagDefaultBg, + border: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`, + borderRadius: token.borderRadiusSM, + opacity: 1, + transition: `all ${token.motionDurationMid}`, + textAlign: 'start', + + // RTL + [`&${componentCls}-rtl`]: { + direction: 'rtl', + }, + + '&, a, a:hover': { + color: token.tagDefaultColor, + }, + + [`${componentCls}-close-icon`]: { + marginInlineStart: iconMarginInline, + color: token.colorTextDescription, + fontSize: token.tagIconSize, + cursor: 'pointer', + transition: `all ${token.motionDurationMid}`, + + '&:hover': { + color: token.colorTextHeading, + }, + }, + + [`&${componentCls}-has-color`]: { + borderColor: 'transparent', + + [`&, a, a:hover, ${token.iconCls}-close, ${token.iconCls}-close:hover`]: { + color: token.colorTextLightSolid, + }, + }, + + [`&-checkable`]: { + backgroundColor: 'transparent', + borderColor: 'transparent', + cursor: 'pointer', + + [`&:not(${componentCls}-checkable-checked):hover`]: { + color: token.colorPrimary, + backgroundColor: token.colorFillSecondary, + }, + + '&:active, &-checked': { + color: token.colorTextLightSolid, + }, + + '&-checked': { + backgroundColor: token.colorPrimary, + '&:hover': { + backgroundColor: token.colorPrimaryHover, + }, + }, + + '&:active': { + backgroundColor: token.colorPrimaryActive, + }, + }, + + [`&-hidden`]: { + display: 'none', + }, + + // To ensure that a space will be placed between character and `Icon`. + [`> ${token.iconCls} + span, > span + ${token.iconCls}`]: { + marginInlineStart: paddingInline, + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Tag', token => { + const { fontSize, lineHeight, lineWidth, fontSizeIcon } = token; + const tagHeight = Math.round(fontSize * lineHeight); + + const tagFontSize = token.fontSizeSM; + const tagLineHeight = tagHeight - lineWidth * 2; + const tagDefaultBg = token.colorFillAlter; + const tagDefaultColor = token.colorText; + + const tagToken = mergeToken(token, { + tagFontSize, + tagLineHeight, + tagDefaultBg, + tagDefaultColor, + tagIconSize: fontSizeIcon - 2 * lineWidth, // Tag icon is much more smaller + tagPaddingHorizontal: 8, // Fixed padding. + }); + + return [ + genBaseStyle(tagToken), + genPresetStyle(tagToken), + genTagStatusStyle(tagToken, 'success', 'Success'), + genTagStatusStyle(tagToken, 'processing', 'Info'), + genTagStatusStyle(tagToken, 'error', 'Error'), + genTagStatusStyle(tagToken, 'warning', 'Warning'), + ]; +}); diff --git a/components/tag/style/index.tsx b/components/tag/style/index.tsx deleted file mode 100644 index 3a3ab0de5..000000000 --- a/components/tag/style/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import '../../style/index.less'; -import './index.less'; diff --git a/components/tag/style/rtl.less b/components/tag/style/rtl.less deleted file mode 100644 index af7d0dfc5..000000000 --- a/components/tag/style/rtl.less +++ /dev/null @@ -1,28 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@tag-prefix-cls: ~'@{ant-prefix}-tag'; - -.@{tag-prefix-cls} { - &&-rtl { - margin-right: 0; - margin-left: 8px; - direction: rtl; - text-align: right; - } - - &-close-icon { - .@{tag-prefix-cls}-rtl & { - margin-right: 3px; - margin-left: 0; - } - } - - > .@{iconfont-css-prefix} + span, - > span + .@{iconfont-css-prefix} { - .@{tag-prefix-cls}-rtl& { - margin-right: 7px; - margin-left: 0; - } - } -} diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index 997061b13..8e7a5bf3c 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -39,7 +39,7 @@ import type { ComponentToken as SpinComponentToken } from '../../spin/style'; // import type { ComponentToken as StepsComponentToken } from '../../steps/style'; // import type { ComponentToken as TableComponentToken } from '../../table/style'; // import type { ComponentToken as TabsComponentToken } from '../../tabs/style'; -// import type { ComponentToken as TagComponentToken } from '../../tag/style'; +import type { ComponentToken as TagComponentToken } from '../../tag/style'; // import type { ComponentToken as TimelineComponentToken } from '../../timeline/style'; import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style'; // import type { ComponentToken as TransferComponentToken } from '../../transfer/style'; @@ -93,7 +93,7 @@ export interface ComponentTokenMap { Spin?: SpinComponentToken; Statistic?: {}; Switch?: {}; - // Tag?: TagComponentToken; + Tag?: TagComponentToken; Tree?: {}; TreeSelect?: {}; // Typography?: TypographyComponentToken;