@spaced-out/ui-design-system
Version:
Sense UI components library
175 lines (156 loc) • 3.98 kB
Flow
// @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}
/>
);
},
);