import { ImageSVG } from '@cloud-ru/ds-icons/interface/system'; import { Markdown } from '@cloud-ru/ds-markdown'; import { BAR_HIDE_STRATEGY, Scroll, SIZE as SCROLL_SIZE } from '@cloud-ru/ds-scroll'; import { Skeleton } from '@cloud-ru/ds-skeleton'; import { Typography } from '@cloud-ru/ds-typography'; import { useCallback, useState } from 'react'; import { MEDIA_STATUS, SURFACE, TEST_IDS } from '../../constants'; import { MediaStatus, NoteItemProps, Surface } from '../../types'; import styles from './styles.module.scss'; /** Размер иконки-заглушки в медиа-слоте, когда иллюстрация не загрузилась. */ const FALLBACK_ICON_SIZE = 48; type NoteItemComponentProps = NoteItemProps & { surface?: Surface; }; /** * Медиа-слот новости: иллюстрация или видео. Размеры слота задаёт разметка, а не intrinsic-размер * файла, поэтому загрузка не меняет раскладку карточки. До готовности файла слот занят скелетоном, * при ошибке загрузки — заглушкой с иконкой. */ function NoteItemMedia({ image, video }: Pick) { const [imageStatus, setImageStatus] = useState(MEDIA_STATUS.Pending); const [videoStatus, setVideoStatus] = useState(MEDIA_STATUS.Pending); // При ошибке видео отображается статичная иллюстрация, и состояние слота определяет уже она. const withVideo = Boolean(video) && videoStatus !== MEDIA_STATUS.Error; const status = withVideo ? videoStatus : imageStatus; // Изображение из кеша может завершить загрузку до подписки на `onLoad` — состояние снимается с узла. const handleImageRef = useCallback((node: HTMLImageElement | null) => { if (!node?.complete || !node.getAttribute('src')) { return; } setImageStatus(node.naturalWidth > 0 ? MEDIA_STATUS.Ready : MEDIA_STATUS.Error); }, []); const handleImageLoad = useCallback(() => setImageStatus(MEDIA_STATUS.Ready), []); const handleImageError = useCallback(() => setImageStatus(MEDIA_STATUS.Error), []); const handleVideoLoad = useCallback(() => setVideoStatus(MEDIA_STATUS.Ready), []); const handleVideoError = useCallback(() => setVideoStatus(MEDIA_STATUS.Error), []); return (
{withVideo ? (
); } export function NoteItem({ title, description, image, video, surface = SURFACE.Modal }: NoteItemComponentProps) { return (
{/* `key` по источнику: карусель переиспользует слайд, а состояние загрузки относится к файлу. */}
{title}
); }