jfs-components 0.0.78 → 0.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/lib/commonjs/components/AppBar/AppBar.js +56 -6
  3. package/lib/commonjs/components/Attached/Attached.js +183 -0
  4. package/lib/commonjs/components/Card/Card.js +25 -2
  5. package/lib/commonjs/components/Checkbox/Checkbox.js +18 -2
  6. package/lib/commonjs/components/Drawer/Drawer.js +6 -1
  7. package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -6
  8. package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
  9. package/lib/commonjs/components/FormField/FormField.js +1 -14
  10. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +9 -7
  11. package/lib/commonjs/components/ListItem/ListItem.js +26 -24
  12. package/lib/commonjs/components/MessageField/MessageField.js +1 -13
  13. package/lib/commonjs/components/PaymentFeedback/PaymentFeedback.js +12 -9
  14. package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +237 -0
  15. package/lib/commonjs/components/Slot/Slot.js +73 -0
  16. package/lib/commonjs/components/Spinner/Spinner.js +217 -0
  17. package/lib/commonjs/components/TextInput/TextInput.js +33 -18
  18. package/lib/commonjs/components/index.js +28 -0
  19. package/lib/commonjs/icons/components/IconArrowdown.js +19 -0
  20. package/lib/commonjs/icons/components/IconArrowup.js +19 -0
  21. package/lib/commonjs/icons/components/IconChevrondowncircle.js +19 -0
  22. package/lib/commonjs/icons/components/IconChevronleftcircle.js +19 -0
  23. package/lib/commonjs/icons/components/IconChevronrightcircle.js +19 -0
  24. package/lib/commonjs/icons/components/IconChevronupcircle.js +19 -0
  25. package/lib/commonjs/icons/components/IconOsnavback.js +19 -0
  26. package/lib/commonjs/icons/components/IconOsnavcenter.js +19 -0
  27. package/lib/commonjs/icons/components/IconOsnavhome.js +19 -0
  28. package/lib/commonjs/icons/components/IconOsnavtask.js +19 -0
  29. package/lib/commonjs/icons/components/IconSignin.js +19 -0
  30. package/lib/commonjs/icons/components/IconSignout.js +19 -0
  31. package/lib/commonjs/icons/components/index.js +132 -0
  32. package/lib/commonjs/icons/registry.js +2 -2
  33. package/lib/module/components/AppBar/AppBar.js +56 -6
  34. package/lib/module/components/Attached/Attached.js +178 -0
  35. package/lib/module/components/Card/Card.js +25 -2
  36. package/lib/module/components/Checkbox/Checkbox.js +18 -2
  37. package/lib/module/components/Drawer/Drawer.js +6 -1
  38. package/lib/module/components/DropdownInput/DropdownInput.js +30 -6
  39. package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +17 -11
  40. package/lib/module/components/FormField/FormField.js +3 -16
  41. package/lib/module/components/FullscreenModal/FullscreenModal.js +9 -7
  42. package/lib/module/components/ListItem/ListItem.js +26 -24
  43. package/lib/module/components/MessageField/MessageField.js +3 -15
  44. package/lib/module/components/PaymentFeedback/PaymentFeedback.js +13 -9
  45. package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +234 -0
  46. package/lib/module/components/Slot/Slot.js +68 -0
  47. package/lib/module/components/Spinner/Spinner.js +212 -0
  48. package/lib/module/components/TextInput/TextInput.js +34 -19
  49. package/lib/module/components/index.js +4 -0
  50. package/lib/module/icons/components/IconArrowdown.js +12 -0
  51. package/lib/module/icons/components/IconArrowup.js +12 -0
  52. package/lib/module/icons/components/IconChevrondowncircle.js +12 -0
  53. package/lib/module/icons/components/IconChevronleftcircle.js +12 -0
  54. package/lib/module/icons/components/IconChevronrightcircle.js +12 -0
  55. package/lib/module/icons/components/IconChevronupcircle.js +12 -0
  56. package/lib/module/icons/components/IconOsnavback.js +12 -0
  57. package/lib/module/icons/components/IconOsnavcenter.js +12 -0
  58. package/lib/module/icons/components/IconOsnavhome.js +12 -0
  59. package/lib/module/icons/components/IconOsnavtask.js +12 -0
  60. package/lib/module/icons/components/IconSignin.js +12 -0
  61. package/lib/module/icons/components/IconSignout.js +12 -0
  62. package/lib/module/icons/components/index.js +12 -0
  63. package/lib/module/icons/registry.js +2 -2
  64. package/lib/typescript/src/components/AppBar/AppBar.d.ts +12 -1
  65. package/lib/typescript/src/components/Attached/Attached.d.ts +64 -0
  66. package/lib/typescript/src/components/Card/Card.d.ts +9 -2
  67. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +3 -2
  68. package/lib/typescript/src/components/ListItem/ListItem.d.ts +16 -6
  69. package/lib/typescript/src/components/PaymentFeedback/PaymentFeedback.d.ts +5 -1
  70. package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +66 -0
  71. package/lib/typescript/src/components/Slot/Slot.d.ts +52 -0
  72. package/lib/typescript/src/components/Spinner/Spinner.d.ts +45 -0
  73. package/lib/typescript/src/components/index.d.ts +4 -0
  74. package/lib/typescript/src/icons/components/IconArrowdown.d.ts +3 -0
  75. package/lib/typescript/src/icons/components/IconArrowup.d.ts +3 -0
  76. package/lib/typescript/src/icons/components/IconChevrondowncircle.d.ts +3 -0
  77. package/lib/typescript/src/icons/components/IconChevronleftcircle.d.ts +3 -0
  78. package/lib/typescript/src/icons/components/IconChevronrightcircle.d.ts +3 -0
  79. package/lib/typescript/src/icons/components/IconChevronupcircle.d.ts +3 -0
  80. package/lib/typescript/src/icons/components/IconOsnavback.d.ts +3 -0
  81. package/lib/typescript/src/icons/components/IconOsnavcenter.d.ts +3 -0
  82. package/lib/typescript/src/icons/components/IconOsnavhome.d.ts +3 -0
  83. package/lib/typescript/src/icons/components/IconOsnavtask.d.ts +3 -0
  84. package/lib/typescript/src/icons/components/IconSignin.d.ts +3 -0
  85. package/lib/typescript/src/icons/components/IconSignout.d.ts +3 -0
  86. package/lib/typescript/src/icons/components/index.d.ts +12 -0
  87. package/lib/typescript/src/icons/registry.d.ts +1 -1
  88. package/package.json +3 -2
  89. package/src/components/AppBar/AppBar.tsx +79 -12
  90. package/src/components/Attached/Attached.tsx +237 -0
  91. package/src/components/Card/Card.tsx +28 -1
  92. package/src/components/Checkbox/Checkbox.tsx +14 -2
  93. package/src/components/Drawer/Drawer.tsx +4 -0
  94. package/src/components/DropdownInput/DropdownInput.tsx +54 -20
  95. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +13 -9
  96. package/src/components/FormField/FormField.tsx +3 -19
  97. package/src/components/FullscreenModal/FullscreenModal.tsx +6 -3
  98. package/src/components/ListItem/ListItem.tsx +42 -25
  99. package/src/components/MessageField/MessageField.tsx +3 -18
  100. package/src/components/PaymentFeedback/PaymentFeedback.tsx +15 -8
  101. package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +316 -0
  102. package/src/components/Slot/Slot.tsx +91 -0
  103. package/src/components/Spinner/Spinner.tsx +273 -0
  104. package/src/components/TextInput/TextInput.tsx +37 -19
  105. package/src/components/index.ts +4 -0
  106. package/src/icons/components/IconArrowdown.tsx +11 -0
  107. package/src/icons/components/IconArrowup.tsx +11 -0
  108. package/src/icons/components/IconChevrondowncircle.tsx +11 -0
  109. package/src/icons/components/IconChevronleftcircle.tsx +11 -0
  110. package/src/icons/components/IconChevronrightcircle.tsx +11 -0
  111. package/src/icons/components/IconChevronupcircle.tsx +11 -0
  112. package/src/icons/components/IconOsnavback.tsx +11 -0
  113. package/src/icons/components/IconOsnavcenter.tsx +11 -0
  114. package/src/icons/components/IconOsnavhome.tsx +11 -0
  115. package/src/icons/components/IconOsnavtask.tsx +11 -0
  116. package/src/icons/components/IconSignin.tsx +11 -0
  117. package/src/icons/components/IconSignout.tsx +11 -0
  118. package/src/icons/components/index.ts +12 -0
  119. package/src/icons/registry.ts +49 -1
@@ -3,7 +3,6 @@
3
3
  import React, { useCallback, useMemo, useRef } from 'react';
4
4
  import { View, Text, Pressable, Platform } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
- import IconCapsule from '../IconCapsule/IconCapsule';
7
6
  import NavArrow from '../NavArrow/NavArrow';
8
7
  import { usePressableWebSupport } from '../../utils/web-platform-utils';
9
8
  import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
@@ -15,9 +14,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
14
  const IS_IOS = Platform.OS === 'ios';
16
15
  const PRESS_DELAY = IS_IOS ? 130 : 0;
17
16
 
18
- // Forced modes for the endSlot — `Context: 'ListItem'` can never be
19
- // overridden by external modes. Frozen so identity is stable across renders.
20
- const END_SLOT_FORCED_MODES = Object.freeze({
17
+ // Forced modes for the leading/trailing slots — `Context: 'ListItem'` can
18
+ // never be overridden by external modes. Frozen so identity is stable across
19
+ // renders. Applied to both slots so they cascade modes identically.
20
+ const SLOT_FORCED_MODES = Object.freeze({
21
21
  Context: 'ListItem'
22
22
  });
23
23
 
@@ -32,7 +32,7 @@ const pressedOverlayStyle = {
32
32
  // ---------------------------------------------------------------------------
33
33
 
34
34
  function resolveListItemTokens(modes) {
35
- // Modes used to cascade into slot children (leading / supportSlot / endSlot).
35
+ // Modes used to cascade into slot children (leading / supportSlot / trailing).
36
36
  // We do NOT inject an `AppearanceBrand` default here: slot content such as
37
37
  // Buttons or Badges carry their own intended appearance, so forcing one onto
38
38
  // them would be surprising.
@@ -131,9 +131,11 @@ const verticalSupportTextOverride = {
131
131
  * - **design-token driven styling** via `getVariableByName` and `modes`
132
132
  *
133
133
  * Wherever the Figma layer name contains "Slot", this component exposes a
134
- * dedicated React "slot" prop:
134
+ * dedicated React "slot" prop. The leading and trailing edges share a
135
+ * symmetric `leading` / `trailing` slot API:
136
+ * - Slot "leading" → `leading`
135
137
  * - Slot "support text" → `supportSlot`
136
- * - Slot "end" → `endSlot`
138
+ * - Slot "trailing" → `trailing`
137
139
  *
138
140
  * @component
139
141
  * @param {Object} props
@@ -141,9 +143,9 @@ const verticalSupportTextOverride = {
141
143
  * @param {string} [props.title='Title'] - Primary title used in the horizontal layout.
142
144
  * @param {string} [props.supportText='Support Text'] - Support text used in both layouts when `supportSlot` is not provided.
143
145
  * @param {boolean} [props.showSupportText=true] - Toggles rendering of the support text in Horizontal layout.
144
- * @param {React.ReactNode} [props.leading] - Optional leading element. Defaults to `IconCapsule`.
146
+ * @param {React.ReactNode|null} [props.leading] - Optional leading slot. Omitted or `null` renders nothing.
145
147
  * @param {React.ReactNode} [props.supportSlot] - Optional custom slot used instead of the default support text block.
146
- * @param {React.ReactNode} [props.endSlot] - Optional custom trailing slot (Figma Slot "end").
148
+ * @param {React.ReactNode} [props.trailing] - Optional trailing slot (Figma Slot "trailing"). Horizontal layout only.
147
149
  * @param {boolean} [props.navArrow=true] - Whether to show NavArrow on the far right (Horizontal layout only).
148
150
  * @param {Object} [props.modes={}] - Modes object passed to `getVariableByName` for all design tokens.
149
151
  * @param {Function} [props.onPress] - When provided, the entire item becomes pressable (navigation variant).
@@ -172,6 +174,7 @@ function ListItemImpl({
172
174
  showSupportText = true,
173
175
  leading,
174
176
  supportSlot,
177
+ trailing,
175
178
  endSlot,
176
179
  navArrow = true,
177
180
  modes = EMPTY_MODES,
@@ -209,13 +212,9 @@ function ListItemImpl({
209
212
  // Process leading slot to pass modes to children. Memoized on
210
213
  // (leading, resolvedModes) so a parent re-render doesn't re-walk the tree.
211
214
  const leadingElement = useMemo(() => {
212
- const processed = leading ? cloneChildrenWithModes(React.Children.toArray(leading), tokens.resolvedModes) : [];
213
- if (processed.length === 0) {
214
- return /*#__PURE__*/_jsx(IconCapsule, {
215
- modes: tokens.resolvedModes,
216
- accessibilityLabel: undefined
217
- });
218
- }
215
+ if (leading == null) return null;
216
+ const processed = cloneChildrenWithModes(React.Children.toArray(leading), tokens.resolvedModes, SLOT_FORCED_MODES);
217
+ if (processed.length === 0) return null;
219
218
  return processed.length === 1 ? processed[0] : processed;
220
219
  }, [leading, tokens.resolvedModes]);
221
220
  const processedSupportSlot = useMemo(() => {
@@ -223,11 +222,14 @@ function ListItemImpl({
223
222
  const processed = cloneChildrenWithModes(React.Children.toArray(supportSlot), tokens.resolvedModes);
224
223
  return processed.length === 1 ? processed[0] : processed;
225
224
  }, [supportSlot, tokens.resolvedModes]);
226
- const processedEndSlot = useMemo(() => {
227
- if (!endSlot) return null;
228
- const processed = cloneChildrenWithModes(React.Children.toArray(endSlot), tokens.resolvedModes, END_SLOT_FORCED_MODES);
225
+
226
+ // `trailing` wins; `endSlot` is the deprecated alias kept for back-compat.
227
+ const trailingContent = trailing ?? endSlot;
228
+ const processedTrailing = useMemo(() => {
229
+ if (!trailingContent) return null;
230
+ const processed = cloneChildrenWithModes(React.Children.toArray(trailingContent), tokens.resolvedModes, SLOT_FORCED_MODES);
229
231
  return processed.length === 1 ? processed[0] : processed;
230
- }, [endSlot, tokens.resolvedModes]);
232
+ }, [trailingContent, tokens.resolvedModes]);
231
233
  const renderSupportContent = () => {
232
234
  if (processedSupportSlot) return processedSupportSlot;
233
235
 
@@ -262,7 +264,7 @@ function ListItemImpl({
262
264
  if (layout === 'Horizontal') {
263
265
  const innerContent = /*#__PURE__*/_jsxs(View, {
264
266
  style: innerContentStyleArray,
265
- children: [leadingElement, /*#__PURE__*/_jsxs(View, {
267
+ children: [leadingElement ?? null, /*#__PURE__*/_jsxs(View, {
266
268
  style: {
267
269
  flex: 1,
268
270
  minWidth: 1,
@@ -273,9 +275,9 @@ function ListItemImpl({
273
275
  numberOfLines: 1,
274
276
  children: title
275
277
  }), showSupportText && renderSupportContent()]
276
- }), processedEndSlot ? /*#__PURE__*/_jsx(View, {
278
+ }), processedTrailing ? /*#__PURE__*/_jsx(View, {
277
279
  style: tokens.trailingWrapperStyle,
278
- children: processedEndSlot
280
+ children: processedTrailing
279
281
  }) : null, navArrow && /*#__PURE__*/_jsx(NavArrow, {
280
282
  direction: "Forward",
281
283
  modes: tokens.resolvedModes
@@ -309,7 +311,7 @@ function ListItemImpl({
309
311
  // Vertical layout — icon on top, support text/slot below
310
312
  const verticalContent = /*#__PURE__*/_jsxs(View, {
311
313
  style: verticalContentStyleArray,
312
- children: [leadingElement, renderSupportContent()]
314
+ children: [leadingElement ?? null, renderSupportContent()]
313
315
  });
314
316
  if (onPress) {
315
317
  return /*#__PURE__*/_jsx(Pressable, {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
- import React, { useCallback, useMemo, useRef, useState } from 'react';
4
- import { View, Text, Pressable, TextInput as RNTextInput } from 'react-native';
3
+ import React, { useCallback, useMemo, useState } from 'react';
4
+ import { View, Text, TextInput as RNTextInput } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
7
  import { EMPTY_MODES } from '../../utils/react-utils';
@@ -140,15 +140,6 @@ function MessageField({
140
140
  const currentValue = isControlled ? value : uncontrolledValue;
141
141
  const [isFocused, setIsFocused] = useState(false);
142
142
  const interactive = !isDisabled && !isReadOnly;
143
-
144
- // Ref to the native textarea so tapping anywhere in the (padded) textarea
145
- // container focuses it on the FIRST tap, fixing the Android "two taps to
146
- // open the keyboard" issue.
147
- const inputRef = useRef(null);
148
- const focusInput = useCallback(() => {
149
- if (!interactive) return;
150
- inputRef.current?.focus();
151
- }, [interactive]);
152
143
  const {
153
144
  modes: globalModes
154
145
  } = useTokens();
@@ -283,12 +274,9 @@ function MessageField({
283
274
  style: requiredIndicatorStyle,
284
275
  children: " *"
285
276
  })]
286
- }), /*#__PURE__*/_jsx(Pressable, {
277
+ }), /*#__PURE__*/_jsx(View, {
287
278
  style: [textareaContainerStyle, textareaStyle],
288
- onPress: focusInput,
289
- accessible: false,
290
279
  children: /*#__PURE__*/_jsx(RNTextInput, {
291
- ref: inputRef,
292
280
  multiline: true,
293
281
  value: currentValue,
294
282
  onChangeText: handleChangeText,
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
2
 
3
- import React, { isValidElement, cloneElement } from 'react';
3
+ import React from 'react';
4
4
  import { View, Text } from 'react-native';
5
5
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
6
  import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
7
  import IconCapsule from '../IconCapsule/IconCapsule';
8
- import { EMPTY_MODES } from '../../utils/react-utils';
8
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  export default function PaymentFeedback({
11
11
  title = '₹50,000',
12
12
  subtitle = 'Payment successful',
13
- body = 'Your payment has been\nsuccessfully processed.',
13
+ body,
14
14
  details = '18 March 2025, 4:15 pm\nTransaction ID: TXN121466784',
15
15
  showDetails = true,
16
16
  iconName = 'ic_confirm',
@@ -97,17 +97,21 @@ export default function PaymentFeedback({
97
97
  fontWeight: String(detailsFontWeight),
98
98
  textAlign: 'center'
99
99
  };
100
- const mediaContent = /*#__PURE__*/isValidElement(renderMedia) ? /*#__PURE__*/cloneElement(renderMedia, {
101
- modes
102
- }) : renderMedia;
100
+
101
+ // Cascade modes into a custom media slot (per the modes-cascade convention);
102
+ // any modes the consumer set on the slot child still take precedence.
103
+ const mediaContent = renderMedia != null ? cloneChildrenWithModes(renderMedia, modes) : null;
103
104
  const defaultMedia = /*#__PURE__*/_jsx(IconCapsule, {
104
- iconName: iconName,
105
+ iconName: iconName
106
+ // `positive` is the default; consumers override the capsule color by
107
+ // passing `AppearanceSystem` (or any other mode) via the `modes` prop.
108
+ ,
105
109
  modes: {
110
+ AppearanceSystem: 'positive',
106
111
  ...modes,
107
112
  'Icon Capsule Size': 'L',
108
113
  Emphasis: 'High',
109
- 'Semantic Intent': 'System',
110
- AppearanceSystem: 'positive'
114
+ 'Semantic Intent': 'System'
111
115
  }
112
116
  });
113
117
  const detailLines = details?.split('\n') ?? [];
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+
3
+ import React from 'react';
4
+ import { View, Text, Pressable, Platform } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
7
+ import Icon from '../../icons/Icon';
8
+
9
+ /** Figma grid: label column 1.8fr, each plan column 1fr. */
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ const LABEL_COLUMN_FR = 1.8;
12
+ const PLAN_COLUMN_FR = 1;
13
+
14
+ /**
15
+ * A single plan column header (the label column has no header of its own).
16
+ */
17
+
18
+ /**
19
+ * Value rendered inside a plan cell.
20
+ * - `string` / `number` → rendered as value text.
21
+ * - `false` → renders the muted "not available" cross icon.
22
+ * - any React node → rendered as-is (e.g. a `Badge`, `MoneyValue`, icon…).
23
+ * - `null` / `undefined` / `true` → empty cell.
24
+ */
25
+
26
+ const DEFAULT_COLUMNS = [{
27
+ label: 'Your plan'
28
+ }, {
29
+ label: 'JioFinance+',
30
+ brand: true
31
+ }];
32
+ const DEFAULT_ROWS = [{
33
+ label: 'JioPoints multiplier',
34
+ values: ['1x', '1.25x']
35
+ }, {
36
+ label: 'Cashback',
37
+ showInfo: true,
38
+ values: [false, 'Upto ₹5000']
39
+ }, {
40
+ label: 'Bonus JioGold',
41
+ showInfo: true,
42
+ values: [false, '1%']
43
+ }];
44
+
45
+ /** Keeps every text layer on a single line. */
46
+ const NO_WRAP_TEXT = {
47
+ flexShrink: 0,
48
+ ...(Platform.OS === 'web' ? {
49
+ whiteSpace: 'nowrap'
50
+ } : {})
51
+ };
52
+ const labelColumnStyle = {
53
+ flex: LABEL_COLUMN_FR,
54
+ minWidth: 0
55
+ };
56
+ const planColumnStyle = {
57
+ flex: PLAN_COLUMN_FR,
58
+ minWidth: 0,
59
+ alignItems: 'center'
60
+ };
61
+
62
+ /**
63
+ * PlanComparisonCard renders a compact comparison table that pits the user's
64
+ * current plan against one or more alternative plans across a set of feature
65
+ * rows. Implementation of Figma node `4498:2968` (`PlanComparisonCard`).
66
+ *
67
+ * Columns use a 1.8fr / 1fr flex ratio (label vs plan), matching the Figma grid.
68
+ *
69
+ * @component
70
+ */
71
+ function PlanComparisonCard({
72
+ columns = DEFAULT_COLUMNS,
73
+ rows = DEFAULT_ROWS,
74
+ modes = EMPTY_MODES,
75
+ style
76
+ }) {
77
+ const gap = getVariableByName('planComparisonCard/gap', modes) ?? 16;
78
+ const headerFg = getVariableByName('planComparisonCard/header/fg', modes) ?? '#ffffff';
79
+ const headerBrandFg = getVariableByName('planComparisonCard/header/brand/fg', modes) ?? '#cea15a';
80
+ const headerFontSize = getVariableByName('planComparisonCard/header/fontSize', modes) ?? 14;
81
+ const headerFontFamily = getVariableByName('planComparisonCard/header/fontFamily', modes) ?? 'JioType Var';
82
+ const headerLineHeight = getVariableByName('planComparisonCard/header/lineHeight', modes) ?? 18;
83
+ const headerFontWeight = getVariableByName('planComparisonCard/header/fontWeight', modes) ?? '500';
84
+ const tableBackground = getVariableByName('planComparisonCard/tableRow/background', modes) ?? '#141414';
85
+ const tableRadius = getVariableByName('planComparisonCard/tableRow/radius', modes) ?? 16;
86
+ const tableBorderSize = getVariableByName('planComparisonCard/tableRow/border/size', modes) ?? 1;
87
+ const tableBorderColor = getVariableByName('planComparisonCard/tableRow/border/color', modes) ?? '#1e1a14';
88
+ const cellPadding = getVariableByName('planComparisonCard/tableCell/padding', modes) ?? 12;
89
+ const cellGap = getVariableByName('planComparisonCard/tableCell/gap', modes) ?? 2;
90
+ const cellMinHeight = getVariableByName('planComparisonCard/tableCell/height', modes) ?? 46;
91
+ const cellBorderSize = getVariableByName('planComparisonCard/tableCell/border/size', modes) ?? 1;
92
+ const cellBorderColor = getVariableByName('planComparisonCard/tableCell/border/color', modes) ?? '#1e1a14';
93
+ const labelColor = getVariableByName('planComparisonCard/tableCell/label/color', modes) ?? '#ffffff';
94
+ const labelDisabledColor = getVariableByName('planComparisonCard/tableCell/label/disabled/color', modes) ?? '#91949c';
95
+ const labelFontSize = getVariableByName('planComparisonCard/tableCell/label/fontSize', modes) ?? 12;
96
+ const labelFontFamily = getVariableByName('planComparisonCard/tableCell/label/fontFamily', modes) ?? 'JioType Var';
97
+ const labelLineHeight = getVariableByName('planComparisonCard/tableCell/label/lineHeight', modes) ?? 16;
98
+ const labelFontWeight = getVariableByName('planComparisonCard/tableCell/label/fontWeight', modes) ?? '400';
99
+ const valueColor = getVariableByName('planComparisonCard/tableCell/value/color', modes) ?? '#ffffff';
100
+ const valueFontSize = getVariableByName('planComparisonCard/tableCell/value/fontSize', modes) ?? 12;
101
+ const valueFontFamily = getVariableByName('planComparisonCard/tableCell/value/fontFamily', modes) ?? 'JioType Var';
102
+ const valueLineHeight = getVariableByName('planComparisonCard/tableCell/value/lineHeight', modes) ?? 16;
103
+ const valueFontWeight = getVariableByName('planComparisonCard/tableCell/value/fontWeight', modes) ?? '500';
104
+ const iconColor = getVariableByName('planComparisonCard/icon/color', modes) ?? '#ffffff';
105
+ const iconSize = getVariableByName('planComparisonCard/icon/size', modes) ?? 16;
106
+ const toWeight = w => typeof w === 'number' ? `${w}` : w;
107
+ const headerTextStyle = {
108
+ ...NO_WRAP_TEXT,
109
+ fontFamily: headerFontFamily,
110
+ fontSize: headerFontSize,
111
+ lineHeight: headerLineHeight,
112
+ fontWeight: toWeight(headerFontWeight),
113
+ textAlign: 'center'
114
+ };
115
+ const labelTextStyle = {
116
+ ...NO_WRAP_TEXT,
117
+ color: labelColor,
118
+ fontFamily: labelFontFamily,
119
+ fontSize: labelFontSize,
120
+ lineHeight: labelLineHeight,
121
+ fontWeight: toWeight(labelFontWeight)
122
+ };
123
+ const valueTextStyle = {
124
+ ...NO_WRAP_TEXT,
125
+ color: valueColor,
126
+ fontFamily: valueFontFamily,
127
+ fontSize: valueFontSize,
128
+ lineHeight: valueLineHeight,
129
+ fontWeight: toWeight(valueFontWeight),
130
+ textAlign: 'center'
131
+ };
132
+ const rowStyle = {
133
+ flexDirection: 'row',
134
+ width: '100%'
135
+ };
136
+ const labelCellStyle = {
137
+ flexDirection: 'row',
138
+ alignItems: 'center',
139
+ gap: cellGap,
140
+ padding: cellPadding,
141
+ minHeight: cellMinHeight
142
+ };
143
+ const valueCellStyle = {
144
+ flexDirection: 'row',
145
+ alignItems: 'center',
146
+ justifyContent: 'center',
147
+ padding: cellPadding,
148
+ minHeight: cellMinHeight,
149
+ width: '100%'
150
+ };
151
+ const renderValue = (value, cellKey) => {
152
+ if (value === false) {
153
+ return /*#__PURE__*/_jsx(Icon, {
154
+ name: "ic_close",
155
+ size: iconSize,
156
+ color: labelDisabledColor
157
+ }, cellKey);
158
+ }
159
+ if (value === null || value === undefined || value === true) {
160
+ return null;
161
+ }
162
+ if (typeof value === 'string' || typeof value === 'number') {
163
+ return /*#__PURE__*/_jsx(Text, {
164
+ style: valueTextStyle,
165
+ children: value
166
+ }, cellKey);
167
+ }
168
+ return cloneChildrenWithModes(value, modes);
169
+ };
170
+ return /*#__PURE__*/_jsxs(View, {
171
+ style: [{
172
+ gap,
173
+ width: '100%'
174
+ }, style],
175
+ children: [/*#__PURE__*/_jsxs(View, {
176
+ style: rowStyle,
177
+ children: [/*#__PURE__*/_jsx(View, {
178
+ style: labelColumnStyle
179
+ }), columns.map((column, index) => /*#__PURE__*/_jsx(View, {
180
+ style: planColumnStyle,
181
+ children: /*#__PURE__*/_jsx(Text, {
182
+ style: [headerTextStyle, {
183
+ color: column.brand ? headerBrandFg : headerFg
184
+ }],
185
+ children: column.label
186
+ })
187
+ }, column.label ?? index))]
188
+ }), /*#__PURE__*/_jsx(View, {
189
+ style: {
190
+ width: '100%',
191
+ backgroundColor: tableBackground,
192
+ borderWidth: tableBorderSize,
193
+ borderColor: tableBorderColor,
194
+ borderRadius: tableRadius,
195
+ overflow: 'hidden'
196
+ },
197
+ children: rows.map((row, rowIndex) => {
198
+ const isLast = rowIndex === rows.length - 1;
199
+ const showInfo = row.showInfo || row.onInfoPress != null;
200
+ return /*#__PURE__*/_jsxs(View, {
201
+ style: [rowStyle, {
202
+ borderBottomWidth: isLast ? 0 : cellBorderSize,
203
+ borderBottomColor: cellBorderColor
204
+ }],
205
+ children: [/*#__PURE__*/_jsxs(View, {
206
+ style: [labelColumnStyle, labelCellStyle],
207
+ children: [/*#__PURE__*/_jsx(Text, {
208
+ style: labelTextStyle,
209
+ children: row.label
210
+ }), showInfo && (row.onInfoPress ? /*#__PURE__*/_jsx(Pressable, {
211
+ onPress: row.onInfoPress,
212
+ accessibilityRole: "button",
213
+ accessibilityLabel: `More information about ${row.label}`,
214
+ hitSlop: 8,
215
+ children: /*#__PURE__*/_jsx(Icon, {
216
+ name: "ic_info",
217
+ size: iconSize,
218
+ color: iconColor
219
+ })
220
+ }) : /*#__PURE__*/_jsx(Icon, {
221
+ name: "ic_info",
222
+ size: iconSize,
223
+ color: iconColor
224
+ }))]
225
+ }), columns.map((column, colIndex) => /*#__PURE__*/_jsx(View, {
226
+ style: [planColumnStyle, valueCellStyle],
227
+ children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
228
+ }, column.label ?? colIndex))]
229
+ }, row.key ?? `${row.label}-${rowIndex}`);
230
+ })
231
+ })]
232
+ });
233
+ }
234
+ export default PlanComparisonCard;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { View } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
+ import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ /**
10
+ * Slot — a token-driven layout container for grouped slot content.
11
+ *
12
+ * Use `Slot` instead of a raw `View` when you need a vertical or horizontal
13
+ * stack with design-token gap spacing and automatic `modes` propagation to
14
+ * children. Typical usage is nesting a column of actions inside a
15
+ * direction-locked parent such as `ActionFooter`:
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <ActionFooter modes={modes}>
20
+ * <Slot layoutDirection="vertical" modes={modes}>
21
+ * <Button label="Continue" modes={primaryModes} />
22
+ * <Disclaimer disclaimer="Terms apply." modes={modes} />
23
+ * </Slot>
24
+ * </ActionFooter>
25
+ * ```
26
+ */
27
+ function Slot({
28
+ children,
29
+ layoutDirection = 'vertical',
30
+ alignCrossAxis,
31
+ justifyMainAxis,
32
+ modes: propModes = EMPTY_MODES,
33
+ style,
34
+ ...rest
35
+ }) {
36
+ const {
37
+ modes: globalModes
38
+ } = useTokens();
39
+ const modes = useMemo(() => ({
40
+ ...globalModes,
41
+ ...propModes
42
+ }), [globalModes, propModes]);
43
+ const {
44
+ containerStyle,
45
+ processedChildren
46
+ } = useMemo(() => {
47
+ const gap = getVariableByName('slot/gap', modes) ?? 8;
48
+ const isHorizontal = layoutDirection === 'horizontal';
49
+ const container = {
50
+ flexDirection: isHorizontal ? 'row' : 'column',
51
+ alignItems: alignCrossAxis ?? (isHorizontal ? 'flex-start' : 'stretch'),
52
+ justifyContent: justifyMainAxis ?? (isHorizontal ? 'center' : undefined),
53
+ alignSelf: 'stretch',
54
+ gap
55
+ };
56
+ const processed = children ? cloneChildrenWithModes(children, modes) : null;
57
+ return {
58
+ containerStyle: container,
59
+ processedChildren: processed
60
+ };
61
+ }, [children, modes, layoutDirection, alignCrossAxis, justifyMainAxis]);
62
+ return /*#__PURE__*/_jsx(View, {
63
+ style: [containerStyle, style],
64
+ ...rest,
65
+ children: processedChildren
66
+ });
67
+ }
68
+ export default /*#__PURE__*/React.memo(Slot);