refactor: badge
parent
e64a19a05a
commit
1e4e3cb3b4
|
@ -1,23 +1,34 @@
|
|||
import type { ElementOf } from './type';
|
||||
import { tuple } from './type';
|
||||
import type { PresetColorKey } from '../theme/interface';
|
||||
import { PresetColors } from '../theme/interface';
|
||||
|
||||
export const PresetStatusColorTypes = tuple('success', 'processing', 'error', 'default', 'warning');
|
||||
type InverseColor = `${PresetColorKey}-inverse`;
|
||||
const inverseColors = PresetColors.map<InverseColor>(color => `${color}-inverse`);
|
||||
|
||||
export const PresetColorTypes = tuple(
|
||||
'pink',
|
||||
'red',
|
||||
'yellow',
|
||||
'orange',
|
||||
'cyan',
|
||||
'green',
|
||||
'blue',
|
||||
'purple',
|
||||
'geekblue',
|
||||
'magenta',
|
||||
'volcano',
|
||||
'gold',
|
||||
'lime',
|
||||
);
|
||||
export const PresetStatusColorTypes = [
|
||||
'success',
|
||||
'processing',
|
||||
'error',
|
||||
'default',
|
||||
'warning',
|
||||
] as const;
|
||||
|
||||
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
|
||||
export type PresetStatusColorType = ElementOf<typeof PresetStatusColorTypes>;
|
||||
export type PresetColorType = PresetColorKey | InverseColor;
|
||||
|
||||
export type PresetStatusColorType = typeof PresetStatusColorTypes[number];
|
||||
|
||||
/**
|
||||
* determine if the color keyword belongs to the `Ant Design` {@link PresetColors}.
|
||||
* @param color color to be judged
|
||||
* @param includeInverse whether to include reversed colors
|
||||
*/
|
||||
export function isPresetColor(color?: any, includeInverse = true) {
|
||||
if (includeInverse) {
|
||||
return [...inverseColors, ...PresetColors].includes(color);
|
||||
}
|
||||
|
||||
return PresetColors.includes(color);
|
||||
}
|
||||
|
||||
export function isPresetStatusColor(color?: any): color is PresetStatusColorType {
|
||||
return PresetStatusColorTypes.includes(color);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer
|
|||
/**
|
||||
* https://github.com/Microsoft/TypeScript/issues/29729
|
||||
*/
|
||||
export type LiteralUnion<T extends U, U> = T | (U & {});
|
||||
export type LiteralUnion<T extends string> = T | (string & {});
|
||||
|
||||
export type Data = Record<string, unknown>;
|
||||
|
||||
|
|
|
@ -7,10 +7,13 @@ import { getTransitionProps, Transition } from '../_util/transition';
|
|||
import type { ExtractPropTypes, CSSProperties, PropType } from 'vue';
|
||||
import { defineComponent, computed, ref, watch } from 'vue';
|
||||
import Ribbon from './Ribbon';
|
||||
import { isPresetColor } from './utils';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
import isNumeric from '../_util/isNumeric';
|
||||
import useStyle from './style';
|
||||
import type { PresetColorKey } from '../theme/interface';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import type { PresetStatusColorType } from '../_util/colors';
|
||||
import { isPresetColor } from '../_util/colors';
|
||||
|
||||
export const badgeProps = () => ({
|
||||
/** Number to show in badge */
|
||||
|
@ -24,7 +27,7 @@ export const badgeProps = () => ({
|
|||
scrollNumberPrefixCls: String,
|
||||
status: { type: String as PropType<PresetStatusColorType> },
|
||||
size: { type: String as PropType<'default' | 'small'>, default: 'default' },
|
||||
color: String,
|
||||
color: String as PropType<LiteralUnion<PresetColorKey>>,
|
||||
text: PropTypes.any,
|
||||
offset: Array as unknown as PropType<[number | string, number | string]>,
|
||||
numberStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||
|
@ -42,6 +45,7 @@ export default defineComponent({
|
|||
slots: ['text', 'count'],
|
||||
setup(props, { slots, attrs }) {
|
||||
const { prefixCls, direction } = useConfigInject('badge', props);
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
|
||||
// ================================ Misc ================================
|
||||
const numberedDisplayCount = computed(() => {
|
||||
|
@ -52,15 +56,16 @@ export default defineComponent({
|
|||
) as string | number | null;
|
||||
});
|
||||
|
||||
const hasStatus = computed(
|
||||
() =>
|
||||
(props.status !== null && props.status !== undefined) ||
|
||||
(props.color !== null && props.color !== undefined),
|
||||
);
|
||||
|
||||
const isZero = computed(
|
||||
() => numberedDisplayCount.value === '0' || numberedDisplayCount.value === 0,
|
||||
);
|
||||
const ignoreCount = computed(() => props.count === null || (isZero.value && !props.showZero));
|
||||
const hasStatus = computed(
|
||||
() =>
|
||||
((props.status !== null && props.status !== undefined) ||
|
||||
(props.color !== null && props.color !== undefined)) &&
|
||||
ignoreCount.value,
|
||||
);
|
||||
|
||||
const showAsDot = computed(() => props.dot && !isZero.value);
|
||||
|
||||
|
@ -92,17 +97,18 @@ export default defineComponent({
|
|||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// InternalColor
|
||||
const isInternalColor = computed(() => isPresetColor(props.color, false));
|
||||
// Shared styles
|
||||
const statusCls = computed(() => ({
|
||||
[`${prefixCls.value}-status-dot`]: hasStatus.value,
|
||||
[`${prefixCls.value}-status-${props.status}`]: !!props.status,
|
||||
[`${prefixCls.value}-status-${props.color}`]: isPresetColor(props.color),
|
||||
[`${prefixCls.value}-status-${props.color}`]: isInternalColor.value,
|
||||
}));
|
||||
|
||||
const statusStyle = computed(() => {
|
||||
if (props.color && !isPresetColor(props.color)) {
|
||||
return { background: props.color };
|
||||
if (props.color && !isInternalColor.value) {
|
||||
return { background: props.color, color: props.color };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
@ -115,7 +121,7 @@ export default defineComponent({
|
|||
[`${prefixCls.value}-multiple-words`]:
|
||||
!isDotRef.value && displayCount.value && displayCount.value.toString().length > 1,
|
||||
[`${prefixCls.value}-status-${props.status}`]: !!props.status,
|
||||
[`${prefixCls.value}-status-${props.color}`]: isPresetColor(props.color),
|
||||
[`${prefixCls.value}-status-${props.color}`]: isInternalColor.value,
|
||||
}));
|
||||
|
||||
return () => {
|
||||
|
@ -179,18 +185,19 @@ export default defineComponent({
|
|||
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
attrs.class,
|
||||
hashId.value,
|
||||
);
|
||||
|
||||
// <Badge status="success" />
|
||||
if (!children && hasStatus.value) {
|
||||
const statusTextColor = mergedStyle.color;
|
||||
return (
|
||||
return wrapSSR(
|
||||
<span {...attrs} class={badgeClassName} style={mergedStyle}>
|
||||
<span class={statusCls.value} style={statusStyle.value} />
|
||||
<span style={{ color: statusTextColor }} class={`${pre}-status-text`}>
|
||||
{text}
|
||||
</span>
|
||||
</span>
|
||||
</span>,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -198,12 +205,12 @@ export default defineComponent({
|
|||
appear: false,
|
||||
});
|
||||
let scrollNumberStyle: CSSProperties = { ...mergedStyle, ...(props.numberStyle as object) };
|
||||
if (color && !isPresetColor(color)) {
|
||||
if (color && !isInternalColor.value) {
|
||||
scrollNumberStyle = scrollNumberStyle || {};
|
||||
scrollNumberStyle.background = color;
|
||||
}
|
||||
|
||||
return (
|
||||
return wrapSSR(
|
||||
<span {...attrs} class={badgeClassName}>
|
||||
{children}
|
||||
<Transition {...transitionProps}>
|
||||
|
@ -221,7 +228,7 @@ export default defineComponent({
|
|||
</ScrollNumber>
|
||||
</Transition>
|
||||
{statusTextNode}
|
||||
</span>
|
||||
</span>,
|
||||
);
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { LiteralUnion } from '../_util/type';
|
||||
import type { PresetColorType } from '../_util/colors';
|
||||
import { isPresetColor } from './utils';
|
||||
import useStyle from './style';
|
||||
import { isPresetColor } from '../_util/colors';
|
||||
import type { CSSProperties, PropType, ExtractPropTypes } from 'vue';
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
|
@ -8,7 +9,7 @@ import useConfigInject from '../_util/hooks/useConfigInject';
|
|||
|
||||
export const ribbonProps = () => ({
|
||||
prefix: String,
|
||||
color: { type: String as PropType<LiteralUnion<PresetColorType, string>> },
|
||||
color: { type: String as PropType<LiteralUnion<PresetColorType>> },
|
||||
text: PropTypes.any,
|
||||
placement: { type: String as PropType<'start' | 'end'>, default: 'end' },
|
||||
});
|
||||
|
@ -23,7 +24,8 @@ export default defineComponent({
|
|||
slots: ['text'],
|
||||
setup(props, { attrs, slots }) {
|
||||
const { prefixCls, direction } = useConfigInject('ribbon', props);
|
||||
const colorInPreset = computed(() => isPresetColor(props.color));
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls);
|
||||
const colorInPreset = computed(() => isPresetColor(props.color, false));
|
||||
const ribbonCls = computed(() => [
|
||||
prefixCls.value,
|
||||
`${prefixCls.value}-placement-${props.placement}`,
|
||||
|
@ -40,17 +42,17 @@ export default defineComponent({
|
|||
colorStyle.background = props.color;
|
||||
cornerColorStyle.color = props.color;
|
||||
}
|
||||
return (
|
||||
<div class={`${prefixCls.value}-wrapper`} {...restAttrs}>
|
||||
return wrapSSR(
|
||||
<div class={`${prefixCls.value}-wrapper ${hashId.value}`} {...restAttrs}>
|
||||
{slots.default?.()}
|
||||
<div
|
||||
class={[ribbonCls.value, className]}
|
||||
class={[ribbonCls.value, className, hashId.value]}
|
||||
style={{ ...colorStyle, ...(style as CSSProperties) }}
|
||||
>
|
||||
<span class={`${prefixCls.value}-text`}>{props.text || slots.text?.()}</span>
|
||||
<div class={`${prefixCls.value}-corner`} style={cornerColorStyle} />
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
);
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,281 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@badge-prefix-cls: ~'@{ant-prefix}-badge';
|
||||
@number-prefix-cls: ~'@{ant-prefix}-scroll-number';
|
||||
|
||||
.@{badge-prefix-cls} {
|
||||
.reset-component();
|
||||
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
|
||||
&-count {
|
||||
z-index: @zindex-badge;
|
||||
min-width: @badge-height;
|
||||
height: @badge-height;
|
||||
padding: 0 6px;
|
||||
color: @badge-text-color;
|
||||
font-weight: @badge-font-weight;
|
||||
font-size: @badge-font-size;
|
||||
line-height: @badge-height;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
background: @badge-color;
|
||||
border-radius: (@badge-height / 2);
|
||||
box-shadow: 0 0 0 1px @shadow-color-inverse;
|
||||
|
||||
a,
|
||||
a:hover {
|
||||
color: @badge-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-count-sm {
|
||||
min-width: @badge-height-sm;
|
||||
height: @badge-height-sm;
|
||||
padding: 0;
|
||||
font-size: @badge-font-size-sm;
|
||||
line-height: @badge-height-sm;
|
||||
border-radius: (@badge-height-sm / 2);
|
||||
}
|
||||
|
||||
&-multiple-words {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
&-dot {
|
||||
z-index: @zindex-badge;
|
||||
width: @badge-dot-size;
|
||||
min-width: @badge-dot-size;
|
||||
height: @badge-dot-size;
|
||||
background: @highlight-color;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0 0 0 1px @shadow-color-inverse;
|
||||
}
|
||||
|
||||
// Tricky way to resolve https://github.com/ant-design/ant-design/issues/30088
|
||||
&-dot.@{number-prefix-cls} {
|
||||
transition: background 1.5s;
|
||||
}
|
||||
|
||||
&-count,
|
||||
&-dot,
|
||||
.@{number-prefix-cls}-custom-component {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(50%, -50%);
|
||||
transform-origin: 100% 0%;
|
||||
|
||||
&.@{iconfont-css-prefix}-spin {
|
||||
animation: antBadgeLoadingCircle 1s infinite linear;
|
||||
}
|
||||
}
|
||||
|
||||
&-status {
|
||||
line-height: inherit;
|
||||
vertical-align: baseline;
|
||||
|
||||
&-dot {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
display: inline-block;
|
||||
width: @badge-status-size;
|
||||
height: @badge-status-size;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&-success {
|
||||
background-color: @success-color;
|
||||
}
|
||||
|
||||
&-processing {
|
||||
position: relative;
|
||||
background-color: @processing-color;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid @processing-color;
|
||||
border-radius: 50%;
|
||||
animation: antStatusProcessing 1.2s infinite ease-in-out;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&-default {
|
||||
background-color: @normal-color;
|
||||
}
|
||||
|
||||
&-error {
|
||||
background-color: @error-color;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
background-color: @warning-color;
|
||||
}
|
||||
|
||||
// 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);
|
||||
@darkColor: '@{color}-6';
|
||||
&-@{color} {
|
||||
background: @@darkColor;
|
||||
}
|
||||
}
|
||||
.make-color-classes();
|
||||
|
||||
&-text {
|
||||
margin-left: 8px;
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
&-zoom-appear,
|
||||
&-zoom-enter {
|
||||
animation: antZoomBadgeIn @animation-duration-slow @ease-out-back;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
&-zoom-leave {
|
||||
animation: antZoomBadgeOut @animation-duration-slow @ease-in-back;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
&-not-a-wrapper {
|
||||
.@{badge-prefix-cls}-zoom-appear,
|
||||
.@{badge-prefix-cls}-zoom-enter {
|
||||
animation: antNoWrapperZoomBadgeIn @animation-duration-slow @ease-out-back;
|
||||
}
|
||||
|
||||
.@{badge-prefix-cls}-zoom-leave {
|
||||
animation: antNoWrapperZoomBadgeOut @animation-duration-slow @ease-in-back;
|
||||
}
|
||||
|
||||
&:not(.@{badge-prefix-cls}-status) {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.@{number-prefix-cls}-custom-component,
|
||||
.@{badge-prefix-cls}-count {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.@{number-prefix-cls}-custom-component,
|
||||
.@{number-prefix-cls} {
|
||||
position: relative;
|
||||
top: auto;
|
||||
display: block;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antStatusProcessing {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(2.4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Safari will blink with transform when inner element has absolute style.
|
||||
.safari-fix-motion() {
|
||||
/* stylelint-disable property-no-vendor-prefix */
|
||||
-webkit-transform-style: preserve-3d;
|
||||
-webkit-backface-visibility: hidden;
|
||||
/* stylelint-enable property-no-vendor-prefix */
|
||||
}
|
||||
|
||||
.@{number-prefix-cls} {
|
||||
overflow: hidden;
|
||||
direction: ltr;
|
||||
|
||||
&-only {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
height: @badge-height;
|
||||
transition: all @animation-duration-slow @ease-in-out;
|
||||
.safari-fix-motion;
|
||||
|
||||
> p.@{number-prefix-cls}-only-unit {
|
||||
height: @badge-height;
|
||||
margin: 0;
|
||||
.safari-fix-motion;
|
||||
}
|
||||
}
|
||||
|
||||
&-symbol {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antZoomBadgeIn {
|
||||
0% {
|
||||
transform: scale(0) translate(50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) translate(50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antZoomBadgeOut {
|
||||
0% {
|
||||
transform: scale(1) translate(50%, -50%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0) translate(50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antNoWrapperZoomBadgeIn {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antNoWrapperZoomBadgeOut {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antBadgeLoadingCircle {
|
||||
0% {
|
||||
transform-origin: 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(50%, -50%) rotate(360deg);
|
||||
transform-origin: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@import './ribbon';
|
||||
@import './rtl';
|
|
@ -0,0 +1,376 @@
|
|||
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 { genPresetColor, resetComponent } from '../../_style';
|
||||
|
||||
interface BadgeToken extends FullToken<'Badge'> {
|
||||
badgeFontHeight: number;
|
||||
badgeZIndex: number | string;
|
||||
badgeHeight: number;
|
||||
badgeHeightSm: number;
|
||||
badgeTextColor: string;
|
||||
badgeFontWeight: string;
|
||||
badgeFontSize: number;
|
||||
badgeColor: string;
|
||||
badgeColorHover: string;
|
||||
badgeDotSize: number;
|
||||
badgeFontSizeSm: number;
|
||||
badgeStatusSize: number;
|
||||
badgeShadowSize: number;
|
||||
badgeShadowColor: string;
|
||||
badgeProcessingDuration: string;
|
||||
badgeRibbonOffset: number;
|
||||
badgeRibbonCornerTransform: string;
|
||||
badgeRibbonCornerFilter: string;
|
||||
}
|
||||
|
||||
const antStatusProcessing = new Keyframes('antStatusProcessing', {
|
||||
'0%': { transform: 'scale(0.8)', opacity: 0.5 },
|
||||
'100%': { transform: 'scale(2.4)', opacity: 0 },
|
||||
});
|
||||
|
||||
const antZoomBadgeIn = new Keyframes('antZoomBadgeIn', {
|
||||
'0%': { transform: 'scale(0) translate(50%, -50%)', opacity: 0 },
|
||||
'100%': { transform: 'scale(1) translate(50%, -50%)' },
|
||||
});
|
||||
|
||||
const antZoomBadgeOut = new Keyframes('antZoomBadgeOut', {
|
||||
'0%': { transform: 'scale(1) translate(50%, -50%)' },
|
||||
'100%': { transform: 'scale(0) translate(50%, -50%)', opacity: 0 },
|
||||
});
|
||||
|
||||
const antNoWrapperZoomBadgeIn = new Keyframes('antNoWrapperZoomBadgeIn', {
|
||||
'0%': { transform: 'scale(0)', opacity: 0 },
|
||||
'100%': { transform: 'scale(1)' },
|
||||
});
|
||||
const antNoWrapperZoomBadgeOut = new Keyframes('antNoWrapperZoomBadgeOut', {
|
||||
'0%': { transform: 'scale(1)' },
|
||||
'100%': { transform: 'scale(0)', opacity: 0 },
|
||||
});
|
||||
const antBadgeLoadingCircle = new Keyframes('antBadgeLoadingCircle', {
|
||||
'0%': { transformOrigin: '50%' },
|
||||
'100%': {
|
||||
transform: 'translate(50%, -50%) rotate(360deg)',
|
||||
transformOrigin: '50%',
|
||||
},
|
||||
});
|
||||
|
||||
const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token: BadgeToken): CSSObject => {
|
||||
const {
|
||||
componentCls,
|
||||
iconCls,
|
||||
antCls,
|
||||
badgeFontHeight,
|
||||
badgeShadowSize,
|
||||
badgeHeightSm,
|
||||
motionDurationSlow,
|
||||
badgeStatusSize,
|
||||
marginXS,
|
||||
badgeRibbonOffset,
|
||||
} = token;
|
||||
const numberPrefixCls = `${antCls}-scroll-number`;
|
||||
const ribbonPrefixCls = `${antCls}-ribbon`;
|
||||
const ribbonWrapperPrefixCls = `${antCls}-ribbon-wrapper`;
|
||||
|
||||
const statusPreset = genPresetColor(token, (colorKey, { darkColor }) => ({
|
||||
[`${componentCls}-status-${colorKey}`]: {
|
||||
background: darkColor,
|
||||
},
|
||||
}));
|
||||
|
||||
const statusRibbonPreset = genPresetColor(token, (colorKey, { darkColor }) => ({
|
||||
[`&${ribbonPrefixCls}-color-${colorKey}`]: {
|
||||
background: darkColor,
|
||||
color: darkColor,
|
||||
},
|
||||
}));
|
||||
|
||||
return {
|
||||
[componentCls]: {
|
||||
...resetComponent(token),
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
width: 'fit-content',
|
||||
lineHeight: 1,
|
||||
|
||||
[`${componentCls}-count`]: {
|
||||
zIndex: token.badgeZIndex,
|
||||
minWidth: token.badgeHeight,
|
||||
height: token.badgeHeight,
|
||||
color: token.badgeTextColor,
|
||||
fontWeight: token.badgeFontWeight,
|
||||
fontSize: token.badgeFontSize,
|
||||
lineHeight: `${token.badgeHeight}px`,
|
||||
whiteSpace: 'nowrap',
|
||||
textAlign: 'center',
|
||||
background: token.badgeColor,
|
||||
borderRadius: token.badgeHeight / 2,
|
||||
boxShadow: `0 0 0 ${badgeShadowSize}px ${token.badgeShadowColor}`,
|
||||
transition: `background ${token.motionDurationMid}`,
|
||||
|
||||
a: {
|
||||
color: token.badgeTextColor,
|
||||
},
|
||||
'a:hover': {
|
||||
color: token.badgeTextColor,
|
||||
},
|
||||
|
||||
'a:hover &': {
|
||||
background: token.badgeColorHover,
|
||||
},
|
||||
},
|
||||
[`${componentCls}-count-sm`]: {
|
||||
minWidth: badgeHeightSm,
|
||||
height: badgeHeightSm,
|
||||
fontSize: token.badgeFontSizeSm,
|
||||
lineHeight: `${badgeHeightSm}px`,
|
||||
borderRadius: badgeHeightSm / 2,
|
||||
},
|
||||
|
||||
[`${componentCls}-multiple-words`]: {
|
||||
padding: `0 ${token.paddingXS}px`,
|
||||
},
|
||||
|
||||
[`${componentCls}-dot`]: {
|
||||
zIndex: token.badgeZIndex,
|
||||
width: token.badgeDotSize,
|
||||
minWidth: token.badgeDotSize,
|
||||
height: token.badgeDotSize,
|
||||
background: token.badgeColor,
|
||||
borderRadius: '100%',
|
||||
boxShadow: `0 0 0 ${badgeShadowSize}px ${token.badgeShadowColor}`,
|
||||
},
|
||||
[`${componentCls}-dot${numberPrefixCls}`]: {
|
||||
transition: `background ${motionDurationSlow}`,
|
||||
},
|
||||
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
transform: 'translate(50%, -50%)',
|
||||
transformOrigin: '100% 0%',
|
||||
[`${iconCls}-spin`]: {
|
||||
animationName: antBadgeLoadingCircle,
|
||||
animationDuration: token.motionDurationMid,
|
||||
animationIterationCount: 'infinite',
|
||||
animationTimingFunction: 'linear',
|
||||
},
|
||||
},
|
||||
[`&${componentCls}-status`]: {
|
||||
lineHeight: 'inherit',
|
||||
verticalAlign: 'baseline',
|
||||
|
||||
[`${componentCls}-status-dot`]: {
|
||||
position: 'relative',
|
||||
top: -1, // Magic number, but seems better experience
|
||||
display: 'inline-block',
|
||||
width: badgeStatusSize,
|
||||
height: badgeStatusSize,
|
||||
verticalAlign: 'middle',
|
||||
borderRadius: '50%',
|
||||
},
|
||||
|
||||
[`${componentCls}-status-success`]: {
|
||||
backgroundColor: token.colorSuccess,
|
||||
},
|
||||
[`${componentCls}-status-processing`]: {
|
||||
position: 'relative',
|
||||
color: token.colorPrimary,
|
||||
backgroundColor: token.colorPrimary,
|
||||
|
||||
'&::after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderWidth: badgeShadowSize,
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'inherit',
|
||||
borderRadius: '50%',
|
||||
animationName: antStatusProcessing,
|
||||
animationDuration: token.badgeProcessingDuration,
|
||||
animationIterationCount: 'infinite',
|
||||
animationTimingFunction: 'ease-in-out',
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
[`${componentCls}-status-default`]: {
|
||||
backgroundColor: token.colorTextPlaceholder,
|
||||
},
|
||||
|
||||
[`${componentCls}-status-error`]: {
|
||||
backgroundColor: token.colorError,
|
||||
},
|
||||
|
||||
[`${componentCls}-status-warning`]: {
|
||||
backgroundColor: token.colorWarning,
|
||||
},
|
||||
...statusPreset,
|
||||
[`${componentCls}-status-text`]: {
|
||||
marginInlineStart: marginXS,
|
||||
color: token.colorText,
|
||||
fontSize: token.fontSize,
|
||||
},
|
||||
},
|
||||
[`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
|
||||
animationName: antZoomBadgeIn,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
animationTimingFunction: token.motionEaseOutBack,
|
||||
animationFillMode: 'both',
|
||||
},
|
||||
[`${componentCls}-zoom-leave`]: {
|
||||
animationName: antZoomBadgeOut,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
animationTimingFunction: token.motionEaseOutBack,
|
||||
animationFillMode: 'both',
|
||||
},
|
||||
[`&${componentCls}-not-a-wrapper`]: {
|
||||
[`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
|
||||
animationName: antNoWrapperZoomBadgeIn,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
animationTimingFunction: token.motionEaseOutBack,
|
||||
},
|
||||
|
||||
[`${componentCls}-zoom-leave`]: {
|
||||
animationName: antNoWrapperZoomBadgeOut,
|
||||
animationDuration: token.motionDurationSlow,
|
||||
animationTimingFunction: token.motionEaseOutBack,
|
||||
},
|
||||
[`&:not(${componentCls}-status)`]: {
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
[`${numberPrefixCls}-custom-component, ${componentCls}-count`]: {
|
||||
transform: 'none',
|
||||
},
|
||||
[`${numberPrefixCls}-custom-component, ${numberPrefixCls}`]: {
|
||||
position: 'relative',
|
||||
top: 'auto',
|
||||
display: 'block',
|
||||
transformOrigin: '50% 50%',
|
||||
},
|
||||
},
|
||||
[`${numberPrefixCls}`]: {
|
||||
overflow: 'hidden',
|
||||
[`${numberPrefixCls}-only`]: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
height: token.badgeHeight,
|
||||
transition: `all ${token.motionDurationSlow} ${token.motionEaseOutBack}`,
|
||||
WebkitTransformStyle: 'preserve-3d',
|
||||
WebkitBackfaceVisibility: 'hidden',
|
||||
[`> p${numberPrefixCls}-only-unit`]: {
|
||||
height: token.badgeHeight,
|
||||
margin: 0,
|
||||
WebkitTransformStyle: 'preserve-3d',
|
||||
WebkitBackfaceVisibility: 'hidden',
|
||||
},
|
||||
},
|
||||
[`${numberPrefixCls}-symbol`]: { verticalAlign: 'top' },
|
||||
},
|
||||
|
||||
// ====================== RTL =======================
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
|
||||
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
|
||||
transform: 'translate(-50%, -50%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
[`${ribbonWrapperPrefixCls}`]: { position: 'relative' },
|
||||
[`${ribbonPrefixCls}`]: {
|
||||
...resetComponent(token),
|
||||
position: 'absolute',
|
||||
top: marginXS,
|
||||
height: badgeFontHeight,
|
||||
padding: `0 ${token.paddingXS}px`,
|
||||
color: token.colorPrimary,
|
||||
lineHeight: `${badgeFontHeight}px`,
|
||||
whiteSpace: 'nowrap',
|
||||
backgroundColor: token.colorPrimary,
|
||||
borderRadius: token.borderRadiusSM,
|
||||
[`${ribbonPrefixCls}-text`]: { color: token.colorTextLightSolid },
|
||||
[`${ribbonPrefixCls}-corner`]: {
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
width: badgeRibbonOffset,
|
||||
height: badgeRibbonOffset,
|
||||
color: 'currentcolor',
|
||||
border: `${badgeRibbonOffset / 2}px solid`,
|
||||
transform: token.badgeRibbonCornerTransform,
|
||||
transformOrigin: 'top',
|
||||
filter: token.badgeRibbonCornerFilter,
|
||||
},
|
||||
...statusRibbonPreset,
|
||||
[`&${ribbonPrefixCls}-placement-end`]: {
|
||||
insetInlineEnd: -badgeRibbonOffset,
|
||||
borderEndEndRadius: 0,
|
||||
[`${ribbonPrefixCls}-corner`]: {
|
||||
insetInlineEnd: 0,
|
||||
borderInlineEndColor: 'transparent',
|
||||
borderBlockEndColor: 'transparent',
|
||||
},
|
||||
},
|
||||
[`&${ribbonPrefixCls}-placement-start`]: {
|
||||
insetInlineStart: -badgeRibbonOffset,
|
||||
borderEndStartRadius: 0,
|
||||
[`${ribbonPrefixCls}-corner`]: {
|
||||
insetInlineStart: 0,
|
||||
borderBlockEndColor: 'transparent',
|
||||
borderInlineStartColor: 'transparent',
|
||||
},
|
||||
},
|
||||
|
||||
// ====================== RTL =======================
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('Badge', token => {
|
||||
const { fontSize, lineHeight, fontSizeSM, lineWidth, marginXS, colorBorderBg } = token;
|
||||
|
||||
const badgeFontHeight = Math.round(fontSize * lineHeight);
|
||||
const badgeShadowSize = lineWidth;
|
||||
const badgeZIndex = 'auto';
|
||||
const badgeHeight = badgeFontHeight - 2 * badgeShadowSize;
|
||||
const badgeTextColor = token.colorBgContainer;
|
||||
const badgeFontWeight = 'normal';
|
||||
const badgeFontSize = fontSizeSM;
|
||||
const badgeColor = token.colorError;
|
||||
const badgeColorHover = token.colorErrorHover;
|
||||
const badgeHeightSm = fontSize;
|
||||
const badgeDotSize = fontSizeSM / 2;
|
||||
const badgeFontSizeSm = fontSizeSM;
|
||||
const badgeStatusSize = fontSizeSM / 2;
|
||||
|
||||
const badgeToken = mergeToken<BadgeToken>(token, {
|
||||
badgeFontHeight,
|
||||
badgeShadowSize,
|
||||
badgeZIndex,
|
||||
badgeHeight,
|
||||
badgeTextColor,
|
||||
badgeFontWeight,
|
||||
badgeFontSize,
|
||||
badgeColor,
|
||||
badgeColorHover,
|
||||
badgeShadowColor: colorBorderBg,
|
||||
badgeHeightSm,
|
||||
badgeDotSize,
|
||||
badgeFontSizeSm,
|
||||
badgeStatusSize,
|
||||
badgeProcessingDuration: '1.2s',
|
||||
badgeRibbonOffset: marginXS,
|
||||
|
||||
// Follow token just by Design. Not related with token
|
||||
badgeRibbonCornerTransform: 'scaleY(0.75)',
|
||||
badgeRibbonCornerFilter: `brightness(75%)`,
|
||||
});
|
||||
|
||||
return [genSharedBadgeStyle(badgeToken)];
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
import '../../style/index.less';
|
||||
import './index.less';
|
|
@ -1,81 +0,0 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@ribbon-prefix-cls: ~'@{ant-prefix}-ribbon';
|
||||
@ribbon-wrapper-prefix-cls: ~'@{ant-prefix}-ribbon-wrapper';
|
||||
|
||||
.@{ribbon-wrapper-prefix-cls} {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.@{ribbon-prefix-cls} {
|
||||
.reset-component();
|
||||
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
height: 22px;
|
||||
padding: 0 8px;
|
||||
color: @badge-text-color;
|
||||
line-height: 22px;
|
||||
white-space: nowrap;
|
||||
background-color: @primary-color;
|
||||
border-radius: @border-radius-sm;
|
||||
|
||||
&-text {
|
||||
color: @white;
|
||||
}
|
||||
|
||||
&-corner {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
color: currentcolor;
|
||||
border: 4px solid;
|
||||
transform: scaleY(0.75);
|
||||
transform-origin: top;
|
||||
// If not support IE 11, use filter: brightness(75%) instead
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
border: inherit;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
// colors
|
||||
// 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);
|
||||
@darkColor: '@{color}-6';
|
||||
&-color-@{color} {
|
||||
color: @@darkColor;
|
||||
background: @@darkColor;
|
||||
}
|
||||
}
|
||||
.make-color-classes();
|
||||
|
||||
// placement
|
||||
&.@{ribbon-prefix-cls}-placement-end {
|
||||
right: -8px;
|
||||
border-bottom-right-radius: 0;
|
||||
.@{ribbon-prefix-cls}-corner {
|
||||
right: 0;
|
||||
border-color: currentcolor transparent transparent currentcolor;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{ribbon-prefix-cls}-placement-start {
|
||||
left: -8px;
|
||||
border-bottom-left-radius: 0;
|
||||
.@{ribbon-prefix-cls}-corner {
|
||||
left: 0;
|
||||
border-color: currentcolor currentcolor transparent transparent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
.@{badge-prefix-cls} {
|
||||
&-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
&:not(&-not-a-wrapper) &-count,
|
||||
&:not(&-not-a-wrapper) &-dot,
|
||||
&:not(&-not-a-wrapper) .@{number-prefix-cls}-custom-component {
|
||||
.@{badge-prefix-cls}-rtl& {
|
||||
right: auto;
|
||||
left: 0;
|
||||
direction: ltr;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
&-rtl&:not(&-not-a-wrapper) .@{number-prefix-cls}-custom-component {
|
||||
right: auto;
|
||||
left: 0;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
|
||||
&-status {
|
||||
&-text {
|
||||
.@{badge-prefix-cls}-rtl & {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(&-not-a-wrapper).@{badge-prefix-cls}-rtl {
|
||||
.@{badge-prefix-cls}-zoom-appear,
|
||||
.@{badge-prefix-cls}-zoom-enter {
|
||||
animation-name: antZoomBadgeInRtl;
|
||||
}
|
||||
|
||||
.@{badge-prefix-cls}-zoom-leave {
|
||||
animation-name: antZoomBadgeOutRtl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{ribbon-prefix-cls}-rtl {
|
||||
direction: rtl;
|
||||
&.@{ribbon-prefix-cls}-placement-end {
|
||||
right: unset;
|
||||
left: -8px;
|
||||
border-bottom-right-radius: @border-radius-sm;
|
||||
border-bottom-left-radius: 0;
|
||||
.@{ribbon-prefix-cls}-corner {
|
||||
right: unset;
|
||||
left: 0;
|
||||
border-color: currentcolor currentcolor transparent transparent;
|
||||
|
||||
&::after {
|
||||
border-color: currentcolor currentcolor transparent transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.@{ribbon-prefix-cls}-placement-start {
|
||||
right: -8px;
|
||||
left: unset;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: @border-radius-sm;
|
||||
.@{ribbon-prefix-cls}-corner {
|
||||
right: 0;
|
||||
left: unset;
|
||||
border-color: currentcolor transparent transparent currentcolor;
|
||||
|
||||
&::after {
|
||||
border-color: currentcolor transparent transparent currentcolor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antZoomBadgeInRtl {
|
||||
0% {
|
||||
transform: scale(0) translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1) translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antZoomBadgeOutRtl {
|
||||
0% {
|
||||
transform: scale(1) translate(-50%, -50%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0) translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import { PresetColorTypes } from '../_util/colors';
|
||||
|
||||
export function isPresetColor(color?: string): boolean {
|
||||
return (PresetColorTypes as any[]).indexOf(color) !== -1;
|
||||
}
|
|
@ -7,7 +7,7 @@ import './tag/style';
|
|||
import './rate/style';
|
||||
import './pagination/style';
|
||||
// import './avatar/style';
|
||||
import './badge/style';
|
||||
// import './badge/style';
|
||||
import './tabs/style';
|
||||
import './input/style';
|
||||
import './tooltip/style';
|
||||
|
|
|
@ -5,18 +5,15 @@ import PropTypes from '../_util/vue-types';
|
|||
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||||
import Wave from '../_util/wave';
|
||||
import type { PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { PresetColorTypes, PresetStatusColorTypes } from '../_util/colors';
|
||||
import { isPresetColor, isPresetStatusColor } from '../_util/colors';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
import CheckableTag from './CheckableTag';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`);
|
||||
const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`);
|
||||
|
||||
export const tagProps = () => ({
|
||||
prefixCls: String,
|
||||
color: {
|
||||
type: String as PropType<LiteralUnion<PresetColorType | PresetStatusColorType, string>>,
|
||||
type: String as PropType<LiteralUnion<PresetColorType | PresetStatusColorType>>,
|
||||
},
|
||||
closable: { type: Boolean, default: false },
|
||||
closeIcon: PropTypes.any,
|
||||
|
@ -60,18 +57,22 @@ const Tag = defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
const isPresetColor = computed(() => {
|
||||
const { color } = props;
|
||||
if (!color) {
|
||||
return false;
|
||||
}
|
||||
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
|
||||
});
|
||||
// const isPresetColor = computed(() => {
|
||||
// const { color } = props;
|
||||
// if (!color) {
|
||||
// return false;
|
||||
// }
|
||||
// return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
|
||||
// });
|
||||
|
||||
const isInternalColor = computed(
|
||||
() => isPresetColor(props.color) || isPresetStatusColor(props.color),
|
||||
);
|
||||
|
||||
const tagClassName = computed(() =>
|
||||
classNames(prefixCls.value, {
|
||||
[`${prefixCls.value}-${props.color}`]: isPresetColor.value,
|
||||
[`${prefixCls.value}-has-color`]: props.color && !isPresetColor.value,
|
||||
[`${prefixCls.value}-${props.color}`]: isInternalColor.value,
|
||||
[`${prefixCls.value}-has-color`]: props.color && !isInternalColor.value,
|
||||
[`${prefixCls.value}-hidden`]: !visible.value,
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
}),
|
||||
|
@ -99,7 +100,7 @@ const Tag = defineComponent({
|
|||
};
|
||||
|
||||
const tagStyle = {
|
||||
backgroundColor: color && !isPresetColor.value ? color : undefined,
|
||||
backgroundColor: color && !isInternalColor.value ? color : undefined,
|
||||
};
|
||||
|
||||
const iconNode = icon || null;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import type { ExtractPropTypes, CSSProperties } from 'vue';
|
||||
import type { ExtractPropTypes } from 'vue';
|
||||
import { computed, watch, defineComponent, onMounted, ref } from 'vue';
|
||||
import VcTooltip from '../vc-tooltip';
|
||||
import classNames from '../_util/classNames';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { PresetColorTypes } from '../_util/colors';
|
||||
import warning from '../_util/warning';
|
||||
import { getStyle, filterEmpty, isValidElement, initDefaultProps } from '../_util/props-util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
|
@ -13,6 +12,7 @@ import useConfigInject from '../_util/hooks/useConfigInject';
|
|||
import getPlacements from './placements';
|
||||
import firstNotUndefined from '../_util/firstNotUndefined';
|
||||
import raf from '../_util/raf';
|
||||
import { parseColor } from './util';
|
||||
export type { AdjustOverflow, PlacementsConfig } from './placements';
|
||||
|
||||
// https://github.com/react-component/tooltip
|
||||
|
@ -39,8 +39,6 @@ const splitObject = (obj: any, keys: string[]) => {
|
|||
return { picked, omitted };
|
||||
};
|
||||
|
||||
const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`);
|
||||
|
||||
export const tooltipProps = () => ({
|
||||
...abstractTooltipProps(),
|
||||
title: PropTypes.any,
|
||||
|
@ -76,7 +74,7 @@ export default defineComponent({
|
|||
slots: ['title'],
|
||||
// emits: ['update:visible', 'visibleChange'],
|
||||
setup(props, { slots, emit, attrs, expose }) {
|
||||
const { prefixCls, getPopupContainer } = useConfigInject('tooltip', props);
|
||||
const { prefixCls, getPopupContainer, direction } = useConfigInject('tooltip', props);
|
||||
|
||||
const visible = ref(firstNotUndefined([props.visible, props.defaultVisible]));
|
||||
|
||||
|
@ -216,9 +214,9 @@ export default defineComponent({
|
|||
}
|
||||
domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`;
|
||||
};
|
||||
|
||||
const colorInfo = computed(() => parseColor(prefixCls.value, props.color));
|
||||
return () => {
|
||||
const { openClassName, color, overlayClassName } = props;
|
||||
const { openClassName, overlayClassName } = props;
|
||||
let children = filterEmpty(slots.default?.()) ?? null;
|
||||
children = children.length === 1 ? children[0] : children;
|
||||
|
||||
|
@ -237,15 +235,19 @@ export default defineComponent({
|
|||
[openClassName || `${prefixCls.value}-open`]: true,
|
||||
[child.props && child.props.class]: child.props && child.props.class,
|
||||
});
|
||||
const customOverlayClassName = classNames(overlayClassName, {
|
||||
[`${prefixCls.value}-${color}`]: color && PresetColorRegex.test(color),
|
||||
});
|
||||
let formattedOverlayInnerStyle: CSSProperties;
|
||||
let arrowContentStyle: CSSProperties;
|
||||
if (color && !PresetColorRegex.test(color)) {
|
||||
formattedOverlayInnerStyle = { backgroundColor: color };
|
||||
arrowContentStyle = { '--antd-arrow-background-color': color };
|
||||
}
|
||||
const customOverlayClassName = classNames(
|
||||
overlayClassName,
|
||||
{
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
},
|
||||
|
||||
colorInfo.value.className,
|
||||
);
|
||||
const formattedOverlayInnerStyle = {
|
||||
...props.overlayInnerStyle,
|
||||
...colorInfo.value.overlayStyle,
|
||||
};
|
||||
const arrowContentStyle = colorInfo.value.arrowStyle;
|
||||
const vcTooltipProps = {
|
||||
...attrs,
|
||||
...(props as TooltipProps),
|
||||
|
|
|
@ -2,7 +2,8 @@ import type { CSSProperties, PropType } from 'vue';
|
|||
import type { AlignType, BuildInPlacements } from '../vc-trigger/interface';
|
||||
import type { AdjustOverflow } from './placements';
|
||||
export type TriggerType = 'hover' | 'focus' | 'click' | 'contextmenu';
|
||||
|
||||
import type { PresetColorType } from '../_util/colors';
|
||||
import type { LiteralUnion } from '../_util/type';
|
||||
export type TooltipPlacement =
|
||||
| 'top'
|
||||
| 'left'
|
||||
|
@ -22,9 +23,13 @@ export default () => ({
|
|||
visible: { type: Boolean, default: undefined },
|
||||
defaultVisible: { type: Boolean, default: undefined },
|
||||
placement: String as PropType<TooltipPlacement>,
|
||||
color: String,
|
||||
color: String as PropType<LiteralUnion<PresetColorType>>,
|
||||
transitionName: String,
|
||||
overlayStyle: { type: Object as PropType<CSSProperties>, default: undefined as CSSProperties },
|
||||
overlayInnerStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: undefined as CSSProperties,
|
||||
},
|
||||
overlayClassName: String,
|
||||
openClassName: String,
|
||||
prefixCls: String,
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import type { CSSProperties } from 'vue';
|
||||
import classNames from '../_util/classNames';
|
||||
import { isPresetColor } from '../_util/colors';
|
||||
|
||||
export function parseColor(prefixCls: string, color?: string) {
|
||||
const isInternalColor = isPresetColor(color);
|
||||
|
||||
const className = classNames({
|
||||
[`${prefixCls}-${color}`]: color && isInternalColor,
|
||||
});
|
||||
|
||||
const overlayStyle: CSSProperties = {};
|
||||
const arrowStyle: CSSProperties = {};
|
||||
|
||||
if (color && !isInternalColor) {
|
||||
overlayStyle.background = color;
|
||||
// @ts-ignore
|
||||
arrowStyle['--antd-arrow-background-color'] = color;
|
||||
}
|
||||
|
||||
return { className, overlayStyle, arrowStyle };
|
||||
}
|
Loading…
Reference in New Issue