@yandex/ui
Version:
Yandex UI components
92 lines (90 loc) • 5.16 kB
JavaScript
;
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();