Skip to content

Commit

Permalink
feat: add new resolve web asset function (#2334)
Browse files Browse the repository at this point in the history
# Summary
Add a new function to resolve Image asset uri.

## Compatibility

| OS      | Implemented |
| ------- | :---------: |
| Web     |    ✅     |
  • Loading branch information
bohdanprog authored Jul 19, 2024
1 parent 657a102 commit 2da02cf
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/ReactNativeSVG.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import type { TextProps } from './elements/Text';
import type { TextPathProps } from './elements/TextPath';
import type { TSpanProps } from './elements/TSpan';
import type { UseProps } from './elements/Use';
import type { GestureResponderEvent, TransformsStyle } from 'react-native';
import type {
GestureResponderEvent,
TransformsStyle,
ImageProps as RNImageProps,
} from 'react-native';
import {
// @ts-ignore it is not seen in exports
unstable_createElement as createElement,
Expand All @@ -35,6 +39,7 @@ import type {
import SvgTouchableMixin from './lib/SvgTouchableMixin';
import { resolve } from './lib/resolve';
import { transformsArrayToProps } from './lib/extract/extractTransform';
import { resolveAssetUri } from './lib/resolveAssetUri';

type BlurEvent = object;
type FocusEvent = object;
Expand All @@ -54,6 +59,7 @@ interface BaseProps {
delayPressOut?: number;
disabled?: boolean;
hitSlop?: EdgeInsetsProp;
href?: RNImageProps['source'] | string | number;
nativeID?: string;
touchSoundDisabled?: boolean;
onBlur?: (e: BlurEvent) => void;
Expand Down Expand Up @@ -200,6 +206,7 @@ const prepare = <T extends BaseProps>(
gradientTransform?: string;
patternTransform?: string;
'transform-origin'?: string;
href?: RNImageProps['source'] | string | null;
style?: object;
ref?: unknown;
} = {
Expand Down Expand Up @@ -270,6 +277,9 @@ const prepare = <T extends BaseProps>(
if (props.onPress != null) {
clean.onClick = props.onPress;
}
if (props.href !== null) {
clean.href = resolveAssetUri(props.href)?.uri;
}
return clean;
};

Expand Down
75 changes: 75 additions & 0 deletions src/lib/resolveAssetUri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import {
ImageResolvedAssetSource,
PixelRatio,
type ImageProps as RNImageProps,
} from 'react-native';
// @ts-expect-error react-native/assets-registry doesn't export types.
import { getAssetByID } from '@react-native/assets-registry/registry';

export type PackagerAsset = {
__packager_asset: boolean;
fileSystemLocation: string;
httpServerLocation: string;
width?: number;
height?: number;
scales: Array<number>;
hash: string;
name: string;
type: string;
};

const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/;

// Based on that function: https://github.com/necolas/react-native-web/blob/54c14d64dabd175e8055e1dc92e9196c821f9b7d/packages/react-native-web/src/exports/Image/index.js#L118-L156
export function resolveAssetUri(
source?: RNImageProps['source'] | string | number
): Partial<ImageResolvedAssetSource> | null {
let src: Partial<ImageResolvedAssetSource> = {};
if (typeof source === 'number') {
// get the URI from the packager
const asset: PackagerAsset | null = getAssetByID(source);
if (asset == null) {
throw new Error(
`Image: asset with ID "${source}" could not be found. Please check the image source or packager.`
);
}
src = {
width: asset.width,
height: asset.height,
scale: asset.scales[0],
};
if (asset.scales.length > 1) {
const preferredScale = PixelRatio.get();
// Get the scale which is closest to the preferred scale
src.scale = asset.scales.reduce((prev, curr) =>
Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale)
? curr
: prev
);
}
const scaleSuffix = src.scale !== 1 ? `@${src.scale}x` : '';
src.uri = asset
? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}`
: '';
} else if (typeof source === 'string') {
src.uri = source;
} else if (
source &&
!Array.isArray(source) &&
typeof source.uri === 'string'
) {
src.uri = source.uri;
}

if (src.uri) {
const match = src?.uri?.match(svgDataUriPattern);
// inline SVG markup may contain characters (e.g., #, ") that need to be escaped
if (match) {
const [, prefix, svg] = match;
const encodedSvg = encodeURIComponent(svg);
src.uri = `${prefix}${encodedSvg}`;
return src;
}
}
return src;
}

0 comments on commit 2da02cf

Please sign in to comment.