UNPKG

@yandex/ui

Version:

Yandex UI components

92 lines (90 loc) 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Image = void 0; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importStar(require("react")); var classname_1 = require("@bem-react/classname"); var mergeRefs_1 = require("../lib/mergeRefs"); require("./Image.css"); var cnImage = classname_1.cn('Image'); var emptyImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='; var ANIMATION_DELAY_MS = 50; /** * Компонент для красивой загрузки картинок * - Пока загружается картинка, пользователь будет видеть `<stub/>` * - После загрузки картинки она будет плавно отображена поверх `<stub/>` * - После плавного показана картинки `<stub/>` будет удалён из `DOM` * - Если картинка загружается из кэша, то пользователь увидит сразу картинку без показа `<stub/>` * - Если картинку не удалось загрузить, то покажется стандартная картинка из свойства `fallback` * @param {IImageProps} props */ var Image = function (props) { var src = props.src, src2x = props.src2x, srcSet = props.srcSet, sizes = props.sizes, fallbackSrc = props.fallbackSrc, className = props.className, imageClassName = props.imageClassName, stub = props.stub, alt = props.alt, ariaLabel = props.ariaLabel, onClick = props.onClick, _a = props.loading, loading = _a === void 0 ? 'lazy' : _a, width = props.width, height = props.height, borderRadius = props.borderRadius, innerRef = props.innerRef; var _b = tslib_1.__read(react_1.useState(false), 2), isLoaded = _b[0], setLoaded = _b[1]; var _c = tslib_1.__read(react_1.useState(false), 2), isFailed = _c[0], setFailed = _c[1]; var _d = tslib_1.__read(react_1.useState(false), 2), needAnimation = _d[0], setNeedAnimation = _d[1]; var _e = tslib_1.__read(react_1.useState(false), 2), canRemoveStub = _e[0], setCanRemoveStub = _e[1]; // нужен реф по условию, а значит useRef не годится // важный момент: в innerRef должна быть актуальная ссылка на ref var imageRef = react_1.useRef(null); // объединяем рефы var mergedRefs = react_1.useMemo(function () { return mergeRefs_1.mergeAllRefs(imageRef, innerRef); }, [imageRef, innerRef]); var imageSrc = src || emptyImage; var legacySrcSet = src2x ? src2x + " 2x" : undefined; var imageSrcSet = srcSet || legacySrcSet; var isRendered = react_1.useCallback(function () { if (!imageRef.current) { return false; } var _a = imageRef.current, naturalWidth = _a.naturalWidth, naturalHeight = _a.naturalHeight; return naturalWidth > 0 && naturalHeight > 0; }, [imageRef]); react_1.useEffect(function () { var timer = setTimeout(function () { if (!isLoaded && !isRendered()) { setNeedAnimation(true); } }, ANIMATION_DELAY_MS); return function () { return clearTimeout(timer); }; }, [isLoaded, isRendered, setNeedAnimation]); var handleLoad = react_1.useCallback(function () { setLoaded(true); }, []); var handleError = react_1.useCallback(function () { setFailed(true); }, []); var containerClassNames = cnImage('Container', [className]); var imageClassNames = cnImage({ loaded: isLoaded, loading: needAnimation, animated: needAnimation && isLoaded, }, // Если нет обертки, то добавляем className в изображение [!stub ? className : undefined, imageClassName]); var handleOnAnimationEnd = react_1.useCallback(function () { setCanRemoveStub(true); }, []); var sizeAttrs = {}; // безопасно устанавливаем стили, чтобы избежать undefined в html if (width !== undefined) { sizeAttrs.width = width; } if (height !== undefined) { sizeAttrs.height = height; } if (borderRadius !== undefined) { sizeAttrs.borderRadius = borderRadius; } var image = (react_1.default.createElement("img", { style: sizeAttrs, alt: alt, "aria-hidden": ariaLabel ? 'false' : 'true', "aria-label": ariaLabel, className: imageClassNames, onAnimationEnd: handleOnAnimationEnd, onClick: onClick, onError: handleError, onLoad: handleLoad, ref: mergedRefs, src: isFailed && fallbackSrc ? fallbackSrc : imageSrc, srcSet: imageSrcSet, sizes: sizes, // TypeScript пока не знает про такой атрибут // @ts-ignore loading: loading })); if (stub) { return (react_1.default.createElement("div", { className: containerClassNames, style: sizeAttrs }, needAnimation && !canRemoveStub && stub, image)); } return image; }; exports.Image = Image; exports.Image.displayName = cnImage();