jfs-components 0.0.64 → 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.
Files changed (40) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/commonjs/components/Carousel/Carousel.js +12 -9
  3. package/lib/commonjs/components/Drawer/Drawer.js +9 -3
  4. package/lib/commonjs/components/IconButton/IconButton.js +42 -6
  5. package/lib/commonjs/components/IconCapsule/IconCapsule.js +5 -0
  6. package/lib/commonjs/components/Popup/Popup.js +2 -2
  7. package/lib/commonjs/components/UpiHandle/UpiHandle.js +19 -7
  8. package/lib/commonjs/icons/Icon.js +72 -75
  9. package/lib/commonjs/icons/registry.js +1 -1
  10. package/lib/commonjs/utils/MediaSource.js +181 -0
  11. package/lib/commonjs/utils/index.js +9 -1
  12. package/lib/module/components/Carousel/Carousel.js +12 -9
  13. package/lib/module/components/Drawer/Drawer.js +9 -3
  14. package/lib/module/components/IconButton/IconButton.js +42 -6
  15. package/lib/module/components/IconCapsule/IconCapsule.js +5 -0
  16. package/lib/module/components/Popup/Popup.js +2 -2
  17. package/lib/module/components/UpiHandle/UpiHandle.js +20 -8
  18. package/lib/module/icons/Icon.js +72 -75
  19. package/lib/module/icons/registry.js +1 -1
  20. package/lib/module/utils/MediaSource.js +176 -0
  21. package/lib/module/utils/index.js +2 -1
  22. package/lib/typescript/src/components/Drawer/Drawer.d.ts +6 -1
  23. package/lib/typescript/src/components/IconButton/IconButton.d.ts +25 -14
  24. package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +12 -1
  25. package/lib/typescript/src/components/UpiHandle/UpiHandle.d.ts +17 -3
  26. package/lib/typescript/src/icons/Icon.d.ts +35 -16
  27. package/lib/typescript/src/icons/registry.d.ts +1 -1
  28. package/lib/typescript/src/utils/MediaSource.d.ts +63 -0
  29. package/lib/typescript/src/utils/index.d.ts +2 -0
  30. package/package.json +1 -1
  31. package/src/components/Carousel/Carousel.tsx +16 -17
  32. package/src/components/Drawer/Drawer.tsx +13 -2
  33. package/src/components/IconButton/IconButton.tsx +70 -11
  34. package/src/components/IconCapsule/IconCapsule.tsx +13 -0
  35. package/src/components/Popup/Popup.tsx +2 -2
  36. package/src/components/UpiHandle/UpiHandle.tsx +37 -11
  37. package/src/icons/Icon.tsx +91 -76
  38. package/src/icons/registry.ts +1 -1
  39. package/src/utils/MediaSource.tsx +220 -0
  40. package/src/utils/index.ts +2 -0
@@ -11,9 +11,19 @@ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
11
11
  import Icon from '../../icons/Icon'
12
12
  import { usePressableWebSupport, type SafePressableProps, type WebAccessibilityProps } from '../../utils/web-platform-utils'
13
13
  import { EMPTY_MODES } from '../../utils/react-utils'
14
+ import type { UnifiedSource } from '../../utils/MediaSource'
14
15
 
15
16
  type IconButtonProps = SafePressableProps & {
17
+ /** Built-in icon name from the registry (default state). */
16
18
  iconName?: string;
19
+ /**
20
+ * Unified fallback source for the default state, used when `iconName` is
21
+ * missing or not in the registry. Accepts a remote URI, an inline SVG XML
22
+ * string, a `require()` asset, an SVG React component, or a React element.
23
+ * The result is tinted with the button's mode-resolved icon color so it
24
+ * follows design tokens just like a built-in icon. See {@link UnifiedSource}.
25
+ */
26
+ source?: UnifiedSource;
17
27
  modes?: Record<string, any>;
18
28
  onPress?: () => void;
19
29
  disabled?: boolean;
@@ -29,10 +39,24 @@ type IconButtonProps = SafePressableProps & {
29
39
  * Icon to display when isToggle is true and isActive is true
30
40
  */
31
41
  activeIcon?: string;
42
+ /**
43
+ * Unified fallback source for the active state. Used when `activeIcon` is
44
+ * missing or not in the registry (and only when `isToggle` is true and
45
+ * `isActive` is true). Falls back to the default `source` if not provided.
46
+ * See {@link UnifiedSource}.
47
+ */
48
+ activeSource?: UnifiedSource;
32
49
  /**
33
50
  * Icon to display when isToggle is true and isActive is false
34
51
  */
35
52
  inactiveIcon?: string;
53
+ /**
54
+ * Unified fallback source for the inactive state. Used when `inactiveIcon`
55
+ * is missing or not in the registry (and only when `isToggle` is true and
56
+ * `isActive` is false). Falls back to the default `source` if not provided.
57
+ * See {@link UnifiedSource}.
58
+ */
59
+ inactiveSource?: UnifiedSource;
36
60
  /**
37
61
  * Whether the toggle button is in active state (only used when isToggle is true)
38
62
  */
@@ -109,8 +133,14 @@ function resolveIconButtonTokens(modes: Record<string, any>, disabled: boolean):
109
133
  * pressed transform mirrored via React state) — removed.
110
134
  * - Wrapped in `React.memo`.
111
135
  */
136
+ // Legacy default icon used when neither a `name` nor a `source` is supplied
137
+ // for the resolved slot. Kept as a constant rather than a destructuring
138
+ // default so source-only call sites don't accidentally render `'ic_card'`.
139
+ const LEGACY_DEFAULT_ICON_NAME = 'ic_card'
140
+
112
141
  function IconButton({
113
- iconName = 'ic_card',
142
+ iconName,
143
+ source,
114
144
  modes = EMPTY_MODES,
115
145
  onPress,
116
146
  disabled = false,
@@ -121,7 +151,9 @@ function IconButton({
121
151
  webAccessibilityProps,
122
152
  isToggle = false,
123
153
  activeIcon,
154
+ activeSource,
124
155
  inactiveIcon,
156
+ inactiveSource,
125
157
  isActive = false,
126
158
  ...rest
127
159
  }: IconButtonProps) {
@@ -160,16 +192,42 @@ function IconButton({
160
192
  userHandlersRef.current.onHoverIn = (rest as any)?.onHoverIn
161
193
  userHandlersRef.current.onHoverOut = (rest as any)?.onHoverOut
162
194
 
163
- // Determine which icon to display
164
- const finalIconName =
165
- isToggle
166
- ? (isActive && activeIcon
167
- ? activeIcon
168
- : (!isActive && inactiveIcon ? inactiveIcon : iconName))
169
- : iconName
195
+ // Resolve the active (name + source) pair for the current slot. Toggle
196
+ // mode picks active/inactive based on `isActive`; per-state overrides
197
+ // fall back to the default `iconName` / `source` when omitted. We then
198
+ // apply the legacy default icon only as a last resort, so a source-only
199
+ // call site (`<IconButton source="…" />`) renders the source instead of
200
+ // bleeding through to `'ic_card'`.
201
+ let resolvedIconName: string | undefined
202
+ let resolvedSource: UnifiedSource | undefined
203
+ if (isToggle) {
204
+ if (isActive) {
205
+ resolvedIconName = activeIcon ?? iconName
206
+ resolvedSource = activeSource ?? source
207
+ } else {
208
+ resolvedIconName = inactiveIcon ?? iconName
209
+ resolvedSource = inactiveSource ?? source
210
+ }
211
+ } else {
212
+ resolvedIconName = iconName
213
+ resolvedSource = source
214
+ }
215
+ if (!resolvedIconName && resolvedSource === undefined) {
216
+ resolvedIconName = LEGACY_DEFAULT_ICON_NAME
217
+ }
170
218
 
171
- // Generate default accessibility label from icon name if not provided
172
- const defaultAccessibilityLabel = accessibilityLabel || iconName.replace(/^ic_/, '').replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
219
+ // Generate default accessibility label from the resolved icon name when
220
+ // possible. Source-only call sites should provide an explicit
221
+ // `accessibilityLabel`; we fall back to a generic 'Icon button' so we
222
+ // never crash on `iconName.replace(...)` when only a `source` is supplied.
223
+ const defaultAccessibilityLabel =
224
+ accessibilityLabel ||
225
+ (resolvedIconName
226
+ ? resolvedIconName
227
+ .replace(/^ic_/, '')
228
+ .replace(/_/g, ' ')
229
+ .replace(/\b\w/g, (l) => l.toUpperCase())
230
+ : 'Icon button')
173
231
 
174
232
  const webProps = usePressableWebSupport({
175
233
  restProps: rest,
@@ -235,7 +293,8 @@ function IconButton({
235
293
  {...webProps}
236
294
  >
237
295
  <Icon
238
- name={finalIconName}
296
+ {...(resolvedIconName !== undefined ? { name: resolvedIconName } : {})}
297
+ {...(resolvedSource !== undefined ? { source: resolvedSource } : {})}
239
298
  size={tokens.iconSize}
240
299
  color={tokens.iconColor}
241
300
  accessibilityElementsHidden={true}
@@ -4,9 +4,19 @@ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import { EMPTY_MODES } from '../../utils/react-utils'
6
6
  import Icon from '../../icons/Icon'
7
+ import type { UnifiedSource } from '../../utils/MediaSource'
7
8
 
8
9
  type IconCapsuleProps = {
9
10
  iconName?: string;
11
+ /**
12
+ * Unified fallback source rendered when `iconName` is missing or not in the
13
+ * registry. Accepts a remote URI, an inline SVG XML string, a `require()`
14
+ * asset, an SVG React component, or an already-rendered element. The
15
+ * resulting media is tinted with the capsule's mode-resolved icon color so
16
+ * it follows design tokens just like a built-in icon. See
17
+ * {@link UnifiedSource}.
18
+ */
19
+ source?: UnifiedSource;
10
20
  modes?: Record<string, any>;
11
21
  accessibilityLabel?: string;
12
22
  accessibilityRole?: string;
@@ -55,6 +65,7 @@ function resolveIconCapsuleTokens(modes: Record<string, any>): IconCapsuleTokens
55
65
  * @component
56
66
  * @param {Object} props - Component props
57
67
  * @param {string} [props.iconName="ic_card"] - The name of the icon to display from the icon registry
68
+ * @param {UnifiedSource} [props.source] - Fallback source (remote URI, inline SVG XML, `require()` asset, SVG React component, or React element). Used when `iconName` is missing or unknown. Tinted with the mode-resolved icon color so it follows design tokens just like a built-in icon.
58
69
  * @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"Appearance": "Primary"})
59
70
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
60
71
  * @param {string} [props.accessibilityRole] - Accessibility role (defaults to "image" for decorative icons)
@@ -68,6 +79,7 @@ function resolveIconCapsuleTokens(modes: Record<string, any>): IconCapsuleTokens
68
79
  */
69
80
  function IconCapsule({
70
81
  iconName = 'ic_card',
82
+ source,
71
83
  modes: propModes = EMPTY_MODES,
72
84
  // accessibilityLabel is accepted on the type for API back-compat but the
73
85
  // component intentionally renders `accessibilityLabel={undefined}` (icons
@@ -105,6 +117,7 @@ function IconCapsule({
105
117
  >
106
118
  <Icon
107
119
  name={iconName}
120
+ {...(source !== undefined ? { source } : {})}
108
121
  size={tokens.iconSize}
109
122
  color={tokens.iconColor}
110
123
  accessibilityElementsHidden={true}
@@ -162,12 +162,12 @@ const Popup = forwardRef<PopupRef, PopupProps>(function Popup(
162
162
  <View style={styles.overlay}>
163
163
  <Animated.View
164
164
  style={[
165
- StyleSheet.absoluteFillObject,
165
+ StyleSheet.absoluteFill,
166
166
  { backgroundColor: backdropColor, opacity: backdropAnim },
167
167
  ]}
168
168
  >
169
169
  <Pressable
170
- style={StyleSheet.absoluteFillObject}
170
+ style={StyleSheet.absoluteFill}
171
171
  onPress={closeOnBackdropPress ? handleClose : undefined}
172
172
  accessibilityRole="button"
173
173
  accessibilityLabel="Close popup"
@@ -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 `avatarSource` prop if needed.
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
- avatarSource?: ImageSourcePropType;
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 {ImageSourcePropType} [props.avatarSource] - Optional custom image source for the avatar.
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
- <Image
208
- source={avatarSource || DEFAULT_AVATAR_IMAGE}
209
- style={tokens.avatarStyle}
210
- resizeMode="cover"
211
- accessibilityElementsHidden={true}
212
- importantForAccessibility="no"
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}
@@ -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
- name: string;
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?: any;
24
+ style?: StyleProp<ViewStyle>;
11
25
  };
12
26
 
13
27
  /**
14
- * Generic Icon Component
15
- *
16
- * Renders an icon from the registry by name with customizable size and color.
17
- *
18
- * @component
19
- * @param {Object} props - Component props
20
- * @param {string} props.name - Icon name from the registry (e.g., 'ic_ccv', 'ic_card')
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
- * ```jsx
36
+ * ```tsx
37
+ * // Built-in icon from the registry.
27
38
  * <Icon name="ic_ccv" size={24} color="#141414" />
28
- * <Icon name="ic_card" size={32} color="#5c00b5" />
29
- * <Icon name="ic_cart" size={20} color="red" />
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
- // Validate icon name
40
- if (!name) {
41
- console.warn('Icon: name prop is required');
42
- return null;
43
- }
44
-
45
- if (!hasIcon(name)) {
46
- const { getIconNames } = require('./registry');
47
- console.warn(`Icon: "${name}" not found in registry. Available icons: ${getIconNames().join(', ')}`);
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
- // Get icon data from registry
52
- const iconData = getIcon(name);
53
- if (!iconData) {
54
- return null;
55
- }
69
+ const iconData = name && hasIcon(name) ? getIcon(name) : null;
56
70
 
57
- // Parse viewBox to get width and height for aspect ratio
58
- const viewBoxParts = iconData.viewBox.split(' ');
59
- // @ts-ignore
60
- const viewBoxWidth = parseFloat(viewBoxParts[2]) || size;
61
- // @ts-ignore
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
- // Calculate aspect ratio to maintain proper scaling
65
- const aspectRatio = viewBoxWidth / viewBoxHeight;
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
- // Determine actual width and height based on size and aspect ratio
68
- let width = size;
69
- let height = size;
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
- // If viewBox is not square, adjust dimensions to maintain aspect ratio
72
- if (Math.abs(aspectRatio - 1) > 0.01) {
73
- if (aspectRatio > 1) {
74
- // Wider than tall
75
- height = size / aspectRatio;
76
- } else {
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
- const containerStyle = {
83
- width: size,
84
- height: size,
85
- alignItems: 'center',
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
- return (
91
- <View style={containerStyle} {...rest}>
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
-
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-04-20T20:41:14.535Z
7
+ * Generated: 2026-04-21T13:27:57.213Z
8
8
  */
9
9
 
10
10
  // Icon name to SVG data mapping