jfs-components 0.0.74 → 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 +109 -0
- package/lib/commonjs/components/Accordion/Accordion.js +55 -55
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +193 -82
- package/lib/commonjs/components/Avatar/Avatar.js +20 -0
- package/lib/commonjs/components/Badge/Badge.js +23 -0
- package/lib/commonjs/components/Button/Button.js +37 -0
- 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/IconButton/IconButton.js +20 -0
- package/lib/commonjs/components/Image/Image.js +26 -1
- package/lib/commonjs/components/ListItem/ListItem.js +25 -10
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.js +116 -0
- package/lib/commonjs/components/LottiePlayer/LottiePlayer.web.js +82 -0
- package/lib/commonjs/components/LottiePlayer/loadNativeLottieView.js +74 -0
- package/lib/commonjs/components/LottiePlayer/loadWebLottieView.js +50 -0
- package/lib/commonjs/components/MessageField/MessageField.js +318 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js +58 -17
- package/lib/commonjs/components/PageHero/PageHero.js +41 -5
- package/lib/commonjs/components/RechargeCard/RechargeCard.js +32 -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/Text/Text.js +31 -1
- package/lib/commonjs/components/TextInput/TextInput.js +16 -1
- package/lib/commonjs/components/Title/Title.js +10 -2
- package/lib/commonjs/components/index.js +35 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/Icon.js +16 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/skeleton/Skeleton.js +234 -0
- package/lib/commonjs/skeleton/SkeletonGroup.js +140 -0
- package/lib/commonjs/skeleton/index.js +58 -0
- package/lib/commonjs/skeleton/shimmer-tokens.js +189 -0
- package/lib/commonjs/skeleton/useReducedMotion.js +64 -0
- package/lib/module/components/Accordion/Accordion.js +56 -56
- package/lib/module/components/ActionFooter/ActionFooter.js +193 -83
- package/lib/module/components/Avatar/Avatar.js +19 -0
- package/lib/module/components/Badge/Badge.js +23 -0
- package/lib/module/components/Button/Button.js +37 -0
- 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/IconButton/IconButton.js +20 -0
- package/lib/module/components/Image/Image.js +25 -1
- package/lib/module/components/ListItem/ListItem.js +25 -10
- package/lib/module/components/LottiePlayer/LottiePlayer.js +111 -0
- package/lib/module/components/LottiePlayer/LottiePlayer.web.js +77 -0
- package/lib/module/components/LottiePlayer/loadNativeLottieView.js +69 -0
- package/lib/module/components/LottiePlayer/loadWebLottieView.js +45 -0
- package/lib/module/components/MessageField/MessageField.js +313 -0
- package/lib/module/components/NavArrow/NavArrow.js +59 -18
- package/lib/module/components/PageHero/PageHero.js +41 -5
- package/lib/module/components/RechargeCard/RechargeCard.js +33 -17
- 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/Text/Text.js +31 -1
- package/lib/module/components/TextInput/TextInput.js +17 -2
- package/lib/module/components/Title/Title.js +10 -2
- package/lib/module/components/index.js +5 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/Icon.js +16 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/skeleton/Skeleton.js +229 -0
- package/lib/module/skeleton/SkeletonGroup.js +133 -0
- package/lib/module/skeleton/index.js +6 -0
- package/lib/module/skeleton/shimmer-tokens.js +181 -0
- package/lib/module/skeleton/useReducedMotion.js +61 -0
- package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -20
- package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +26 -21
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +7 -1
- package/lib/typescript/src/components/Badge/Badge.d.ts +7 -1
- package/lib/typescript/src/components/Button/Button.d.ts +8 -1
- 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/IconButton/IconButton.d.ts +7 -1
- package/lib/typescript/src/components/Image/Image.d.ts +8 -1
- package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +85 -0
- package/lib/typescript/src/components/LottiePlayer/LottiePlayer.web.d.ts +28 -0
- package/lib/typescript/src/components/LottiePlayer/loadNativeLottieView.d.ts +11 -0
- package/lib/typescript/src/components/LottiePlayer/loadWebLottieView.d.ts +11 -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/PageHero/PageHero.d.ts +31 -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/Text/Text.d.ts +20 -1
- package/lib/typescript/src/components/index.d.ts +8 -3
- package/lib/typescript/src/icons/Icon.d.ts +7 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/skeleton/Skeleton.d.ts +60 -0
- package/lib/typescript/src/skeleton/SkeletonGroup.d.ts +78 -0
- package/lib/typescript/src/skeleton/index.d.ts +5 -0
- package/lib/typescript/src/skeleton/shimmer-tokens.d.ts +160 -0
- package/lib/typescript/src/skeleton/useReducedMotion.d.ts +15 -0
- package/package.json +11 -1
- package/src/components/Accordion/Accordion.tsx +113 -73
- package/src/components/ActionFooter/ActionFooter.tsx +210 -92
- package/src/components/Avatar/Avatar.tsx +26 -0
- package/src/components/Badge/Badge.tsx +27 -0
- package/src/components/Button/Button.tsx +40 -0
- 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/IconButton/IconButton.tsx +27 -0
- package/src/components/Image/Image.tsx +25 -0
- package/src/components/ListItem/ListItem.tsx +21 -10
- package/src/components/LottiePlayer/LottiePlayer.tsx +145 -0
- package/src/components/LottiePlayer/LottiePlayer.web.tsx +94 -0
- package/src/components/LottiePlayer/loadNativeLottieView.tsx +87 -0
- package/src/components/LottiePlayer/loadWebLottieView.tsx +64 -0
- package/src/components/MessageField/MessageField.tsx +543 -0
- package/src/components/NavArrow/NavArrow.tsx +81 -17
- package/src/components/PageHero/PageHero.tsx +61 -4
- package/src/components/RechargeCard/RechargeCard.tsx +32 -24
- 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/Text/Text.tsx +54 -0
- package/src/components/TextInput/TextInput.tsx +14 -1
- package/src/components/Title/Title.tsx +13 -2
- package/src/components/index.ts +8 -3
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/icons/Icon.tsx +17 -0
- package/src/icons/registry.ts +1 -1
- package/src/index.ts +1 -0
- package/src/skeleton/Skeleton.tsx +298 -0
- package/src/skeleton/SkeletonGroup.tsx +193 -0
- package/src/skeleton/index.ts +10 -0
- package/src/skeleton/shimmer-tokens.ts +221 -0
- package/src/skeleton/useReducedMotion.ts +72 -0
|
@@ -8,7 +8,10 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
10
|
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
|
|
12
|
+
var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
|
|
11
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
15
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
13
16
|
const avatarImage = require('./31595e70c4181263f9971590224b12934b280c9b.png');
|
|
14
17
|
|
|
@@ -128,11 +131,19 @@ function Avatar({
|
|
|
128
131
|
// component intentionally renders `accessibilityLabel={undefined}` on the
|
|
129
132
|
// wrapper (the inner Text/Image carry the label instead).
|
|
130
133
|
accessibilityLabel: _accessibilityLabel,
|
|
134
|
+
loading,
|
|
131
135
|
...rest
|
|
132
136
|
}) {
|
|
133
137
|
const isMonogram = style === 'Monogram';
|
|
134
138
|
const tokens = (0, _react.useMemo)(() => resolveAvatarTokens(modes, isMonogram), [modes, isMonogram]);
|
|
135
139
|
|
|
140
|
+
// Skeleton context — read unconditionally; the actual short-circuit
|
|
141
|
+
// happens AFTER all remaining hooks below.
|
|
142
|
+
const {
|
|
143
|
+
active: groupActive
|
|
144
|
+
} = (0, _SkeletonGroup.useSkeleton)();
|
|
145
|
+
const isLoading = loading ?? groupActive;
|
|
146
|
+
|
|
136
147
|
// Focus is a sustained visible state — keep mirroring on web; gate the
|
|
137
148
|
// setter so it never fires on native (where focus events don't fire on
|
|
138
149
|
// these elements anyway).
|
|
@@ -163,6 +174,15 @@ function Avatar({
|
|
|
163
174
|
pressed
|
|
164
175
|
}) => [tokens.containerStyle, pressed ? pressedOverlayStyle : null, isFocused ? focusOverlayStyle : null], [tokens.containerStyle, isFocused]);
|
|
165
176
|
const staticContainerStyle = (0, _react.useMemo)(() => [tokens.containerStyle, isFocused ? focusOverlayStyle : null], [tokens.containerStyle, isFocused]);
|
|
177
|
+
if (isLoading) {
|
|
178
|
+
const size = tokens.containerStyle.width;
|
|
179
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
|
|
180
|
+
kind: "other",
|
|
181
|
+
width: size,
|
|
182
|
+
height: size,
|
|
183
|
+
modes: modes
|
|
184
|
+
});
|
|
185
|
+
}
|
|
166
186
|
|
|
167
187
|
// The inner content varies; everything else (wrapper, handlers, style) is shared.
|
|
168
188
|
const innerContent = isMonogram ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -8,6 +8,8 @@ var _react = _interopRequireDefault(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
10
|
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
|
|
12
|
+
var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
|
|
11
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
14
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
15
|
function Badge({
|
|
@@ -17,6 +19,7 @@ function Badge({
|
|
|
17
19
|
accessibilityLabel,
|
|
18
20
|
style,
|
|
19
21
|
labelStyle,
|
|
22
|
+
loading,
|
|
20
23
|
...rest
|
|
21
24
|
}) {
|
|
22
25
|
// Resolve token values (fall back to sensible defaults)
|
|
@@ -29,6 +32,26 @@ function Badge({
|
|
|
29
32
|
const paddingVertical = Number((0, _figmaVariablesResolver.getVariableByName)('badge/padding/vertical', modes)) || 4;
|
|
30
33
|
const borderRadius = Number((0, _figmaVariablesResolver.getVariableByName)('badge/radius', modes)) || 4;
|
|
31
34
|
const lineHeight = Number((0, _figmaVariablesResolver.getVariableByName)('badge/label/lineHeight', modes)) || Math.round(fontSize * 1.2);
|
|
35
|
+
|
|
36
|
+
// Skeleton short-circuit. Size derived from the same tokens the loaded
|
|
37
|
+
// badge would use so the placeholder occupies the same box.
|
|
38
|
+
const {
|
|
39
|
+
active: groupActive
|
|
40
|
+
} = (0, _SkeletonGroup.useSkeleton)();
|
|
41
|
+
const isLoading = loading ?? groupActive;
|
|
42
|
+
if (isLoading) {
|
|
43
|
+
const charWidth = fontSize * 0.55;
|
|
44
|
+
const labelWidth = Math.max(label.length, 3) * charWidth;
|
|
45
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
|
|
46
|
+
kind: "badge",
|
|
47
|
+
width: paddingHorizontal * 2 + labelWidth,
|
|
48
|
+
height: paddingVertical * 2 + lineHeight,
|
|
49
|
+
style: {
|
|
50
|
+
alignSelf: 'flex-start'
|
|
51
|
+
},
|
|
52
|
+
modes: modes
|
|
53
|
+
});
|
|
54
|
+
}
|
|
32
55
|
const Container = onPress ? _reactNative.Pressable : _reactNative.View;
|
|
33
56
|
const containerStyle = {
|
|
34
57
|
backgroundColor,
|
|
@@ -10,6 +10,8 @@ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resol
|
|
|
10
10
|
var _webPlatformUtils = require("../../utils/web-platform-utils");
|
|
11
11
|
var _reactUtils = require("../../utils/react-utils");
|
|
12
12
|
var _Icon = _interopRequireDefault(require("../../icons/Icon"));
|
|
13
|
+
var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
|
|
14
|
+
var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
|
|
13
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
17
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
@@ -173,6 +175,7 @@ function ButtonImpl({
|
|
|
173
175
|
accessibilityHint,
|
|
174
176
|
accessibilityState,
|
|
175
177
|
webAccessibilityProps,
|
|
178
|
+
loading,
|
|
176
179
|
...rest
|
|
177
180
|
}) {
|
|
178
181
|
// Hover state is web-only in practice; the setter is gated so native taps
|
|
@@ -188,6 +191,14 @@ function ButtonImpl({
|
|
|
188
191
|
userHandlersRef.current.onHoverOut = rest?.onHoverOut;
|
|
189
192
|
const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled), [modes, disabled]);
|
|
190
193
|
|
|
194
|
+
// Skeleton context — read unconditionally so React's hook order stays
|
|
195
|
+
// stable. The actual short-circuit return happens AFTER all remaining
|
|
196
|
+
// hooks have been called below.
|
|
197
|
+
const {
|
|
198
|
+
active: groupActive
|
|
199
|
+
} = (0, _SkeletonGroup.useSkeleton)();
|
|
200
|
+
const isLoading = loading ?? groupActive;
|
|
201
|
+
|
|
191
202
|
// Active label color: base by default; hover override (web-only) when hovered.
|
|
192
203
|
// Press color is intentionally NOT applied to the label on native — applying
|
|
193
204
|
// it would require a React render per touch and re-introduce the flicker.
|
|
@@ -265,6 +276,32 @@ function ButtonImpl({
|
|
|
265
276
|
console.warn('[Button] Custom content is used without an explicit `accessibilityLabel` or string `label`. ' + 'Screen readers may not announce this button correctly.');
|
|
266
277
|
}
|
|
267
278
|
}
|
|
279
|
+
if (isLoading) {
|
|
280
|
+
const {
|
|
281
|
+
container,
|
|
282
|
+
baseLabel,
|
|
283
|
+
iconSize,
|
|
284
|
+
accessoryOffset
|
|
285
|
+
} = tokens;
|
|
286
|
+
const paddingHorizontal = container.paddingHorizontal ?? 20;
|
|
287
|
+
const paddingVertical = container.paddingVertical ?? 12;
|
|
288
|
+
const lineHeight = baseLabel.lineHeight ?? 19;
|
|
289
|
+
const fontSize = baseLabel.fontSize ?? 16;
|
|
290
|
+
const labelText = typeof label === 'string' ? label : 'Button';
|
|
291
|
+
const charWidth = fontSize * 0.55;
|
|
292
|
+
const labelWidth = Math.max(labelText.length, 4) * charWidth;
|
|
293
|
+
const hasAccessory = !!(leading || trailing || icon);
|
|
294
|
+
const accessoryWidth = hasAccessory ? iconSize + accessoryOffset * 2 : 0;
|
|
295
|
+
const skeletonWidth = paddingHorizontal * 2 + labelWidth + accessoryWidth;
|
|
296
|
+
const skeletonHeight = paddingVertical * 2 + lineHeight;
|
|
297
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
|
|
298
|
+
kind: "other",
|
|
299
|
+
width: skeletonWidth,
|
|
300
|
+
height: skeletonHeight,
|
|
301
|
+
style: style,
|
|
302
|
+
modes: modes
|
|
303
|
+
});
|
|
304
|
+
}
|
|
268
305
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
269
306
|
accessibilityRole: "button",
|
|
270
307
|
accessibilityLabel: undefined,
|
|
@@ -49,6 +49,15 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
/** Minimum touch target per iOS HIG / Material accessibility guidance. */
|
|
54
|
+
const MIN_TOUCH_TARGET = 44;
|
|
55
|
+
const touchTargetStyle = {
|
|
56
|
+
minWidth: MIN_TOUCH_TARGET,
|
|
57
|
+
minHeight: MIN_TOUCH_TARGET,
|
|
58
|
+
alignItems: 'center',
|
|
59
|
+
justifyContent: 'center'
|
|
60
|
+
};
|
|
52
61
|
/**
|
|
53
62
|
* Checkbox component that maps directly to the Figma design using design tokens.
|
|
54
63
|
*
|
|
@@ -174,7 +183,7 @@ function Checkbox({
|
|
|
174
183
|
};
|
|
175
184
|
const markColor = disabled && isChecked ? disabledActiveMark : selectedMarkColor;
|
|
176
185
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
177
|
-
style: [
|
|
186
|
+
style: [touchTargetStyle, style],
|
|
178
187
|
onPress: handlePress,
|
|
179
188
|
disabled: disabled,
|
|
180
189
|
onHoverIn: () => setIsHovered(true),
|
|
@@ -186,14 +195,17 @@ function Checkbox({
|
|
|
186
195
|
disabled
|
|
187
196
|
},
|
|
188
197
|
accessibilityLabel: accessibilityLabel,
|
|
189
|
-
children:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
199
|
+
style: resolveStyle(),
|
|
200
|
+
children: isChecked && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.default, {
|
|
201
|
+
width: 12,
|
|
202
|
+
height: 9,
|
|
203
|
+
viewBox: "0 0 12 9",
|
|
204
|
+
fill: "none",
|
|
205
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Path, {
|
|
206
|
+
d: "M4.00091 8.66939C3.91321 8.6699 3.82628 8.65309 3.74509 8.61991C3.6639 8.58673 3.59006 8.53785 3.52779 8.47606L0.195972 5.14273C0.0704931 5.01719 -1.86978e-09 4.84693 0 4.66939C1.86978e-09 4.49186 0.0704931 4.3216 0.195972 4.19606C0.321451 4.07053 0.491636 4 0.66909 4C0.846544 4 1.01673 4.07053 1.14221 4.19606L4.00091 7.06273L10.8578 0.196061C10.9833 0.0705253 11.1535 0 11.3309 0C11.5084 0 11.6785 0.0705253 11.804 0.196061C11.9295 0.321597 12 0.49186 12 0.669394C12 0.846929 11.9295 1.01719 11.804 1.14273L4.47403 8.47606C4.41176 8.53785 4.33792 8.58673 4.25673 8.61991C4.17554 8.65309 4.08861 8.6699 4.00091 8.66939Z",
|
|
207
|
+
fill: markColor
|
|
208
|
+
})
|
|
197
209
|
})
|
|
198
210
|
})
|
|
199
211
|
});
|
|
@@ -36,25 +36,36 @@ function useChevronTokens(modes) {
|
|
|
36
36
|
};
|
|
37
37
|
}, [modes]);
|
|
38
38
|
}
|
|
39
|
+
function toNumber(value, fallback) {
|
|
40
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
41
|
+
if (typeof value === 'string') {
|
|
42
|
+
const parsed = parseFloat(value);
|
|
43
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
44
|
+
}
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
39
47
|
function useFormFieldTokens(modes) {
|
|
40
48
|
return (0, _react.useMemo)(() => {
|
|
41
|
-
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('formField/label/color', modes) || '#
|
|
49
|
+
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('formField/label/color', modes) || '#000000';
|
|
42
50
|
const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontFamily', modes) || 'JioType Var';
|
|
43
|
-
const labelFontSize =
|
|
44
|
-
const labelLineHeight =
|
|
51
|
+
const labelFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/label/fontSize', modes), 14);
|
|
52
|
+
const labelLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/label/lineHeight', modes), 17);
|
|
45
53
|
const labelFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontWeight', modes) || '500';
|
|
46
|
-
const gap =
|
|
47
|
-
const inputPaddingH =
|
|
48
|
-
const inputGap =
|
|
49
|
-
const inputRadius =
|
|
54
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/gap', modes), 8);
|
|
55
|
+
const inputPaddingH = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/padding/horizontal', modes), 12);
|
|
56
|
+
const inputGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/gap', modes), 8);
|
|
57
|
+
const inputRadius = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/radius', modes), 8);
|
|
50
58
|
const inputBackground = (0, _figmaVariablesResolver.getVariableByName)('formField/input/background', modes) || '#ffffff';
|
|
51
|
-
const inputFontSize =
|
|
52
|
-
const inputLineHeight =
|
|
59
|
+
const inputFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontSize', modes), 16);
|
|
60
|
+
const inputLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/lineHeight', modes), 45);
|
|
53
61
|
const inputFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontFamily', modes) || 'JioType Var';
|
|
54
62
|
const inputFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontWeight', modes) || '400';
|
|
55
63
|
const inputTextColor = (0, _figmaVariablesResolver.getVariableByName)('states/formField/input/label/color', modes) || (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/color', modes) || '#24262b';
|
|
56
64
|
const inputBorderColor = (0, _figmaVariablesResolver.getVariableByName)('states/formField/input/border/color', modes) || (0, _figmaVariablesResolver.getVariableByName)('formField/input/border/color', modes) || '#b5b6b7';
|
|
57
|
-
|
|
65
|
+
// Figma spec: 1.5px. Using parseFloat (via toNumber) preserves the
|
|
66
|
+
// fractional value — parseInt was truncating it to 1, leaving the
|
|
67
|
+
// resolved row height ~1px shorter than the Figma reference.
|
|
68
|
+
const inputBorderSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/border/size', modes), 1.5);
|
|
58
69
|
return {
|
|
59
70
|
labelColor,
|
|
60
71
|
labelFontFamily,
|
|
@@ -134,7 +145,7 @@ function DropdownInput({
|
|
|
134
145
|
supportText,
|
|
135
146
|
errorMessage,
|
|
136
147
|
menuMaxHeight = 240,
|
|
137
|
-
menuOffset =
|
|
148
|
+
menuOffset = 6,
|
|
138
149
|
matchTriggerWidth = true,
|
|
139
150
|
closeOnBackdropPress = true,
|
|
140
151
|
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
@@ -340,19 +351,23 @@ function DropdownInput({
|
|
|
340
351
|
};
|
|
341
352
|
|
|
342
353
|
// Focus ring uses the resolved input border color from FormField States so
|
|
343
|
-
// active/error look consistent with TextInput-based FormField.
|
|
344
|
-
//
|
|
354
|
+
// active/error look consistent with TextInput-based FormField. Only the
|
|
355
|
+
// color changes between states — width stays constant to avoid layout
|
|
356
|
+
// shift when opening the menu (a shift would invalidate the measured
|
|
357
|
+
// trigger rect and visually shove the popup).
|
|
345
358
|
const inputRowStyle = {
|
|
346
359
|
flexDirection: 'row',
|
|
347
360
|
alignItems: 'center',
|
|
348
361
|
backgroundColor: tokens.inputBackground,
|
|
349
362
|
borderColor: tokens.inputBorderColor,
|
|
350
|
-
borderWidth:
|
|
363
|
+
borderWidth: tokens.inputBorderSize,
|
|
364
|
+
borderStyle: 'solid',
|
|
351
365
|
borderRadius: tokens.inputRadius,
|
|
352
366
|
paddingHorizontal: tokens.inputPaddingH,
|
|
353
367
|
paddingVertical: 0,
|
|
354
368
|
gap: tokens.inputGap,
|
|
355
|
-
minHeight: tokens.inputLineHeight
|
|
369
|
+
minHeight: tokens.inputLineHeight,
|
|
370
|
+
width: '100%'
|
|
356
371
|
};
|
|
357
372
|
const valueTextStyle = {
|
|
358
373
|
flex: 1,
|
|
@@ -500,7 +515,6 @@ function DropdownInput({
|
|
|
500
515
|
transparent: true,
|
|
501
516
|
animationType: "fade",
|
|
502
517
|
onRequestClose: closeMenu,
|
|
503
|
-
statusBarTranslucent: true,
|
|
504
518
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
505
519
|
style: _reactNative.StyleSheet.absoluteFill,
|
|
506
520
|
onPress: closeOnBackdropPress ? closeMenu : undefined,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Checkbox = _interopRequireDefault(require("../Checkbox/Checkbox"));
|
|
12
|
+
var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
16
|
+
/**
|
|
17
|
+
* Default modes applied to the inner toggle `Button`. These resolve the
|
|
18
|
+
* tertiary-style pill in the Figma reference (small, transparent background,
|
|
19
|
+
* brand purple foreground). Any value supplied via the consumer `modes` prop
|
|
20
|
+
* takes precedence over these defaults.
|
|
21
|
+
*/
|
|
22
|
+
const BUTTON_DEFAULT_MODES = {
|
|
23
|
+
'Button / Size': 'XS',
|
|
24
|
+
AppearanceBrand: 'Secondary',
|
|
25
|
+
Emphasis: 'Low'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ExpandableCheckbox composes a `Checkbox`, a long-form label and a
|
|
30
|
+
* "Read more" / "Read less" toggle. Mirrors the Figma "Expandable Checkbox"
|
|
31
|
+
* component with two states:
|
|
32
|
+
*
|
|
33
|
+
* - **Idle (collapsed)** — checkbox + truncated label + toggle button arranged
|
|
34
|
+
* in a horizontal row (cross-axis centered).
|
|
35
|
+
* - **Open (expanded)** — checkbox + full multi-line label, with the toggle
|
|
36
|
+
* button right-aligned beneath the row.
|
|
37
|
+
*
|
|
38
|
+
* The checkbox and the toggle button have independent press handlers — pressing
|
|
39
|
+
* the toggle does not affect the checked state, and toggling the checkbox does
|
|
40
|
+
* not collapse / expand the row.
|
|
41
|
+
*
|
|
42
|
+
* @component
|
|
43
|
+
* @param {ExpandableCheckboxProps} props
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <ExpandableCheckbox
|
|
48
|
+
* label="By checking this box, I (a) acknowledge and (b) agree to the full terms…"
|
|
49
|
+
* defaultChecked
|
|
50
|
+
* onValueChange={setAccepted}
|
|
51
|
+
* modes={{ 'Color Mode': 'Light' }}
|
|
52
|
+
* />
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function ExpandableCheckbox({
|
|
56
|
+
label = '',
|
|
57
|
+
checked: controlledChecked,
|
|
58
|
+
defaultChecked = false,
|
|
59
|
+
onValueChange,
|
|
60
|
+
expanded: controlledExpanded,
|
|
61
|
+
defaultExpanded = false,
|
|
62
|
+
onExpandedChange,
|
|
63
|
+
disabled = false,
|
|
64
|
+
readMoreLabel = 'Read more',
|
|
65
|
+
readLessLabel = 'Read less',
|
|
66
|
+
collapsedLines = 1,
|
|
67
|
+
modes = _reactUtils.EMPTY_MODES,
|
|
68
|
+
style,
|
|
69
|
+
labelStyle,
|
|
70
|
+
accessibilityLabel
|
|
71
|
+
}) {
|
|
72
|
+
const isCheckedControlled = controlledChecked !== undefined;
|
|
73
|
+
const [internalChecked, setInternalChecked] = (0, _react.useState)(defaultChecked);
|
|
74
|
+
const isChecked = isCheckedControlled ? controlledChecked : internalChecked;
|
|
75
|
+
const isExpandedControlled = controlledExpanded !== undefined;
|
|
76
|
+
const [internalExpanded, setInternalExpanded] = (0, _react.useState)(defaultExpanded);
|
|
77
|
+
const isExpanded = isExpandedControlled ? controlledExpanded : internalExpanded;
|
|
78
|
+
const handleToggleChecked = (0, _react.useCallback)(next => {
|
|
79
|
+
if (disabled) return;
|
|
80
|
+
if (!isCheckedControlled) setInternalChecked(next);
|
|
81
|
+
onValueChange?.(next);
|
|
82
|
+
}, [disabled, isCheckedControlled, onValueChange]);
|
|
83
|
+
const handleToggleExpanded = (0, _react.useCallback)(() => {
|
|
84
|
+
if (disabled) return;
|
|
85
|
+
const next = !isExpanded;
|
|
86
|
+
if (!isExpandedControlled) setInternalExpanded(next);
|
|
87
|
+
onExpandedChange?.(next);
|
|
88
|
+
}, [disabled, isExpanded, isExpandedControlled, onExpandedChange]);
|
|
89
|
+
const gap = (0, _figmaVariablesResolver.getVariableByName)('expandableCheckbox/gap', modes) ?? 8;
|
|
90
|
+
const rowGap = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/gap', modes) ?? 8;
|
|
91
|
+
const rowPaddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/horizontal', modes) ?? 0;
|
|
92
|
+
const rowPaddingVertical = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/padding/vertical', modes) ?? 0;
|
|
93
|
+
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/foreground', modes) ?? '#1a1c1f';
|
|
94
|
+
const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontFamily', modes) ?? 'JioType Var';
|
|
95
|
+
const labelFontSize = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontSize', modes) ?? 14;
|
|
96
|
+
const labelLineHeight = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/lineHeight', modes) ?? 19;
|
|
97
|
+
const labelFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('checkboxItem/label/fontWeight', modes) ?? 400;
|
|
98
|
+
const labelFontWeight = String(labelFontWeightRaw);
|
|
99
|
+
const containerStyle = (0, _react.useMemo)(() => ({
|
|
100
|
+
flexDirection: isExpanded ? 'column' : 'row',
|
|
101
|
+
alignItems: isExpanded ? 'flex-end' : 'center',
|
|
102
|
+
gap,
|
|
103
|
+
width: '100%',
|
|
104
|
+
...(disabled ? {
|
|
105
|
+
opacity: 0.6
|
|
106
|
+
} : null)
|
|
107
|
+
}), [isExpanded, gap, disabled]);
|
|
108
|
+
const rowStyle = (0, _react.useMemo)(() => ({
|
|
109
|
+
flex: isExpanded ? undefined : 1,
|
|
110
|
+
alignSelf: isExpanded ? 'stretch' : 'auto',
|
|
111
|
+
minWidth: 0,
|
|
112
|
+
flexDirection: 'row',
|
|
113
|
+
alignItems: 'flex-start',
|
|
114
|
+
gap: rowGap,
|
|
115
|
+
paddingHorizontal: rowPaddingHorizontal,
|
|
116
|
+
paddingVertical: rowPaddingVertical
|
|
117
|
+
}), [isExpanded, rowGap, rowPaddingHorizontal, rowPaddingVertical]);
|
|
118
|
+
const resolvedLabelStyle = (0, _react.useMemo)(() => ({
|
|
119
|
+
flex: 1,
|
|
120
|
+
minWidth: 0,
|
|
121
|
+
color: labelColor,
|
|
122
|
+
fontFamily: labelFontFamily,
|
|
123
|
+
fontSize: labelFontSize,
|
|
124
|
+
lineHeight: labelLineHeight,
|
|
125
|
+
fontWeight: labelFontWeight
|
|
126
|
+
}), [labelColor, labelFontFamily, labelFontSize, labelLineHeight, labelFontWeight]);
|
|
127
|
+
const buttonModes = (0, _react.useMemo)(() => ({
|
|
128
|
+
...BUTTON_DEFAULT_MODES,
|
|
129
|
+
...modes
|
|
130
|
+
}), [modes]);
|
|
131
|
+
const a11yLabel = accessibilityLabel ?? (typeof label === 'string' ? label : undefined);
|
|
132
|
+
const buttonLabel = isExpanded ? readLessLabel : readMoreLabel;
|
|
133
|
+
const labelNumberOfLinesProps = !isExpanded && collapsedLines > 0 ? {
|
|
134
|
+
numberOfLines: collapsedLines,
|
|
135
|
+
ellipsizeMode: 'tail'
|
|
136
|
+
} : null;
|
|
137
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
138
|
+
style: [containerStyle, style],
|
|
139
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
140
|
+
style: rowStyle,
|
|
141
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Checkbox.default, {
|
|
142
|
+
checked: isChecked,
|
|
143
|
+
disabled: disabled,
|
|
144
|
+
onValueChange: handleToggleChecked,
|
|
145
|
+
modes: modes,
|
|
146
|
+
...(a11yLabel !== undefined ? {
|
|
147
|
+
accessibilityLabel: a11yLabel
|
|
148
|
+
} : {})
|
|
149
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
150
|
+
style: [resolvedLabelStyle, labelStyle],
|
|
151
|
+
selectable: false,
|
|
152
|
+
...(labelNumberOfLinesProps ?? {}),
|
|
153
|
+
children: label
|
|
154
|
+
})]
|
|
155
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
156
|
+
label: buttonLabel,
|
|
157
|
+
onPress: handleToggleExpanded,
|
|
158
|
+
disabled: disabled,
|
|
159
|
+
modes: buttonModes,
|
|
160
|
+
accessibilityLabel: buttonLabel,
|
|
161
|
+
accessibilityState: {
|
|
162
|
+
expanded: isExpanded
|
|
163
|
+
}
|
|
164
|
+
})]
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
var _default = exports.default = ExpandableCheckbox;
|
|
@@ -208,6 +208,16 @@ function FormField({
|
|
|
208
208
|
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
209
209
|
const interactive = !isDisabled && !isReadOnly;
|
|
210
210
|
|
|
211
|
+
// Ref to the native input so tapping anywhere in the input row (padding,
|
|
212
|
+
// leading/trailing gutters) focuses it on the FIRST tap — fixing the Android
|
|
213
|
+
// "two taps to open the keyboard" issue caused by the row intercepting the
|
|
214
|
+
// initial touch.
|
|
215
|
+
const inputRef = (0, _react.useRef)(null);
|
|
216
|
+
const focusInput = (0, _react.useCallback)(() => {
|
|
217
|
+
if (!interactive) return;
|
|
218
|
+
inputRef.current?.focus();
|
|
219
|
+
}, [interactive]);
|
|
220
|
+
|
|
211
221
|
// FormField States cascade — error > read only/disabled > active (focused) > idle.
|
|
212
222
|
// Disabled maps to "Read Only" since there is no dedicated disabled mode and
|
|
213
223
|
// the visual treatment is closest. This is only the DEFAULT — an explicit
|
|
@@ -340,13 +350,16 @@ function FormField({
|
|
|
340
350
|
style: requiredIndicatorStyle,
|
|
341
351
|
children: " *"
|
|
342
352
|
})]
|
|
343
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.
|
|
353
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
344
354
|
style: [inputRowStyle, inputStyle],
|
|
355
|
+
onPress: focusInput,
|
|
356
|
+
accessible: false,
|
|
345
357
|
children: [processedLeading != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
346
358
|
accessibilityElementsHidden: true,
|
|
347
359
|
importantForAccessibility: "no",
|
|
348
360
|
children: processedLeading
|
|
349
361
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
362
|
+
ref: inputRef,
|
|
350
363
|
style: [inputTextStyles, inputTextStyle],
|
|
351
364
|
value: value ?? '',
|
|
352
365
|
onChangeText: handleChangeText,
|