react-native-responsive-image-view
Version:
React Native component for scaling an Image within the parent View
230 lines (220 loc) • 6.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ResponsiveImageView = ResponsiveImageView;
exports.useResponsiveImageView = useResponsiveImageView;
var React = _interopRequireWildcard(require("react"));
var _reactNative = require("react-native");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
// Output
// Hook options
// Component props
// A cancelable version of Image.getSize, adapted from
// https://github.com/kodefox/react-native-flex-image
function getImageSize(uri, onImageSizeSuccess, onImageSizeFailure) {
let totallyCanceled = false;
_reactNative.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(_) {}
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 = _reactNative.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
};
}
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__*/(0, _jsxRuntime.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__*/(0, _jsxRuntime.jsx)(React.Fragment, {
children: React.Children.only(children)
});
}
return null;
}
ResponsiveImageView.displayName = 'ResponsiveImageView';
//# sourceMappingURL=index.js.map