soapbox/app/soapbox/components/ui/card/card.tsx

105 wiersze
3.0 KiB
TypeScript
Czysty Zwykły widok Historia

2023-02-06 18:01:03 +00:00
import clsx from 'clsx';
2022-03-21 18:09:01 +00:00
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { HStack, Text } from 'soapbox/components/ui';
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
2022-03-24 23:56:22 +00:00
2022-03-21 18:09:01 +00:00
const sizes = {
md: 'p-4 sm:rounded-xl',
lg: 'p-4 sm:p-6 sm:rounded-xl',
xl: 'p-4 sm:p-10 sm:rounded-3xl',
};
const messages = defineMessages({
back: { id: 'card.back.label', defaultMessage: 'Back' },
});
interface ICard {
/** The type of card. */
2022-10-04 19:08:22 +00:00
variant?: 'default' | 'rounded'
/** Card size preset. */
2022-10-04 19:08:22 +00:00
size?: keyof typeof sizes
/** Extra classnames for the <div> element. */
2022-10-04 19:08:22 +00:00
className?: string
/** Elements inside the card. */
2022-10-04 19:08:22 +00:00
children: React.ReactNode
2022-03-21 18:09:01 +00:00
}
/** An opaque backdrop to hold a collection of related elements. */
2022-08-12 17:58:35 +00:00
const Card = React.forwardRef<HTMLDivElement, ICard>(({ children, variant = 'default', size = 'md', className, ...filteredProps }, ref): JSX.Element => (
2022-03-21 18:09:01 +00:00
<div
ref={ref}
{...filteredProps}
2023-02-06 18:01:03 +00:00
className={clsx({
2022-11-27 17:50:55 +00:00
'bg-white dark:bg-primary-900 text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-none overflow-hidden': variant === 'rounded',
[sizes[size]]: variant === 'rounded',
}, className)}
2022-03-21 18:09:01 +00:00
>
{children}
</div>
));
interface ICardHeader {
backHref?: string
2022-03-21 18:09:01 +00:00
onBackClick?: (event: React.MouseEvent) => void
2022-11-30 17:19:16 +00:00
className?: string
2023-01-10 23:03:15 +00:00
children?: React.ReactNode
2022-03-21 18:09:01 +00:00
}
/**
* Card header container with back button.
* Typically holds a CardTitle.
*/
2022-11-30 17:19:16 +00:00
const CardHeader: React.FC<ICardHeader> = ({ className, children, backHref, onBackClick }): JSX.Element => {
2022-03-21 18:09:01 +00:00
const intl = useIntl();
const renderBackButton = () => {
if (!backHref && !onBackClick) {
return null;
}
const Comp: React.ElementType = backHref ? Link : 'button';
const backAttributes = backHref ? { to: backHref } : { onClick: onBackClick };
return (
2023-02-01 22:13:42 +00:00
<Comp {...backAttributes} className='text-gray-900 focus:ring-2 focus:ring-primary-500 dark:text-gray-100' aria-label={intl.formatMessage(messages.back)}>
2022-12-05 01:40:09 +00:00
<SvgIcon src={require('@tabler/icons/arrow-left.svg')} className='h-6 w-6 rtl:rotate-180' />
2022-05-07 17:04:28 +00:00
<span className='sr-only' data-testid='back-button'>{intl.formatMessage(messages.back)}</span>
2022-03-21 18:09:01 +00:00
</Comp>
);
};
return (
2023-02-06 18:01:03 +00:00
<HStack alignItems='center' space={2} className={clsx('mb-4', className)}>
2022-03-21 18:09:01 +00:00
{renderBackButton()}
{children}
</HStack>
2022-03-21 18:09:01 +00:00
);
};
interface ICardTitle {
title: React.ReactNode
2022-03-21 18:09:01 +00:00
}
/** A card's title. */
2022-05-01 18:29:36 +00:00
const CardTitle: React.FC<ICardTitle> = ({ title }): JSX.Element => (
<Text size='xl' weight='bold' tag='h1' data-testid='card-title' truncate>{title}</Text>
2022-03-21 18:09:01 +00:00
);
interface ICardBody {
/** Classnames for the <div> element. */
className?: string
2023-01-10 23:03:15 +00:00
/** Children to appear inside the card. */
children: React.ReactNode
}
/** A card's body. */
const CardBody: React.FC<ICardBody> = ({ className, children }): JSX.Element => (
<div data-testid='card-body' className={className}>{children}</div>
2022-03-21 18:09:01 +00:00
);
export { Card, CardHeader, CardTitle, CardBody };