Browse Source
* feat: basic implements * feat: add rtl * feat: reorganize types * test: add test cases for capitalize * test: improve coverage * chore: improve types * chore: update * chore: updatepull/34511/head
vagusX
3 years ago
committed by
GitHub
8 changed files with 479 additions and 141 deletions
@ -0,0 +1,17 @@ |
|||
import capitalize from '../capitalize'; |
|||
|
|||
describe('capitalize', () => { |
|||
it('should capitalize the first character of a string', () => { |
|||
expect(capitalize('antd')).toBe('Antd'); |
|||
expect(capitalize('Antd')).toBe('Antd'); |
|||
expect(capitalize(' antd')).toBe(' antd'); |
|||
expect(capitalize('')).toBe(''); |
|||
}); |
|||
|
|||
it('should return the original value when is not a string', () => { |
|||
expect(capitalize(1 as any)).toBe(1); |
|||
expect(capitalize(true as any)).toBe(true); |
|||
expect(capitalize(undefined as any)).toBe(undefined); |
|||
expect(capitalize(null as any)).toBe(null); |
|||
}); |
|||
}); |
@ -0,0 +1,8 @@ |
|||
export default function capitalize<T extends string>(str: T): Capitalize<T> { |
|||
if (typeof str !== 'string') { |
|||
return str; |
|||
} |
|||
|
|||
const ret = str.charAt(0).toUpperCase() + str.slice(1); |
|||
return ret as Capitalize<T>; |
|||
} |
@ -1,129 +1,129 @@ |
|||
@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: @border-radius-base; |
|||
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'; |
|||
// @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: @border-radius-base; |
|||
// 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'; |
|||
|
@ -1,2 +1,198 @@ |
|||
import '../../style/index.less'; |
|||
import './index.less'; |
|||
// deps-lint-skip-all
|
|||
import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs'; |
|||
import capitalize from '../../_util/capitalize'; |
|||
import { |
|||
PresetColorType, |
|||
DerivativeToken, |
|||
useStyleRegister, |
|||
useToken, |
|||
resetComponent, |
|||
UseComponentStyleResult, |
|||
PresetColorKeys, |
|||
} from '../../_util/theme'; |
|||
|
|||
interface TagToken extends DerivativeToken { |
|||
tagFontSize: number; |
|||
tagLineHeight: React.CSSProperties['lineHeight']; |
|||
tagDefaultBg: string; |
|||
tagDefaultColor: string; |
|||
} |
|||
|
|||
// ============================== Styles ==============================
|
|||
|
|||
type CssVariableType = 'success' | 'info' | 'error' | 'warning'; |
|||
|
|||
const genTagStatusStyle = ( |
|||
prefixCls: string, |
|||
token: TagToken, |
|||
status: 'success' | 'processing' | 'error' | 'warning', |
|||
cssVariableType: CssVariableType, |
|||
): CSSInterpolation => { |
|||
const capitalizedCssVariableType = capitalize<CssVariableType>(cssVariableType); |
|||
return { |
|||
[`.${prefixCls}-${status}`]: { |
|||
color: token[`${cssVariableType}Color`], |
|||
background: token[`tmp${capitalizedCssVariableType}ColorDeprecatedBg`], |
|||
borderColor: token[`tmp${capitalizedCssVariableType}ColorDeprecatedBorder`], |
|||
}, |
|||
}; |
|||
}; |
|||
|
|||
// FIXME: special preset colors
|
|||
const genTagColorStyle = (prefixCls: string, token: TagToken): CSSInterpolation => |
|||
PresetColorKeys.reduce((prev: CSSObject, colorKey: keyof PresetColorType) => { |
|||
const lightColor = token[`${colorKey}-1`]; |
|||
const lightBorderColor = token[`${colorKey}-3`]; |
|||
const darkColor = token[`${colorKey}-6`]; |
|||
const textColor = token[`${colorKey}-7`]; |
|||
return { |
|||
...prev, |
|||
[`.${prefixCls}-${colorKey}`]: { |
|||
color: textColor, |
|||
background: lightColor, |
|||
borderColor: lightBorderColor, |
|||
}, |
|||
[`.${prefixCls}-${colorKey}-inverse`]: { |
|||
color: token.textColorInverse, |
|||
background: darkColor, |
|||
borderColor: darkColor, |
|||
}, |
|||
}; |
|||
}, {} as CSSObject); |
|||
|
|||
const genBaseStyle = ( |
|||
prefixCls: string, |
|||
iconPrefixCls: string, |
|||
token: TagToken, |
|||
): CSSInterpolation => ({ |
|||
// Result
|
|||
[`.${prefixCls}`]: { |
|||
...resetComponent(token), |
|||
display: 'inline-block', |
|||
height: 'auto', |
|||
marginInlineStart: token.marginXS, |
|||
// FIXME: hard code
|
|||
padding: '0 7px', |
|||
fontSize: token.tagFontSize, |
|||
lineHeight: token.tagLineHeight, |
|||
whiteSpace: 'nowrap', |
|||
background: token.tagDefaultBg, |
|||
border: `${token.borderWidth}px ${token.borderStyle} ${token.borderColor}`, |
|||
borderRadius: token.borderRadius, |
|||
// FIXME: hard code
|
|||
opacity: 1, |
|||
transition: `all ${token.duration}`, |
|||
|
|||
// RTL
|
|||
'&&-rtl': { |
|||
direction: 'rtl', |
|||
textAlign: 'right', |
|||
}, |
|||
|
|||
'&, a, a:hover': { |
|||
color: token.tagDefaultColor, |
|||
}, |
|||
|
|||
[`.${prefixCls}-close-icon`]: { |
|||
// FIXME: hard code
|
|||
marginInlineStart: 3, |
|||
color: token.textColorSecondary, |
|||
// FIXME: hard code
|
|||
fontSize: 10, |
|||
cursor: 'pointer', |
|||
transition: `all ${token.duration}`, |
|||
|
|||
'&:hover': { |
|||
color: token.headingColor, |
|||
}, |
|||
}, |
|||
|
|||
[`&&-has-color`]: { |
|||
borderColor: 'transparent', |
|||
|
|||
[`&, a, a:hover, .${iconPrefixCls}-close, .${iconPrefixCls}-close:hover`]: { |
|||
color: token.textColorInverse, |
|||
}, |
|||
}, |
|||
|
|||
[`.${prefixCls}-checkable`]: { |
|||
backgroundColor: 'transparent', |
|||
borderColor: 'transparent', |
|||
cursor: 'pointer', |
|||
|
|||
'&:not(&-checked):hover': { |
|||
color: token.primaryColor, |
|||
}, |
|||
|
|||
'&:active, &-checked': { |
|||
color: token.textColorInverse, |
|||
}, |
|||
|
|||
'&-checked': { |
|||
backgroundColor: token.tmpPrimaryColor6, |
|||
}, |
|||
|
|||
'&:active': { |
|||
backgroundColor: token.tmpPrimaryColor7, |
|||
}, |
|||
}, |
|||
|
|||
[`.${prefixCls}-hidden`]: { |
|||
display: 'none', |
|||
}, |
|||
|
|||
// To ensure that a space will be placed between character and `Icon`.
|
|||
[`> .${iconPrefixCls} + span, > span + .${iconPrefixCls}`]: { |
|||
// FIXME: hard code
|
|||
marginInlineStart: 7, |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
export const genTagStyle = ( |
|||
prefixCls: string, |
|||
iconPrefixCls: string, |
|||
token: DerivativeToken, |
|||
): CSSInterpolation => { |
|||
const tagFontSize = token.fontSizeSM; |
|||
// FIXME: hard code
|
|||
const tagLineHeight = '18px'; |
|||
const tagDefaultBg = token.backgroundLight; |
|||
const tagDefaultColor = token.textColor; |
|||
|
|||
const tagToken = { |
|||
...token, |
|||
tagFontSize, |
|||
tagLineHeight, |
|||
tagDefaultBg, |
|||
tagDefaultColor, |
|||
}; |
|||
|
|||
return [ |
|||
genBaseStyle(prefixCls, iconPrefixCls, tagToken), |
|||
genTagColorStyle(prefixCls, tagToken), |
|||
genTagStatusStyle(prefixCls, tagToken, 'success', 'success'), |
|||
genTagStatusStyle(prefixCls, tagToken, 'processing', 'info'), |
|||
genTagStatusStyle(prefixCls, tagToken, 'error', 'error'), |
|||
genTagStatusStyle(prefixCls, tagToken, 'warning', 'warning'), |
|||
]; |
|||
}; |
|||
|
|||
// ============================== Export ==============================
|
|||
export function getStyle(prefixCls: string, iconPrefixCls: string, token: DerivativeToken) { |
|||
return [genTagStyle(prefixCls, iconPrefixCls, token)]; |
|||
} |
|||
|
|||
export default function useStyle( |
|||
prefixCls: string, |
|||
iconPrefixCls: string, |
|||
): UseComponentStyleResult { |
|||
const [theme, token, hashId] = useToken(); |
|||
|
|||
return [ |
|||
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => |
|||
getStyle(prefixCls, iconPrefixCls, token), |
|||
), |
|||
hashId, |
|||
]; |
|||
} |
|||
|
Loading…
Reference in new issue