UNPKG

@fluentui/react

Version:

Reusable React components for building web experiences.

127 lines 6.83 kB
import { __assign } from "tslib"; import * as React from 'react'; import { classNamesFunction, getNativeProps, imgProperties } from '../../Utilities'; import { ImageCoverStyle, ImageFit, ImageLoadState } from './Image.types'; import { useIsomorphicLayoutEffect, useMergedRefs } from '@fluentui/react-hooks'; var getClassNames = classNamesFunction(); var SVG_REGEX = /\.svg$/i; var KEY_PREFIX = 'fabricImage'; function useLoadState(props, imageElement) { var onLoadingStateChange = props.onLoadingStateChange, onLoad = props.onLoad, onError = props.onError, src = props.src; var _a = React.useState(ImageLoadState.notLoaded), loadState = _a[0], setLoadState = _a[1]; useIsomorphicLayoutEffect(function () { // If the src property changes, reset the load state // (does nothing if the load state is already notLoaded) setLoadState(ImageLoadState.notLoaded); }, [src]); // eslint-disable-next-line react-hooks/exhaustive-deps -- intended to run every render React.useEffect(function () { if (loadState === ImageLoadState.notLoaded) { // testing if naturalWidth and naturalHeight are greater than zero is better than checking // .complete, because .complete will also be set to true if the image breaks. However, // for some browsers, SVG images do not have a naturalWidth or naturalHeight, so fall back // to checking .complete for these images. var isLoaded = imageElement.current ? (src && imageElement.current.naturalWidth > 0 && imageElement.current.naturalHeight > 0) || (imageElement.current.complete && SVG_REGEX.test(src)) : false; if (isLoaded) { setLoadState(ImageLoadState.loaded); } } }); React.useEffect(function () { onLoadingStateChange === null || onLoadingStateChange === void 0 ? void 0 : onLoadingStateChange(loadState); // eslint-disable-next-line react-hooks/exhaustive-deps -- should only run when loadState changes }, [loadState]); var onImageLoaded = React.useCallback(function (ev) { onLoad === null || onLoad === void 0 ? void 0 : onLoad(ev); if (src) { setLoadState(ImageLoadState.loaded); } }, [src, onLoad]); var onImageError = React.useCallback(function (ev) { onError === null || onError === void 0 ? void 0 : onError(ev); setLoadState(ImageLoadState.error); }, [onError]); return [loadState, onImageLoaded, onImageError]; } export var ImageBase = React.forwardRef(function (props, forwardedRef) { var frameElement = React.useRef(); var imageElement = React.useRef(); var _a = useLoadState(props, imageElement), loadState = _a[0], onImageLoaded = _a[1], onImageError = _a[2]; var imageProps = getNativeProps(props, imgProperties, [ 'width', 'height', ]); var src = props.src, alt = props.alt, width = props.width, height = props.height, _b = props.shouldFadeIn, shouldFadeIn = _b === void 0 ? true : _b, shouldStartVisible = props.shouldStartVisible, className = props.className, imageFit = props.imageFit, role = props.role, maximizeFrame = props.maximizeFrame, styles = props.styles, theme = props.theme, loading = props.loading; var coverStyle = useCoverStyle(props, loadState, imageElement, frameElement); var classNames = getClassNames(styles, { theme: theme, className: className, width: width, height: height, maximizeFrame: maximizeFrame, shouldFadeIn: shouldFadeIn, shouldStartVisible: shouldStartVisible, isLoaded: loadState === ImageLoadState.loaded || (loadState === ImageLoadState.notLoaded && props.shouldStartVisible), isLandscape: coverStyle === ImageCoverStyle.landscape, isCenter: imageFit === ImageFit.center, isCenterContain: imageFit === ImageFit.centerContain, isCenterCover: imageFit === ImageFit.centerCover, isContain: imageFit === ImageFit.contain, isCover: imageFit === ImageFit.cover, isNone: imageFit === ImageFit.none, isError: loadState === ImageLoadState.error, isNotImageFit: imageFit === undefined, }); // If image dimensions aren't specified, the natural size of the image is used. return (React.createElement("div", { className: classNames.root, style: { width: width, height: height }, ref: frameElement }, React.createElement("img", __assign({}, imageProps, { onLoad: onImageLoaded, onError: onImageError, key: KEY_PREFIX + props.src || '', className: classNames.image, ref: useMergedRefs(imageElement, forwardedRef), src: src, alt: alt, role: role, loading: loading })))); }); ImageBase.displayName = 'ImageBase'; function useCoverStyle(props, loadState, imageElement, frameElement) { var previousLoadState = React.useRef(loadState); var coverStyle = React.useRef(); if (coverStyle === undefined || (previousLoadState.current === ImageLoadState.notLoaded && loadState === ImageLoadState.loaded)) { coverStyle.current = computeCoverStyle(props, loadState, imageElement, frameElement); } previousLoadState.current = loadState; return coverStyle.current; } function computeCoverStyle(props, loadState, imageElement, frameElement) { var imageFit = props.imageFit, width = props.width, height = props.height; // Do not compute cover style if it was already specified in props if (props.coverStyle !== undefined) { return props.coverStyle; } else if (loadState === ImageLoadState.loaded && (imageFit === ImageFit.cover || imageFit === ImageFit.contain || imageFit === ImageFit.centerContain || imageFit === ImageFit.centerCover) && imageElement.current && frameElement.current) { // Determine the desired ratio using the width and height props. // If those props aren't available, measure measure the frame. var desiredRatio = void 0; if (typeof width === 'number' && typeof height === 'number' && imageFit !== ImageFit.centerContain && imageFit !== ImageFit.centerCover) { desiredRatio = width / height; } else { desiredRatio = frameElement.current.clientWidth / frameElement.current.clientHeight; } // Examine the source image to determine its original ratio. var naturalRatio = imageElement.current.naturalWidth / imageElement.current.naturalHeight; // Should we crop from the top or the sides? if (naturalRatio > desiredRatio) { return ImageCoverStyle.landscape; } } return ImageCoverStyle.portrait; } //# sourceMappingURL=Image.base.js.map