jfs-components 0.0.2 → 0.0.4
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 +217 -0
- package/lib/commonjs/components/Accordion/Accordion.js.map +1 -0
- package/lib/commonjs/components/Accordion/Accordion.mdx +122 -0
- package/lib/commonjs/components/ActionTile/ActionTile.js +93 -0
- package/lib/commonjs/components/ActionTile/ActionTile.js.map +1 -0
- package/lib/commonjs/components/ActionTile/ActionTile.mdx +54 -0
- package/lib/commonjs/components/ActionTile/index.js +14 -0
- package/lib/commonjs/components/ActionTile/index.js.map +1 -0
- package/lib/commonjs/components/CtaCard/CtaCard.js +122 -0
- package/lib/commonjs/components/CtaCard/CtaCard.js.map +1 -0
- package/lib/commonjs/components/CtaCard/CtaCard.mdx +66 -0
- package/lib/commonjs/components/CtaCard/index.js +14 -0
- package/lib/commonjs/components/CtaCard/index.js.map +1 -0
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/commonjs/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/commonjs/components/ThreadHero/ThreadHero.js +114 -0
- package/lib/commonjs/components/ThreadHero/ThreadHero.js.map +1 -0
- package/lib/commonjs/components/ThreadHero/ThreadHero.mdx +69 -0
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js +101 -0
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.js.map +1 -0
- package/lib/commonjs/components/TransactionBubble/TransactionBubble.mdx +40 -0
- package/lib/commonjs/components/TransactionDetails/TransactionDetails.js +177 -0
- package/lib/commonjs/components/TransactionDetails/TransactionDetails.js.map +1 -0
- package/lib/commonjs/components/TransactionDetails/TransactionDetails.mdx +98 -0
- package/lib/commonjs/components/TransactionStatus/TransactionStatus.js +82 -0
- package/lib/commonjs/components/TransactionStatus/TransactionStatus.js.map +1 -0
- package/lib/commonjs/components/TransactionStatus/TransactionStatus.mdx +41 -0
- package/lib/commonjs/components/index.js +35 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/design-tokens/figma-variables-resolver.js +3 -9
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Accordion/Accordion.js +212 -0
- package/lib/module/components/Accordion/Accordion.js.map +1 -0
- package/lib/module/components/Accordion/Accordion.mdx +122 -0
- package/lib/module/components/ActionTile/ActionTile.js +88 -0
- package/lib/module/components/ActionTile/ActionTile.js.map +1 -0
- package/lib/module/components/ActionTile/ActionTile.mdx +54 -0
- package/lib/module/components/ActionTile/index.js +4 -0
- package/lib/module/components/ActionTile/index.js.map +1 -0
- package/lib/module/components/CtaCard/CtaCard.js +117 -0
- package/lib/module/components/CtaCard/CtaCard.js.map +1 -0
- package/lib/module/components/CtaCard/CtaCard.mdx +66 -0
- package/lib/module/components/CtaCard/index.js +4 -0
- package/lib/module/components/CtaCard/index.js.map +1 -0
- package/lib/module/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/module/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/module/components/ThreadHero/ThreadHero.js +109 -0
- package/lib/module/components/ThreadHero/ThreadHero.js.map +1 -0
- package/lib/module/components/ThreadHero/ThreadHero.mdx +69 -0
- package/lib/module/components/TransactionBubble/TransactionBubble.js +96 -0
- package/lib/module/components/TransactionBubble/TransactionBubble.js.map +1 -0
- package/lib/module/components/TransactionBubble/TransactionBubble.mdx +40 -0
- package/lib/module/components/TransactionDetails/TransactionDetails.js +174 -0
- package/lib/module/components/TransactionDetails/TransactionDetails.js.map +1 -0
- package/lib/module/components/TransactionDetails/TransactionDetails.mdx +98 -0
- package/lib/module/components/TransactionStatus/TransactionStatus.js +77 -0
- package/lib/module/components/TransactionStatus/TransactionStatus.js.map +1 -0
- package/lib/module/components/TransactionStatus/TransactionStatus.mdx +41 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/components/Accordion/Accordion.d.ts +58 -0
- package/lib/typescript/components/Accordion/Accordion.d.ts.map +1 -0
- package/lib/typescript/components/ActionTile/ActionTile.d.ts +26 -0
- package/lib/typescript/components/ActionTile/ActionTile.d.ts.map +1 -0
- package/lib/typescript/components/ActionTile/index.d.ts +2 -0
- package/lib/typescript/components/ActionTile/index.d.ts.map +1 -0
- package/lib/typescript/components/CtaCard/CtaCard.d.ts +61 -0
- package/lib/typescript/components/CtaCard/CtaCard.d.ts.map +1 -0
- package/lib/typescript/components/CtaCard/index.d.ts +2 -0
- package/lib/typescript/components/CtaCard/index.d.ts.map +1 -0
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts +9 -2
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts.map +1 -1
- package/lib/typescript/components/ThreadHero/ThreadHero.d.ts +21 -0
- package/lib/typescript/components/ThreadHero/ThreadHero.d.ts.map +1 -0
- package/lib/typescript/components/TransactionBubble/TransactionBubble.d.ts +24 -0
- package/lib/typescript/components/TransactionBubble/TransactionBubble.d.ts.map +1 -0
- package/lib/typescript/components/TransactionDetails/TransactionDetails.d.ts +57 -0
- package/lib/typescript/components/TransactionDetails/TransactionDetails.d.ts.map +1 -0
- package/lib/typescript/components/TransactionStatus/TransactionStatus.d.ts +16 -0
- package/lib/typescript/components/TransactionStatus/TransactionStatus.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +5 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/icons/registry.d.ts +1 -1
- package/package.json +5 -2
- package/src/components/.token-metadata.json +161 -0
- package/src/components/Accordion/Accordion.mdx +122 -0
- package/src/components/Accordion/Accordion.tsx +279 -0
- package/src/components/ActionTile/ActionTile.mdx +54 -0
- package/src/components/ActionTile/ActionTile.tsx +100 -0
- package/src/components/ActionTile/index.ts +1 -0
- package/src/components/CtaCard/CtaCard.mdx +66 -0
- package/src/components/CtaCard/CtaCard.tsx +185 -0
- package/src/components/CtaCard/index.ts +1 -0
- package/src/components/MoneyValue/MoneyValue.tsx +39 -3
- package/src/components/ThreadHero/ThreadHero.mdx +69 -0
- package/src/components/ThreadHero/ThreadHero.tsx +124 -0
- package/src/components/TransactionBubble/TransactionBubble.mdx +40 -0
- package/src/components/TransactionBubble/TransactionBubble.tsx +113 -0
- package/src/components/TransactionDetails/TransactionDetails.mdx +98 -0
- package/src/components/TransactionDetails/TransactionDetails.tsx +236 -0
- package/src/components/TransactionStatus/TransactionStatus.mdx +41 -0
- package/src/components/TransactionStatus/TransactionStatus.tsx +94 -0
- package/src/components/index.ts +5 -0
- package/src/icons/registry.ts +1 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, type StyleProp, type ViewStyle, type TextStyle } from 'react-native';
|
|
3
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
4
|
+
import IconCapsule from '../IconCapsule/IconCapsule';
|
|
5
|
+
import Button from '../Button/Button';
|
|
6
|
+
|
|
7
|
+
type CtaCardProps = {
|
|
8
|
+
/**
|
|
9
|
+
* Title text for the card
|
|
10
|
+
*/
|
|
11
|
+
title?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Body text for the card
|
|
14
|
+
*/
|
|
15
|
+
body?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Name of the icon to display in the IconCapsule (if iconSlot is not provided)
|
|
18
|
+
*/
|
|
19
|
+
iconName?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Label for the button (if buttonSlot is not provided)
|
|
22
|
+
*/
|
|
23
|
+
buttonLabel?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Callback for the button press (if buttonSlot is not provided)
|
|
26
|
+
*/
|
|
27
|
+
onPressButton?: () => void;
|
|
28
|
+
/**
|
|
29
|
+
* Modes object for design token resolution
|
|
30
|
+
*/
|
|
31
|
+
modes?: Record<string, any>;
|
|
32
|
+
/**
|
|
33
|
+
* Optional custom slot for the icon area.
|
|
34
|
+
* If provided, overrides the default IconCapsule.
|
|
35
|
+
* `modes` will be automatically passed to this slot if it's a valid React element.
|
|
36
|
+
*/
|
|
37
|
+
iconSlot?: React.ReactNode;
|
|
38
|
+
/**
|
|
39
|
+
* Optional custom slot for the title area.
|
|
40
|
+
* If provided, overrides the default title Text.
|
|
41
|
+
* `modes` will be automatically passed to this slot if it's a valid React element.
|
|
42
|
+
*/
|
|
43
|
+
titleSlot?: React.ReactNode;
|
|
44
|
+
/**
|
|
45
|
+
* Optional custom slot for the body area.
|
|
46
|
+
* If provided, overrides the default body Text.
|
|
47
|
+
* `modes` will be automatically passed to this slot if it's a valid React element.
|
|
48
|
+
*/
|
|
49
|
+
bodySlot?: React.ReactNode;
|
|
50
|
+
/**
|
|
51
|
+
* Optional custom slot for the button area.
|
|
52
|
+
* If provided, overrides the default Button.
|
|
53
|
+
* `modes` will be automatically passed to this slot if it's a valid React element.
|
|
54
|
+
*/
|
|
55
|
+
buttonSlot?: React.ReactNode;
|
|
56
|
+
|
|
57
|
+
style?: StyleProp<ViewStyle>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* CtaCard component that maps directly to the Figma design using design tokens.
|
|
62
|
+
*
|
|
63
|
+
* @component
|
|
64
|
+
*/
|
|
65
|
+
function CtaCard({
|
|
66
|
+
title = "Lets create your UPI ID",
|
|
67
|
+
body = "Send and receive money securely.",
|
|
68
|
+
iconName = "ic_upi_number",
|
|
69
|
+
buttonLabel = "Get started",
|
|
70
|
+
onPressButton,
|
|
71
|
+
modes = {},
|
|
72
|
+
iconSlot,
|
|
73
|
+
titleSlot,
|
|
74
|
+
bodySlot,
|
|
75
|
+
buttonSlot,
|
|
76
|
+
style,
|
|
77
|
+
}: CtaCardProps) {
|
|
78
|
+
// --- Token Resolution ---
|
|
79
|
+
// Container
|
|
80
|
+
const backgroundColor = getVariableByName('ctaCard/background', modes) || '#0078ad';
|
|
81
|
+
const radius = getVariableByName('ctaCard/radius', modes) || 16;
|
|
82
|
+
const paddingHorizontal = getVariableByName('ctaCard/padding/horizontal', modes) || 20;
|
|
83
|
+
const paddingVertical = getVariableByName('ctaCard/padding/vertical', modes) || 23;
|
|
84
|
+
const gap = getVariableByName('ctaCard/gap', modes) || 16;
|
|
85
|
+
|
|
86
|
+
// Content Wrap
|
|
87
|
+
const contentGap = getVariableByName('ctaCard/content/gap', modes) || 8;
|
|
88
|
+
|
|
89
|
+
// Title Text
|
|
90
|
+
const titleColor = getVariableByName('ctaCard/title/color', modes) || '#ffffff';
|
|
91
|
+
const titleFontFamily = getVariableByName('ctaCard/title/fontFamily', modes) || 'JioType Var';
|
|
92
|
+
const titleFontSize = getVariableByName('ctaCard/title/fontSize', modes) || 23;
|
|
93
|
+
const titleLineHeight = getVariableByName('ctaCard/title/lineHeight', modes) || 23;
|
|
94
|
+
|
|
95
|
+
// Body Text
|
|
96
|
+
const bodyColor = getVariableByName('ctaCard/body/color', modes) || '#ffffff';
|
|
97
|
+
const bodyFontFamily = getVariableByName('ctaCard/body/fontFamily', modes) || 'JioType Var';
|
|
98
|
+
const bodyFontSize = getVariableByName('ctaCard/body/fontSize', modes) || 14;
|
|
99
|
+
const bodyLineHeight = getVariableByName('ctaCard/body/lineHeight', modes) || 17;
|
|
100
|
+
|
|
101
|
+
// --- Styles ---
|
|
102
|
+
const containerStyle: ViewStyle = {
|
|
103
|
+
backgroundColor,
|
|
104
|
+
borderRadius: radius,
|
|
105
|
+
paddingHorizontal,
|
|
106
|
+
paddingVertical,
|
|
107
|
+
gap,
|
|
108
|
+
alignItems: 'flex-start',
|
|
109
|
+
width: '100%', // Adjust based on layout needs, usually cards take full width available
|
|
110
|
+
overflow: 'hidden',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const contentWrapStyle: ViewStyle = {
|
|
114
|
+
gap: contentGap,
|
|
115
|
+
alignItems: 'flex-start',
|
|
116
|
+
width: '100%',
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const titleStyle: TextStyle = {
|
|
120
|
+
color: titleColor,
|
|
121
|
+
fontFamily: titleFontFamily,
|
|
122
|
+
fontSize: titleFontSize,
|
|
123
|
+
lineHeight: titleLineHeight,
|
|
124
|
+
fontWeight: '900', // "Black" in Figma often maps to 900
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const bodyStyle: TextStyle = {
|
|
128
|
+
color: bodyColor,
|
|
129
|
+
fontFamily: bodyFontFamily,
|
|
130
|
+
fontSize: bodyFontSize,
|
|
131
|
+
lineHeight: bodyLineHeight,
|
|
132
|
+
fontWeight: '400', // Regular
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// --- Slot Helper ---
|
|
136
|
+
// Helper to clone element with modes if no modes are already present on it (standard in this library)
|
|
137
|
+
const renderSlot = (slot: React.ReactNode) => {
|
|
138
|
+
if (React.isValidElement(slot)) {
|
|
139
|
+
return React.cloneElement(slot as React.ReactElement<any>, {
|
|
140
|
+
modes: { ...modes, ...(slot as any).props.modes },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return slot;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<View style={[containerStyle, style]}>
|
|
148
|
+
<View style={contentWrapStyle}>
|
|
149
|
+
{/* Icon Slot */}
|
|
150
|
+
{iconSlot ? (
|
|
151
|
+
renderSlot(iconSlot)
|
|
152
|
+
) : (
|
|
153
|
+
<IconCapsule iconName={iconName} modes={modes} />
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{/* Title Slot */}
|
|
157
|
+
{titleSlot ? (
|
|
158
|
+
renderSlot(titleSlot)
|
|
159
|
+
) : (
|
|
160
|
+
<Text style={titleStyle}>{title}</Text>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{/* Body Slot */}
|
|
164
|
+
{bodySlot ? (
|
|
165
|
+
renderSlot(bodySlot)
|
|
166
|
+
) : (
|
|
167
|
+
<Text style={bodyStyle}>{body}</Text>
|
|
168
|
+
)}
|
|
169
|
+
</View>
|
|
170
|
+
|
|
171
|
+
{/* Button Slot - placed outside content wrap based on Figma structure (flex-col gap=16) */}
|
|
172
|
+
{buttonSlot ? (
|
|
173
|
+
renderSlot(buttonSlot)
|
|
174
|
+
) : (
|
|
175
|
+
<Button
|
|
176
|
+
label={buttonLabel}
|
|
177
|
+
onPress={onPressButton}
|
|
178
|
+
modes={modes}
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
</View>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export default CtaCard;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './CtaCard';
|
|
@@ -40,10 +40,12 @@ const CURRENCY_SYMBOLS = {
|
|
|
40
40
|
type MoneyValueProps = {
|
|
41
41
|
value?: string | number;
|
|
42
42
|
currency?: string;
|
|
43
|
+
negative?: boolean;
|
|
43
44
|
modes?: Record<string, any>;
|
|
44
45
|
style?: StyleProp<ViewStyle>;
|
|
45
46
|
valueStyle?: StyleProp<TextStyle>;
|
|
46
47
|
currencyStyle?: StyleProp<TextStyle>;
|
|
48
|
+
negativeSignStyle?: StyleProp<TextStyle>;
|
|
47
49
|
accessibilityLabel?: string;
|
|
48
50
|
accessibilityHint?: string;
|
|
49
51
|
} & React.ComponentProps<typeof View>;
|
|
@@ -58,29 +60,54 @@ type MoneyValueProps = {
|
|
|
58
60
|
* (e.g. "INR", "USD"). When an ISO code is provided, it will be mapped to the
|
|
59
61
|
* appropriate symbol, with special care to support INR and other major currencies.
|
|
60
62
|
*
|
|
63
|
+
* Negative values are auto-detected from the value prop (e.g., -500 or "-500").
|
|
64
|
+
* The `negative` prop can be used to explicitly override this behavior.
|
|
65
|
+
*
|
|
61
66
|
* @component
|
|
62
67
|
* @param {Object} props
|
|
63
|
-
* @param {string|number} [props.value="500"] - Monetary value to display.
|
|
68
|
+
* @param {string|number} [props.value="500"] - Monetary value to display. Negative values are auto-detected.
|
|
64
69
|
* @param {string} [props.currency="₹"] - Currency symbol or ISO code (e.g. "INR").
|
|
70
|
+
* @param {boolean} [props.negative] - Explicitly override negative display. If undefined, auto-detects from value.
|
|
65
71
|
* @param {Object} [props.modes={}] - Modes object passed directly to `getVariableByName`.
|
|
66
72
|
* Example: {"MoneyValue / Theme": "Default"}
|
|
67
73
|
* @param {Object} [props.style] - Optional container style overrides.
|
|
68
74
|
* @param {Object} [props.valueStyle] - Optional value text style overrides.
|
|
69
75
|
* @param {Object} [props.currencyStyle] - Optional currency text style overrides.
|
|
76
|
+
* @param {Object} [props.negativeSignStyle] - Optional negative sign text style overrides.
|
|
70
77
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, generates from value and currency
|
|
71
78
|
* @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
|
|
72
79
|
*/
|
|
73
80
|
function MoneyValue({
|
|
74
81
|
value = '500',
|
|
75
82
|
currency = '₹',
|
|
83
|
+
negative,
|
|
76
84
|
modes = {},
|
|
77
85
|
style,
|
|
78
86
|
valueStyle,
|
|
79
87
|
currencyStyle,
|
|
88
|
+
negativeSignStyle,
|
|
80
89
|
accessibilityLabel,
|
|
81
90
|
accessibilityHint,
|
|
82
91
|
...rest
|
|
83
92
|
}: MoneyValueProps) {
|
|
93
|
+
// Auto-detect negative from value and compute display value
|
|
94
|
+
const { isNegative, displayValue } = React.useMemo(() => {
|
|
95
|
+
const stringValue = String(value)
|
|
96
|
+
const trimmed = stringValue.trim()
|
|
97
|
+
const valueIsNegative = trimmed.startsWith('-') || (typeof value === 'number' && value < 0)
|
|
98
|
+
|
|
99
|
+
// Strip leading minus sign for display (we show it separately)
|
|
100
|
+
const absoluteValue = trimmed.startsWith('-') ? trimmed.slice(1) : trimmed
|
|
101
|
+
|
|
102
|
+
// Use explicit negative prop if provided, otherwise use auto-detected
|
|
103
|
+
const showNegative = negative !== undefined ? negative : valueIsNegative
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
isNegative: showNegative,
|
|
107
|
+
displayValue: absoluteValue,
|
|
108
|
+
}
|
|
109
|
+
}, [value, negative])
|
|
110
|
+
|
|
84
111
|
// Resolve typography and layout tokens from Figma
|
|
85
112
|
const textColor =
|
|
86
113
|
getVariableByName('moneyValue/text/color', modes) || '#0f0d0a'
|
|
@@ -122,7 +149,7 @@ function MoneyValue({
|
|
|
122
149
|
|
|
123
150
|
// Generate default accessibility label from value and currency
|
|
124
151
|
const defaultAccessibilityLabel = accessibilityLabel ||
|
|
125
|
-
`${resolvedCurrency} ${
|
|
152
|
+
`${isNegative ? 'negative ' : ''}${resolvedCurrency} ${displayValue}`
|
|
126
153
|
|
|
127
154
|
return (
|
|
128
155
|
<View
|
|
@@ -132,6 +159,15 @@ function MoneyValue({
|
|
|
132
159
|
accessibilityHint={accessibilityHint}
|
|
133
160
|
{...rest}
|
|
134
161
|
>
|
|
162
|
+
{isNegative && (
|
|
163
|
+
<Text
|
|
164
|
+
style={[baseTextStyle, negativeSignStyle]}
|
|
165
|
+
accessibilityElementsHidden={true}
|
|
166
|
+
importantForAccessibility="no"
|
|
167
|
+
>
|
|
168
|
+
-
|
|
169
|
+
</Text>
|
|
170
|
+
)}
|
|
135
171
|
<Text
|
|
136
172
|
style={[baseTextStyle, currencyStyle]}
|
|
137
173
|
accessibilityElementsHidden={true}
|
|
@@ -144,7 +180,7 @@ function MoneyValue({
|
|
|
144
180
|
accessibilityElementsHidden={true}
|
|
145
181
|
importantForAccessibility="no"
|
|
146
182
|
>
|
|
147
|
-
{
|
|
183
|
+
{displayValue}
|
|
148
184
|
</Text>
|
|
149
185
|
</View>
|
|
150
186
|
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as ThreadHeroStories from './ThreadHero.stories';
|
|
3
|
+
import ThreadHero from './ThreadHero';
|
|
4
|
+
|
|
5
|
+
<Meta of={ThreadHeroStories} />
|
|
6
|
+
|
|
7
|
+
# ThreadHero
|
|
8
|
+
|
|
9
|
+
The `ThreadHero` component displays a central user identity with an avatar, title, subtitle, and an optional caption.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Available Collections and Modes
|
|
13
|
+
|
|
14
|
+
This component does not use any design token collections with multiple modes.
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import ThreadHero from './ThreadHero';
|
|
19
|
+
import Avatar from '../Avatar/Avatar';
|
|
20
|
+
|
|
21
|
+
<ThreadHero
|
|
22
|
+
title="Subhash Rajan"
|
|
23
|
+
subtitle="Banking name: SHIVASHANKAR RAJAN"
|
|
24
|
+
caption="+91 00000 00000"
|
|
25
|
+
renderAvatar={<Avatar size="small" initials="SR" />}
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
<ArgsTable of={ThreadHero} />
|
|
32
|
+
|
|
33
|
+
## Stories
|
|
34
|
+
|
|
35
|
+
### Default
|
|
36
|
+
|
|
37
|
+
<Canvas>
|
|
38
|
+
<Story of={ThreadHeroStories.Default} />
|
|
39
|
+
</Canvas>
|
|
40
|
+
|
|
41
|
+
### Without Caption
|
|
42
|
+
|
|
43
|
+
<Canvas>
|
|
44
|
+
<Story of={ThreadHeroStories.WithoutCaption} />
|
|
45
|
+
</Canvas>
|
|
46
|
+
|
|
47
|
+
## Design Tokens
|
|
48
|
+
|
|
49
|
+
This component uses the following design tokens, resolved through `getVariableByName`:
|
|
50
|
+
|
|
51
|
+
- **`threadHero/caption/color`**
|
|
52
|
+
- **`threadHero/caption/fontFamily`**
|
|
53
|
+
- **`threadHero/caption/fontSize`**
|
|
54
|
+
- **`threadHero/caption/fontWeight`**
|
|
55
|
+
- **`threadHero/caption/lineHeight`**
|
|
56
|
+
- **`threadHero/details/gap`**
|
|
57
|
+
- **`threadHero/gap`**
|
|
58
|
+
- **`threadHero/subtitle/color`**
|
|
59
|
+
- **`threadHero/subtitle/fontFamily`**
|
|
60
|
+
- **`threadHero/subtitle/fontSize`**
|
|
61
|
+
- **`threadHero/subtitle/fontWeight`**
|
|
62
|
+
- **`threadHero/subtitle/lineHeight`**
|
|
63
|
+
- **`threadHero/title/color`**
|
|
64
|
+
- **`threadHero/title/fontFamily`**
|
|
65
|
+
- **`threadHero/title/fontSize`**
|
|
66
|
+
- **`threadHero/title/fontWeight`**
|
|
67
|
+
- **`threadHero/title/lineHeight`**
|
|
68
|
+
|
|
69
|
+
All tokens support mode-based theming through the `modes` prop.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React, { isValidElement, cloneElement, type ReactNode } from 'react'
|
|
2
|
+
import { View, Text, type ViewStyle, type TextStyle } from 'react-native'
|
|
3
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
|
+
|
|
5
|
+
export type ThreadHeroProps = {
|
|
6
|
+
/** Title text, e.g. "Subhash Rajan" */
|
|
7
|
+
title?: string
|
|
8
|
+
/** Subtitle text, e.g. "Banking name: SHIVASHANKAR RAJAN" */
|
|
9
|
+
subtitle?: string
|
|
10
|
+
/** Optional caption text below the details, e.g. "+91 00000 00000" */
|
|
11
|
+
caption?: string
|
|
12
|
+
/** Slot for Avatar component */
|
|
13
|
+
renderAvatar?: ReactNode
|
|
14
|
+
/** Modes for design token resolution */
|
|
15
|
+
modes?: Record<string, any>
|
|
16
|
+
/** Optional container style */
|
|
17
|
+
style?: ViewStyle
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* ThreadHero component displaying a central identity with avatar, title, subtitle, and caption.
|
|
22
|
+
*/
|
|
23
|
+
export default function ThreadHero({
|
|
24
|
+
title = 'Subhash Rajan',
|
|
25
|
+
subtitle = 'Banking name: SHIVASHANKAR RAJAN',
|
|
26
|
+
caption,
|
|
27
|
+
renderAvatar,
|
|
28
|
+
modes = {},
|
|
29
|
+
style,
|
|
30
|
+
}: ThreadHeroProps) {
|
|
31
|
+
// Container Gaps
|
|
32
|
+
const containerGap = Number(getVariableByName('threadHero/gap', modes)) || 8
|
|
33
|
+
const detailsGap = Number(getVariableByName('threadHero/details/gap', modes)) || 2
|
|
34
|
+
|
|
35
|
+
// Title Styles
|
|
36
|
+
const titleColor = getVariableByName('threadHero/title/color', modes) || '#191b1e'
|
|
37
|
+
const titleFontSize = Number(getVariableByName('threadHero/title/fontSize', modes)) || 16
|
|
38
|
+
const titleFontFamily = getVariableByName('threadHero/title/fontFamily', modes) || 'System'
|
|
39
|
+
const titleLineHeight = Number(getVariableByName('threadHero/title/lineHeight', modes)) || 20
|
|
40
|
+
const titleFontWeight = getVariableByName('threadHero/title/fontWeight', modes) || '600'
|
|
41
|
+
|
|
42
|
+
// Subtitle Styles
|
|
43
|
+
const subtitleColor = getVariableByName('threadHero/subtitle/color', modes) || '#5d5d61'
|
|
44
|
+
const subtitleFontSize = Number(getVariableByName('threadHero/subtitle/fontSize', modes)) || 12
|
|
45
|
+
const subtitleFontFamily = getVariableByName('threadHero/subtitle/fontFamily', modes) || 'System'
|
|
46
|
+
const subtitleLineHeight = Number(getVariableByName('threadHero/subtitle/lineHeight', modes)) || 18
|
|
47
|
+
const subtitleFontWeight = getVariableByName('threadHero/subtitle/fontWeight', modes) || '500'
|
|
48
|
+
|
|
49
|
+
// Caption Styles
|
|
50
|
+
const captionColor = getVariableByName('threadHero/caption/color', modes) || '#5d5d61'
|
|
51
|
+
const captionFontSize = Number(getVariableByName('threadHero/caption/fontSize', modes)) || 12
|
|
52
|
+
const captionFontFamily = getVariableByName('threadHero/caption/fontFamily', modes) || 'System'
|
|
53
|
+
const captionLineHeight = Number(getVariableByName('threadHero/caption/lineHeight', modes)) || 18
|
|
54
|
+
const captionFontWeight = getVariableByName('threadHero/caption/fontWeight', modes) || '500'
|
|
55
|
+
|
|
56
|
+
// Styles
|
|
57
|
+
const containerStyle: ViewStyle = {
|
|
58
|
+
flexDirection: 'column',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
gap: containerGap,
|
|
61
|
+
...style,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const detailsStyle: ViewStyle = {
|
|
65
|
+
flexDirection: 'column',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
gap: detailsGap,
|
|
68
|
+
width: '100%',
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const titleStyle: TextStyle = {
|
|
72
|
+
color: titleColor,
|
|
73
|
+
fontSize: titleFontSize,
|
|
74
|
+
fontFamily: titleFontFamily,
|
|
75
|
+
lineHeight: titleLineHeight,
|
|
76
|
+
fontWeight: titleFontWeight as any,
|
|
77
|
+
textAlign: 'center',
|
|
78
|
+
minWidth: '100%',
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const subtitleStyle: TextStyle = {
|
|
82
|
+
color: subtitleColor,
|
|
83
|
+
fontSize: subtitleFontSize,
|
|
84
|
+
fontFamily: subtitleFontFamily,
|
|
85
|
+
lineHeight: subtitleLineHeight,
|
|
86
|
+
fontWeight: subtitleFontWeight as any,
|
|
87
|
+
textAlign: 'center',
|
|
88
|
+
minWidth: '100%',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const captionStyle: TextStyle = {
|
|
92
|
+
color: captionColor,
|
|
93
|
+
fontSize: captionFontSize,
|
|
94
|
+
fontFamily: captionFontFamily,
|
|
95
|
+
lineHeight: captionLineHeight,
|
|
96
|
+
fontWeight: captionFontWeight as any,
|
|
97
|
+
textAlign: 'center',
|
|
98
|
+
width: '100%',
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Process Avatar Slot to inject modes
|
|
102
|
+
const avatarContent = isValidElement(renderAvatar)
|
|
103
|
+
? cloneElement(renderAvatar as React.ReactElement<any>, { modes })
|
|
104
|
+
: renderAvatar
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<View style={containerStyle}>
|
|
108
|
+
<View style={detailsStyle}>
|
|
109
|
+
{avatarContent}
|
|
110
|
+
{title ? <Text style={titleStyle}>{title}</Text> : null}
|
|
111
|
+
{subtitle ? (
|
|
112
|
+
<Text style={subtitleStyle} numberOfLines={1} ellipsizeMode="tail">
|
|
113
|
+
{subtitle}
|
|
114
|
+
</Text>
|
|
115
|
+
) : null}
|
|
116
|
+
</View>
|
|
117
|
+
{caption ? (
|
|
118
|
+
<Text style={captionStyle} numberOfLines={1} ellipsizeMode="tail">
|
|
119
|
+
{caption}
|
|
120
|
+
</Text>
|
|
121
|
+
) : null}
|
|
122
|
+
</View>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as TransactionBubbleStories from './TransactionBubble.stories';
|
|
3
|
+
import TransactionBubble from './TransactionBubble';
|
|
4
|
+
|
|
5
|
+
<Meta of={TransactionBubbleStories} />
|
|
6
|
+
|
|
7
|
+
# TransactionBubble
|
|
8
|
+
|
|
9
|
+
TransactionBubble component with design-token-driven styling.
|
|
10
|
+
|
|
11
|
+
## Available Collections and Modes
|
|
12
|
+
|
|
13
|
+
This component does not use any design token collections with multiple modes.
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
<Canvas>
|
|
17
|
+
<Story of={TransactionBubbleStories.Default} />
|
|
18
|
+
</Canvas>
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
<ArgsTable of={TransactionBubble} />
|
|
23
|
+
|
|
24
|
+
## Design Tokens
|
|
25
|
+
|
|
26
|
+
This component uses the following design tokens, resolved through `getVariableByName`:
|
|
27
|
+
|
|
28
|
+
- **`transactionBubble/background`**
|
|
29
|
+
- **`transactionBubble/border/color`**
|
|
30
|
+
- **`transactionBubble/border/size`**
|
|
31
|
+
- **`transactionBubble/description/color`**
|
|
32
|
+
- **`transactionBubble/description/fontFamily`**
|
|
33
|
+
- **`transactionBubble/description/fontSize`**
|
|
34
|
+
- **`transactionBubble/description/lineHeight`**
|
|
35
|
+
- **`transactionBubble/gap`**
|
|
36
|
+
- **`transactionBubble/padding`**
|
|
37
|
+
- **`transactionBubble/radius`**
|
|
38
|
+
- **`transactionBubble/statusWrap/height`**
|
|
39
|
+
|
|
40
|
+
All tokens support mode-based theming through the `modes` prop.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, type ViewStyle, type TextStyle } from 'react-native'
|
|
3
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
|
+
import MoneyValue from '../MoneyValue/MoneyValue'
|
|
5
|
+
import NavArrow from '../NavArrow/NavArrow'
|
|
6
|
+
import TransactionStatus from '../TransactionStatus/TransactionStatus'
|
|
7
|
+
|
|
8
|
+
export type TransactionBubbleProps = {
|
|
9
|
+
/** Description text, e.g. "Payment to Uber India" */
|
|
10
|
+
description?: string
|
|
11
|
+
/** Money amount */
|
|
12
|
+
amount?: string | number
|
|
13
|
+
/** Currency code or symbol */
|
|
14
|
+
currency?: string
|
|
15
|
+
/** Transaction status text, e.g. "Expired" */
|
|
16
|
+
status?: string
|
|
17
|
+
/** Date/time string associated with status */
|
|
18
|
+
statusDate?: string
|
|
19
|
+
/** Modes for design token resolution */
|
|
20
|
+
modes?: Record<string, any>
|
|
21
|
+
/** Optional container style */
|
|
22
|
+
style?: ViewStyle
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* TransactionBubble component displaying transaction details in a bubble card.
|
|
27
|
+
*
|
|
28
|
+
* @component
|
|
29
|
+
*/
|
|
30
|
+
export default function TransactionBubble({
|
|
31
|
+
description = 'Payment to Uber India',
|
|
32
|
+
amount = '56',
|
|
33
|
+
currency = '₹',
|
|
34
|
+
status = 'Expired',
|
|
35
|
+
statusDate = '20 Mar 2025',
|
|
36
|
+
modes = {},
|
|
37
|
+
style,
|
|
38
|
+
}: TransactionBubbleProps) {
|
|
39
|
+
// --- Container Tokens ---
|
|
40
|
+
const backgroundColor = getVariableByName('transactionBubble/background', modes) || '#ffffff'
|
|
41
|
+
const borderColor = getVariableByName('transactionBubble/border/color', modes) || '#e5e5e5'
|
|
42
|
+
const borderWidth = Number(getVariableByName('transactionBubble/border/size', modes)) || 1
|
|
43
|
+
const borderRadius = Number(getVariableByName('transactionBubble/radius', modes)) || 23
|
|
44
|
+
const padding = Number(getVariableByName('transactionBubble/padding', modes)) || 16
|
|
45
|
+
const gap = Number(getVariableByName('transactionBubble/gap', modes)) || 8
|
|
46
|
+
|
|
47
|
+
// --- Description Tokens ---
|
|
48
|
+
const descColor = getVariableByName('transactionBubble/description/color', modes) || '#24262b'
|
|
49
|
+
const descFontSize = Number(getVariableByName('transactionBubble/description/fontSize', modes)) || 14
|
|
50
|
+
const descLineHeight = Number(getVariableByName('transactionBubble/description/lineHeight', modes)) || 17
|
|
51
|
+
const descFontFamily = getVariableByName('transactionBubble/description/fontFamily', modes) || 'System'
|
|
52
|
+
|
|
53
|
+
// --- Status Row Tokens ---
|
|
54
|
+
// Status wrapping gap from TransactionBubble definition for the row container
|
|
55
|
+
const statusHeight = Number(getVariableByName('transactionBubble/statusWrap/height', modes)) || 18
|
|
56
|
+
|
|
57
|
+
const containerStyle: ViewStyle = {
|
|
58
|
+
backgroundColor,
|
|
59
|
+
borderColor,
|
|
60
|
+
borderWidth,
|
|
61
|
+
borderRadius,
|
|
62
|
+
padding,
|
|
63
|
+
gap,
|
|
64
|
+
alignItems: 'flex-start',
|
|
65
|
+
width: 231, // Fixed width from design or prop? Design shows w-[231px]. We can make it auto or fixed. Let's respect style prop.
|
|
66
|
+
...style,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const descriptionStyle: TextStyle = {
|
|
70
|
+
color: descColor,
|
|
71
|
+
fontSize: descFontSize,
|
|
72
|
+
lineHeight: descLineHeight,
|
|
73
|
+
fontFamily: descFontFamily,
|
|
74
|
+
fontWeight: '500', // Hardcoded in design as 'Medium' -> 500
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<View style={containerStyle}>
|
|
79
|
+
{/* Description & Money Wrapper (Vertical) */}
|
|
80
|
+
<View style={{ gap: 8, width: '100%' }}>
|
|
81
|
+
<Text style={descriptionStyle} numberOfLines={1} ellipsizeMode='tail'>
|
|
82
|
+
{description}
|
|
83
|
+
</Text>
|
|
84
|
+
|
|
85
|
+
<MoneyValue
|
|
86
|
+
value={amount}
|
|
87
|
+
currency={currency}
|
|
88
|
+
modes={modes}
|
|
89
|
+
/>
|
|
90
|
+
</View>
|
|
91
|
+
|
|
92
|
+
{/* Status Row */}
|
|
93
|
+
<View style={{
|
|
94
|
+
flexDirection: 'row',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
justifyContent: 'space-between',
|
|
97
|
+
width: '100%',
|
|
98
|
+
height: statusHeight, // Enforce height from design
|
|
99
|
+
}}>
|
|
100
|
+
<TransactionStatus
|
|
101
|
+
status={status}
|
|
102
|
+
date={statusDate}
|
|
103
|
+
modes={modes}
|
|
104
|
+
/>
|
|
105
|
+
|
|
106
|
+
<NavArrow
|
|
107
|
+
direction="Forward"
|
|
108
|
+
modes={modes}
|
|
109
|
+
/>
|
|
110
|
+
</View>
|
|
111
|
+
</View>
|
|
112
|
+
)
|
|
113
|
+
}
|