mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-06 07:06:11 +00:00
feat: add new resolve web asset function (#2334)
# Summary
Add a new function to resolve Image asset uri.
## Compatibility
| OS | Implemented |
| ------- | :---------: |
| Web | ✅ |
This commit is contained in:
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
} = {
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
75
src/lib/resolveAssetUri.ts
Normal file
75
src/lib/resolveAssetUri.ts
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user