mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-24 04:15:54 +08:00
feat(card): add footer component
This commit is contained in:
52
components/card/card-footer.tsx
Normal file
52
components/card/card-footer.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react'
|
||||
import useTheme from '../styles/use-theme'
|
||||
import withDefaults from '../utils/with-defaults'
|
||||
|
||||
interface Props {
|
||||
disableAutoMargin?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const defaultProps = {
|
||||
disableAutoMargin: false,
|
||||
className: '',
|
||||
}
|
||||
|
||||
type NativeAttrs = Omit<React.HTMLAttributes<any>, keyof Props>
|
||||
export type CardFooterProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const CardFooter: React.FC<React.PropsWithChildren<CardFooterProps>> = ({
|
||||
children, className, disableAutoMargin, ...props
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<footer className={`${disableAutoMargin ? '' : 'auto-margin'} ${className}`}
|
||||
{...props}>
|
||||
{children}
|
||||
<style jsx>{`
|
||||
footer {
|
||||
padding: ${theme.layout.gapHalf} ${theme.layout.gap};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
font-size: .875rem;
|
||||
border-top: 1px solid ${theme.palette.border};
|
||||
border-bottom-left-radius: ${theme.layout.radius};
|
||||
border-bottom-right-radius: ${theme.layout.radius};
|
||||
min-height: calc(2.5 * ${theme.layout.gap});
|
||||
}
|
||||
|
||||
.auto-margin :global(*) {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-right: ${theme.layout.gapQuarter};
|
||||
}
|
||||
`}</style>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default withDefaults(CardFooter, defaultProps)
|
||||
@@ -1,13 +1,16 @@
|
||||
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'
|
||||
import CardFooter from './card-footer'
|
||||
import Image from '../image'
|
||||
import { pickChild } from '../utils/collections'
|
||||
|
||||
interface Props {
|
||||
hoverable?: boolean
|
||||
shadow?: boolean
|
||||
className?: string
|
||||
width?: string
|
||||
type?: CardTypes
|
||||
}
|
||||
|
||||
@@ -15,6 +18,7 @@ const defaultProps = {
|
||||
type: 'default' as CardTypes,
|
||||
hoverable: false,
|
||||
shadow: false,
|
||||
width: '100%',
|
||||
className: '',
|
||||
}
|
||||
|
||||
@@ -22,7 +26,7 @@ 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, type, ...props
|
||||
children, hoverable, className, shadow, type, width, ...props
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const hoverShadow = useMemo(() => {
|
||||
@@ -34,16 +38,25 @@ const Card: React.FC<React.PropsWithChildren<CardProps>> = React.memo(({
|
||||
[type, theme.palette, shadow],
|
||||
)
|
||||
|
||||
const [withoutFooterChildren, footerChildren] = pickChild(children, CardFooter)
|
||||
const [withoutImageChildren, imageChildren] = pickChild(withoutFooterChildren, Image)
|
||||
|
||||
|
||||
return (
|
||||
<div className={`card ${className}`} {...props}>
|
||||
{children}
|
||||
{imageChildren}
|
||||
<div className="content">
|
||||
{withoutImageChildren}
|
||||
</div>
|
||||
{footerChildren}
|
||||
|
||||
<style jsx>{`
|
||||
.card {
|
||||
background: ${theme.palette.background};
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
width: ${width};
|
||||
transition: all .2s ease;
|
||||
padding: ${theme.layout.gap} ${theme.layout.gap};
|
||||
border-radius: ${theme.layout.radius};
|
||||
box-shadow: ${shadow ? theme.expressiveness.shadowSmall : 'none'};
|
||||
box-sizing: border-box;
|
||||
@@ -52,6 +65,11 @@ const Card: React.FC<React.PropsWithChildren<CardProps>> = React.memo(({
|
||||
border: 1px solid ${borderColor};
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
padding: ${theme.layout.gap} ${theme.layout.gap};
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: ${hoverShadow};
|
||||
}
|
||||
@@ -63,9 +81,27 @@ const Card: React.FC<React.PropsWithChildren<CardProps>> = React.memo(({
|
||||
.card :global(*:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card :global(img) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card :global(.image) {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default withDefaults(Card, defaultProps)
|
||||
type CardComponent<P = {}> = React.FC<P> & {
|
||||
Footer: typeof CardFooter
|
||||
Actions: typeof CardFooter
|
||||
}
|
||||
|
||||
type ComponentProps = Partial<typeof defaultProps> & Omit<Props, keyof typeof defaultProps> & NativeAttrs
|
||||
|
||||
(Card as CardComponent<ComponentProps>).defaultProps = defaultProps
|
||||
|
||||
export default Card as CardComponent<ComponentProps>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import Card from './card'
|
||||
import CardFooter from './card-footer'
|
||||
|
||||
Card.Footer = CardFooter
|
||||
Card.Actions = CardFooter
|
||||
|
||||
export default Card
|
||||
|
||||
Reference in New Issue
Block a user