jfs-components 0.0.55 → 0.0.57
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/lib/commonjs/components/Accordion/Accordion.js +2 -34
- package/lib/commonjs/components/AppBar/AppBar.js +4 -36
- package/lib/commonjs/components/FilterBar/FilterBar.js +2 -34
- package/lib/commonjs/components/LazyList/LazyList.js +2 -34
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +1 -1
- package/lib/commonjs/components/NavArrow/NavArrow.js +45 -44
- package/lib/commonjs/components/Numpad/Numpad.js +6 -5
- package/lib/commonjs/components/Section/Section.js +7 -8
- package/lib/commonjs/components/TextInput/TextInput.js +4 -38
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +149 -0
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/design-tokens/JFSThemeProvider.js +38 -3
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/react-utils.js +18 -13
- package/lib/module/components/Accordion/Accordion.js +1 -33
- package/lib/module/components/AppBar/AppBar.js +1 -34
- package/lib/module/components/FilterBar/FilterBar.js +1 -35
- package/lib/module/components/LazyList/LazyList.js +1 -35
- package/lib/module/components/MoneyValue/MoneyValue.js +1 -1
- package/lib/module/components/NavArrow/NavArrow.js +44 -44
- package/lib/module/components/Numpad/Numpad.js +5 -5
- package/lib/module/components/Section/Section.js +8 -9
- package/lib/module/components/TextInput/TextInput.js +2 -36
- package/lib/module/components/TransactionBubble/TransactionBubble.js +144 -0
- package/lib/module/components/index.js +1 -0
- package/lib/module/design-tokens/JFSThemeProvider.js +35 -3
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/react-utils.js +18 -13
- package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +6 -11
- package/lib/typescript/src/components/TransactionBubble/TransactionBubble.d.ts +39 -0
- package/lib/typescript/src/components/index.d.ts +1 -0
- package/lib/typescript/src/design-tokens/JFSThemeProvider.d.ts +15 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.tsx +1 -44
- package/src/components/AppBar/AppBar.tsx +1 -44
- package/src/components/FilterBar/FilterBar.tsx +1 -44
- package/src/components/LazyList/LazyList.tsx +1 -41
- package/src/components/MoneyValue/MoneyValue.tsx +1 -1
- package/src/components/NavArrow/NavArrow.tsx +46 -43
- package/src/components/Numpad/Numpad.tsx +5 -5
- package/src/components/Section/Section.tsx +8 -8
- package/src/components/TextInput/TextInput.tsx +1 -44
- package/src/components/TransactionBubble/TransactionBubble.tsx +155 -0
- package/src/components/index.ts +1 -0
- package/src/design-tokens/JFSThemeProvider.tsx +37 -2
- package/src/icons/registry.ts +1 -1
- package/src/utils/react-utils.ts +29 -21
|
@@ -12,17 +12,25 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
12
12
|
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
13
13
|
*/
|
|
14
14
|
function cloneChildrenWithModes(children, modes, forcedModes) {
|
|
15
|
-
|
|
15
|
+
const result = [];
|
|
16
|
+
_react.default.Children.forEach(children, child => {
|
|
16
17
|
if (! /*#__PURE__*/_react.default.isValidElement(child)) {
|
|
17
|
-
|
|
18
|
+
if (child !== null && child !== undefined) {
|
|
19
|
+
result.push(child);
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
//
|
|
24
|
+
// Unwrap Fragments: Fragments can't accept arbitrary props like `modes`,
|
|
25
|
+
// so recurse into their children and process each one individually.
|
|
26
|
+
if (child.type === _react.default.Fragment) {
|
|
27
|
+
const fragment = child;
|
|
28
|
+
result.push(...cloneChildrenWithModes(fragment.props.children, modes, forcedModes));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
21
31
|
const childChildren = child.props?.children;
|
|
22
32
|
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
23
33
|
|
|
24
|
-
// Clone the child with modes prop if it doesn't already have one
|
|
25
|
-
// or merge with existing modes if it does
|
|
26
34
|
// Merge order: parent modes first, then child's explicit modes override them,
|
|
27
35
|
// then forcedModes (if provided) are applied last and can never be overridden
|
|
28
36
|
const existingModes = child.props?.modes;
|
|
@@ -34,16 +42,13 @@ function cloneChildrenWithModes(children, modes, forcedModes) {
|
|
|
34
42
|
...modes,
|
|
35
43
|
...existingModes
|
|
36
44
|
} : modes;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(_react.default.Children.toArray(childChildren), modes, forcedModes) : undefined;
|
|
40
|
-
|
|
41
|
-
// Clone element with modes and processed children
|
|
42
|
-
return /*#__PURE__*/_react.default.cloneElement(child, {
|
|
45
|
+
const processedChildren = hasChildren ? cloneChildrenWithModes(childChildren, modes, forcedModes) : undefined;
|
|
46
|
+
result.push(/*#__PURE__*/_react.default.cloneElement(child, {
|
|
43
47
|
...child.props,
|
|
44
48
|
modes: mergedModes
|
|
45
|
-
}, processedChildren);
|
|
46
|
-
})
|
|
49
|
+
}, processedChildren));
|
|
50
|
+
});
|
|
51
|
+
return result;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/**
|
|
@@ -5,45 +5,13 @@ import { View, Text, Pressable, LayoutAnimation, Platform, UIManager } from 'rea
|
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import Icon from '../../icons/Icon';
|
|
7
7
|
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
8
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
8
9
|
|
|
9
10
|
// Enable LayoutAnimation on Android
|
|
10
11
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
12
|
if (Platform.OS === 'android' && UIManager.setLayoutAnimationEnabledExperimental) {
|
|
12
13
|
UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
17
|
-
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
18
|
-
*/
|
|
19
|
-
function cloneChildrenWithModes(children, modes) {
|
|
20
|
-
const result = React.Children.map(children, child => {
|
|
21
|
-
if (! /*#__PURE__*/React.isValidElement(child)) {
|
|
22
|
-
return child;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Get existing children
|
|
26
|
-
const childChildren = child.props?.children;
|
|
27
|
-
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
28
|
-
|
|
29
|
-
// Merge modes: parent modes first, then child's explicit modes override them
|
|
30
|
-
const existingModes = child.props?.modes;
|
|
31
|
-
const mergedModes = existingModes ? {
|
|
32
|
-
...modes,
|
|
33
|
-
...existingModes
|
|
34
|
-
} : modes;
|
|
35
|
-
|
|
36
|
-
// Recursively process children if they exist
|
|
37
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(React.Children.toArray(childChildren), modes) : undefined;
|
|
38
|
-
|
|
39
|
-
// Clone element with modes and processed children
|
|
40
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
41
|
-
...child.props,
|
|
42
|
-
modes: mergedModes
|
|
43
|
-
}, processedChildren);
|
|
44
|
-
});
|
|
45
|
-
return result || [];
|
|
46
|
-
}
|
|
47
15
|
/**
|
|
48
16
|
* Accordion component that mirrors the Figma "Accordion" component.
|
|
49
17
|
*
|
|
@@ -5,41 +5,8 @@ import { View, Pressable } from 'react-native';
|
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
7
7
|
import NavArrow from '../NavArrow/NavArrow';
|
|
8
|
-
|
|
9
|
-
// The user prompt mentioned "Use getVariableByName... strict camelCase".
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
13
|
-
*/
|
|
8
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
14
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
|
-
function cloneChildrenWithModes(children, modes) {
|
|
16
|
-
return React.Children.map(children, child => {
|
|
17
|
-
if (! /*#__PURE__*/React.isValidElement(child)) {
|
|
18
|
-
return child;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Get existing children
|
|
22
|
-
const childChildren = child.props?.children;
|
|
23
|
-
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
24
|
-
|
|
25
|
-
// Clone the child with modes prop if it doesn't already have one
|
|
26
|
-
// or merge with existing modes if it does
|
|
27
|
-
const existingModes = child.props?.modes;
|
|
28
|
-
const mergedModes = existingModes ? {
|
|
29
|
-
...modes,
|
|
30
|
-
...existingModes
|
|
31
|
-
} : modes;
|
|
32
|
-
|
|
33
|
-
// Recursively process children if they exist
|
|
34
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(React.Children.toArray(childChildren), modes) : undefined;
|
|
35
|
-
|
|
36
|
-
// Clone element with modes and processed children
|
|
37
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
38
|
-
...child.props,
|
|
39
|
-
modes: mergedModes
|
|
40
|
-
}, processedChildren);
|
|
41
|
-
})?.filter(child => child !== null && child !== undefined) ?? [];
|
|
42
|
-
}
|
|
43
10
|
export default function AppBar({
|
|
44
11
|
type = 'MainPage',
|
|
45
12
|
leadingSlot,
|
|
@@ -4,42 +4,8 @@ import React, { useState } from 'react';
|
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import TextInput from '../TextInput/TextInput';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
10
|
-
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
11
|
-
*/
|
|
7
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
12
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
function cloneChildrenWithModes(children, modes) {
|
|
14
|
-
const result = React.Children.map(children, child => {
|
|
15
|
-
if (! /*#__PURE__*/React.isValidElement(child)) {
|
|
16
|
-
return child;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Get existing children
|
|
20
|
-
const childChildren = child.props?.children;
|
|
21
|
-
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
22
|
-
|
|
23
|
-
// Clone the child with modes prop if it doesn't already have one
|
|
24
|
-
// or merge with existing modes if it does
|
|
25
|
-
// Merge order: parent modes first, then child's explicit modes override them
|
|
26
|
-
const existingModes = child.props?.modes;
|
|
27
|
-
const mergedModes = existingModes ? {
|
|
28
|
-
...modes,
|
|
29
|
-
...existingModes
|
|
30
|
-
} : modes;
|
|
31
|
-
|
|
32
|
-
// Recursively process children if they exist
|
|
33
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(React.Children.toArray(childChildren), modes) : undefined;
|
|
34
|
-
|
|
35
|
-
// Clone element with modes and processed children
|
|
36
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
37
|
-
...child.props,
|
|
38
|
-
modes: mergedModes
|
|
39
|
-
}, processedChildren);
|
|
40
|
-
});
|
|
41
|
-
return result || [];
|
|
42
|
-
}
|
|
43
9
|
/**
|
|
44
10
|
* FilterBar component that mirrors the Figma "filterBar" component.
|
|
45
11
|
*
|
|
@@ -3,42 +3,8 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
9
|
-
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
10
|
-
*/
|
|
6
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
11
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
|
-
function cloneChildrenWithModes(children, modes) {
|
|
13
|
-
return React.Children.map(children, child => {
|
|
14
|
-
if (! /*#__PURE__*/React.isValidElement(child)) {
|
|
15
|
-
return child;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Get existing children
|
|
19
|
-
const childProps = child.props;
|
|
20
|
-
const childChildren = childProps?.children;
|
|
21
|
-
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
22
|
-
|
|
23
|
-
// Clone the child with modes prop if it doesn't already have one
|
|
24
|
-
// or merge with existing modes if it does
|
|
25
|
-
// Merge order: parent modes first, then child's explicit modes override them
|
|
26
|
-
const existingModes = childProps?.modes;
|
|
27
|
-
const mergedModes = existingModes ? {
|
|
28
|
-
...modes,
|
|
29
|
-
...existingModes
|
|
30
|
-
} : modes;
|
|
31
|
-
|
|
32
|
-
// Recursively process children if they exist
|
|
33
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(React.Children.toArray(childChildren), modes) : undefined;
|
|
34
|
-
|
|
35
|
-
// Clone element with modes and processed children
|
|
36
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
37
|
-
...childProps,
|
|
38
|
-
modes: mergedModes
|
|
39
|
-
}, processedChildren);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
8
|
/**
|
|
43
9
|
* LazyList component that mirrors the Figma "LazyList" component.
|
|
44
10
|
*
|
|
@@ -95,7 +95,7 @@ function MoneyValue({
|
|
|
95
95
|
const fontWeightValue = getVariableByName('moneyValue/fontWeight', modes) || 500;
|
|
96
96
|
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
|
|
97
97
|
const fontFamily = getVariableByName('moneyValue/fontFamily', modes) || 'System';
|
|
98
|
-
const gap = getVariableByName('moneyValue/gap', modes)
|
|
98
|
+
const gap = getVariableByName('moneyValue/gap', modes) ?? 4;
|
|
99
99
|
|
|
100
100
|
// Resolve currency to a symbol, supporting both symbols and ISO codes
|
|
101
101
|
const resolvedCurrency = useMemo(() => {
|
|
@@ -2,27 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
|
+
import Svg, { Polyline } from 'react-native-svg';
|
|
5
6
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import Icon from '../../icons/Icon';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
/**
|
|
9
|
-
* NavArrow component that displays a
|
|
9
|
+
* NavArrow component that displays a chevron arrow for navigation.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Renders a stroked SVG chevron whose dimensions and thickness are
|
|
12
|
+
* fully driven by design tokens:
|
|
12
13
|
* - navArrow/icon/color - chevron stroke color
|
|
13
|
-
* - navArrow/icon/width -
|
|
14
|
-
* - navArrow/icon/height -
|
|
15
|
-
* - navArrow/icon/strokeWeight - stroke
|
|
14
|
+
* - navArrow/icon/width - chevron arm width (horizontal spread)
|
|
15
|
+
* - navArrow/icon/height - chevron arm height (vertical spread)
|
|
16
|
+
* - navArrow/icon/strokeWeight - stroke thickness
|
|
16
17
|
* - navArrow/width - container width
|
|
17
18
|
* - navArrow/height - container height
|
|
18
19
|
* - navArrow/radius - border radius
|
|
19
20
|
* - navArrow/background - background color
|
|
20
|
-
*
|
|
21
|
-
* @component
|
|
22
|
-
* @param {Object} props
|
|
23
|
-
* @param {'Back'|'Forward'|'Down'} [props.direction='Back'] - Arrow direction
|
|
24
|
-
* @param {Object} [props.modes={}] - Modes for design token resolution
|
|
25
|
-
* @param {Object} [props.style] - Additional container styles
|
|
26
21
|
*/
|
|
27
22
|
export default function NavArrow({
|
|
28
23
|
direction = 'Back',
|
|
@@ -31,22 +26,20 @@ export default function NavArrow({
|
|
|
31
26
|
accessibilityLabel,
|
|
32
27
|
...rest
|
|
33
28
|
}) {
|
|
34
|
-
// Resolve design tokens
|
|
35
29
|
const iconColor = getVariableByName('navArrow/icon/color', modes) || '#24262b';
|
|
36
|
-
|
|
37
|
-
// Dimensions from tokens
|
|
38
30
|
const widthToken = Number(getVariableByName('navArrow/width', modes)) || 6;
|
|
39
31
|
const heightToken = Number(getVariableByName('navArrow/height', modes)) || 10;
|
|
40
32
|
const borderRadius = Number(getVariableByName('navArrow/radius', modes)) || 0;
|
|
41
33
|
const backgroundColor = getVariableByName('navArrow/background', modes) || 'transparent';
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const iconWidth = Number(getVariableByName('navArrow/icon/width', modes)) || 4;
|
|
35
|
+
const iconHeight = Number(getVariableByName('navArrow/icon/height', modes)) || 8;
|
|
36
|
+
const strokeWeight = Number(getVariableByName('navArrow/icon/strokeWeight', modes)) || 2;
|
|
44
37
|
const isDown = direction === 'Down';
|
|
45
|
-
const
|
|
46
|
-
const
|
|
38
|
+
const containerWidth = isDown ? heightToken : widthToken;
|
|
39
|
+
const containerHeight = isDown ? widthToken : heightToken;
|
|
47
40
|
const containerStyle = {
|
|
48
|
-
width,
|
|
49
|
-
height,
|
|
41
|
+
width: containerWidth,
|
|
42
|
+
height: containerHeight,
|
|
50
43
|
borderRadius,
|
|
51
44
|
backgroundColor,
|
|
52
45
|
alignItems: 'center',
|
|
@@ -54,34 +47,41 @@ export default function NavArrow({
|
|
|
54
47
|
...(style || {})
|
|
55
48
|
};
|
|
56
49
|
const defaultAccessibilityLabel = accessibilityLabel || (direction === 'Back' ? 'Go back' : direction === 'Forward' ? 'Go forward' : 'Go down');
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
50
|
+
const chevronW = isDown ? iconHeight : iconWidth;
|
|
51
|
+
const chevronH = isDown ? iconWidth : iconHeight;
|
|
52
|
+
const pad = strokeWeight / 2;
|
|
53
|
+
const svgWidth = chevronW + pad * 2;
|
|
54
|
+
const svgHeight = chevronH + pad * 2;
|
|
55
|
+
let points;
|
|
56
|
+
switch (direction) {
|
|
57
|
+
case 'Forward':
|
|
58
|
+
points = `${pad},${pad} ${chevronW + pad},${chevronH / 2 + pad} ${pad},${chevronH + pad}`;
|
|
59
|
+
break;
|
|
60
|
+
case 'Down':
|
|
61
|
+
points = `${pad},${pad} ${chevronW / 2 + pad},${chevronH + pad} ${chevronW + pad},${pad}`;
|
|
62
|
+
break;
|
|
63
|
+
case 'Back':
|
|
64
|
+
default:
|
|
65
|
+
points = `${chevronW + pad},${pad} ${pad},${chevronH / 2 + pad} ${chevronW + pad},${chevronH + pad}`;
|
|
66
|
+
break;
|
|
64
67
|
}
|
|
65
68
|
return /*#__PURE__*/_jsx(View, {
|
|
66
69
|
style: containerStyle,
|
|
67
70
|
accessibilityRole: "image",
|
|
68
|
-
accessibilityLabel:
|
|
71
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
69
72
|
...rest,
|
|
70
|
-
children: /*#__PURE__*/_jsx(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// We might want overflow: 'hidden' if strictly following design clip.
|
|
83
|
-
// Figma design had "overflow-clip" class.
|
|
84
|
-
}
|
|
73
|
+
children: /*#__PURE__*/_jsx(Svg, {
|
|
74
|
+
width: svgWidth,
|
|
75
|
+
height: svgHeight,
|
|
76
|
+
viewBox: `0 0 ${svgWidth} ${svgHeight}`,
|
|
77
|
+
children: /*#__PURE__*/_jsx(Polyline, {
|
|
78
|
+
points: points,
|
|
79
|
+
stroke: iconColor,
|
|
80
|
+
strokeWidth: strokeWeight,
|
|
81
|
+
strokeLinecap: "round",
|
|
82
|
+
strokeLinejoin: "round",
|
|
83
|
+
fill: "none"
|
|
84
|
+
})
|
|
85
85
|
})
|
|
86
86
|
});
|
|
87
87
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { useMemo, useCallback } from 'react';
|
|
4
4
|
import { View, Text, Pressable } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import
|
|
6
|
+
import Icon from '../../icons/Icon';
|
|
7
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
function shuffleArray(arr) {
|
|
9
9
|
const shuffled = [...arr];
|
|
@@ -87,10 +87,10 @@ function Numpad({
|
|
|
87
87
|
onPress: () => handlePress(key),
|
|
88
88
|
accessibilityRole: "button",
|
|
89
89
|
accessibilityLabel: isBackspace ? 'Backspace' : key,
|
|
90
|
-
children: isBackspace ? /*#__PURE__*/_jsx(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
children: isBackspace ? /*#__PURE__*/_jsx(Icon, {
|
|
91
|
+
name: "ic_delete_backspace",
|
|
92
|
+
size: fontSize,
|
|
93
|
+
color: foreground
|
|
94
94
|
}) : /*#__PURE__*/_jsx(Text, {
|
|
95
95
|
style: [textStyle, keyTextStyle],
|
|
96
96
|
children: key
|
|
@@ -5,7 +5,7 @@ import { View, Text, Pressable, Platform } from 'react-native';
|
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import NavArrow from '../NavArrow/NavArrow';
|
|
7
7
|
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
8
|
-
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
8
|
+
import { cloneChildrenWithModes, flattenChildren } from '../../utils/react-utils';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
10
10
|
/**
|
|
11
11
|
* Section component that mirrors the Figma "Section" component.
|
|
@@ -60,7 +60,8 @@ function Section({
|
|
|
60
60
|
} : {};
|
|
61
61
|
// Resolve section container tokens
|
|
62
62
|
const backgroundColor = getVariableByName('section/background/color', modes) || '#ffffff';
|
|
63
|
-
const
|
|
63
|
+
const sectionGap = getVariableByName('section/gap', modes) || 12;
|
|
64
|
+
const slotGap = getVariableByName('slot/gap', modes) || 12;
|
|
64
65
|
const paddingHorizontal = getVariableByName('section/padding/horizontal', modes) || 12;
|
|
65
66
|
const paddingVertical = getVariableByName('section/padding/vertical', modes) || 16;
|
|
66
67
|
const radius = getVariableByName('section/radius', modes) || 12;
|
|
@@ -90,7 +91,7 @@ function Section({
|
|
|
90
91
|
paddingHorizontal,
|
|
91
92
|
paddingVertical,
|
|
92
93
|
borderRadius: radius,
|
|
93
|
-
gap
|
|
94
|
+
gap: sectionGap
|
|
94
95
|
};
|
|
95
96
|
const headerStyle = {
|
|
96
97
|
paddingHorizontal: headerPaddingHorizontal,
|
|
@@ -198,9 +199,9 @@ function Section({
|
|
|
198
199
|
}), slot && /*#__PURE__*/_jsx(View, {
|
|
199
200
|
style: {
|
|
200
201
|
flexDirection: slotDirection,
|
|
201
|
-
gap
|
|
202
|
+
gap: slotGap
|
|
202
203
|
},
|
|
203
|
-
children: cloneChildrenWithModes(
|
|
204
|
+
children: cloneChildrenWithModes(flattenChildren(slot), modes)
|
|
204
205
|
})]
|
|
205
206
|
});
|
|
206
207
|
}
|
|
@@ -248,10 +249,8 @@ function SectionBento({
|
|
|
248
249
|
borderRadius: radius,
|
|
249
250
|
gap
|
|
250
251
|
};
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const processedNavSlot = navSlot ? cloneChildrenWithModes(React.Children.toArray(navSlot), modes) : null;
|
|
254
|
-
const processedUpiSlot = upiSlot ? cloneChildrenWithModes(React.Children.toArray(upiSlot), modes) : null;
|
|
252
|
+
const processedNavSlot = navSlot ? cloneChildrenWithModes(flattenChildren(navSlot), modes) : null;
|
|
253
|
+
const processedUpiSlot = upiSlot ? cloneChildrenWithModes(flattenChildren(upiSlot), modes) : null;
|
|
255
254
|
return /*#__PURE__*/_jsxs(View, {
|
|
256
255
|
style: [containerStyle, style],
|
|
257
256
|
...(Platform.OS === 'web' ? {
|
|
@@ -4,42 +4,7 @@ import React, { useState } from 'react';
|
|
|
4
4
|
import { Pressable, View, TextInput as RNTextInput } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import Icon from '../../icons/Icon';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
10
|
-
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
11
|
-
*/
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
function cloneChildrenWithModes(children, modes) {
|
|
14
|
-
const result = React.Children.map(children, child => {
|
|
15
|
-
if (! /*#__PURE__*/React.isValidElement(child)) {
|
|
16
|
-
return child;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Get existing children
|
|
20
|
-
const childChildren = child.props?.children;
|
|
21
|
-
const hasChildren = childChildren !== undefined && childChildren !== null;
|
|
22
|
-
|
|
23
|
-
// Clone the child with modes prop if it doesn't already have one
|
|
24
|
-
// or merge with existing modes if it does
|
|
25
|
-
// Merge order: parent modes first, then child's explicit modes override them
|
|
26
|
-
const existingModes = child.props?.modes;
|
|
27
|
-
const mergedModes = existingModes ? {
|
|
28
|
-
...modes,
|
|
29
|
-
...existingModes
|
|
30
|
-
} : modes;
|
|
31
|
-
|
|
32
|
-
// Recursively process children if they exist
|
|
33
|
-
const processedChildren = hasChildren ? cloneChildrenWithModes(React.Children.toArray(childChildren), modes) : undefined;
|
|
34
|
-
|
|
35
|
-
// Clone element with modes and processed children
|
|
36
|
-
return /*#__PURE__*/React.cloneElement(child, {
|
|
37
|
-
...child.props,
|
|
38
|
-
modes: mergedModes
|
|
39
|
-
}, processedChildren);
|
|
40
|
-
});
|
|
41
|
-
return result || [];
|
|
42
|
-
}
|
|
7
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
43
8
|
|
|
44
9
|
/**
|
|
45
10
|
* TextInput component that mirrors the Figma "textInput" component.
|
|
@@ -73,6 +38,7 @@ function cloneChildrenWithModes(children, modes) {
|
|
|
73
38
|
* Helper function to convert a color to a more transparent version for placeholder text.
|
|
74
39
|
* Takes a color string (hex, rgb, rgba) and returns it with reduced opacity.
|
|
75
40
|
*/
|
|
41
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
76
42
|
function makePlaceholderColor(color, opacity = 0.5) {
|
|
77
43
|
if (!color || typeof color !== 'string') {
|
|
78
44
|
return color || '';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View, Text, Pressable } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import MoneyValue from '../MoneyValue/MoneyValue';
|
|
7
|
+
import TransactionStatus from '../TransactionStatus/TransactionStatus';
|
|
8
|
+
import NavArrow from '../NavArrow/NavArrow';
|
|
9
|
+
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
10
|
+
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
/**
|
|
13
|
+
* TransactionBubble — Figma node 1517:1155.
|
|
14
|
+
*
|
|
15
|
+
* Layout (vertical stack inside a rounded bordered pill):
|
|
16
|
+
*
|
|
17
|
+
* ┌──────────────────────────────────────────────┐
|
|
18
|
+
* │ Description │
|
|
19
|
+
* │ ₹56 │
|
|
20
|
+
* │ [slot / children content] │
|
|
21
|
+
* │ ⚠ Expired · 20 Mar 2025 › │
|
|
22
|
+
* └──────────────────────────────────────────────┘
|
|
23
|
+
*
|
|
24
|
+
* moneyValueWrap: description + MoneyValue, vertical with `transactionBubble/wrap/gap`.
|
|
25
|
+
* slotWrap: children (optional), rendered between moneyValueWrap and statusWrap.
|
|
26
|
+
* statusWrap: TransactionStatus + NavArrow, horizontal row with `transactionBubble/statusWrap/gap`.
|
|
27
|
+
* Container gap between sections: `transactionBubble/gap`.
|
|
28
|
+
*/
|
|
29
|
+
function TransactionBubble({
|
|
30
|
+
description = 'Payment to Uber India',
|
|
31
|
+
value = '56',
|
|
32
|
+
currency = '₹',
|
|
33
|
+
status = 'Expired',
|
|
34
|
+
date = '20 Mar 2025',
|
|
35
|
+
statusSlot,
|
|
36
|
+
children,
|
|
37
|
+
modes = {},
|
|
38
|
+
onPress,
|
|
39
|
+
style,
|
|
40
|
+
accessibilityLabel,
|
|
41
|
+
accessibilityHint,
|
|
42
|
+
webAccessibilityProps,
|
|
43
|
+
...rest
|
|
44
|
+
}) {
|
|
45
|
+
const resolvedModes = {
|
|
46
|
+
...modes,
|
|
47
|
+
'Context3': 'Transaction Bubble'
|
|
48
|
+
};
|
|
49
|
+
const padding = Number(getVariableByName('transactionBubble/padding', resolvedModes)) || 16;
|
|
50
|
+
const radius = Number(getVariableByName('transactionBubble/radius', resolvedModes)) || 23;
|
|
51
|
+
const borderSize = Number(getVariableByName('transactionBubble/border/size', resolvedModes)) || 1;
|
|
52
|
+
const backgroundColor = getVariableByName('transactionBubble/background', resolvedModes) || '#ffffff';
|
|
53
|
+
const borderColor = getVariableByName('transactionBubble/border/color', resolvedModes) || '#e5e5e5';
|
|
54
|
+
const bubbleGap = Number(getVariableByName('transactionBubble/gap', resolvedModes)) || 8;
|
|
55
|
+
const moneyValueWrapGap = Number(getVariableByName('transactionBubble/wrap/gap', resolvedModes)) || 8;
|
|
56
|
+
const descriptionColor = getVariableByName('transactionBubble/description/color', resolvedModes) || '#24262b';
|
|
57
|
+
const descriptionFontSize = Number(getVariableByName('transactionBubble/description/fontSize', resolvedModes)) || 14;
|
|
58
|
+
const descriptionLineHeight = Number(getVariableByName('transactionBubble/description/lineHeight', resolvedModes)) || 17;
|
|
59
|
+
const descriptionFontFamily = getVariableByName('transactionBubble/description/fontFamily', resolvedModes) || 'JioType Var';
|
|
60
|
+
const statusWrapGap = Number(getVariableByName('transactionBubble/statusWrap/gap', resolvedModes)) || 4;
|
|
61
|
+
const statusWrapHeight = Number(getVariableByName('transactionBubble/statusWrap/height', resolvedModes)) || 18;
|
|
62
|
+
const containerStyle = {
|
|
63
|
+
padding,
|
|
64
|
+
borderRadius: radius,
|
|
65
|
+
borderWidth: borderSize,
|
|
66
|
+
borderColor,
|
|
67
|
+
backgroundColor
|
|
68
|
+
};
|
|
69
|
+
const descriptionStyle = {
|
|
70
|
+
color: descriptionColor,
|
|
71
|
+
fontSize: descriptionFontSize,
|
|
72
|
+
lineHeight: descriptionLineHeight,
|
|
73
|
+
fontFamily: descriptionFontFamily
|
|
74
|
+
};
|
|
75
|
+
const processedChildren = children ? cloneChildrenWithModes(React.Children.toArray(children), resolvedModes) : null;
|
|
76
|
+
const processedStatusSlot = statusSlot ? cloneChildrenWithModes(React.Children.toArray(statusSlot), resolvedModes) : null;
|
|
77
|
+
const defaultAccessibilityLabel = accessibilityLabel || `${description} • ${status}`;
|
|
78
|
+
const webProps = usePressableWebSupport({
|
|
79
|
+
restProps: rest,
|
|
80
|
+
onPress,
|
|
81
|
+
disabled: false,
|
|
82
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
83
|
+
webAccessibilityProps
|
|
84
|
+
});
|
|
85
|
+
const statusRow = processedStatusSlot || /*#__PURE__*/_jsxs(View, {
|
|
86
|
+
style: {
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
justifyContent: 'space-between',
|
|
90
|
+
gap: statusWrapGap,
|
|
91
|
+
height: statusWrapHeight
|
|
92
|
+
},
|
|
93
|
+
children: [/*#__PURE__*/_jsx(TransactionStatus, {
|
|
94
|
+
status: status,
|
|
95
|
+
date: date,
|
|
96
|
+
modes: resolvedModes
|
|
97
|
+
}), /*#__PURE__*/_jsx(NavArrow, {
|
|
98
|
+
direction: "Forward",
|
|
99
|
+
modes: resolvedModes
|
|
100
|
+
})]
|
|
101
|
+
});
|
|
102
|
+
const mainContent = /*#__PURE__*/_jsxs(View, {
|
|
103
|
+
style: {
|
|
104
|
+
gap: bubbleGap
|
|
105
|
+
},
|
|
106
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
107
|
+
style: {
|
|
108
|
+
gap: moneyValueWrapGap
|
|
109
|
+
},
|
|
110
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
111
|
+
style: descriptionStyle,
|
|
112
|
+
numberOfLines: 1,
|
|
113
|
+
children: description
|
|
114
|
+
}), /*#__PURE__*/_jsx(MoneyValue, {
|
|
115
|
+
value: value,
|
|
116
|
+
currency: currency,
|
|
117
|
+
modes: resolvedModes
|
|
118
|
+
})]
|
|
119
|
+
}), processedChildren, statusRow]
|
|
120
|
+
});
|
|
121
|
+
if (onPress) {
|
|
122
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
123
|
+
onPress: onPress,
|
|
124
|
+
style: ({
|
|
125
|
+
pressed
|
|
126
|
+
}) => [containerStyle, style, pressed && {
|
|
127
|
+
opacity: 0.85
|
|
128
|
+
}],
|
|
129
|
+
accessibilityRole: "button",
|
|
130
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
131
|
+
accessibilityHint: accessibilityHint,
|
|
132
|
+
...webProps,
|
|
133
|
+
...rest,
|
|
134
|
+
children: mainContent
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return /*#__PURE__*/_jsx(View, {
|
|
138
|
+
style: [containerStyle, style],
|
|
139
|
+
accessibilityLabel: defaultAccessibilityLabel,
|
|
140
|
+
accessibilityHint: accessibilityHint,
|
|
141
|
+
children: mainContent
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
export default TransactionBubble;
|
|
@@ -48,6 +48,7 @@ export { default as ThreadHero } from './ThreadHero/ThreadHero';
|
|
|
48
48
|
export { Tooltip } from './Tooltip/Tooltip';
|
|
49
49
|
export { default as TransactionDetails } from './TransactionDetails/TransactionDetails';
|
|
50
50
|
export { default as TransactionStatus } from './TransactionStatus/TransactionStatus';
|
|
51
|
+
export { default as TransactionBubble } from './TransactionBubble/TransactionBubble';
|
|
51
52
|
export { default as UpiHandle } from './UpiHandle/UpiHandle';
|
|
52
53
|
export { default as VStack } from './VStack/VStack';
|
|
53
54
|
export { default as ChipGroup } from './ChipGroup/ChipGroup';
|