jfs-components 0.0.77 → 0.0.78

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 (70) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +55 -55
  3. package/lib/commonjs/components/ActionFooter/ActionFooter.js +48 -2
  4. package/lib/commonjs/components/Checkbox/Checkbox.js +21 -9
  5. package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -16
  6. package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +167 -0
  7. package/lib/commonjs/components/FormField/FormField.js +14 -1
  8. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +355 -0
  9. package/lib/commonjs/components/ListItem/ListItem.js +25 -10
  10. package/lib/commonjs/components/MessageField/MessageField.js +318 -0
  11. package/lib/commonjs/components/NavArrow/NavArrow.js +58 -17
  12. package/lib/commonjs/components/Stepper/Step.js +47 -60
  13. package/lib/commonjs/components/Stepper/StepLabel.js +40 -10
  14. package/lib/commonjs/components/Stepper/Stepper.js +15 -17
  15. package/lib/commonjs/components/SuggestiveSearch/SuggestiveSearch.js +487 -0
  16. package/lib/commonjs/components/TextInput/TextInput.js +16 -1
  17. package/lib/commonjs/components/Title/Title.js +10 -2
  18. package/lib/commonjs/components/index.js +28 -0
  19. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  20. package/lib/commonjs/icons/registry.js +1 -1
  21. package/lib/module/components/Accordion/Accordion.js +56 -56
  22. package/lib/module/components/ActionFooter/ActionFooter.js +50 -4
  23. package/lib/module/components/Checkbox/Checkbox.js +22 -10
  24. package/lib/module/components/DropdownInput/DropdownInput.js +30 -16
  25. package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +161 -0
  26. package/lib/module/components/FormField/FormField.js +16 -3
  27. package/lib/module/components/FullscreenModal/FullscreenModal.js +350 -0
  28. package/lib/module/components/ListItem/ListItem.js +25 -10
  29. package/lib/module/components/MessageField/MessageField.js +313 -0
  30. package/lib/module/components/NavArrow/NavArrow.js +59 -18
  31. package/lib/module/components/Stepper/Step.js +48 -61
  32. package/lib/module/components/Stepper/StepLabel.js +40 -10
  33. package/lib/module/components/Stepper/Stepper.js +15 -17
  34. package/lib/module/components/SuggestiveSearch/SuggestiveSearch.js +481 -0
  35. package/lib/module/components/TextInput/TextInput.js +17 -2
  36. package/lib/module/components/Title/Title.js +10 -2
  37. package/lib/module/components/index.js +4 -0
  38. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  39. package/lib/module/icons/registry.js +1 -1
  40. package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -20
  41. package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +63 -0
  42. package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +99 -0
  43. package/lib/typescript/src/components/MessageField/MessageField.d.ts +81 -0
  44. package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +10 -5
  45. package/lib/typescript/src/components/Stepper/Step.d.ts +4 -1
  46. package/lib/typescript/src/components/Stepper/StepLabel.d.ts +4 -1
  47. package/lib/typescript/src/components/Stepper/Stepper.d.ts +3 -1
  48. package/lib/typescript/src/components/SuggestiveSearch/SuggestiveSearch.d.ts +123 -0
  49. package/lib/typescript/src/components/index.d.ts +7 -3
  50. package/lib/typescript/src/icons/registry.d.ts +1 -1
  51. package/package.json +1 -1
  52. package/src/components/Accordion/Accordion.tsx +113 -73
  53. package/src/components/ActionFooter/ActionFooter.tsx +56 -4
  54. package/src/components/Checkbox/Checkbox.tsx +22 -9
  55. package/src/components/DropdownInput/DropdownInput.tsx +67 -39
  56. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +237 -0
  57. package/src/components/FormField/FormField.tsx +19 -3
  58. package/src/components/FullscreenModal/FullscreenModal.tsx +414 -0
  59. package/src/components/ListItem/ListItem.tsx +21 -10
  60. package/src/components/MessageField/MessageField.tsx +543 -0
  61. package/src/components/NavArrow/NavArrow.tsx +81 -17
  62. package/src/components/Stepper/Step.tsx +52 -51
  63. package/src/components/Stepper/StepLabel.tsx +46 -9
  64. package/src/components/Stepper/Stepper.tsx +20 -15
  65. package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +756 -0
  66. package/src/components/TextInput/TextInput.tsx +14 -1
  67. package/src/components/Title/Title.tsx +13 -2
  68. package/src/components/index.ts +7 -3
  69. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  70. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,237 @@
1
+ import React, { useCallback, useMemo, useState } from 'react'
2
+ import {
3
+ View,
4
+ Text,
5
+ type StyleProp,
6
+ type ViewStyle,
7
+ type TextStyle,
8
+ } from 'react-native'
9
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+ import { EMPTY_MODES } from '../../utils/react-utils'
11
+ import Checkbox from '../Checkbox/Checkbox'
12
+ import Button from '../Button/Button'
13
+
14
+ export type ExpandableCheckboxProps = {
15
+ /** Long text label rendered next to the checkbox. */
16
+ label?: string
17
+ /** Whether the checkbox is checked (controlled). */
18
+ checked?: boolean
19
+ /** Initial checked state (uncontrolled). */
20
+ defaultChecked?: boolean
21
+ /** Callback fired when the checked state changes. */
22
+ onValueChange?: (checked: boolean) => void
23
+ /** Whether the row is expanded to reveal the full label (controlled). */
24
+ expanded?: boolean
25
+ /** Initial expanded state (uncontrolled). Defaults to `false` (Idle). */
26
+ defaultExpanded?: boolean
27
+ /** Callback fired when the expanded state changes. */
28
+ onExpandedChange?: (expanded: boolean) => void
29
+ /** Whether the entire row is disabled. */
30
+ disabled?: boolean
31
+ /** Label for the toggle button shown when the row is collapsed. */
32
+ readMoreLabel?: string
33
+ /** Label for the toggle button shown when the row is expanded. */
34
+ readLessLabel?: string
35
+ /** Number of lines to show when collapsed. Defaults to `1`. */
36
+ collapsedLines?: number
37
+ /** Design token modes for theming (e.g. `{ 'Color Mode': 'Light' }`). */
38
+ modes?: Record<string, any>
39
+ /** Override outer container styles. */
40
+ style?: StyleProp<ViewStyle>
41
+ /** Override the label text styles. */
42
+ labelStyle?: StyleProp<TextStyle>
43
+ /** Accessibility label for the checkbox. Falls back to `label`. */
44
+ accessibilityLabel?: string
45
+ }
46
+
47
+ /**
48
+ * Default modes applied to the inner toggle `Button`. These resolve the
49
+ * tertiary-style pill in the Figma reference (small, transparent background,
50
+ * brand purple foreground). Any value supplied via the consumer `modes` prop
51
+ * takes precedence over these defaults.
52
+ */
53
+ const BUTTON_DEFAULT_MODES = {
54
+ 'Button / Size': 'XS',
55
+ AppearanceBrand: 'Secondary',
56
+ Emphasis: 'Low',
57
+ } as const
58
+
59
+ /**
60
+ * ExpandableCheckbox composes a `Checkbox`, a long-form label and a
61
+ * "Read more" / "Read less" toggle. Mirrors the Figma "Expandable Checkbox"
62
+ * component with two states:
63
+ *
64
+ * - **Idle (collapsed)** — checkbox + truncated label + toggle button arranged
65
+ * in a horizontal row (cross-axis centered).
66
+ * - **Open (expanded)** — checkbox + full multi-line label, with the toggle
67
+ * button right-aligned beneath the row.
68
+ *
69
+ * The checkbox and the toggle button have independent press handlers — pressing
70
+ * the toggle does not affect the checked state, and toggling the checkbox does
71
+ * not collapse / expand the row.
72
+ *
73
+ * @component
74
+ * @param {ExpandableCheckboxProps} props
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * <ExpandableCheckbox
79
+ * label="By checking this box, I (a) acknowledge and (b) agree to the full terms…"
80
+ * defaultChecked
81
+ * onValueChange={setAccepted}
82
+ * modes={{ 'Color Mode': 'Light' }}
83
+ * />
84
+ * ```
85
+ */
86
+ function ExpandableCheckbox({
87
+ label = '',
88
+ checked: controlledChecked,
89
+ defaultChecked = false,
90
+ onValueChange,
91
+ expanded: controlledExpanded,
92
+ defaultExpanded = false,
93
+ onExpandedChange,
94
+ disabled = false,
95
+ readMoreLabel = 'Read more',
96
+ readLessLabel = 'Read less',
97
+ collapsedLines = 1,
98
+ modes = EMPTY_MODES,
99
+ style,
100
+ labelStyle,
101
+ accessibilityLabel,
102
+ }: ExpandableCheckboxProps) {
103
+ const isCheckedControlled = controlledChecked !== undefined
104
+ const [internalChecked, setInternalChecked] = useState(defaultChecked)
105
+ const isChecked = isCheckedControlled ? controlledChecked : internalChecked
106
+
107
+ const isExpandedControlled = controlledExpanded !== undefined
108
+ const [internalExpanded, setInternalExpanded] = useState(defaultExpanded)
109
+ const isExpanded = isExpandedControlled ? controlledExpanded : internalExpanded
110
+
111
+ const handleToggleChecked = useCallback(
112
+ (next: boolean) => {
113
+ if (disabled) return
114
+ if (!isCheckedControlled) setInternalChecked(next)
115
+ onValueChange?.(next)
116
+ },
117
+ [disabled, isCheckedControlled, onValueChange]
118
+ )
119
+
120
+ const handleToggleExpanded = useCallback(() => {
121
+ if (disabled) return
122
+ const next = !isExpanded
123
+ if (!isExpandedControlled) setInternalExpanded(next)
124
+ onExpandedChange?.(next)
125
+ }, [disabled, isExpanded, isExpandedControlled, onExpandedChange])
126
+
127
+ const gap =
128
+ (getVariableByName('expandableCheckbox/gap', modes) as number | null) ?? 8
129
+
130
+ const rowGap =
131
+ (getVariableByName('checkboxItem/gap', modes) as number | null) ?? 8
132
+ const rowPaddingHorizontal =
133
+ (getVariableByName('checkboxItem/padding/horizontal', modes) as number | null) ?? 0
134
+ const rowPaddingVertical =
135
+ (getVariableByName('checkboxItem/padding/vertical', modes) as number | null) ?? 0
136
+
137
+ const labelColor =
138
+ (getVariableByName('checkboxItem/foreground', modes) as string | null) ?? '#1a1c1f'
139
+ const labelFontFamily =
140
+ (getVariableByName('checkboxItem/label/fontFamily', modes) as string | null) ?? 'JioType Var'
141
+ const labelFontSize =
142
+ (getVariableByName('checkboxItem/label/fontSize', modes) as number | null) ?? 14
143
+ const labelLineHeight =
144
+ (getVariableByName('checkboxItem/label/lineHeight', modes) as number | null) ?? 19
145
+ const labelFontWeightRaw =
146
+ getVariableByName('checkboxItem/label/fontWeight', modes) ?? 400
147
+ const labelFontWeight = String(labelFontWeightRaw) as TextStyle['fontWeight']
148
+
149
+ const containerStyle: ViewStyle = useMemo(
150
+ () => ({
151
+ flexDirection: isExpanded ? 'column' : 'row',
152
+ alignItems: isExpanded ? 'flex-end' : 'center',
153
+ gap,
154
+ width: '100%',
155
+ ...(disabled ? { opacity: 0.6 } : null),
156
+ }),
157
+ [isExpanded, gap, disabled]
158
+ )
159
+
160
+ const rowStyle: ViewStyle = useMemo(
161
+ () => ({
162
+ flex: isExpanded ? undefined : 1,
163
+ alignSelf: isExpanded ? 'stretch' : 'auto',
164
+ minWidth: 0,
165
+ flexDirection: 'row',
166
+ alignItems: 'flex-start',
167
+ gap: rowGap,
168
+ paddingHorizontal: rowPaddingHorizontal,
169
+ paddingVertical: rowPaddingVertical,
170
+ }),
171
+ [isExpanded, rowGap, rowPaddingHorizontal, rowPaddingVertical]
172
+ )
173
+
174
+ const resolvedLabelStyle: TextStyle = useMemo(
175
+ () => ({
176
+ flex: 1,
177
+ minWidth: 0,
178
+ color: labelColor,
179
+ fontFamily: labelFontFamily,
180
+ fontSize: labelFontSize,
181
+ lineHeight: labelLineHeight,
182
+ fontWeight: labelFontWeight,
183
+ }),
184
+ [
185
+ labelColor,
186
+ labelFontFamily,
187
+ labelFontSize,
188
+ labelLineHeight,
189
+ labelFontWeight,
190
+ ]
191
+ )
192
+
193
+ const buttonModes = useMemo(
194
+ () => ({ ...BUTTON_DEFAULT_MODES, ...modes }),
195
+ [modes]
196
+ )
197
+
198
+ const a11yLabel =
199
+ accessibilityLabel ?? (typeof label === 'string' ? label : undefined)
200
+ const buttonLabel = isExpanded ? readLessLabel : readMoreLabel
201
+
202
+ const labelNumberOfLinesProps =
203
+ !isExpanded && collapsedLines > 0
204
+ ? { numberOfLines: collapsedLines, ellipsizeMode: 'tail' as const }
205
+ : null
206
+
207
+ return (
208
+ <View style={[containerStyle, style]}>
209
+ <View style={rowStyle}>
210
+ <Checkbox
211
+ checked={isChecked}
212
+ disabled={disabled}
213
+ onValueChange={handleToggleChecked}
214
+ modes={modes}
215
+ {...(a11yLabel !== undefined ? { accessibilityLabel: a11yLabel } : {})}
216
+ />
217
+ <Text
218
+ style={[resolvedLabelStyle, labelStyle]}
219
+ selectable={false}
220
+ {...(labelNumberOfLinesProps ?? {})}
221
+ >
222
+ {label}
223
+ </Text>
224
+ </View>
225
+ <Button
226
+ label={buttonLabel}
227
+ onPress={handleToggleExpanded}
228
+ disabled={disabled}
229
+ modes={buttonModes}
230
+ accessibilityLabel={buttonLabel}
231
+ accessibilityState={{ expanded: isExpanded }}
232
+ />
233
+ </View>
234
+ )
235
+ }
236
+
237
+ export default ExpandableCheckbox
@@ -1,7 +1,8 @@
1
- import React, { useCallback, useMemo, useState } from 'react'
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react'
2
2
  import {
3
3
  View,
4
4
  Text,
5
+ Pressable,
5
6
  TextInput as RNTextInput,
6
7
  type StyleProp,
7
8
  type TextInputProps as RNTextInputProps,
@@ -346,6 +347,16 @@ function FormField({
346
347
  const [isFocused, setIsFocused] = useState(false)
347
348
  const interactive = !isDisabled && !isReadOnly
348
349
 
350
+ // Ref to the native input so tapping anywhere in the input row (padding,
351
+ // leading/trailing gutters) focuses it on the FIRST tap — fixing the Android
352
+ // "two taps to open the keyboard" issue caused by the row intercepting the
353
+ // initial touch.
354
+ const inputRef = useRef<RNTextInput>(null)
355
+ const focusInput = useCallback(() => {
356
+ if (!interactive) return
357
+ inputRef.current?.focus()
358
+ }, [interactive])
359
+
349
360
  // FormField States cascade — error > read only/disabled > active (focused) > idle.
350
361
  // Disabled maps to "Read Only" since there is no dedicated disabled mode and
351
362
  // the visual treatment is closest. This is only the DEFAULT — an explicit
@@ -541,7 +552,11 @@ function FormField({
541
552
  </View>
542
553
  )}
543
554
 
544
- <View style={[inputRowStyle, inputStyle]}>
555
+ <Pressable
556
+ style={[inputRowStyle, inputStyle]}
557
+ onPress={focusInput}
558
+ accessible={false}
559
+ >
545
560
  {processedLeading != null && (
546
561
  <View
547
562
  accessibilityElementsHidden
@@ -551,6 +566,7 @@ function FormField({
551
566
  </View>
552
567
  )}
553
568
  <RNTextInput
569
+ ref={inputRef}
554
570
  style={[inputTextStyles, inputTextStyle]}
555
571
  value={value ?? ''}
556
572
  onChangeText={handleChangeText}
@@ -578,7 +594,7 @@ function FormField({
578
594
  {processedTrailing}
579
595
  </View>
580
596
  )}
581
- </View>
597
+ </Pressable>
582
598
 
583
599
  {supportLabel != null && supportLabel !== '' && (
584
600
  <SupportText