@vandrei977/react-native-render-html
Version:
The hackable, full-featured Open Source HTML rendering solution for React Native.
162 lines (154 loc) • 4.2 kB
text/typescript
import { useMemo } from 'react';
import { ImageStyle } from 'react-native';
import { ImageDimensions } from '../shared-types';
import {
IncompleteImageDimensions,
UseIMGElementStateProps
} from './img-types';
export type ConcreteDimensionsProps = {
flatStyle: ImageStyle;
naturalDimensions: ImageDimensions | null;
specifiedDimensions: IncompleteImageDimensions;
} & Pick<UseIMGElementStateProps, 'computeMaxWidth' | 'contentWidth'>;
function extractHorizontalSpace({
marginHorizontal,
leftMargin,
rightMargin,
margin
}: any) {
const realLeftMargin = leftMargin || marginHorizontal || margin || 0;
const realRightMargin = rightMargin || marginHorizontal || margin || 0;
return realLeftMargin + realRightMargin;
}
function scaleUp(
minDimensions: ImageDimensions,
desiredDimensions: ImageDimensions
): ImageDimensions {
const aspectRatio = desiredDimensions.width / desiredDimensions.height;
if (desiredDimensions.width < minDimensions.width) {
return scaleUp(minDimensions, {
width: minDimensions.width,
height: minDimensions.width / aspectRatio
});
}
if (desiredDimensions.height < minDimensions.height) {
return scaleUp(minDimensions, {
height: minDimensions.height,
width: minDimensions.height * aspectRatio
});
}
return desiredDimensions;
}
function scaleDown(
maxDimensions: ImageDimensions,
desiredDimensions: ImageDimensions
): ImageDimensions {
const aspectRatio = desiredDimensions.width / desiredDimensions.height;
if (desiredDimensions.width > maxDimensions.width) {
return scaleDown(maxDimensions, {
width: maxDimensions.width,
height: maxDimensions.width / aspectRatio
});
}
if (desiredDimensions.height > maxDimensions.height) {
return scaleDown(maxDimensions, {
height: maxDimensions.height,
width: maxDimensions.height * aspectRatio
});
}
return desiredDimensions;
}
function scale(
{ minBox, maxBox }: { maxBox: ImageDimensions; minBox: ImageDimensions },
originalBox: ImageDimensions
) {
return scaleDown(maxBox, scaleUp(minBox, originalBox));
}
function computeConcreteDimensions(params: any) {
const {
computeMaxWidth,
contentWidth,
flattenStyles,
naturalWidth,
naturalHeight,
specifiedWidth,
specifiedHeight
} = params;
const horizontalSpace = extractHorizontalSpace(flattenStyles);
const {
maxWidth = Infinity,
maxHeight = Infinity,
minWidth = 0,
minHeight = 0
} = flattenStyles;
const imagesMaxWidth =
typeof contentWidth === 'number' ? computeMaxWidth(contentWidth) : Infinity;
const minBox = {
width: minWidth,
height: minHeight
};
const maxBox = {
width:
Math.min(
imagesMaxWidth,
maxWidth,
typeof specifiedWidth === 'number' ? specifiedWidth : Infinity
) - horizontalSpace,
height: Math.min(
typeof specifiedHeight === 'number' ? specifiedHeight : Infinity,
maxHeight
)
};
if (
typeof specifiedWidth === 'number' &&
typeof specifiedHeight === 'number'
) {
return scale(
{ minBox, maxBox },
{
width: specifiedWidth,
height: specifiedHeight
}
);
}
if (naturalWidth != null && naturalHeight != null) {
return scale(
{ minBox, maxBox },
{
width: naturalWidth,
height: naturalHeight
}
);
}
return null;
}
export default function useImageConcreteDimensions<
P extends ConcreteDimensionsProps
>({
flatStyle,
naturalDimensions,
specifiedDimensions,
computeMaxWidth,
contentWidth
}: P): P['naturalDimensions'] extends ImageDimensions
? ImageDimensions
: ImageDimensions | null {
return useMemo(() => {
return computeConcreteDimensions({
flattenStyles: flatStyle,
computeMaxWidth,
contentWidth,
naturalWidth: naturalDimensions?.width,
naturalHeight: naturalDimensions?.height,
specifiedWidth: specifiedDimensions.width,
specifiedHeight: specifiedDimensions.height
}) as any;
}, [
computeMaxWidth,
contentWidth,
flatStyle,
naturalDimensions,
specifiedDimensions.height,
specifiedDimensions.width
]);
}