jfs-components 0.0.56 → 0.0.58
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/ActionFooter/ActionFooter.js +20 -14
- package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +10 -7
- package/lib/commonjs/components/ListGroup/ListGroup.js +1 -1
- package/lib/commonjs/components/Section/Section.js +71 -8
- package/lib/commonjs/components/Tabs/Tabs.js +6 -3
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +24 -20
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/ActionFooter/ActionFooter.js +21 -15
- package/lib/module/components/ButtonGroup/ButtonGroup.js +10 -7
- package/lib/module/components/ListGroup/ListGroup.js +1 -1
- package/lib/module/components/Section/Section.js +73 -9
- package/lib/module/components/Tabs/Tabs.js +6 -3
- package/lib/module/components/TransactionBubble/TransactionBubble.js +24 -20
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +4 -10
- package/lib/typescript/src/components/ButtonGroup/ButtonGroup.d.ts +3 -1
- package/lib/typescript/src/components/TransactionBubble/TransactionBubble.d.ts +9 -6
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/ActionFooter/ActionFooter.tsx +20 -14
- package/src/components/ButtonGroup/ButtonGroup.tsx +16 -10
- package/src/components/ListGroup/ListGroup.tsx +1 -1
- package/src/components/Section/Section.tsx +90 -8
- package/src/components/Tabs/Tabs.tsx +2 -2
- package/src/components/TransactionBubble/TransactionBubble.tsx +17 -14
- package/src/icons/registry.ts +1 -1
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { View, Platform } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
6
|
+
import { cloneChildrenWithModes, flattenChildren } from '../../utils/react-utils';
|
|
7
|
+
import IconButton from '../IconButton/IconButton';
|
|
7
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
9
|
/**
|
|
9
10
|
* ActionFooter component that provides a fixed footer container for action buttons.
|
|
@@ -23,18 +24,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
23
24
|
*
|
|
24
25
|
* @example
|
|
25
26
|
* ```tsx
|
|
26
|
-
* // Basic usage - modes are automatically passed to all children
|
|
27
|
+
* // Basic usage - modes are automatically passed to all children.
|
|
28
|
+
* // Non-IconButton children (e.g., Button) are auto-stretched to fill.
|
|
27
29
|
* <ActionFooter modes={modes}>
|
|
28
30
|
* <IconButton iconName="ic_split" />
|
|
29
|
-
* <Button label="Request"
|
|
30
|
-
* <Button label="Pay"
|
|
31
|
-
* </ActionFooter>
|
|
32
|
-
*
|
|
33
|
-
* // Children can override with their own modes (merged with parent)
|
|
34
|
-
* <ActionFooter modes={modes}>
|
|
35
|
-
* <IconButton iconName="ic_split" />
|
|
36
|
-
* <Button label="Request" modes={{ Appearance: 'secondary' }} style={{ flex: 1 }} />
|
|
37
|
-
* <Button label="Pay" modes={{ Appearance: 'primary' }} style={{ flex: 1 }} />
|
|
31
|
+
* <Button label="Request" />
|
|
32
|
+
* <Button label="Pay" />
|
|
38
33
|
* </ActionFooter>
|
|
39
34
|
* ```
|
|
40
35
|
*/
|
|
@@ -90,16 +85,27 @@ function ActionFooter({
|
|
|
90
85
|
const webShadow = Platform.OS === 'web' ? {
|
|
91
86
|
boxShadow: '0px -12px 24px 0px rgba(12, 13, 16, 0.12), 0px -16px 48px 0px rgba(12, 13, 16, 0.16)'
|
|
92
87
|
} : {};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
88
|
+
const flatChildren = flattenChildren(children);
|
|
89
|
+
const processedChildren = cloneChildrenWithModes(flatChildren, modes);
|
|
90
|
+
const enhancedChildren = processedChildren.map((child, index) => {
|
|
91
|
+
if (! /*#__PURE__*/React.isValidElement(child)) return child;
|
|
92
|
+
const element = child;
|
|
93
|
+
const isIconButton = element.type === IconButton;
|
|
94
|
+
const stretchStyle = isIconButton ? undefined : {
|
|
95
|
+
flex: 1
|
|
96
|
+
};
|
|
97
|
+
return /*#__PURE__*/React.cloneElement(element, {
|
|
98
|
+
key: element.key ?? index,
|
|
99
|
+
style: [stretchStyle, element.props.style]
|
|
100
|
+
});
|
|
101
|
+
});
|
|
96
102
|
return /*#__PURE__*/_jsx(View, {
|
|
97
103
|
style: [containerStyle, webShadow, style],
|
|
98
104
|
accessibilityRole: "toolbar",
|
|
99
105
|
accessibilityLabel: undefined,
|
|
100
106
|
children: /*#__PURE__*/_jsx(View, {
|
|
101
107
|
style: slotStyle,
|
|
102
|
-
children:
|
|
108
|
+
children: enhancedChildren
|
|
103
109
|
})
|
|
104
110
|
});
|
|
105
111
|
}
|
|
@@ -4,19 +4,22 @@ import React from 'react';
|
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { flattenChildren } from '../../utils/react-utils';
|
|
7
|
+
import IconButton from '../IconButton/IconButton';
|
|
7
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
9
|
/**
|
|
9
10
|
* ButtonGroup component that aggregates multiple buttons (e.g., IconButton)
|
|
10
11
|
* and handles their layout and styling/theming via design tokens.
|
|
11
12
|
*
|
|
12
13
|
* It passes the provided `modes` to all its immediate React Element children.
|
|
14
|
+
* Non-IconButton children (e.g., Button) are automatically stretched to fill
|
|
15
|
+
* available space. IconButton children keep their natural width.
|
|
13
16
|
*
|
|
14
17
|
* @component
|
|
15
18
|
* @example
|
|
16
19
|
* ```jsx
|
|
17
20
|
* <ButtonGroup modes={{"Appearance": "light"}}>
|
|
18
21
|
* <IconButton iconName="ic_qr_code" />
|
|
19
|
-
* <
|
|
22
|
+
* <Button label="Pay" />
|
|
20
23
|
* </ButtonGroup>
|
|
21
24
|
* ```
|
|
22
25
|
*/
|
|
@@ -41,21 +44,21 @@ function ButtonGroup({
|
|
|
41
44
|
|
|
42
45
|
// Flatten children to handle Fragments properly
|
|
43
46
|
const flatChildren = flattenChildren(children);
|
|
44
|
-
|
|
45
|
-
// Clone children to pass `modes` prop
|
|
46
47
|
const childrenWithModes = React.Children.map(flatChildren, child => {
|
|
47
48
|
if (/*#__PURE__*/React.isValidElement(child)) {
|
|
48
|
-
// We safely try to pass `modes` to the child.
|
|
49
|
-
// We merge the group modes with any modes defined on the child explicitly.
|
|
50
|
-
// Child modes take precedence.
|
|
51
49
|
const element = child;
|
|
52
50
|
const childModes = element.props.modes || {};
|
|
53
51
|
const mergedModes = {
|
|
54
52
|
...modes,
|
|
55
53
|
...childModes
|
|
56
54
|
};
|
|
55
|
+
const isIconButton = element.type === IconButton;
|
|
56
|
+
const stretchStyle = isIconButton ? undefined : {
|
|
57
|
+
flex: 1
|
|
58
|
+
};
|
|
57
59
|
return /*#__PURE__*/React.cloneElement(element, {
|
|
58
|
-
modes: mergedModes
|
|
60
|
+
modes: mergedModes,
|
|
61
|
+
style: [stretchStyle, element.props.style]
|
|
59
62
|
});
|
|
60
63
|
}
|
|
61
64
|
return child;
|
|
@@ -1,12 +1,76 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
3
|
+
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
4
4
|
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
8
|
import { cloneChildrenWithModes, flattenChildren } from '../../utils/react-utils';
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Shared grid layout: measures widest child, enforces uniform width,
|
|
12
|
+
// chunks into fixed rows of up to maxColumns, space-between per row.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
9
14
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
15
|
+
const SLOT_GRID_MAX_COLUMNS = 4;
|
|
16
|
+
function SlotGrid({
|
|
17
|
+
items,
|
|
18
|
+
gap,
|
|
19
|
+
maxColumns = SLOT_GRID_MAX_COLUMNS
|
|
20
|
+
}) {
|
|
21
|
+
const [maxItemWidth, setMaxItemWidth] = useState(null);
|
|
22
|
+
const itemWidthsRef = useRef(new Map());
|
|
23
|
+
const totalItems = items.length;
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
itemWidthsRef.current.clear();
|
|
26
|
+
setMaxItemWidth(null);
|
|
27
|
+
}, [totalItems]);
|
|
28
|
+
const handleItemLayout = useCallback((index, width) => {
|
|
29
|
+
itemWidthsRef.current.set(index, width);
|
|
30
|
+
if (itemWidthsRef.current.size >= totalItems && totalItems > 0) {
|
|
31
|
+
setMaxItemWidth(Math.max(...itemWidthsRef.current.values()));
|
|
32
|
+
}
|
|
33
|
+
}, [totalItems]);
|
|
34
|
+
const isMeasured = maxItemWidth !== null;
|
|
35
|
+
const columns = Math.min(maxColumns, totalItems || 1);
|
|
36
|
+
const rows = [];
|
|
37
|
+
for (let i = 0; i < items.length; i += columns) {
|
|
38
|
+
rows.push(items.slice(i, i + columns));
|
|
39
|
+
}
|
|
40
|
+
return /*#__PURE__*/_jsx(View, {
|
|
41
|
+
style: {
|
|
42
|
+
gap,
|
|
43
|
+
...(isMeasured ? {} : {
|
|
44
|
+
opacity: 0
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
children: rows.map((row, rowIndex) => {
|
|
48
|
+
const spacersNeeded = row.length < columns ? columns - row.length : 0;
|
|
49
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
50
|
+
style: {
|
|
51
|
+
flexDirection: 'row',
|
|
52
|
+
justifyContent: 'space-between'
|
|
53
|
+
},
|
|
54
|
+
children: [row.map((child, colIndex) => {
|
|
55
|
+
const itemIndex = rowIndex * columns + colIndex;
|
|
56
|
+
return /*#__PURE__*/_jsx(View, {
|
|
57
|
+
onLayout: !isMeasured ? e => handleItemLayout(itemIndex, e.nativeEvent.layout.width) : undefined,
|
|
58
|
+
style: isMeasured ? {
|
|
59
|
+
width: maxItemWidth
|
|
60
|
+
} : undefined,
|
|
61
|
+
children: child
|
|
62
|
+
}, itemIndex);
|
|
63
|
+
}), isMeasured && spacersNeeded > 0 && Array.from({
|
|
64
|
+
length: spacersNeeded
|
|
65
|
+
}, (_, i) => /*#__PURE__*/_jsx(View, {
|
|
66
|
+
style: {
|
|
67
|
+
width: maxItemWidth
|
|
68
|
+
}
|
|
69
|
+
}, `spacer-${i}`))]
|
|
70
|
+
}, rowIndex);
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
}
|
|
10
74
|
/**
|
|
11
75
|
* Section component that mirrors the Figma "Section" component.
|
|
12
76
|
*
|
|
@@ -196,9 +260,12 @@ function Section({
|
|
|
196
260
|
}) : /*#__PURE__*/_jsx(View, {
|
|
197
261
|
style: headerStyle,
|
|
198
262
|
children: headerContent
|
|
199
|
-
}), slot && /*#__PURE__*/_jsx(
|
|
263
|
+
}), slot && slotDirection === 'row' && /*#__PURE__*/_jsx(SlotGrid, {
|
|
264
|
+
items: cloneChildrenWithModes(flattenChildren(slot), modes),
|
|
265
|
+
gap: sectionGap
|
|
266
|
+
}), slot && slotDirection === 'column' && /*#__PURE__*/_jsx(View, {
|
|
200
267
|
style: {
|
|
201
|
-
flexDirection:
|
|
268
|
+
flexDirection: 'column',
|
|
202
269
|
gap: slotGap
|
|
203
270
|
},
|
|
204
271
|
children: cloneChildrenWithModes(flattenChildren(slot), modes)
|
|
@@ -259,12 +326,9 @@ function SectionBento({
|
|
|
259
326
|
accessibilityLabel: undefined,
|
|
260
327
|
accessibilityHint: accessibilityHint,
|
|
261
328
|
...rest,
|
|
262
|
-
children: [processedNavSlot && /*#__PURE__*/_jsx(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
gap
|
|
266
|
-
},
|
|
267
|
-
children: processedNavSlot
|
|
329
|
+
children: [processedNavSlot && /*#__PURE__*/_jsx(SlotGrid, {
|
|
330
|
+
items: processedNavSlot,
|
|
331
|
+
gap: gap
|
|
268
332
|
}), processedUpiSlot && /*#__PURE__*/_jsx(View, {
|
|
269
333
|
style: {
|
|
270
334
|
flexDirection: 'row',
|
|
@@ -37,8 +37,6 @@ function Tabs({
|
|
|
37
37
|
const paddingBottom = getVariableByName('tabs/padding/bottom', modes) ?? 0;
|
|
38
38
|
const paddingLeft = getVariableByName('tabs/padding/left', modes) ?? 0;
|
|
39
39
|
const paddingRight = getVariableByName('tabs/padding/right', modes) ?? 0;
|
|
40
|
-
|
|
41
|
-
// Forward modes to all TabItem children
|
|
42
40
|
const enhancedChildren = React.Children.map(children, child => {
|
|
43
41
|
if (/*#__PURE__*/React.isValidElement(child) && child.type === TabItem) {
|
|
44
42
|
const childElement = child;
|
|
@@ -46,7 +44,12 @@ function Tabs({
|
|
|
46
44
|
modes: {
|
|
47
45
|
...modes,
|
|
48
46
|
...(childElement.props.modes ?? {})
|
|
49
|
-
}
|
|
47
|
+
},
|
|
48
|
+
...(scrollable ? {} : {
|
|
49
|
+
style: [{
|
|
50
|
+
flex: 1
|
|
51
|
+
}, childElement.props.style]
|
|
52
|
+
})
|
|
50
53
|
});
|
|
51
54
|
}
|
|
52
55
|
return child;
|
|
@@ -12,16 +12,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
/**
|
|
13
13
|
* TransactionBubble — Figma node 1517:1155.
|
|
14
14
|
*
|
|
15
|
-
* Layout (
|
|
15
|
+
* Layout (vertical stack inside a rounded bordered pill):
|
|
16
16
|
*
|
|
17
17
|
* ┌──────────────────────────────────────────────┐
|
|
18
|
-
* │ Description
|
|
19
|
-
* │ ₹56
|
|
18
|
+
* │ Description │
|
|
19
|
+
* │ ₹56 │
|
|
20
|
+
* │ [slot / children content] │
|
|
21
|
+
* │ ⚠ Expired · 20 Mar 2025 › │
|
|
20
22
|
* └──────────────────────────────────────────────┘
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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`.
|
|
25
28
|
*/
|
|
26
29
|
function TransactionBubble({
|
|
27
30
|
description = 'Payment to Uber India',
|
|
@@ -49,12 +52,13 @@ function TransactionBubble({
|
|
|
49
52
|
const backgroundColor = getVariableByName('transactionBubble/background', resolvedModes) || '#ffffff';
|
|
50
53
|
const borderColor = getVariableByName('transactionBubble/border/color', resolvedModes) || '#e5e5e5';
|
|
51
54
|
const bubbleGap = Number(getVariableByName('transactionBubble/gap', resolvedModes)) || 8;
|
|
52
|
-
const
|
|
55
|
+
const moneyValueWrapGap = Number(getVariableByName('transactionBubble/wrap/gap', resolvedModes)) || 8;
|
|
53
56
|
const descriptionColor = getVariableByName('transactionBubble/description/color', resolvedModes) || '#24262b';
|
|
54
57
|
const descriptionFontSize = Number(getVariableByName('transactionBubble/description/fontSize', resolvedModes)) || 14;
|
|
55
58
|
const descriptionLineHeight = Number(getVariableByName('transactionBubble/description/lineHeight', resolvedModes)) || 17;
|
|
56
59
|
const descriptionFontFamily = getVariableByName('transactionBubble/description/fontFamily', resolvedModes) || 'JioType Var';
|
|
57
60
|
const statusWrapGap = Number(getVariableByName('transactionBubble/statusWrap/gap', resolvedModes)) || 4;
|
|
61
|
+
const statusWrapHeight = Number(getVariableByName('transactionBubble/statusWrap/height', resolvedModes)) || 18;
|
|
58
62
|
const containerStyle = {
|
|
59
63
|
padding,
|
|
60
64
|
borderRadius: radius,
|
|
@@ -78,11 +82,13 @@ function TransactionBubble({
|
|
|
78
82
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
79
83
|
webAccessibilityProps
|
|
80
84
|
});
|
|
81
|
-
const
|
|
85
|
+
const statusRow = processedStatusSlot || /*#__PURE__*/_jsxs(View, {
|
|
82
86
|
style: {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
justifyContent: 'space-between',
|
|
90
|
+
gap: statusWrapGap,
|
|
91
|
+
height: statusWrapHeight
|
|
86
92
|
},
|
|
87
93
|
children: [/*#__PURE__*/_jsx(TransactionStatus, {
|
|
88
94
|
status: status,
|
|
@@ -95,13 +101,11 @@ function TransactionBubble({
|
|
|
95
101
|
});
|
|
96
102
|
const mainContent = /*#__PURE__*/_jsxs(View, {
|
|
97
103
|
style: {
|
|
98
|
-
|
|
99
|
-
gap: wrapGap
|
|
104
|
+
gap: bubbleGap
|
|
100
105
|
},
|
|
101
106
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
102
107
|
style: {
|
|
103
|
-
|
|
104
|
-
gap: bubbleGap
|
|
108
|
+
gap: moneyValueWrapGap
|
|
105
109
|
},
|
|
106
110
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
107
111
|
style: descriptionStyle,
|
|
@@ -112,10 +116,10 @@ function TransactionBubble({
|
|
|
112
116
|
currency: currency,
|
|
113
117
|
modes: resolvedModes
|
|
114
118
|
})]
|
|
115
|
-
}),
|
|
119
|
+
}), processedChildren, statusRow]
|
|
116
120
|
});
|
|
117
121
|
if (onPress) {
|
|
118
|
-
return /*#__PURE__*/
|
|
122
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
119
123
|
onPress: onPress,
|
|
120
124
|
style: ({
|
|
121
125
|
pressed
|
|
@@ -127,14 +131,14 @@ function TransactionBubble({
|
|
|
127
131
|
accessibilityHint: accessibilityHint,
|
|
128
132
|
...webProps,
|
|
129
133
|
...rest,
|
|
130
|
-
children:
|
|
134
|
+
children: mainContent
|
|
131
135
|
});
|
|
132
136
|
}
|
|
133
|
-
return /*#__PURE__*/
|
|
137
|
+
return /*#__PURE__*/_jsx(View, {
|
|
134
138
|
style: [containerStyle, style],
|
|
135
139
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
136
140
|
accessibilityHint: accessibilityHint,
|
|
137
|
-
children:
|
|
141
|
+
children: mainContent
|
|
138
142
|
});
|
|
139
143
|
}
|
|
140
144
|
export default TransactionBubble;
|