jfs-components 0.0.63 → 0.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/lib/commonjs/components/Carousel/Carousel.js +12 -9
- package/lib/commonjs/components/Drawer/Drawer.js +116 -50
- package/lib/commonjs/components/IconButton/IconButton.js +42 -6
- package/lib/commonjs/components/IconCapsule/IconCapsule.js +5 -0
- package/lib/commonjs/components/Popup/Popup.js +2 -2
- package/lib/commonjs/components/Section/Section.js +280 -58
- package/lib/commonjs/components/UpiHandle/UpiHandle.js +19 -7
- package/lib/commonjs/icons/Icon.js +72 -75
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/MediaSource.js +181 -0
- package/lib/commonjs/utils/index.js +9 -1
- package/lib/module/components/Carousel/Carousel.js +12 -9
- package/lib/module/components/Drawer/Drawer.js +116 -50
- package/lib/module/components/IconButton/IconButton.js +42 -6
- package/lib/module/components/IconCapsule/IconCapsule.js +5 -0
- package/lib/module/components/Popup/Popup.js +2 -2
- package/lib/module/components/Section/Section.js +280 -58
- package/lib/module/components/UpiHandle/UpiHandle.js +20 -8
- package/lib/module/icons/Icon.js +72 -75
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/MediaSource.js +176 -0
- package/lib/module/utils/index.js +2 -1
- package/lib/typescript/src/components/Drawer/Drawer.d.ts +6 -1
- package/lib/typescript/src/components/IconButton/IconButton.d.ts +25 -14
- package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +12 -1
- package/lib/typescript/src/components/Section/Section.d.ts +42 -1
- package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +17 -3
- package/lib/typescript/src/icons/Icon.d.ts +35 -16
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/MediaSource.d.ts +63 -0
- package/lib/typescript/src/utils/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/Carousel/Carousel.tsx +16 -17
- package/src/components/Drawer/Drawer.tsx +136 -60
- package/src/components/IconButton/IconButton.tsx +70 -11
- package/src/components/IconCapsule/IconCapsule.tsx +13 -0
- package/src/components/Popup/Popup.tsx +2 -2
- package/src/components/Section/Section.tsx +411 -71
- package/src/components/UpiHandle/UpiHandle.tsx +37 -11
- package/src/icons/Icon.tsx +91 -76
- package/src/icons/registry.ts +1 -1
- package/src/utils/MediaSource.tsx +220 -0
- package/src/utils/index.ts +2 -0
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
Pressable,
|
|
4
4
|
View,
|
|
5
5
|
Text,
|
|
6
|
-
Image,
|
|
7
6
|
Platform,
|
|
8
7
|
type ViewStyle,
|
|
9
8
|
type TextStyle,
|
|
@@ -15,10 +14,11 @@ import {
|
|
|
15
14
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
16
15
|
import { useTokens } from '../../design-tokens/JFSThemeProvider'
|
|
17
16
|
import { EMPTY_MODES } from '../../utils/react-utils'
|
|
17
|
+
import MediaSource, { type UnifiedSource } from '../../utils/MediaSource'
|
|
18
18
|
import Icon from '../../icons/Icon'
|
|
19
19
|
|
|
20
20
|
// Default static asset from the component folder.
|
|
21
|
-
// Consumers can override the image via the `
|
|
21
|
+
// Consumers can override the image via the `source` prop if needed.
|
|
22
22
|
const DEFAULT_AVATAR_IMAGE = require('./Image.png')
|
|
23
23
|
|
|
24
24
|
const IS_WEB = Platform.OS === 'web'
|
|
@@ -33,7 +33,19 @@ type UpiHandleProps = {
|
|
|
33
33
|
modes?: Record<string, any>;
|
|
34
34
|
showIcon?: boolean;
|
|
35
35
|
iconName?: string;
|
|
36
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Unified avatar source. Accepts a remote URI (raster or `.svg`), an
|
|
38
|
+
* inline SVG XML string, a `require()` asset, an SVG React component, or
|
|
39
|
+
* an already-rendered element. See {@link UnifiedSource}. Avatars are
|
|
40
|
+
* intentionally **not** tinted — the source renders as-is.
|
|
41
|
+
*/
|
|
42
|
+
source?: UnifiedSource;
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated Use `source` instead. Kept as an alias for back-compat.
|
|
45
|
+
* Accepts the same shapes as `source` plus the legacy
|
|
46
|
+
* `ImageSourcePropType` from the previous API.
|
|
47
|
+
*/
|
|
48
|
+
avatarSource?: ImageSourcePropType | UnifiedSource;
|
|
37
49
|
accessibilityLabel?: string;
|
|
38
50
|
accessibilityHint?: string;
|
|
39
51
|
onPress?: () => void;
|
|
@@ -115,7 +127,8 @@ function resolveUpiHandleTokens(modes: Record<string, any>): UpiHandleTokens {
|
|
|
115
127
|
* @param {Object} [props.modes={}] - Modes object passed directly to `getVariableByName`.
|
|
116
128
|
* @param {boolean} [props.showIcon=true] - Toggles the trailing icon visibility.
|
|
117
129
|
* @param {string} [props.iconName='ic_scan_qr_code'] - Icon name from the actions set.
|
|
118
|
-
* @param {
|
|
130
|
+
* @param {UnifiedSource} [props.source] - Unified avatar source (URI, inline SVG XML, `require()` asset, SVG React component, or React element). Smart-detects raster vs SVG so the same prop works on iOS, Android and web.
|
|
131
|
+
* @param {ImageSourcePropType|UnifiedSource} [props.avatarSource] - Deprecated alias for `source`; kept for back-compat.
|
|
119
132
|
* @param {Function} [props.onClick] - Click/tap handler. Works as an alias for `onPress`.
|
|
120
133
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
|
|
121
134
|
* @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
|
|
@@ -133,6 +146,7 @@ function UpiHandle({
|
|
|
133
146
|
modes: propModes = EMPTY_MODES,
|
|
134
147
|
showIcon = true,
|
|
135
148
|
iconName = 'ic_scan_qr_code',
|
|
149
|
+
source,
|
|
136
150
|
avatarSource,
|
|
137
151
|
onPress,
|
|
138
152
|
onClick,
|
|
@@ -202,15 +216,27 @@ function UpiHandle({
|
|
|
202
216
|
[tokens.containerStyle, isFocused]
|
|
203
217
|
)
|
|
204
218
|
|
|
219
|
+
// `source` wins; `avatarSource` is the legacy fallback. Both are accepted
|
|
220
|
+
// as a UnifiedSource (string / number / {uri} / component / element), and
|
|
221
|
+
// the legacy `ImageSourcePropType` shapes naturally fit that union too.
|
|
222
|
+
const resolvedAvatarSource: UnifiedSource =
|
|
223
|
+
(source as UnifiedSource | undefined) ??
|
|
224
|
+
(avatarSource as UnifiedSource | undefined) ??
|
|
225
|
+
(DEFAULT_AVATAR_IMAGE as UnifiedSource)
|
|
226
|
+
|
|
227
|
+
const avatarSize = (tokens.avatarStyle.width as number | undefined) ?? 23
|
|
228
|
+
|
|
205
229
|
const innerContent = (
|
|
206
230
|
<>
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
231
|
+
<View style={tokens.avatarStyle}>
|
|
232
|
+
<MediaSource
|
|
233
|
+
source={resolvedAvatarSource}
|
|
234
|
+
size={avatarSize}
|
|
235
|
+
resizeMode="cover"
|
|
236
|
+
accessibilityElementsHidden={true}
|
|
237
|
+
importantForAccessibility="no"
|
|
238
|
+
/>
|
|
239
|
+
</View>
|
|
214
240
|
<Text
|
|
215
241
|
style={tokens.labelStyle}
|
|
216
242
|
numberOfLines={1}
|
package/src/icons/Icon.tsx
CHANGED
|
@@ -1,109 +1,124 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View, AccessibilityProps } from 'react-native';
|
|
2
|
+
import { View, AccessibilityProps, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import Svg, { Path } from 'react-native-svg';
|
|
4
4
|
import { getIcon, hasIcon } from './registry';
|
|
5
|
+
import MediaSource, { type UnifiedSource } from '../utils/MediaSource';
|
|
5
6
|
|
|
6
7
|
type IconProps = AccessibilityProps & {
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Built-in icon name from the registry (e.g. `'ic_card'`, `'ic_scan_qr_code'`).
|
|
10
|
+
* If omitted or not found in the registry, the component falls back to
|
|
11
|
+
* `source` (when provided).
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Unified fallback source rendered when `name` is missing or not in the
|
|
16
|
+
* registry. Accepts a remote URI, an inline SVG XML string, a `require()`
|
|
17
|
+
* asset, an SVG React component, or an already-rendered element. See
|
|
18
|
+
* {@link UnifiedSource}. The icon is tinted with `color` so it follows
|
|
19
|
+
* design-token modes the same way built-in icons do.
|
|
20
|
+
*/
|
|
21
|
+
source?: UnifiedSource;
|
|
8
22
|
size?: number;
|
|
9
23
|
color?: string;
|
|
10
|
-
style?:
|
|
24
|
+
style?: StyleProp<ViewStyle>;
|
|
11
25
|
};
|
|
12
26
|
|
|
13
27
|
/**
|
|
14
|
-
* Generic Icon
|
|
15
|
-
*
|
|
16
|
-
* Renders an icon from the registry by name
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @param {number} [props.size=24] - Icon size in pixels (width and height)
|
|
22
|
-
* @param {string} [props.color='#141414'] - Icon color (hex, rgb, or named color)
|
|
23
|
-
* @param {Object} [props.style] - Additional styles for the container View
|
|
24
|
-
*
|
|
28
|
+
* Generic Icon component.
|
|
29
|
+
*
|
|
30
|
+
* Renders an icon from the registry by `name`, or falls back to a
|
|
31
|
+
* smart-detected `source` (SVG / PNG / JPG / require / SVG component /
|
|
32
|
+
* remote URI). External sources are tinted with `color` so they participate
|
|
33
|
+
* in the design-token modes just like built-in icons.
|
|
34
|
+
*
|
|
25
35
|
* @example
|
|
26
|
-
* ```
|
|
36
|
+
* ```tsx
|
|
37
|
+
* // Built-in icon from the registry.
|
|
27
38
|
* <Icon name="ic_ccv" size={24} color="#141414" />
|
|
28
|
-
*
|
|
29
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* // Fallback to a remote SVG (auto-detected by the .svg extension).
|
|
41
|
+
* <Icon source="https://cdn.example.com/avatar.svg" size={24} color="#5c00b5" />
|
|
42
|
+
*
|
|
43
|
+
* // Fallback to a local raster asset.
|
|
44
|
+
* <Icon source={require('./brand.png')} size={32} />
|
|
45
|
+
*
|
|
46
|
+
* // Fallback to an SVG React component (e.g. via react-native-svg-transformer).
|
|
47
|
+
* import BrandLogo from './brand.svg';
|
|
48
|
+
* <Icon source={BrandLogo} size={24} color="red" />
|
|
30
49
|
* ```
|
|
31
50
|
*/
|
|
32
51
|
function Icon({
|
|
33
52
|
name,
|
|
53
|
+
source,
|
|
34
54
|
size = 24,
|
|
35
55
|
color = '#141414',
|
|
36
56
|
style,
|
|
37
57
|
...rest
|
|
38
58
|
}: IconProps) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
59
|
+
const containerStyle: StyleProp<ViewStyle> = [
|
|
60
|
+
{
|
|
61
|
+
width: size,
|
|
62
|
+
height: size,
|
|
63
|
+
alignItems: 'center',
|
|
64
|
+
justifyContent: 'center',
|
|
65
|
+
},
|
|
66
|
+
style,
|
|
67
|
+
];
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
const iconData = getIcon(name);
|
|
53
|
-
if (!iconData) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
69
|
+
const iconData = name && hasIcon(name) ? getIcon(name) : null;
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const viewBoxHeight = parseFloat(viewBoxParts[3]) || size;
|
|
71
|
+
if (iconData) {
|
|
72
|
+
const viewBoxParts = iconData.viewBox.split(' ');
|
|
73
|
+
const viewBoxWidth = parseFloat(viewBoxParts[2] ?? `${size}`) || size;
|
|
74
|
+
const viewBoxHeight = parseFloat(viewBoxParts[3] ?? `${size}`) || size;
|
|
75
|
+
const aspectRatio = viewBoxWidth / viewBoxHeight;
|
|
63
76
|
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
let width = size;
|
|
78
|
+
let height = size;
|
|
79
|
+
if (Math.abs(aspectRatio - 1) > 0.01) {
|
|
80
|
+
if (aspectRatio > 1) {
|
|
81
|
+
height = size / aspectRatio;
|
|
82
|
+
} else {
|
|
83
|
+
width = size * aspectRatio;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
66
86
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
return (
|
|
88
|
+
<View style={containerStyle} {...rest}>
|
|
89
|
+
<Svg
|
|
90
|
+
width={width}
|
|
91
|
+
height={height}
|
|
92
|
+
viewBox={iconData.viewBox}
|
|
93
|
+
preserveAspectRatio="xMidYMid meet"
|
|
94
|
+
>
|
|
95
|
+
<Path
|
|
96
|
+
d={iconData.path}
|
|
97
|
+
fill={color}
|
|
98
|
+
fillRule={(iconData.fillRule || 'nonzero') as 'nonzero' | 'evenodd'}
|
|
99
|
+
/>
|
|
100
|
+
</Svg>
|
|
101
|
+
</View>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Taller than wide
|
|
78
|
-
width = size * aspectRatio;
|
|
79
|
-
}
|
|
105
|
+
if (source !== undefined) {
|
|
106
|
+
return (
|
|
107
|
+
<View style={containerStyle} {...rest}>
|
|
108
|
+
<MediaSource source={source} size={size} tintColor={color} resizeMode="contain" />
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
80
111
|
}
|
|
81
112
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
justifyContent: 'center',
|
|
87
|
-
...style,
|
|
88
|
-
};
|
|
113
|
+
if (!name) {
|
|
114
|
+
console.warn('Icon: either `name` or `source` is required');
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
89
117
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<Svg
|
|
93
|
-
width={width}
|
|
94
|
-
height={height}
|
|
95
|
-
viewBox={iconData.viewBox}
|
|
96
|
-
preserveAspectRatio="xMidYMid meet"
|
|
97
|
-
>
|
|
98
|
-
<Path
|
|
99
|
-
d={iconData.path}
|
|
100
|
-
fill={color}
|
|
101
|
-
fillRule={(iconData.fillRule || 'nonzero') as any}
|
|
102
|
-
/>
|
|
103
|
-
</Svg>
|
|
104
|
-
</View>
|
|
118
|
+
console.warn(
|
|
119
|
+
`Icon: "${name}" not found in registry and no \`source\` fallback was provided.`
|
|
105
120
|
);
|
|
121
|
+
return null;
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
export default Icon;
|
|
109
|
-
|
package/src/icons/registry.ts
CHANGED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Image,
|
|
4
|
+
type ImageStyle,
|
|
5
|
+
type ImageURISource,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
} from 'react-native'
|
|
8
|
+
import { SvgUri, SvgXml } from 'react-native-svg'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A unified, "do-the-right-thing" image source accepted by `MediaSource` and
|
|
12
|
+
* by the `source` prop on `Icon`, `IconCapsule` and `UpiHandle`.
|
|
13
|
+
*
|
|
14
|
+
* Accepts any of:
|
|
15
|
+
* - `string` — a URI (raster or `.svg`) **or** an inline SVG XML document
|
|
16
|
+
* (`'<svg …>…</svg>'`).
|
|
17
|
+
* - `number` — a Metro asset id from `require('./foo.png')`.
|
|
18
|
+
* - `{ uri, … }` — the standard RN `ImageURISource` object (works for both
|
|
19
|
+
* raster and `.svg` URIs).
|
|
20
|
+
* - `React.ComponentType` — an SVG React component (e.g. produced by
|
|
21
|
+
* `react-native-svg-transformer`, by `@svgr/*`,
|
|
22
|
+
* or hand-written). It is rendered with
|
|
23
|
+
* `{ width, height, color, fill }` so it can be
|
|
24
|
+
* tinted just like a built-in icon.
|
|
25
|
+
* - `React.ReactElement` — an already-rendered node, passed through verbatim.
|
|
26
|
+
*
|
|
27
|
+
* The helper sniffs the input shape (no extension hint required from the
|
|
28
|
+
* caller) and picks the correct renderer for the platform.
|
|
29
|
+
*/
|
|
30
|
+
export type UnifiedSource =
|
|
31
|
+
| string
|
|
32
|
+
| number
|
|
33
|
+
| ImageURISource
|
|
34
|
+
| React.ComponentType<{
|
|
35
|
+
width?: number
|
|
36
|
+
height?: number
|
|
37
|
+
color?: string
|
|
38
|
+
fill?: string
|
|
39
|
+
}>
|
|
40
|
+
| React.ReactElement
|
|
41
|
+
|
|
42
|
+
export type MediaSourceProps = {
|
|
43
|
+
/** Smart source. See {@link UnifiedSource}. */
|
|
44
|
+
source: UnifiedSource
|
|
45
|
+
/** Convenience shorthand that sets both width and height. */
|
|
46
|
+
size?: number
|
|
47
|
+
width?: number
|
|
48
|
+
height?: number
|
|
49
|
+
/**
|
|
50
|
+
* Optional tint applied to the rendered media.
|
|
51
|
+
* - Raster `<Image>` → mapped to `style.tintColor` (works on iOS, Android,
|
|
52
|
+
* and react-native-web).
|
|
53
|
+
* - SVG (XML / URI / component) → mapped to the `color` / `fill` props.
|
|
54
|
+
* For an SVG to actually re-color, its source must use `currentColor`
|
|
55
|
+
* (or have no hardcoded fill). SVGs with baked-in `fill="#xxx"` cannot
|
|
56
|
+
* be re-tinted without parsing — this is a `react-native-svg`
|
|
57
|
+
* limitation, not something we can paper over here.
|
|
58
|
+
*/
|
|
59
|
+
tintColor?: string
|
|
60
|
+
/** Extra style merged into the underlying `<Image>` (raster path only). */
|
|
61
|
+
style?: StyleProp<ImageStyle>
|
|
62
|
+
resizeMode?: 'cover' | 'contain' | 'stretch' | 'center' | 'repeat'
|
|
63
|
+
accessibilityElementsHidden?: boolean
|
|
64
|
+
importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const SVG_XML_RE = /<svg[\s>]/i
|
|
68
|
+
const SVG_URI_RE = /\.svg(\?|#|$)/i
|
|
69
|
+
|
|
70
|
+
function isSvgXml(s: string): boolean {
|
|
71
|
+
return /^\s*</.test(s) && SVG_XML_RE.test(s)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isSvgUri(s: string): boolean {
|
|
75
|
+
return SVG_URI_RE.test(s)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isUriObject(
|
|
79
|
+
v: unknown
|
|
80
|
+
): v is ImageURISource & { uri: string } {
|
|
81
|
+
return (
|
|
82
|
+
typeof v === 'object' &&
|
|
83
|
+
v !== null &&
|
|
84
|
+
'uri' in v &&
|
|
85
|
+
typeof (v as { uri: unknown }).uri === 'string'
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Smart renderer that picks the right primitive for the source shape. See
|
|
91
|
+
* {@link UnifiedSource}.
|
|
92
|
+
*
|
|
93
|
+
* Designed to be used internally by `Icon`, `IconCapsule`, and `UpiHandle`,
|
|
94
|
+
* but also exported for ad-hoc consumer use.
|
|
95
|
+
*/
|
|
96
|
+
function MediaSource(props: MediaSourceProps) {
|
|
97
|
+
const {
|
|
98
|
+
source,
|
|
99
|
+
size,
|
|
100
|
+
width,
|
|
101
|
+
height,
|
|
102
|
+
tintColor,
|
|
103
|
+
style,
|
|
104
|
+
resizeMode = 'cover',
|
|
105
|
+
accessibilityElementsHidden,
|
|
106
|
+
importantForAccessibility,
|
|
107
|
+
} = props
|
|
108
|
+
|
|
109
|
+
const w = width ?? size
|
|
110
|
+
const h = height ?? size
|
|
111
|
+
|
|
112
|
+
// Pre-rendered element — pass through.
|
|
113
|
+
if (React.isValidElement(source)) {
|
|
114
|
+
return source
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// SVG / icon component.
|
|
118
|
+
if (typeof source === 'function') {
|
|
119
|
+
const Comp = source as React.ComponentType<{
|
|
120
|
+
width?: number
|
|
121
|
+
height?: number
|
|
122
|
+
color?: string
|
|
123
|
+
fill?: string
|
|
124
|
+
}>
|
|
125
|
+
return (
|
|
126
|
+
<Comp
|
|
127
|
+
{...(w !== undefined ? { width: w } : {})}
|
|
128
|
+
{...(h !== undefined ? { height: h } : {})}
|
|
129
|
+
{...(tintColor !== undefined ? { color: tintColor, fill: tintColor } : {})}
|
|
130
|
+
/>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const sizeStyle: ImageStyle | null =
|
|
135
|
+
w !== undefined || h !== undefined ? { width: w, height: h } : null
|
|
136
|
+
const tintStyle: ImageStyle | null = tintColor ? { tintColor } : null
|
|
137
|
+
const composedStyle: StyleProp<ImageStyle> = [sizeStyle, tintStyle, style]
|
|
138
|
+
|
|
139
|
+
if (typeof source === 'string') {
|
|
140
|
+
if (isSvgXml(source)) {
|
|
141
|
+
return (
|
|
142
|
+
<SvgXml
|
|
143
|
+
xml={source}
|
|
144
|
+
{...(w !== undefined ? { width: w } : {})}
|
|
145
|
+
{...(h !== undefined ? { height: h } : {})}
|
|
146
|
+
{...(tintColor !== undefined ? { color: tintColor } : {})}
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
if (isSvgUri(source)) {
|
|
151
|
+
return (
|
|
152
|
+
<SvgUri
|
|
153
|
+
uri={source}
|
|
154
|
+
{...(w !== undefined ? { width: w } : {})}
|
|
155
|
+
{...(h !== undefined ? { height: h } : {})}
|
|
156
|
+
{...(tintColor !== undefined ? { color: tintColor } : {})}
|
|
157
|
+
/>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
return (
|
|
161
|
+
<Image
|
|
162
|
+
source={{ uri: source }}
|
|
163
|
+
style={composedStyle}
|
|
164
|
+
resizeMode={resizeMode}
|
|
165
|
+
{...(accessibilityElementsHidden !== undefined
|
|
166
|
+
? { accessibilityElementsHidden }
|
|
167
|
+
: {})}
|
|
168
|
+
{...(importantForAccessibility !== undefined
|
|
169
|
+
? { importantForAccessibility }
|
|
170
|
+
: {})}
|
|
171
|
+
/>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (isUriObject(source)) {
|
|
176
|
+
if (isSvgUri(source.uri)) {
|
|
177
|
+
return (
|
|
178
|
+
<SvgUri
|
|
179
|
+
uri={source.uri}
|
|
180
|
+
{...(w !== undefined ? { width: w } : {})}
|
|
181
|
+
{...(h !== undefined ? { height: h } : {})}
|
|
182
|
+
{...(tintColor !== undefined ? { color: tintColor } : {})}
|
|
183
|
+
/>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
return (
|
|
187
|
+
<Image
|
|
188
|
+
source={source}
|
|
189
|
+
style={composedStyle}
|
|
190
|
+
resizeMode={resizeMode}
|
|
191
|
+
{...(accessibilityElementsHidden !== undefined
|
|
192
|
+
? { accessibilityElementsHidden }
|
|
193
|
+
: {})}
|
|
194
|
+
{...(importantForAccessibility !== undefined
|
|
195
|
+
? { importantForAccessibility }
|
|
196
|
+
: {})}
|
|
197
|
+
/>
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (typeof source === 'number') {
|
|
202
|
+
return (
|
|
203
|
+
<Image
|
|
204
|
+
source={source}
|
|
205
|
+
style={composedStyle}
|
|
206
|
+
resizeMode={resizeMode}
|
|
207
|
+
{...(accessibilityElementsHidden !== undefined
|
|
208
|
+
? { accessibilityElementsHidden }
|
|
209
|
+
: {})}
|
|
210
|
+
{...(importantForAccessibility !== undefined
|
|
211
|
+
? { importantForAccessibility }
|
|
212
|
+
: {})}
|
|
213
|
+
/>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return null
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export default React.memo(MediaSource)
|
package/src/utils/index.ts
CHANGED