UNPKG

react-native-responsive-image-view

Version:

React Native component for scaling an Image within the parent View

223 lines (213 loc) 5.49 kB
"use strict"; import * as React from 'react'; import { Image } from 'react-native'; // Output // Hook options // Component props import { jsx as _jsx } from "react/jsx-runtime"; // A cancelable version of Image.getSize, adapted from // https://github.com/kodefox/react-native-flex-image function getImageSize(uri, onImageSizeSuccess, onImageSizeFailure) { let totallyCanceled = false; Image.getSize(uri, (width, height) => { if (!totallyCanceled) { onImageSizeSuccess(width, height); } }, err => { if (!totallyCanceled) { onImageSizeFailure?.(err); } }); return { cancel: () => { totallyCanceled = true; } }; } const initialState = { loading: true, error: '', retryCount: 0, aspectRatio: undefined }; function reducer(state, action) { switch (action.type) { case 'SUCCESS': { return { ...initialState, loading: false, retryCount: state.retryCount, aspectRatio: action.payload }; } case 'FAILURE': { return { ...initialState, loading: false, error: action.payload, retryCount: state.retryCount }; } case 'RETRY': { return { ...initialState, retryCount: state.retryCount + 1 }; } /* istanbul ignore next: this will never happen */ default: { throw new Error('Unexpected action type'); } } } function defaultOnLoad() {} function defaultOnError(_) {} export function useResponsiveImageView({ aspectRatio: controlledAspectRatio, source: initialSource, onLoad = defaultOnLoad, onError = defaultOnError }) { if (!initialSource) { throw new Error('"source" is required'); } // Latest ref pattern for callbacks const onLoadRef = React.useRef(onLoad); const onErrorRef = React.useRef(onError); React.useEffect(() => { onLoadRef.current = onLoad; onErrorRef.current = onError; }, [onError, onLoad]); // Extract and memoize only the relevant information const imageIdOrUri = React.useMemo(() => { const imgIdOrUri = typeof initialSource === 'number' ? initialSource : initialSource.uri; if (!imgIdOrUri) { throw new Error(`"source" must be a valid URI or resource`); } return imgIdOrUri; }, [initialSource]); const [state, dispatch] = React.useReducer(reducer, initialState); function retry() { dispatch({ type: 'RETRY' }); } function isAspectRatioControlled() { return controlledAspectRatio !== undefined; } function getAspectRatio() { return isAspectRatioControlled() ? controlledAspectRatio : state.aspectRatio; } function getImageProps({ source, style = {}, ...props } = {}) { const imageProps = { source: initialSource, style: [style, { height: '100%', width: '100%' }], ...props }; return imageProps; } function getViewProps({ style = {}, ...props } = {}) { return { style: [style, { aspectRatio: getAspectRatio() }], ...props }; } React.useEffect(() => { let pendingGetImageSize = { cancel: /* istanbul ignore next: just a stub */() => {} }; function handleImageSizeSuccess(width, height) { onLoadRef.current(); dispatch({ type: 'SUCCESS', payload: width / height }); } function handleImageSizeFailure(err) { const errMessage = err instanceof Error ? err.message : /* istanbul ignore next */String(err); onErrorRef.current(errMessage); dispatch({ type: 'FAILURE', payload: errMessage }); } if (typeof imageIdOrUri === 'string') { // Retrieve image dimensions from URI pendingGetImageSize = getImageSize(imageIdOrUri, handleImageSizeSuccess, handleImageSizeFailure); } else { // Retrieve image dimensions from imported resource const imageSource = Image.resolveAssetSource(imageIdOrUri); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (imageSource) { handleImageSizeSuccess(imageSource.width, imageSource.height); } else { handleImageSizeFailure(new Error('Failed to retrieve image dimensions.')); } } return () => { pendingGetImageSize.cancel(); }; }, [imageIdOrUri, state.retryCount]); return { loading: state.loading, error: state.error, retry, getViewProps, getImageProps }; } export function ResponsiveImageView({ source, component: Component = undefined, render = undefined, children = undefined, aspectRatio = undefined, onLoad = defaultOnLoad, onError = defaultOnError }) { const bag = useResponsiveImageView({ aspectRatio, source, onLoad, onError }); // component injection if (Component) { return /*#__PURE__*/_jsx(Component, { ...bag }); } // render prop if (typeof render === 'function') { return render(bag); } // function-as-children if (typeof children === 'function') { return children(bag); } // no renderer provided, but children exist - just render the children as-is if (children && React.Children.count(children) > 0) { return /*#__PURE__*/_jsx(React.Fragment, { children: React.Children.only(children) }); } return null; } ResponsiveImageView.displayName = 'ResponsiveImageView'; //# sourceMappingURL=index.js.map