jfs-components 0.0.54 → 0.0.56
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/Drawer/Drawer.js +11 -4
- package/lib/commonjs/components/FilterBar/FilterBar.js +2 -34
- package/lib/commonjs/components/LazyList/LazyList.js +2 -34
- package/lib/commonjs/components/ListGroup/ListGroup.js +15 -9
- 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 +145 -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/Drawer/Drawer.js +11 -4
- package/lib/module/components/FilterBar/FilterBar.js +1 -35
- package/lib/module/components/LazyList/LazyList.js +1 -35
- package/lib/module/components/ListGroup/ListGroup.js +15 -9
- 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 +140 -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/ListGroup/ListGroup.d.ts +12 -7
- package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +6 -11
- package/lib/typescript/src/components/TransactionBubble/TransactionBubble.d.ts +36 -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/Drawer/Drawer.tsx +12 -4
- package/src/components/FilterBar/FilterBar.tsx +1 -44
- package/src/components/LazyList/LazyList.tsx +1 -41
- package/src/components/ListGroup/ListGroup.tsx +21 -11
- 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 +152 -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,
|
|
@@ -181,17 +181,24 @@ function Drawer({
|
|
|
181
181
|
const totalRange = maxTranslateY - minTranslateY;
|
|
182
182
|
const currentRatio = totalRange > 0 ? (translateY.value - minTranslateY) / totalRange : 0;
|
|
183
183
|
|
|
184
|
-
//
|
|
185
|
-
|
|
184
|
+
// How far the drawer sheet itself moved from its position at gesture
|
|
185
|
+
// start. When the scroll view consumed the gesture (content was not
|
|
186
|
+
// at the top), the drawer stays put and displacement ≈ 0 — even
|
|
187
|
+
// though the finger moved fast. We must NOT use velocity to snap in
|
|
188
|
+
// that case; otherwise a fast content-scroll collapses the drawer.
|
|
189
|
+
const drawerDisplacement = Math.abs(translateY.value - context.value.y);
|
|
190
|
+
const drawerMovedEnough = drawerDisplacement > 10;
|
|
191
|
+
if (drawerMovedEnough && event.velocityY < -500) {
|
|
186
192
|
scrollTo(minTranslateY);
|
|
187
193
|
isFullyExpanded.value = true;
|
|
188
194
|
runOnJS(updateMode)('expanded');
|
|
189
|
-
} else if (event.velocityY > 500) {
|
|
195
|
+
} else if (drawerMovedEnough && event.velocityY > 500) {
|
|
190
196
|
scrollTo(maxTranslateY);
|
|
191
197
|
isFullyExpanded.value = false;
|
|
192
198
|
runOnJS(updateMode)('collapsed');
|
|
193
199
|
} else {
|
|
194
|
-
// Slow / medium gesture
|
|
200
|
+
// Slow / medium gesture, or the drawer barely moved → position
|
|
201
|
+
// based snap with asymmetric thresholds.
|
|
195
202
|
// Down stroke: must cross 75% to collapse (hard to dismiss).
|
|
196
203
|
// Up stroke: must reach top 35% to expand (35% from top).
|
|
197
204
|
const isDraggingDown = event.velocityY >= 0;
|
|
@@ -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
|
*
|
|
@@ -15,17 +15,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
*
|
|
16
16
|
* This component supports:
|
|
17
17
|
* - **label** text at the top
|
|
18
|
-
* - **listGroupSlot** for
|
|
18
|
+
* - **listGroupSlot** or **children** for content (typically ListItem components)
|
|
19
19
|
* - **design-token driven styling** via `getVariableByName` and `modes`
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* -
|
|
21
|
+
* Content can be provided in two interchangeable ways:
|
|
22
|
+
* - Via the `listGroupSlot` prop (Figma Slot "List group")
|
|
23
|
+
* - Via `children`
|
|
24
|
+
*
|
|
25
|
+
* Both produce identical results. If both are provided, they are merged
|
|
26
|
+
* (listGroupSlot items render first, then children).
|
|
24
27
|
*
|
|
25
28
|
* @component
|
|
26
29
|
* @param {Object} props
|
|
27
30
|
* @param {string} [props.label=''] - Label text displayed at the top of the list group
|
|
28
|
-
* @param {React.ReactNode} [props.listGroupSlot] -
|
|
31
|
+
* @param {React.ReactNode} [props.listGroupSlot] - Slot for list items (Figma Slot "List group")
|
|
32
|
+
* @param {React.ReactNode} [props.children] - Children for list items (equivalent to listGroupSlot)
|
|
29
33
|
* @param {Object} [props.modes={}] - Modes object passed to `getVariableByName` for all design tokens
|
|
30
34
|
* @param {Object} [props.style] - Optional container style overrides
|
|
31
35
|
* @param {string} [props.accessibilityLabel] - Accessibility label for the list group. If not provided, uses label
|
|
@@ -34,6 +38,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
34
38
|
function ListGroup({
|
|
35
39
|
label = '',
|
|
36
40
|
listGroupSlot,
|
|
41
|
+
children,
|
|
37
42
|
modes = {},
|
|
38
43
|
style,
|
|
39
44
|
accessibilityLabel,
|
|
@@ -70,10 +75,11 @@ function ListGroup({
|
|
|
70
75
|
fontWeight: labelFontWeight
|
|
71
76
|
};
|
|
72
77
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
const
|
|
78
|
+
// Merge listGroupSlot and children into a single list, then flatten and
|
|
79
|
+
// propagate modes. Both props are interchangeable; when both are provided
|
|
80
|
+
// the slot items render first, followed by children.
|
|
81
|
+
const rawItems = [...(listGroupSlot ? flattenChildren(listGroupSlot) : []), ...(children ? flattenChildren(children) : [])];
|
|
82
|
+
const processedSlot = rawItems.length > 0 ? cloneChildrenWithModes(rawItems, modes) : null;
|
|
77
83
|
|
|
78
84
|
// Use provided accessibilityLabel or fall back to label
|
|
79
85
|
const defaultAccessibilityLabel = accessibilityLabel || label || "List group";
|
|
@@ -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 || '';
|