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
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { Image } from 'react-native';
5
+ import { SvgUri, SvgXml } from 'react-native-svg';
6
+
7
+ /**
8
+ * A unified, "do-the-right-thing" image source accepted by `MediaSource` and
9
+ * by the `source` prop on `Icon`, `IconCapsule` and `UpiHandle`.
10
+ *
11
+ * Accepts any of:
12
+ * - `string` — a URI (raster or `.svg`) **or** an inline SVG XML document
13
+ * (`'<svg …>…</svg>'`).
14
+ * - `number` — a Metro asset id from `require('./foo.png')`.
15
+ * - `{ uri, … }` — the standard RN `ImageURISource` object (works for both
16
+ * raster and `.svg` URIs).
17
+ * - `React.ComponentType` — an SVG React component (e.g. produced by
18
+ * `react-native-svg-transformer`, by `@svgr/*`,
19
+ * or hand-written). It is rendered with
20
+ * `{ width, height, color, fill }` so it can be
21
+ * tinted just like a built-in icon.
22
+ * - `React.ReactElement` — an already-rendered node, passed through verbatim.
23
+ *
24
+ * The helper sniffs the input shape (no extension hint required from the
25
+ * caller) and picks the correct renderer for the platform.
26
+ */
27
+ import { jsx as _jsx } from "react/jsx-runtime";
28
+ const SVG_XML_RE = /<svg[\s>]/i;
29
+ const SVG_URI_RE = /\.svg(\?|#|$)/i;
30
+ function isSvgXml(s) {
31
+ return /^\s*</.test(s) && SVG_XML_RE.test(s);
32
+ }
33
+ function isSvgUri(s) {
34
+ return SVG_URI_RE.test(s);
35
+ }
36
+ function isUriObject(v) {
37
+ return typeof v === 'object' && v !== null && 'uri' in v && typeof v.uri === 'string';
38
+ }
39
+
40
+ /**
41
+ * Smart renderer that picks the right primitive for the source shape. See
42
+ * {@link UnifiedSource}.
43
+ *
44
+ * Designed to be used internally by `Icon`, `IconCapsule`, and `UpiHandle`,
45
+ * but also exported for ad-hoc consumer use.
46
+ */
47
+ function MediaSource(props) {
48
+ const {
49
+ source,
50
+ size,
51
+ width,
52
+ height,
53
+ tintColor,
54
+ style,
55
+ resizeMode = 'cover',
56
+ accessibilityElementsHidden,
57
+ importantForAccessibility
58
+ } = props;
59
+ const w = width ?? size;
60
+ const h = height ?? size;
61
+
62
+ // Pre-rendered element — pass through.
63
+ if (/*#__PURE__*/React.isValidElement(source)) {
64
+ return source;
65
+ }
66
+
67
+ // SVG / icon component.
68
+ if (typeof source === 'function') {
69
+ const Comp = source;
70
+ return /*#__PURE__*/_jsx(Comp, {
71
+ ...(w !== undefined ? {
72
+ width: w
73
+ } : {}),
74
+ ...(h !== undefined ? {
75
+ height: h
76
+ } : {}),
77
+ ...(tintColor !== undefined ? {
78
+ color: tintColor,
79
+ fill: tintColor
80
+ } : {})
81
+ });
82
+ }
83
+ const sizeStyle = w !== undefined || h !== undefined ? {
84
+ width: w,
85
+ height: h
86
+ } : null;
87
+ const tintStyle = tintColor ? {
88
+ tintColor
89
+ } : null;
90
+ const composedStyle = [sizeStyle, tintStyle, style];
91
+ if (typeof source === 'string') {
92
+ if (isSvgXml(source)) {
93
+ return /*#__PURE__*/_jsx(SvgXml, {
94
+ xml: source,
95
+ ...(w !== undefined ? {
96
+ width: w
97
+ } : {}),
98
+ ...(h !== undefined ? {
99
+ height: h
100
+ } : {}),
101
+ ...(tintColor !== undefined ? {
102
+ color: tintColor
103
+ } : {})
104
+ });
105
+ }
106
+ if (isSvgUri(source)) {
107
+ return /*#__PURE__*/_jsx(SvgUri, {
108
+ uri: source,
109
+ ...(w !== undefined ? {
110
+ width: w
111
+ } : {}),
112
+ ...(h !== undefined ? {
113
+ height: h
114
+ } : {}),
115
+ ...(tintColor !== undefined ? {
116
+ color: tintColor
117
+ } : {})
118
+ });
119
+ }
120
+ return /*#__PURE__*/_jsx(Image, {
121
+ source: {
122
+ uri: source
123
+ },
124
+ style: composedStyle,
125
+ resizeMode: resizeMode,
126
+ ...(accessibilityElementsHidden !== undefined ? {
127
+ accessibilityElementsHidden
128
+ } : {}),
129
+ ...(importantForAccessibility !== undefined ? {
130
+ importantForAccessibility
131
+ } : {})
132
+ });
133
+ }
134
+ if (isUriObject(source)) {
135
+ if (isSvgUri(source.uri)) {
136
+ return /*#__PURE__*/_jsx(SvgUri, {
137
+ uri: source.uri,
138
+ ...(w !== undefined ? {
139
+ width: w
140
+ } : {}),
141
+ ...(h !== undefined ? {
142
+ height: h
143
+ } : {}),
144
+ ...(tintColor !== undefined ? {
145
+ color: tintColor
146
+ } : {})
147
+ });
148
+ }
149
+ return /*#__PURE__*/_jsx(Image, {
150
+ source: source,
151
+ style: composedStyle,
152
+ resizeMode: resizeMode,
153
+ ...(accessibilityElementsHidden !== undefined ? {
154
+ accessibilityElementsHidden
155
+ } : {}),
156
+ ...(importantForAccessibility !== undefined ? {
157
+ importantForAccessibility
158
+ } : {})
159
+ });
160
+ }
161
+ if (typeof source === 'number') {
162
+ return /*#__PURE__*/_jsx(Image, {
163
+ source: source,
164
+ style: composedStyle,
165
+ resizeMode: resizeMode,
166
+ ...(accessibilityElementsHidden !== undefined ? {
167
+ accessibilityElementsHidden
168
+ } : {}),
169
+ ...(importantForAccessibility !== undefined ? {
170
+ importantForAccessibility
171
+ } : {})
172
+ });
173
+ }
174
+ return null;
175
+ }
176
+ export default /*#__PURE__*/React.memo(MediaSource);
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export { cloneChildrenWithModes, flattenChildren } from './react-utils';
3
+ export { cloneChildrenWithModes, flattenChildren } from './react-utils';
4
+ export { default as MediaSource } from './MediaSource';
@@ -24,11 +24,16 @@ type DrawerProps = {
24
24
  * as a tab bar. Defaults to 80.
25
25
  */
26
26
  bottomInset?: number;
27
+ /**
28
+ * Called whenever the drawer settles into a new state (collapsed or
29
+ * expanded), so parent components can react programmatically.
30
+ */
31
+ onStateChange?: (state: 'collapsed' | 'expanded') => void;
27
32
  };
28
33
  /**
29
34
  * Drawer component with nested scrolling support.
30
35
  * Uses react-native-gesture-handler and react-native-reanimated.
31
36
  */
32
- declare function Drawer({ modes, style, title, header, children, collapsedHeight, expandedRatio, initialState, contentStyle, sheetStyle, accessibilityLabel, accessibilityHint, contentContainerStyle, showsVerticalScrollIndicator, bottomInset, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
37
+ declare function Drawer({ modes, style, title, header, children, collapsedHeight, expandedRatio, initialState, contentStyle, sheetStyle, accessibilityLabel, accessibilityHint, contentContainerStyle, showsVerticalScrollIndicator, bottomInset, onStateChange, }: DrawerProps): import("react/jsx-runtime").JSX.Element;
33
38
  export default Drawer;
34
39
  //# sourceMappingURL=Drawer.d.ts.map
@@ -1,8 +1,18 @@
1
1
  import React from 'react';
2
2
  import { type AccessibilityState, type StyleProp, type ViewStyle } from 'react-native';
3
3
  import { type SafePressableProps, type WebAccessibilityProps } from '../../utils/web-platform-utils';
4
+ import type { UnifiedSource } from '../../utils/MediaSource';
4
5
  type IconButtonProps = SafePressableProps & {
6
+ /** Built-in icon name from the registry (default state). */
5
7
  iconName?: string;
8
+ /**
9
+ * Unified fallback source for the default state, used when `iconName` is
10
+ * missing or not in the registry. Accepts a remote URI, an inline SVG XML
11
+ * string, a `require()` asset, an SVG React component, or a React element.
12
+ * The result is tinted with the button's mode-resolved icon color so it
13
+ * follows design tokens just like a built-in icon. See {@link UnifiedSource}.
14
+ */
15
+ source?: UnifiedSource;
6
16
  modes?: Record<string, any>;
7
17
  onPress?: () => void;
8
18
  disabled?: boolean;
@@ -18,10 +28,24 @@ type IconButtonProps = SafePressableProps & {
18
28
  * Icon to display when isToggle is true and isActive is true
19
29
  */
20
30
  activeIcon?: string;
31
+ /**
32
+ * Unified fallback source for the active state. Used when `activeIcon` is
33
+ * missing or not in the registry (and only when `isToggle` is true and
34
+ * `isActive` is true). Falls back to the default `source` if not provided.
35
+ * See {@link UnifiedSource}.
36
+ */
37
+ activeSource?: UnifiedSource;
21
38
  /**
22
39
  * Icon to display when isToggle is true and isActive is false
23
40
  */
24
41
  inactiveIcon?: string;
42
+ /**
43
+ * Unified fallback source for the inactive state. Used when `inactiveIcon`
44
+ * is missing or not in the registry (and only when `isToggle` is true and
45
+ * `isActive` is false). Falls back to the default `source` if not provided.
46
+ * See {@link UnifiedSource}.
47
+ */
48
+ inactiveSource?: UnifiedSource;
25
49
  /**
26
50
  * Whether the toggle button is in active state (only used when isToggle is true)
27
51
  */
@@ -31,20 +55,7 @@ type IconButtonProps = SafePressableProps & {
31
55
  */
32
56
  webAccessibilityProps?: WebAccessibilityProps;
33
57
  };
34
- /**
35
- * IconButton component that displays an icon within a pressable button container.
36
- *
37
- * Performance notes:
38
- * - Token reads collapsed into a single `useMemo([modes, disabled, isToggle, isActive])`.
39
- * - Press visual goes through Pressable's `({ pressed })` style callback so
40
- * a scroll-cancelled touch never schedules a React render. iOS gets
41
- * `unstable_pressDelay={130}`.
42
- * - Hover and focus state are mirrored on web only (gated setters).
43
- * - The previous version had a redundant `manualPressStyle` (a duplicate
44
- * pressed transform mirrored via React state) — removed.
45
- * - Wrapped in `React.memo`.
46
- */
47
- declare function IconButton({ iconName, modes, onPress, disabled, style, accessibilityLabel, accessibilityHint, accessibilityState, webAccessibilityProps, isToggle, activeIcon, inactiveIcon, isActive, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
58
+ declare function IconButton({ iconName, source, modes, onPress, disabled, style, accessibilityLabel, accessibilityHint, accessibilityState, webAccessibilityProps, isToggle, activeIcon, activeSource, inactiveIcon, inactiveSource, isActive, ...rest }: IconButtonProps): import("react/jsx-runtime").JSX.Element;
48
59
  declare const _default: React.MemoExoticComponent<typeof IconButton>;
49
60
  export default _default;
50
61
  //# sourceMappingURL=IconButton.d.ts.map
@@ -1,7 +1,17 @@
1
1
  import React from 'react';
2
2
  import { View } from 'react-native';
3
+ import type { UnifiedSource } from '../../utils/MediaSource';
3
4
  type IconCapsuleProps = {
4
5
  iconName?: string;
6
+ /**
7
+ * Unified fallback source rendered when `iconName` is missing or not in the
8
+ * registry. Accepts a remote URI, an inline SVG XML string, a `require()`
9
+ * asset, an SVG React component, or an already-rendered element. The
10
+ * resulting media is tinted with the capsule's mode-resolved icon color so
11
+ * it follows design tokens just like a built-in icon. See
12
+ * {@link UnifiedSource}.
13
+ */
14
+ source?: UnifiedSource;
5
15
  modes?: Record<string, any>;
6
16
  accessibilityLabel?: string;
7
17
  accessibilityRole?: string;
@@ -14,6 +24,7 @@ type IconCapsuleProps = {
14
24
  * @component
15
25
  * @param {Object} props - Component props
16
26
  * @param {string} [props.iconName="ic_card"] - The name of the icon to display from the icon registry
27
+ * @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.
17
28
  * @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"Appearance": "Primary"})
18
29
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
19
30
  * @param {string} [props.accessibilityRole] - Accessibility role (defaults to "image" for decorative icons)
@@ -25,7 +36,7 @@ type IconCapsuleProps = {
25
36
  * - Wrapped in `React.memo`; with the shared `EMPTY_MODES` default the
26
37
  * common path benefits from full memoization.
27
38
  */
28
- declare function IconCapsule({ iconName, modes: propModes, accessibilityLabel: _accessibilityLabel, accessibilityRole, style: styleProp, ...rest }: IconCapsuleProps): import("react/jsx-runtime").JSX.Element;
39
+ declare function IconCapsule({ iconName, source, modes: propModes, accessibilityLabel: _accessibilityLabel, accessibilityRole, style: styleProp, ...rest }: IconCapsuleProps): import("react/jsx-runtime").JSX.Element;
29
40
  declare const _default: React.MemoExoticComponent<typeof IconCapsule>;
30
41
  export default _default;
31
42
  //# sourceMappingURL=IconCapsule.d.ts.map
@@ -1,11 +1,24 @@
1
1
  import React from 'react';
2
2
  import { View, type ImageSourcePropType } from 'react-native';
3
+ import { type UnifiedSource } from '../../utils/MediaSource';
3
4
  type UpiHandleProps = {
4
5
  label?: string;
5
6
  modes?: Record<string, any>;
6
7
  showIcon?: boolean;
7
8
  iconName?: string;
8
- avatarSource?: ImageSourcePropType;
9
+ /**
10
+ * Unified avatar source. Accepts a remote URI (raster or `.svg`), an
11
+ * inline SVG XML string, a `require()` asset, an SVG React component, or
12
+ * an already-rendered element. See {@link UnifiedSource}. Avatars are
13
+ * intentionally **not** tinted — the source renders as-is.
14
+ */
15
+ source?: UnifiedSource;
16
+ /**
17
+ * @deprecated Use `source` instead. Kept as an alias for back-compat.
18
+ * Accepts the same shapes as `source` plus the legacy
19
+ * `ImageSourcePropType` from the previous API.
20
+ */
21
+ avatarSource?: ImageSourcePropType | UnifiedSource;
9
22
  accessibilityLabel?: string;
10
23
  accessibilityHint?: string;
11
24
  onPress?: () => void;
@@ -21,7 +34,8 @@ type UpiHandleProps = {
21
34
  * @param {Object} [props.modes={}] - Modes object passed directly to `getVariableByName`.
22
35
  * @param {boolean} [props.showIcon=true] - Toggles the trailing icon visibility.
23
36
  * @param {string} [props.iconName='ic_scan_qr_code'] - Icon name from the actions set.
24
- * @param {ImageSourcePropType} [props.avatarSource] - Optional custom image source for the avatar.
37
+ * @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.
38
+ * @param {ImageSourcePropType|UnifiedSource} [props.avatarSource] - Deprecated alias for `source`; kept for back-compat.
25
39
  * @param {Function} [props.onClick] - Click/tap handler. Works as an alias for `onPress`.
26
40
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
27
41
  * @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
@@ -34,7 +48,7 @@ type UpiHandleProps = {
34
48
  * - Focus state is mirrored on web only (gated setter).
35
49
  * - Wrapped in `React.memo`.
36
50
  */
37
- declare function UpiHandle({ label, modes: propModes, showIcon, iconName, avatarSource, onPress, onClick, disabled, accessibilityLabel: _accessibilityLabel, accessibilityHint, ...rest }: UpiHandleProps): import("react/jsx-runtime").JSX.Element;
51
+ declare function UpiHandle({ label, modes: propModes, showIcon, iconName, source, avatarSource, onPress, onClick, disabled, accessibilityLabel: _accessibilityLabel, accessibilityHint, ...rest }: UpiHandleProps): import("react/jsx-runtime").JSX.Element;
38
52
  declare const _default: React.MemoExoticComponent<typeof UpiHandle>;
39
53
  export default _default;
40
54
  //# sourceMappingURL=UpiHandle.d.ts.map
@@ -1,29 +1,48 @@
1
- import { AccessibilityProps } from 'react-native';
1
+ import { AccessibilityProps, type StyleProp, type ViewStyle } from 'react-native';
2
+ import { type UnifiedSource } from '../utils/MediaSource';
2
3
  type IconProps = AccessibilityProps & {
3
- name: string;
4
+ /**
5
+ * Built-in icon name from the registry (e.g. `'ic_card'`, `'ic_scan_qr_code'`).
6
+ * If omitted or not found in the registry, the component falls back to
7
+ * `source` (when provided).
8
+ */
9
+ name?: string;
10
+ /**
11
+ * Unified fallback source rendered when `name` is missing or not in the
12
+ * registry. Accepts a remote URI, an inline SVG XML string, a `require()`
13
+ * asset, an SVG React component, or an already-rendered element. See
14
+ * {@link UnifiedSource}. The icon is tinted with `color` so it follows
15
+ * design-token modes the same way built-in icons do.
16
+ */
17
+ source?: UnifiedSource;
4
18
  size?: number;
5
19
  color?: string;
6
- style?: any;
20
+ style?: StyleProp<ViewStyle>;
7
21
  };
8
22
  /**
9
- * Generic Icon Component
23
+ * Generic Icon component.
10
24
  *
11
- * Renders an icon from the registry by name with customizable size and color.
12
- *
13
- * @component
14
- * @param {Object} props - Component props
15
- * @param {string} props.name - Icon name from the registry (e.g., 'ic_ccv', 'ic_card')
16
- * @param {number} [props.size=24] - Icon size in pixels (width and height)
17
- * @param {string} [props.color='#141414'] - Icon color (hex, rgb, or named color)
18
- * @param {Object} [props.style] - Additional styles for the container View
25
+ * Renders an icon from the registry by `name`, or falls back to a
26
+ * smart-detected `source` (SVG / PNG / JPG / require / SVG component /
27
+ * remote URI). External sources are tinted with `color` so they participate
28
+ * in the design-token modes just like built-in icons.
19
29
  *
20
30
  * @example
21
- * ```jsx
31
+ * ```tsx
32
+ * // Built-in icon from the registry.
22
33
  * <Icon name="ic_ccv" size={24} color="#141414" />
23
- * <Icon name="ic_card" size={32} color="#5c00b5" />
24
- * <Icon name="ic_cart" size={20} color="red" />
34
+ *
35
+ * // Fallback to a remote SVG (auto-detected by the .svg extension).
36
+ * <Icon source="https://cdn.example.com/avatar.svg" size={24} color="#5c00b5" />
37
+ *
38
+ * // Fallback to a local raster asset.
39
+ * <Icon source={require('./brand.png')} size={32} />
40
+ *
41
+ * // Fallback to an SVG React component (e.g. via react-native-svg-transformer).
42
+ * import BrandLogo from './brand.svg';
43
+ * <Icon source={BrandLogo} size={24} color="red" />
25
44
  * ```
26
45
  */
27
- declare function Icon({ name, size, color, style, ...rest }: IconProps): import("react/jsx-runtime").JSX.Element;
46
+ declare function Icon({ name, source, size, color, style, ...rest }: IconProps): import("react/jsx-runtime").JSX.Element;
28
47
  export default Icon;
29
48
  //# sourceMappingURL=Icon.d.ts.map
@@ -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
  export declare const iconRegistry: Record<string, {
10
10
  path: string;
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import { type ImageStyle, type ImageURISource, type StyleProp } from 'react-native';
3
+ /**
4
+ * A unified, "do-the-right-thing" image source accepted by `MediaSource` and
5
+ * by the `source` prop on `Icon`, `IconCapsule` and `UpiHandle`.
6
+ *
7
+ * Accepts any of:
8
+ * - `string` — a URI (raster or `.svg`) **or** an inline SVG XML document
9
+ * (`'<svg …>…</svg>'`).
10
+ * - `number` — a Metro asset id from `require('./foo.png')`.
11
+ * - `{ uri, … }` — the standard RN `ImageURISource` object (works for both
12
+ * raster and `.svg` URIs).
13
+ * - `React.ComponentType` — an SVG React component (e.g. produced by
14
+ * `react-native-svg-transformer`, by `@svgr/*`,
15
+ * or hand-written). It is rendered with
16
+ * `{ width, height, color, fill }` so it can be
17
+ * tinted just like a built-in icon.
18
+ * - `React.ReactElement` — an already-rendered node, passed through verbatim.
19
+ *
20
+ * The helper sniffs the input shape (no extension hint required from the
21
+ * caller) and picks the correct renderer for the platform.
22
+ */
23
+ export type UnifiedSource = string | number | ImageURISource | React.ComponentType<{
24
+ width?: number;
25
+ height?: number;
26
+ color?: string;
27
+ fill?: string;
28
+ }> | React.ReactElement;
29
+ export type MediaSourceProps = {
30
+ /** Smart source. See {@link UnifiedSource}. */
31
+ source: UnifiedSource;
32
+ /** Convenience shorthand that sets both width and height. */
33
+ size?: number;
34
+ width?: number;
35
+ height?: number;
36
+ /**
37
+ * Optional tint applied to the rendered media.
38
+ * - Raster `<Image>` → mapped to `style.tintColor` (works on iOS, Android,
39
+ * and react-native-web).
40
+ * - SVG (XML / URI / component) → mapped to the `color` / `fill` props.
41
+ * For an SVG to actually re-color, its source must use `currentColor`
42
+ * (or have no hardcoded fill). SVGs with baked-in `fill="#xxx"` cannot
43
+ * be re-tinted without parsing — this is a `react-native-svg`
44
+ * limitation, not something we can paper over here.
45
+ */
46
+ tintColor?: string;
47
+ /** Extra style merged into the underlying `<Image>` (raster path only). */
48
+ style?: StyleProp<ImageStyle>;
49
+ resizeMode?: 'cover' | 'contain' | 'stretch' | 'center' | 'repeat';
50
+ accessibilityElementsHidden?: boolean;
51
+ importantForAccessibility?: 'auto' | 'yes' | 'no' | 'no-hide-descendants';
52
+ };
53
+ /**
54
+ * Smart renderer that picks the right primitive for the source shape. See
55
+ * {@link UnifiedSource}.
56
+ *
57
+ * Designed to be used internally by `Icon`, `IconCapsule`, and `UpiHandle`,
58
+ * but also exported for ad-hoc consumer use.
59
+ */
60
+ declare function MediaSource(props: MediaSourceProps): import("react/jsx-runtime").JSX.Element;
61
+ declare const _default: React.MemoExoticComponent<typeof MediaSource>;
62
+ export default _default;
63
+ //# sourceMappingURL=MediaSource.d.ts.map
@@ -1,2 +1,4 @@
1
1
  export { cloneChildrenWithModes, flattenChildren } from './react-utils';
2
+ export { default as MediaSource } from './MediaSource';
3
+ export type { UnifiedSource, MediaSourceProps } from './MediaSource';
2
4
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfs-components",
3
- "version": "0.0.64",
3
+ "version": "0.0.65",
4
4
  "description": "React Native Jio Finance Components Library",
5
5
  "author": "sunshuaiqi@gmail.com",
6
6
  "license": "MIT",
@@ -102,9 +102,8 @@ export function Carousel({
102
102
  const containerPaddingV = parseFloat(
103
103
  getVariableByName('carousel/padding/vertical', modes) || '0',
104
104
  )
105
- const paginationGap = parseFloat(
106
- getVariableByName('carousel/pagination/gap', modes) || '12',
107
- )
105
+ // Spacing between the cards row and the pagination dots uses `carousel/gap`.
106
+ const paginationOffset = gap
108
107
 
109
108
  // ---- Refs & state ----
110
109
  const scrollRef = useRef<ScrollView>(null)
@@ -306,7 +305,7 @@ export function Carousel({
306
305
  {showPagination && totalItems > 1 && (
307
306
  <Pagination
308
307
  modes={modes}
309
- style={{ marginTop: paginationGap }}
308
+ style={{ marginTop: paginationOffset }}
310
309
  />
311
310
  )}
312
311
  </View>
@@ -346,26 +345,26 @@ export function Pagination({ modes: propModes, style }: PaginationProps) {
346
345
  useContext(CarouselContext)
347
346
  const modes = propModes || ctxModes || {}
348
347
 
349
- // Token resolution for dots
350
- const dotSize = parseFloat(
351
- getVariableByName('carousel/pagination/dotSize', modes) || '8',
352
- )
353
- const dotActiveWidth = parseFloat(
354
- getVariableByName('carousel/pagination/dotActiveWidth', modes) || '24',
355
- )
348
+ // Token resolution for dots — matches Figma tokens
349
+ // (carousel/pagination/gap, carousel/pagination/indicator/{activecolor,inactivecolor,radius}).
350
+ // Dot dimensions are fixed per Figma spec: inactive 6x6, active 16x6.
351
+ const dotSize = 6
352
+ const dotActiveWidth = 16
356
353
  const dotGap = parseFloat(
357
- getVariableByName('carousel/pagination/dotGap', modes) || '8',
354
+ getVariableByName('carousel/pagination/gap', modes) || '4',
358
355
  )
359
356
  const dotColor =
360
- (getVariableByName('carousel/pagination/dotColor', modes) as string) ||
361
- 'rgba(255,255,255,0.35)'
357
+ (getVariableByName(
358
+ 'carousel/pagination/indicator/inactivecolor',
359
+ modes,
360
+ ) as string) || 'rgba(0,0,0,0.3)'
362
361
  const dotActiveColor =
363
362
  (getVariableByName(
364
- 'carousel/pagination/dotActiveColor',
363
+ 'carousel/pagination/indicator/activecolor',
365
364
  modes,
366
- ) as string) || '#ffffff'
365
+ ) as string) || '#170d0a'
367
366
  const dotRadius = parseFloat(
368
- getVariableByName('carousel/pagination/dotRadius', modes) || '4',
367
+ getVariableByName('carousel/pagination/indicator/radius', modes) || '9999',
369
368
  )
370
369
 
371
370
  const containerStyle: ViewStyle = {
@@ -79,6 +79,11 @@ type DrawerProps = {
79
79
  * as a tab bar. Defaults to 80.
80
80
  */
81
81
  bottomInset?: number
82
+ /**
83
+ * Called whenever the drawer settles into a new state (collapsed or
84
+ * expanded), so parent components can react programmatically.
85
+ */
86
+ onStateChange?: (state: 'collapsed' | 'expanded') => void
82
87
  }
83
88
 
84
89
  /**
@@ -102,6 +107,7 @@ function Drawer({
102
107
  contentContainerStyle,
103
108
  showsVerticalScrollIndicator = false,
104
109
  bottomInset = 80,
110
+ onStateChange,
105
111
  }: DrawerProps) {
106
112
  const { height: screenHeight } = useWindowDimensions()
107
113
 
@@ -164,8 +170,13 @@ function Drawer({
164
170
 
165
171
  // Update JS state for accessibility/logic if needed
166
172
  const updateMode = useCallback((newMode: 'collapsed' | 'expanded') => {
167
- setMode(newMode)
168
- }, [])
173
+ setMode((prev) => {
174
+ if (prev !== newMode) {
175
+ onStateChange?.(newMode)
176
+ }
177
+ return newMode
178
+ })
179
+ }, [onStateChange])
169
180
 
170
181
  // Gesture policy:
171
182
  // • activeOffsetY: require a clear *vertical* drag (10px) before this