import { Card, PolymorphicRef } from '@cloud-ru/ds-card'; import { PromoTag, PromoTagProps } from '@cloud-ru/ds-promo-tag'; import { Favourite } from '@cloud-ru/ds-toggles'; import { TruncateString } from '@cloud-ru/ds-truncate-string'; import { Typography } from '@cloud-ru/ds-typography'; import { useValueControl, withInnerRefSupport } from '@cloud-ru/ds-utils'; import cn from 'classnames'; import mergeRefs from 'merge-refs'; import { ComponentPropsWithoutRef, ElementType, JSXElementConstructor, KeyboardEventHandler, MouseEventHandler, ReactElement, useCallback, useRef, } from 'react'; import { TEST_IDS, VISIBILITY_STRATEGY } from '../../constants'; import { FavoriteProps, VisibilityStrategy } from '../../types'; import styles from './styles.module.scss'; type BaseCardServiceSmallProps = { /** Заголовок карточки */ title: string; /** * Эмблема: объект с иконкой-компонентом. * Принимает `{ icon: JSXElementConstructor }` или произвольный ReactElement. */ emblem: { icon: JSXElementConstructor<{ className?: string; size?: number }> } | ReactElement; /** Настройки обрезки заголовка */ truncate?: { /** Максимальное количество строк */ title?: number; }; /** Выбранное состояние */ checked?: boolean; /** Неактивное состояние */ disabled?: boolean; /** Рамка вокруг карточки */ outline?: boolean; /** * Промо-тег (бейдж). Тип — `PromoTagProps` из `@cloud-ru/ds-promo-tag`. */ promoBadge?: PromoTagProps; /** Настройки кнопки «Избранное» */ favorite?: FavoriteProps; /** * Формат отображения кнопки «Избранное»: всегда или при наведении и фокусе * @default 'hover' */ actionsVisibility?: VisibilityStrategy; /** CSS-класс корневого элемента */ className?: string; /** Support prop для тестов */ 'data-test-id'?: string; }; export type CardServiceSmallProps = BaseCardServiceSmallProps & { /** Полиморфный элемент: `'div'`, `'a'`, `{Link}` и т.д. */ as?: T; /** Ref на реальный DOM-элемент / инстанс */ innerRef?: PolymorphicRef; } & Omit, keyof BaseCardServiceSmallProps | 'as' | 'ref'>; function isIconEmblem( emblem: BaseCardServiceSmallProps['emblem'], ): emblem is { icon: JSXElementConstructor<{ className?: string; size?: number }> } { return 'icon' in emblem; } export function CardServiceSmall({ title, emblem, truncate, checked, disabled, outline, promoBadge, favorite, actionsVisibility, className, as, innerRef, 'data-test-id': dataTestId, ...rest }: CardServiceSmallProps): ReactElement | null { const [isFavourite, setIsFavourite] = useValueControl({ value: favorite?.checked, defaultValue: false, onChange: favorite?.onChange, }); const cardRef = useRef(null); const favouriteRef = useRef(null); const handleCardKeyDown: KeyboardEventHandler = useCallback(e => { if (e.code === 'ArrowRight') { favouriteRef.current?.focus(); } }, []); const handleFavouriteKeyUp: KeyboardEventHandler = useCallback( e => { if (e.code === 'ArrowLeft') { e.preventDefault(); cardRef.current?.focus(); } if (e.code === 'Space' || e.code === 'Enter') { e.preventDefault(); setIsFavourite(!isFavourite); } }, [isFavourite, setIsFavourite], ); const handleFavouriteClick: MouseEventHandler = useCallback(e => { e.stopPropagation(); favouriteRef.current?.blur(); }, []); const IconComponent = isIconEmblem(emblem) ? emblem.icon : null; return (
as={as} innerRef={mergeRefs(innerRef, cardRef)} disabled={disabled} interactive radius='s' checked={checked} view={outline ? 'outline' : undefined} className={styles.card} data-test-id={dataTestId ?? TEST_IDS.cardServiceSmall} onKeyDown={handleCardKeyDown as KeyboardEventHandler} {...(rest as ComponentPropsWithoutRef)} >
{IconComponent ? ( ) : ( !isIconEmblem(emblem) &&
{emblem}
)}
{favorite?.enabled && (
)}
{promoBadge && (
)}
); } withInnerRefSupport(CardServiceSmall);