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.
- package/CHANGELOG.md +17 -0
- package/lib/commonjs/components/Accordion/Accordion.js +55 -55
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +48 -2
- package/lib/commonjs/components/Checkbox/Checkbox.js +21 -9
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -16
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +167 -0
- package/lib/commonjs/components/FormField/FormField.js +14 -1
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +355 -0
- package/lib/commonjs/components/ListItem/ListItem.js +25 -10
- package/lib/commonjs/components/MessageField/MessageField.js +318 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js +58 -17
- package/lib/commonjs/components/Stepper/Step.js +47 -60
- package/lib/commonjs/components/Stepper/StepLabel.js +40 -10
- package/lib/commonjs/components/Stepper/Stepper.js +15 -17
- package/lib/commonjs/components/SuggestiveSearch/SuggestiveSearch.js +487 -0
- package/lib/commonjs/components/TextInput/TextInput.js +16 -1
- package/lib/commonjs/components/Title/Title.js +10 -2
- package/lib/commonjs/components/index.js +28 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Accordion/Accordion.js +56 -56
- package/lib/module/components/ActionFooter/ActionFooter.js +50 -4
- package/lib/module/components/Checkbox/Checkbox.js +22 -10
- package/lib/module/components/DropdownInput/DropdownInput.js +30 -16
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +161 -0
- package/lib/module/components/FormField/FormField.js +16 -3
- package/lib/module/components/FullscreenModal/FullscreenModal.js +350 -0
- package/lib/module/components/ListItem/ListItem.js +25 -10
- package/lib/module/components/MessageField/MessageField.js +313 -0
- package/lib/module/components/NavArrow/NavArrow.js +59 -18
- package/lib/module/components/Stepper/Step.js +48 -61
- package/lib/module/components/Stepper/StepLabel.js +40 -10
- package/lib/module/components/Stepper/Stepper.js +15 -17
- package/lib/module/components/SuggestiveSearch/SuggestiveSearch.js +481 -0
- package/lib/module/components/TextInput/TextInput.js +17 -2
- package/lib/module/components/Title/Title.js +10 -2
- package/lib/module/components/index.js +4 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -20
- package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +63 -0
- package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +99 -0
- package/lib/typescript/src/components/MessageField/MessageField.d.ts +81 -0
- package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +10 -5
- package/lib/typescript/src/components/Stepper/Step.d.ts +4 -1
- package/lib/typescript/src/components/Stepper/StepLabel.d.ts +4 -1
- package/lib/typescript/src/components/Stepper/Stepper.d.ts +3 -1
- package/lib/typescript/src/components/SuggestiveSearch/SuggestiveSearch.d.ts +123 -0
- package/lib/typescript/src/components/index.d.ts +7 -3
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.tsx +113 -73
- package/src/components/ActionFooter/ActionFooter.tsx +56 -4
- package/src/components/Checkbox/Checkbox.tsx +22 -9
- package/src/components/DropdownInput/DropdownInput.tsx +67 -39
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +237 -0
- package/src/components/FormField/FormField.tsx +19 -3
- package/src/components/FullscreenModal/FullscreenModal.tsx +414 -0
- package/src/components/ListItem/ListItem.tsx +21 -10
- package/src/components/MessageField/MessageField.tsx +543 -0
- package/src/components/NavArrow/NavArrow.tsx +81 -17
- package/src/components/Stepper/Step.tsx +52 -51
- package/src/components/Stepper/StepLabel.tsx +46 -9
- package/src/components/Stepper/Stepper.tsx +20 -15
- package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +756 -0
- package/src/components/TextInput/TextInput.tsx +14 -1
- package/src/components/Title/Title.tsx +13 -2
- package/src/components/index.ts +7 -3
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- 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
|
-
<
|
|
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
|
-
</
|
|
597
|
+
</Pressable>
|
|
582
598
|
|
|
583
599
|
{supportLabel != null && supportLabel !== '' && (
|
|
584
600
|
<SupportText
|