react-native-responsive-image-view
Version:
React Native component for scaling an Image within the parent View
223 lines (213 loc) • 5.49 kB
JavaScript
;
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