mirror of
https://github.com/zhigang1992/react.git
synced 2026-05-11 23:48:45 +08:00
feat(card): append types to card
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import { CardTypes } from '../utils/prop-types'
|
||||
import { getStyles } from './styles'
|
||||
|
||||
interface Props {
|
||||
hoverable?: boolean
|
||||
shadow?: boolean
|
||||
className?: string
|
||||
type?: CardTypes
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
type: 'default' as CardTypes,
|
||||
hoverable: false,
|
||||
shadow: false,
|
||||
className: '',
|
||||
@@ -18,13 +22,17 @@ type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type CardProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const Card: React.FC<React.PropsWithChildren<CardProps>> = React.memo(({
|
||||
children, hoverable, className, shadow, ...props
|
||||
children, hoverable, className, shadow, type, ...props
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const hoverShadow = useMemo(() => {
|
||||
if (shadow) return theme.expressiveness.shadowMedium
|
||||
return hoverable ? theme.expressiveness.shadowSmall : 'none'
|
||||
}, [hoverable, shadow, theme.expressiveness])
|
||||
const { color, bgColor, borderColor } = useMemo(
|
||||
() => getStyles(type, theme.palette, shadow),
|
||||
[type, theme.palette, shadow],
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={`card ${className}`} {...props}>
|
||||
@@ -37,19 +45,23 @@ const Card: React.FC<React.PropsWithChildren<CardProps>> = React.memo(({
|
||||
transition: all .2s ease;
|
||||
padding: ${theme.layout.gap} ${theme.layout.gap};
|
||||
border-radius: ${theme.layout.radius};
|
||||
border: 1px solid ${shadow ? 'transparent' : theme.palette.border};
|
||||
box-shadow: ${shadow ? theme.expressiveness.shadowSmall : 'none'};
|
||||
box-sizing: border-box;
|
||||
color: ${color};
|
||||
background-color: ${bgColor};
|
||||
border: 1px solid ${borderColor};
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: ${hoverShadow};
|
||||
}
|
||||
|
||||
.card :global(p), .card :global(h1), .card :global(h2),
|
||||
.card :global(h3), .card :global(h4), .card :global(h5),
|
||||
.card :global(h6) {
|
||||
margin: 0;
|
||||
.card :global(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.card :global(*:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
|
||||
66
components/card/styles.ts
Normal file
66
components/card/styles.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { CardTypes } from '../utils/prop-types'
|
||||
import { ZeitUIThemesPalette } from '../styles/themes'
|
||||
|
||||
export type CardStyles = {
|
||||
color: string
|
||||
bgColor: string
|
||||
borderColor: string
|
||||
}
|
||||
|
||||
export const getStyles = (
|
||||
type: CardTypes,
|
||||
palette: ZeitUIThemesPalette,
|
||||
isShadow?: boolean,
|
||||
): CardStyles => {
|
||||
const colors: { [key in CardTypes]: Omit<CardStyles, 'borderColor'> } = {
|
||||
default: {
|
||||
color: palette.foreground,
|
||||
bgColor: palette.background,
|
||||
},
|
||||
dark: {
|
||||
color: palette.background,
|
||||
bgColor: palette.foreground,
|
||||
},
|
||||
secondary: {
|
||||
color: palette.background,
|
||||
bgColor: palette.secondary,
|
||||
},
|
||||
success: {
|
||||
color: palette.background,
|
||||
bgColor: palette.success,
|
||||
},
|
||||
warning: {
|
||||
color: palette.background,
|
||||
bgColor: palette.warning,
|
||||
},
|
||||
error: {
|
||||
color: palette.background,
|
||||
bgColor: palette.error,
|
||||
},
|
||||
lite: {
|
||||
color: palette.foreground,
|
||||
bgColor: palette.background,
|
||||
},
|
||||
alert: {
|
||||
color: 'white',
|
||||
bgColor: palette.alert,
|
||||
},
|
||||
purple: {
|
||||
color: 'white',
|
||||
bgColor: palette.purple,
|
||||
},
|
||||
violet: {
|
||||
color: 'white',
|
||||
bgColor: palette.violet,
|
||||
},
|
||||
cyan: {
|
||||
color: 'black',
|
||||
bgColor: palette.cyan,
|
||||
},
|
||||
}
|
||||
const showBorder = type === 'default' && !isShadow
|
||||
return {
|
||||
...colors[type],
|
||||
borderColor: showBorder ? palette.border : 'transparent',
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,20 @@ const snippetTypes = tuple(
|
||||
'lite',
|
||||
)
|
||||
|
||||
const cardTypes = tuple(
|
||||
'default',
|
||||
'secondary',
|
||||
'success',
|
||||
'warning',
|
||||
'error',
|
||||
'dark',
|
||||
'lite',
|
||||
'alert',
|
||||
'purple',
|
||||
'violet',
|
||||
'cyan',
|
||||
)
|
||||
|
||||
const copyTypes = tuple(
|
||||
'default',
|
||||
'slient',
|
||||
@@ -76,6 +90,8 @@ export type ThemeTypes = typeof themeTypes[number]
|
||||
|
||||
export type SnippetTypes = typeof snippetTypes[number]
|
||||
|
||||
export type CardTypes = typeof cardTypes[number]
|
||||
|
||||
export type CopyTypes = typeof copyTypes[number]
|
||||
|
||||
export type TriggerTypes = typeof triggerTypes[number]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Layout, Playground, Attributes } from 'lib/components'
|
||||
import { Card } from 'components'
|
||||
import { Card, Spacer, Row, Col } from 'components'
|
||||
|
||||
export const meta = {
|
||||
title: 'card',
|
||||
@@ -39,6 +39,37 @@ A common container component.
|
||||
</Card>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="Types"
|
||||
desc="Show different states with colors."
|
||||
scope={{ Card, Spacer, Row, Col }}
|
||||
code={`
|
||||
() => {
|
||||
const typeLeft = ['secondary', 'success', 'warning', 'error', 'dark']
|
||||
const typeRight = ['alert', 'purple', 'violet', 'cyan', 'lite']
|
||||
return (
|
||||
<>
|
||||
{typeLeft.map((left, index) => (
|
||||
<Row justify="space-around" key={left} style={{ marginBottom: '18px' }}>
|
||||
<Col span="10">
|
||||
<Card type={left}>
|
||||
<h4 style={{ textTransform: 'capitalize' }}>{left}</h4>
|
||||
<span>{left}</span>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Card type={typeRight[index]}>
|
||||
<h4 style={{ textTransform: 'capitalize' }}>{typeRight[index]}</h4>
|
||||
<span>{typeRight[index]}</span>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
`} />
|
||||
|
||||
<Attributes edit="/pages/en-us/components/card.mdx">
|
||||
<Attributes.Title>Card.Props</Attributes.Title>
|
||||
|
||||
@@ -46,8 +77,25 @@ A common container component.
|
||||
| ---------- | ---------- | ---- | -------------- | ------ |
|
||||
| **hoverable** | add effect on hover | `boolean` | - | `false` |
|
||||
| **shadow** | show shadow | `boolean` | - | `false` |
|
||||
| **type** | card type | [CardType](#cardtype) | - | `default` |
|
||||
| ... | native props | `HTMLAttributes` | `'className', ...` | - |
|
||||
|
||||
<Attributes.Title>CardType</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type CardType = 'default'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'dark'
|
||||
| 'lite'
|
||||
| 'alert'
|
||||
| 'purple'
|
||||
| 'violet'
|
||||
| 'cyan'
|
||||
```
|
||||
|
||||
</Attributes>
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Layout, Playground, Attributes } from 'lib/components'
|
||||
import { Card } from 'components'
|
||||
import { Card, Row, Col } from 'components'
|
||||
|
||||
export const meta = {
|
||||
title: '卡片 Card',
|
||||
@@ -10,7 +10,6 @@ export const meta = {
|
||||
|
||||
基础的组件容器。
|
||||
|
||||
|
||||
<Playground
|
||||
scope={{ Card }}
|
||||
code={`
|
||||
@@ -38,6 +37,41 @@ export const meta = {
|
||||
</Card>
|
||||
`} />
|
||||
|
||||
<Playground
|
||||
title="类型"
|
||||
desc="以各式色彩表达不同的类型或状态。"
|
||||
scope={{ Card, Row, Col }}
|
||||
code={`
|
||||
() => {
|
||||
const typeLeft = ['secondary', 'success', 'warning', 'error', 'dark']
|
||||
const typeRight = ['alert', 'purple', 'violet', 'cyan', 'lite']
|
||||
const locales = {
|
||||
secondary: '次要的', success: '成功', warning: '警告', error: '错误', dark: '暗黑',
|
||||
alert: '提示', purple: '紫色', violet: '紫罗兰', cyan: '青色', lite: '精简',
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{typeLeft.map((left, index) => (
|
||||
<Row justify="space-around" key={left} style={{ marginBottom: '18px' }}>
|
||||
<Col span="10">
|
||||
<Card type={left}>
|
||||
<h4 style={{ textTransform: 'capitalize' }}>{locales[left]}</h4>
|
||||
<span>{left}</span>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span="10">
|
||||
<Card type={typeRight[index]}>
|
||||
<h4 style={{ textTransform: 'capitalize' }}>{locales[typeRight[index]]}</h4>
|
||||
<span>{typeRight[index]}</span>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
`} />
|
||||
|
||||
<Attributes edit="/pages/zh-cn/components/card.mdx">
|
||||
<Attributes.Title>Card.Props</Attributes.Title>
|
||||
|
||||
@@ -45,8 +79,25 @@ export const meta = {
|
||||
| ---------- | ---------- | ---- | -------------- | ------ |
|
||||
| **hoverable** | 是否在悬停时增加阴影 | `boolean` | - | `false` |
|
||||
| **shadow** | 是否总是显示阴影 | `boolean` | - | `false` |
|
||||
| **type** | 卡片的类型 | [CardType](#cardtype) | - | `default` |
|
||||
| ... | 原生属性 | `HTMLAttributes` | `'className', ...` | - |
|
||||
|
||||
<Attributes.Title>CardType</Attributes.Title>
|
||||
|
||||
```ts
|
||||
type CardType = 'default'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| 'error'
|
||||
| 'dark'
|
||||
| 'lite'
|
||||
| 'alert'
|
||||
| 'purple'
|
||||
| 'violet'
|
||||
| 'cyan'
|
||||
```
|
||||
|
||||
</Attributes>
|
||||
|
||||
export default ({ children }) => <Layout meta={meta}>{children}</Layout>
|
||||
|
||||
Reference in New Issue
Block a user