Merge pull request #158 from unix/badge

feat(badge): supoort to anchor with other components
This commit is contained in:
witt
2020-04-30 22:57:51 +08:00
committed by GitHub
9 changed files with 610 additions and 29 deletions

View File

@@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BadgeAnchor should be support multiple position 1`] = `
"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
span {
display: inline-block;
padding: 4px 7px;
border-radius: 16px;
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #000;
color: #fff;
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
</style></span></sup><style>
.anchor {
position: relative;
display: inline-flex;
vertical-align: middle;
flex-shrink: 0;
box-sizing: border-box;
}
sup {
position: absolute;
top: 0;
left: auto;
right: 0;
bottom: auto;
transform: translate(50%, -50%);
transform-origin: 100% 0%;
z-index: 1;
}
</style></div>"
`;
exports[`BadgeAnchor should be support multiple position 2`] = `
"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
span {
display: inline-block;
padding: 4px 7px;
border-radius: 16px;
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #000;
color: #fff;
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
</style></span></sup><style>
.anchor {
position: relative;
display: inline-flex;
vertical-align: middle;
flex-shrink: 0;
box-sizing: border-box;
}
sup {
position: absolute;
top: 0;
left: 0;
right: auto;
bottom: auto;
transform: translate(-50%, -50%);
transform-origin: 0% 0%;
z-index: 1;
}
</style></div>"
`;
exports[`BadgeAnchor should be support multiple position 3`] = `
"<div class=\\"anchor\\"><button>btn</button><sup><span class=\\" \\">test<style>
span {
display: inline-block;
padding: 4px 7px;
border-radius: 16px;
font-variant: tabular-nums;
line-height: 1;
vertical-align: middle;
background-color: #000;
color: #fff;
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
</style></span></sup><style>
.anchor {
position: relative;
display: inline-flex;
vertical-align: middle;
flex-shrink: 0;
box-sizing: border-box;
}
sup {
position: absolute;
top: auto;
left: auto;
right: 0;
bottom: 0;
transform: translate(50%, 50%);
transform-origin: 100% 100%;
z-index: 1;
}
</style></div>"
`;

View File

@@ -11,7 +11,7 @@ initialize {
"children": Array [
Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -33,6 +33,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -70,6 +75,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -97,7 +107,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -119,6 +129,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -156,6 +171,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -183,7 +203,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -205,6 +225,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -242,6 +267,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -269,7 +299,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -291,6 +321,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -328,6 +363,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -396,7 +436,7 @@ initialize {
},
Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -418,6 +458,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -455,6 +500,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -482,7 +532,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -504,6 +554,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -541,6 +596,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -568,7 +628,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -590,6 +650,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -627,6 +692,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -676,7 +746,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -698,6 +768,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -735,6 +810,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -781,7 +861,7 @@ initialize {
},
Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -803,6 +883,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -840,6 +925,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -867,7 +957,7 @@ initialize {
"namespace": "http://www.w3.org/1999/xhtml",
"next": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -889,6 +979,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -926,6 +1021,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -965,7 +1065,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -987,6 +1087,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1024,6 +1129,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1053,7 +1163,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1075,6 +1185,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1112,6 +1227,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1166,7 +1286,7 @@ initialize {
},
Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1188,6 +1308,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1225,6 +1350,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1254,7 +1384,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1276,6 +1406,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1313,6 +1448,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1342,7 +1482,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1364,6 +1504,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1401,6 +1546,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1430,7 +1580,7 @@ initialize {
"parent": [Circular],
"prev": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1452,6 +1602,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1489,6 +1644,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1588,7 +1748,7 @@ exports[`Badge should supoort text 1`] = `
initialize {
"0": Object {
"attribs": Object {
"class": "",
"class": " ",
},
"children": Array [
Object {
@@ -1610,6 +1770,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],
@@ -1647,6 +1812,11 @@ initialize {
font-size: .875rem;
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
",
"next": null,
"parent": [Circular],

View File

@@ -0,0 +1,40 @@
import React from 'react'
import { mount } from 'enzyme'
import { Badge } from 'components'
describe('BadgeAnchor', () => {
it('should render correctly', () => {
const wrapper = mount(
<Badge.Anchor>
<Badge>test</Badge>
<a>link</a>
</Badge.Anchor>
)
expect(() => wrapper.unmount()).not.toThrow()
})
it('should be work without Badge', () => {
const wrapper = mount(
<Badge.Anchor>
<a>link</a>
</Badge.Anchor>
)
expect(() => wrapper.unmount()).not.toThrow()
})
it('should be support multiple position', () => {
const wrapper = mount(
<Badge.Anchor>
<Badge>test</Badge>
<button>btn</button>
</Badge.Anchor>
)
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ placement: 'topLeft' })
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ placement: 'bottomRight' })
expect(wrapper.html()).toMatchSnapshot()
})
})

View File

@@ -52,4 +52,10 @@ describe('Badge', () => {
expect(span.props().style).not.toBeUndefined()
expect((span.props().style as any).background).toBe('white')
})
it('should hide content when in dot mode', () => {
const wrapper = mount(<Badge dot>test-value</Badge>)
expect(wrapper.html()).not.toContain('test-value')
expect(() => wrapper.unmount()).not.toThrow()
})
})

View File

@@ -0,0 +1,102 @@
import React, { useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import { pickChild } from '../utils/collections'
import { tuple } from '../utils/prop-types'
import Badge from './badge'
const placement = tuple(
'topLeft', 'topRight', 'bottomLeft', 'bottomRight',
)
type BadgeAnchorPlacement = typeof placement[number]
interface Props {
placement?: BadgeAnchorPlacement
className?: string
}
const defaultProps = {
placement: 'topRight' as BadgeAnchorPlacement,
className: '',
}
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
export type BadgeAnchorProps = Props & typeof defaultProps & NativeAttrs
type TransformStyles = {
top?: string
bottom?: string
left?: string
right?: string
value: string
origin: string
}
const getTransform = (placement: BadgeAnchorPlacement): TransformStyles => {
const styles: { [key in BadgeAnchorPlacement]: TransformStyles } = {
topLeft: {
top: '0', left: '0',
value: 'translate(-50%, -50%)',
origin: '0% 0%',
},
topRight: {
top: '0', right: '0',
value: 'translate(50%, -50%)',
origin: '100% 0%',
},
bottomLeft: {
left: '0', bottom: '0',
value: 'translate(-50%, 50%)',
origin: '0% 100%',
},
bottomRight: {
right: '0', bottom: '0',
value: 'translate(50%, 50%)',
origin: '100% 100%',
}
}
return styles[placement]
}
const BadgeAnchor: React.FC<React.PropsWithChildren<BadgeAnchorProps>> = ({
children, placement,
}) => {
const [withoutBadgeChildren, badgeChldren] = pickChild(children, Badge)
const {
top, bottom, left, right, value, origin,
} = useMemo(() => getTransform(placement), [placement])
return (
<div className="anchor">
{withoutBadgeChildren}
<sup>
{badgeChldren}
</sup>
<style jsx>{`
.anchor {
position: relative;
display: inline-flex;
vertical-align: middle;
flex-shrink: 0;
box-sizing: border-box;
}
sup {
position: absolute;
top: ${top || 'auto'};
left: ${left || 'auto'};
right: ${right || 'auto'};
bottom: ${bottom || 'auto'};
transform: ${value};
transform-origin: ${origin};
z-index: 1;
}
`}</style>
</div>
)
}
const MemoBadgeAnchor = React.memo(BadgeAnchor)
export default withDefaults(MemoBadgeAnchor, defaultProps)

View File

@@ -1,18 +1,20 @@
import React, { useMemo } from 'react'
import withDefaults from '../utils/with-defaults'
import useTheme from '../styles/use-theme'
import { NormalSizes, NormalTypes } from '../utils/prop-types'
import { ZeitUIThemesPalette } from 'components/styles/themes'
import BadgeAnchor from './badge-anchor'
interface Props {
type?: NormalTypes
size?: NormalSizes
dot?: boolean
className?: string
}
const defaultProps = {
type: 'default' as NormalTypes,
size: 'medium' as NormalSizes,
dot: false,
className: '',
}
@@ -41,7 +43,7 @@ const getBgColor = (type: NormalTypes, palette: ZeitUIThemesPalette) => {
}
const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
type, size, className, children, ...props
type, size, className, children, dot, ...props
}) => {
const theme = useTheme()
const bg = useMemo(() => getBgColor(type, theme.palette), [type, theme.palette])
@@ -52,8 +54,8 @@ const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
}, [type, theme.palette.background])
return (
<span className={className} {...props}>
{children}
<span className={`${dot ? 'dot' : ''} ${className}`} {...props}>
{!dot && children}
<style jsx>{`
span {
display: inline-block;
@@ -67,11 +69,21 @@ const Badge: React.FC<React.PropsWithChildren<BadgeProps>> = ({
font-size: ${font};
border: 0;
}
.dot {
padding: 4px;
border-radius: 50%;
}
`}</style>
</span>
)
}
const MemoBadge = React.memo<React.PropsWithChildren<BadgeProps>>(Badge)
type MemoBadgeComponent<P = {}> = React.NamedExoticComponent<P> & {
Anchor: typeof BadgeAnchor
}
type ComponentProps = Partial<typeof defaultProps> & Omit<Props, keyof typeof defaultProps> & NativeAttrs
export default withDefaults(MemoBadge, defaultProps)
Badge.defaultProps = defaultProps
export default React.memo(Badge) as MemoBadgeComponent<ComponentProps>

View File

@@ -1,3 +1,6 @@
import Badge from './badge'
import BadgeAnchor from './badge-anchor'
Badge.Anchor = BadgeAnchor
export default Badge

View File

@@ -1,5 +1,5 @@
import { Layout, Playground, Attributes } from 'lib/components'
import { Badge, Spacer } from 'components'
import { Badge, Spacer, Avatar, Button, Link } from 'components'
export const meta = {
title: 'Badge',
@@ -48,16 +48,78 @@ Display an indicator that requires attention.
</>
`} />
<Playground
title="Anchor"
desc="Fix the `Badge` in the designated position."
scope={{ Badge, Avatar, Spacer, Button, Link }}
code={`
<>
<Badge.Anchor>
<Badge size="mini">10</Badge>
<Avatar src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor placement="bottomRight">
<Badge size="mini" type="success">10</Badge>
<Avatar size={40} isSquare src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="warning">99+</Badge>
<Button size="small" auto>Action</Button>
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="error" dot />
<Link pure target="_blank" href="https://github.com/zeit-ui/react/">ZEIT UI</Link>
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="error" dot style={{ padding: '7px' }} />
<Link pure target="_blank" href="https://github.com/zeit-ui/react/">Share Link</Link>
</Badge.Anchor>
</>
`} />
<Attributes edit="/pages/en-us/components/badge.mdx">
<Attributes.Title>Badge.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **type** | badge type | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
| **size** | badge size | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
| **type** | badge type | `NormalTypes` | [NormalTypes](#normaltypes) | `default` |
| **size** | badge size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
| **dot** | show dot and ignore content | `boolean` | - | `false` |
| ... | native props | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
<Attributes.Title>Badge.Anchor.Props</Attributes.Title>
| Attribute | Description | Type | Accepted values | Default
| ---------- | ---------- | ---- | -------------- | ------ |
| **placement** | fixe position of Badge | `AnchorPlacement` | [AnchorPlacement](#anchorplacement) | `topRight` |
| ... | native props | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
<Attributes.Title>NormalTypes</Attributes.Title>
```ts
type NormalTypes = 'default'
| 'secondary'
| 'success'
| 'warning'
| 'error'
```
<Attributes.Title>NormalSizes</Attributes.Title>
```ts
type NormalSizes = 'medium' | 'mini' | 'small' | 'large'
```
<Attributes.Title>AnchorPlacement</Attributes.Title>
```ts
type AnchorPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
```
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>

View File

@@ -1,5 +1,5 @@
import { Layout, Playground, Attributes } from 'lib/components'
import { Badge, Spacer } from 'components'
import { Badge, Spacer, Button, Link, Avatar } from 'components'
export const meta = {
title: '徽章 Badge',
@@ -48,16 +48,78 @@ export const meta = {
</>
`} />
<Playground
title="锚点"
desc="将徽章固定在指定位置。"
scope={{ Badge, Avatar, Spacer, Button, Link }}
code={`
<>
<Badge.Anchor>
<Badge size="mini">10</Badge>
<Avatar src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor placement="bottomRight">
<Badge size="mini" type="success">10</Badge>
<Avatar size={40} isSquare src="https://zeit.co/api/www/avatar/?u=evilrabbit&s=160" />
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="warning">99+</Badge>
<Button size="small" auto>按钮</Button>
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="error" dot />
<Link pure target="_blank" href="https://github.com/zeit-ui/react/">组件库</Link>
</Badge.Anchor>
<Spacer inline x={1.5} />
<Badge.Anchor>
<Badge size="mini" type="error" dot style={{ padding: '7px' }} />
<Link pure target="_blank" href="https://github.com/zeit-ui/react/">分享链接</Link>
</Badge.Anchor>
</>
`} />
<Attributes edit="/pages/zh-cn/components/badge.mdx">
<Attributes.Title>Badge.Props</Attributes.Title>
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| **type** | 徽章的类型 | `NormalTypes` | `'default', 'secondary', 'success', 'warning', 'error'` | `default` |
| **size** | 徽章的大小 | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` |
| **type** | 徽章的类型 | `NormalTypes` | [NormalTypes](#normaltypes) | `default` |
| **size** | 徽章的大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` |
| **dot** | 忽略内容并显示圆点 | `boolean` | - | `false` |
| ... | 原生属性 | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
<Attributes.Title>Badge.Anchor.Props</Attributes.Title>
| 属性 | 描述 | 类型 | 推荐值 | 默认
| ---------- | ---------- | ---- | -------------- | ------ |
| **placement** | 固定徽章的位置 | `AnchorPlacement` | [AnchorPlacement](#anchorplacement) | `topRight` |
| ... | 原生属性 | `HTMLAttributes` | `'alt', 'id', 'className', ...` | - |
<Attributes.Title>NormalTypes</Attributes.Title>
```ts
type NormalTypes = 'default'
| 'secondary'
| 'success'
| 'warning'
| 'error'
```
<Attributes.Title>NormalSizes</Attributes.Title>
```ts
type NormalSizes = 'medium' | 'mini' | 'small' | 'large'
```
<Attributes.Title>AnchorPlacement</Attributes.Title>
```ts
type AnchorPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
```
</Attributes>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>