UNPKG

@spaced-out/ui-design-system

Version:
175 lines (156 loc) 3.98 kB
// @flow strict import * as React from 'react'; import * as SPACES from '../../styles/variables/_space'; import classify from '../../utils/classify'; import {capitalize} from '../../utils/string'; import css from './Card.module.css'; type ClassNames = $ReadOnly<{wrapper?: string}>; export const PADDING_SIZES = Object.freeze({ small: 'small', medium: 'medium', large: 'large', none: 'none', }); export type PaddingSizeType = $Values<typeof PADDING_SIZES>; export type CardProps = { classNames?: ClassNames, children: React.Node, paddingTop?: PaddingSizeType, paddingRight?: PaddingSizeType, paddingBottom?: PaddingSizeType, paddingLeft?: PaddingSizeType, ... }; export type CardChildProps = { className?: string, children: React.Node, ... }; export type ClickableCardProps = { ...CardProps, onClick?: ?(SyntheticEvent<HTMLElement>) => mixed, disabled?: boolean, ... }; const getPaddingValue = (size: PaddingSizeType): string => { const spaceKey = 'space' + capitalize(size); return SPACES[spaceKey] || SPACES['spaceNone']; }; export const CardHeader = ({ className, children, ...props }: CardChildProps): React.Node => ( <div {...props} className={classify(css.cardHeader, className)}> {children} </div> ); export const CardTitle = ({ className, children, ...props }: CardChildProps): React.Node => ( <div {...props} className={classify(css.cardTitle, className)}> {children} </div> ); export const CardActions = ({ className, children, ...props }: CardChildProps): React.Node => ( <div {...props} className={classify(css.cardActions, className)}> {children} </div> ); export const CardFooter = ({ className, children, ...props }: CardChildProps): React.Node => ( <div {...props} className={classify(css.cardFooter, className)}> {children} </div> ); export const CardContent = ({ className, children, ...props }: CardChildProps): React.Node => ( <div {...props} className={classify(css.cardContent, className)}> {children} </div> ); export const Card: React$AbstractComponent<CardProps, HTMLDivElement> = React.forwardRef<CardProps, HTMLDivElement>( ( { children, classNames, paddingTop = 'medium', paddingRight = 'medium', paddingBottom = 'medium', paddingLeft = 'medium', ...props }: CardProps, ref, ): React.Node => ( <div {...props} style={{ '--card-padding-top': getPaddingValue(paddingTop), '--card-padding-right': getPaddingValue(paddingRight), '--card-padding-bottom': getPaddingValue(paddingBottom), '--card-padding-left': getPaddingValue(paddingLeft), }} data-testid="Card" className={classify(css.cardWrapper, classNames?.wrapper)} ref={ref} > {children} </div> ), ); export const ClickableCard: React$AbstractComponent< ClickableCardProps, HTMLDivElement, > = React.forwardRef<ClickableCardProps, HTMLDivElement>( ( {classNames, onClick, disabled = false, ...props}: ClickableCardProps, ref, ): React.Node => { const componentRef = React.useRef(null); React.useImperativeHandle(ref, () => componentRef.current); React.useEffect(() => { if (disabled) { componentRef.current?.blur(); } }, [disabled]); const onClickHandler = (e) => { if (!disabled) { onClick && onClick(e); } }; const onKeyDownHandler = (e) => { if (e.key === 'Enter') { onClickHandler(e); } }; return ( <Card {...props} classNames={{ wrapper: classify( css.clickableCard, {[css.disabled]: disabled}, classNames?.wrapper, ), }} tabIndex={disabled ? -1 : 0} onClick={onClickHandler} onKeyDown={onKeyDownHandler} ref={ref} /> ); }, );