refactor(tag): less to cssinjs (#6227)

pull/6228/head
bqy_fe 2023-02-03 09:50:40 +08:00 committed by GitHub
parent 9b5a07220b
commit a205615af9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 215 additions and 220 deletions

View File

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

View File

@ -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<ExtractPropTypes<ReturnType<typeof check
const CheckableTag = defineComponent({
compatConfig: { MODE: 3 },
name: 'ACheckableTag',
inheritAttrs: false,
props: checkableTagProps(),
// emits: ['update:checked', 'change', 'click'],
setup(props, { slots, emit }) {
setup(props, { slots, emit, attrs }) {
const { prefixCls } = useConfigInject('tag', props);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const handleClick = (e: MouseEvent) => {
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 (
<span class={cls.value} onClick={handleClick}>
return wrapSSR(
<span {...attrs} class={cls.value} onClick={handleClick}>
{slots.default?.()}
</span>
</span>,
);
};
},

View File

@ -1,33 +0,0 @@
<docs>
---
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.
</docs>
<template>
<a-tag v-model:visible="visible" closable>Movies</a-tag>
<br />
<a-button size="small" @click="visible = !visible">Toggle</a-button>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
return {
visible: ref(false),
};
},
});
</script>

View File

@ -4,7 +4,6 @@
<Checkable />
<Colorful />
<Control />
<Controlled />
<HotTags />
<Icon />
<Status />
@ -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,

View File

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

View File

@ -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<ExtractPropTypes<ReturnType<type
const Tag = defineComponent({
compatConfig: { MODE: 3 },
name: 'ATag',
inheritAttrs: false,
props: tagProps(),
// emits: ['update:visible', 'close'],
slots: ['closeIcon', 'icon'],
setup(props: TagProps, { slots, emit, attrs }) {
const { prefixCls, direction } = useConfigInject('tag', props);
const [wrapSSR, hashId] = useStyle(prefixCls);
const visible = ref(true);
// Warning for deprecated usage
if (process.env.NODE_ENV !== 'production') {
warning(
!('visible' in props),
'Tag',
'`visible` is deprecated, please use `<Tag v-show="visible" />` 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 = (
<span class={tagClassName.value} style={tagStyle}>
<span {...attrs} class={tagClassName.value} style={tagStyle}>
{kids}
{renderCloseIcon()}
</span>
);
return isNeedWave ? <Wave>{tagNode}</Wave> : tagNode;
return wrapSSR(isNeedWave ? <Wave>{tagNode}</Wave> : tagNode);
};
},
});

View File

@ -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 |
### 事件

View File

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

View File

@ -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<TagToken>(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'),
];
});

View File

@ -1,2 +0,0 @@
import '../../style/index.less';
import './index.less';

View File

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

View File

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