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
|
@@ -0,0 +1,487 @@
|
|
|
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 _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
11
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
12
|
+
var _SupportText = _interopRequireDefault(require("../SupportText/SupportText"));
|
|
13
|
+
var _Dropdown = _interopRequireWildcard(require("../Dropdown/Dropdown"));
|
|
14
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
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); }
|
|
17
|
+
const IS_WEB = _reactNative.Platform.OS === 'web';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Types
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Suggestions accept either a bare string (used as both value and label) or a
|
|
25
|
+
* full `{ value, label }` option object for richer data.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
function toNumber(value, fallback) {
|
|
33
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
const parsed = parseFloat(value);
|
|
36
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
37
|
+
}
|
|
38
|
+
return fallback;
|
|
39
|
+
}
|
|
40
|
+
function normalizeItem(item) {
|
|
41
|
+
if (typeof item === 'string') return {
|
|
42
|
+
value: item,
|
|
43
|
+
label: item
|
|
44
|
+
};
|
|
45
|
+
return item;
|
|
46
|
+
}
|
|
47
|
+
const defaultFilter = (query, option) => option.label.toLowerCase().includes(query.toLowerCase());
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Token resolution
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
function useFormFieldTokens(modes) {
|
|
54
|
+
return (0, _react.useMemo)(() => {
|
|
55
|
+
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('formField/label/color', modes) || '#000000';
|
|
56
|
+
const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontFamily', modes) || 'JioType Var';
|
|
57
|
+
const labelFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/label/fontSize', modes), 14);
|
|
58
|
+
const labelLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/label/lineHeight', modes), 17);
|
|
59
|
+
const labelFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontWeight', modes) || '500';
|
|
60
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/gap', modes), 8);
|
|
61
|
+
const inputPaddingH = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/padding/horizontal', modes), 12);
|
|
62
|
+
const inputGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/gap', modes), 8);
|
|
63
|
+
const inputRadius = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/radius', modes), 8);
|
|
64
|
+
const inputBorderSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/border/size', modes), 1.5);
|
|
65
|
+
const inputBackground = (0, _figmaVariablesResolver.getVariableByName)('formField/input/background', modes) || '#ffffff';
|
|
66
|
+
const inputBorderColor = (0, _figmaVariablesResolver.getVariableByName)('formField/input/border/color', modes) || '#b5b6b7';
|
|
67
|
+
const inputFontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontSize', modes), 16);
|
|
68
|
+
const inputLineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/lineHeight', modes), 45);
|
|
69
|
+
const inputFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontFamily', modes) || 'JioType Var';
|
|
70
|
+
const inputFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontWeight', modes) || '400';
|
|
71
|
+
const inputTextColor = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/color', modes) || '#24262b';
|
|
72
|
+
return {
|
|
73
|
+
labelColor,
|
|
74
|
+
labelFontFamily,
|
|
75
|
+
labelFontSize,
|
|
76
|
+
labelLineHeight,
|
|
77
|
+
labelFontWeight,
|
|
78
|
+
gap,
|
|
79
|
+
inputPaddingH,
|
|
80
|
+
inputGap,
|
|
81
|
+
inputRadius,
|
|
82
|
+
inputBorderSize,
|
|
83
|
+
inputBackground,
|
|
84
|
+
inputBorderColor,
|
|
85
|
+
inputFontSize,
|
|
86
|
+
inputLineHeight,
|
|
87
|
+
inputFontFamily,
|
|
88
|
+
inputFontWeight,
|
|
89
|
+
inputTextColor
|
|
90
|
+
};
|
|
91
|
+
}, [modes]);
|
|
92
|
+
}
|
|
93
|
+
function useDropdownItemTextTokens(modes) {
|
|
94
|
+
return (0, _react.useMemo)(() => {
|
|
95
|
+
const foreground = (0, _figmaVariablesResolver.getVariableByName)('dropdownItem/foreground', modes) || '#000000';
|
|
96
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('dropdownItem/fontFamily', modes) || 'JioType Var';
|
|
97
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('dropdownItem/fontSize', modes), 16);
|
|
98
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('dropdownItem/lineHeight', modes), 19);
|
|
99
|
+
return {
|
|
100
|
+
foreground,
|
|
101
|
+
fontFamily,
|
|
102
|
+
fontSize,
|
|
103
|
+
lineHeight
|
|
104
|
+
};
|
|
105
|
+
}, [modes]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Component
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
function SuggestiveSearch({
|
|
113
|
+
label,
|
|
114
|
+
placeholder = 'Search',
|
|
115
|
+
items,
|
|
116
|
+
inputValue,
|
|
117
|
+
defaultInputValue = '',
|
|
118
|
+
onInputChange,
|
|
119
|
+
value,
|
|
120
|
+
defaultValue = null,
|
|
121
|
+
onValueChange,
|
|
122
|
+
filter = defaultFilter,
|
|
123
|
+
minChars = 1,
|
|
124
|
+
maxResults,
|
|
125
|
+
highlightMatch = true,
|
|
126
|
+
emptyMessage,
|
|
127
|
+
renderItem,
|
|
128
|
+
open,
|
|
129
|
+
defaultOpen = false,
|
|
130
|
+
onOpenChange,
|
|
131
|
+
menuMaxHeight = 240,
|
|
132
|
+
menuOffset = 6,
|
|
133
|
+
isRequired = false,
|
|
134
|
+
isDisabled = false,
|
|
135
|
+
isInvalid = false,
|
|
136
|
+
isReadOnly = false,
|
|
137
|
+
supportText,
|
|
138
|
+
errorMessage,
|
|
139
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
140
|
+
style,
|
|
141
|
+
inputStyle,
|
|
142
|
+
inputTextStyle,
|
|
143
|
+
menuStyle,
|
|
144
|
+
accessibilityLabel,
|
|
145
|
+
accessibilityHint,
|
|
146
|
+
onFocus,
|
|
147
|
+
onBlur,
|
|
148
|
+
testID
|
|
149
|
+
}) {
|
|
150
|
+
// ---------------- Modes ----------------
|
|
151
|
+
const {
|
|
152
|
+
modes: globalModes
|
|
153
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
154
|
+
const baseModes = (0, _react.useMemo)(() => ({
|
|
155
|
+
...globalModes,
|
|
156
|
+
...propModes
|
|
157
|
+
}), [globalModes, propModes]);
|
|
158
|
+
const interactive = !isDisabled && !isReadOnly;
|
|
159
|
+
|
|
160
|
+
// ---------------- Query state ----------------
|
|
161
|
+
const isControlledInput = inputValue !== undefined;
|
|
162
|
+
const [internalInput, setInternalInput] = (0, _react.useState)(defaultInputValue);
|
|
163
|
+
const query = isControlledInput ? inputValue : internalInput;
|
|
164
|
+
|
|
165
|
+
// ---------------- Selected value state ----------------
|
|
166
|
+
const isControlledValue = value !== undefined;
|
|
167
|
+
const [internalValue, setInternalValue] = (0, _react.useState)(defaultValue);
|
|
168
|
+
const currentValue = isControlledValue ? value : internalValue;
|
|
169
|
+
|
|
170
|
+
// ---------------- Open state ----------------
|
|
171
|
+
const isControlledOpen = open !== undefined;
|
|
172
|
+
const [internalOpen, setInternalOpen] = (0, _react.useState)(defaultOpen);
|
|
173
|
+
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
174
|
+
const setOpenState = (0, _react.useCallback)(next => {
|
|
175
|
+
if (!isControlledOpen) setInternalOpen(next);
|
|
176
|
+
onOpenChange?.(next);
|
|
177
|
+
}, [isControlledOpen, onOpenChange]);
|
|
178
|
+
|
|
179
|
+
// ---------------- Suggestions ----------------
|
|
180
|
+
const normalizedItems = (0, _react.useMemo)(() => (items ?? []).map(normalizeItem), [items]);
|
|
181
|
+
const suggestions = (0, _react.useMemo)(() => {
|
|
182
|
+
const trimmed = query.trim();
|
|
183
|
+
if (trimmed.length < minChars) return [];
|
|
184
|
+
const matched = normalizedItems.filter(opt => filter(query, opt));
|
|
185
|
+
return maxResults != null ? matched.slice(0, maxResults) : matched;
|
|
186
|
+
}, [normalizedItems, query, minChars, filter, maxResults]);
|
|
187
|
+
const hasSuggestions = suggestions.length > 0;
|
|
188
|
+
const showEmpty = Boolean(emptyMessage && query.trim().length >= minChars && !hasSuggestions);
|
|
189
|
+
const hasMenuContent = hasSuggestions || showEmpty;
|
|
190
|
+
|
|
191
|
+
// Resolved open state: an explicit `open` prop wins; otherwise the dropdown
|
|
192
|
+
// tracks the internal "wants suggestions" flag (set on focus / typing,
|
|
193
|
+
// cleared on blur / select). Blur and outside-press handle dismissal, so we
|
|
194
|
+
// intentionally do NOT gate on `isFocused` here — that would suppress
|
|
195
|
+
// `defaultOpen` on mount.
|
|
196
|
+
const isOpen = interactive && (isControlledOpen ? Boolean(open) : internalOpen) && hasMenuContent;
|
|
197
|
+
|
|
198
|
+
// ---------------- Token modes (state cascade) ----------------
|
|
199
|
+
const modes = (0, _react.useMemo)(() => ({
|
|
200
|
+
...baseModes,
|
|
201
|
+
'FormField States': isInvalid ? 'Error' : isReadOnly || isDisabled ? 'Read Only' : isFocused ? 'Active' : baseModes['FormField States'] || 'Idle'
|
|
202
|
+
}), [baseModes, isInvalid, isReadOnly, isDisabled, isFocused]);
|
|
203
|
+
const tokens = useFormFieldTokens(modes);
|
|
204
|
+
const itemTextTokens = useDropdownItemTextTokens(modes);
|
|
205
|
+
|
|
206
|
+
// ---------------- Handlers ----------------
|
|
207
|
+
const inputRef = (0, _react.useRef)(null);
|
|
208
|
+
const blurTimer = (0, _react.useRef)(null);
|
|
209
|
+
const clearBlurTimer = (0, _react.useCallback)(() => {
|
|
210
|
+
if (blurTimer.current) {
|
|
211
|
+
clearTimeout(blurTimer.current);
|
|
212
|
+
blurTimer.current = null;
|
|
213
|
+
}
|
|
214
|
+
}, []);
|
|
215
|
+
(0, _react.useEffect)(() => () => clearBlurTimer(), [clearBlurTimer]);
|
|
216
|
+
const setQuery = (0, _react.useCallback)(text => {
|
|
217
|
+
if (!isControlledInput) setInternalInput(text);
|
|
218
|
+
onInputChange?.(text);
|
|
219
|
+
}, [isControlledInput, onInputChange]);
|
|
220
|
+
const handleChangeText = (0, _react.useCallback)(text => {
|
|
221
|
+
setQuery(text);
|
|
222
|
+
// Typing invalidates a prior selection unless the text still
|
|
223
|
+
// exactly matches the selected option's label.
|
|
224
|
+
if (currentValue != null) {
|
|
225
|
+
const selected = normalizedItems.find(o => o.value === currentValue);
|
|
226
|
+
if (!selected || selected.label !== text) {
|
|
227
|
+
if (!isControlledValue) setInternalValue(null);
|
|
228
|
+
onValueChange?.(null);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!isControlledOpen) setInternalOpen(true);
|
|
232
|
+
}, [setQuery, currentValue, normalizedItems, isControlledValue, onValueChange, isControlledOpen]);
|
|
233
|
+
const handleSelect = (0, _react.useCallback)(selectedValue => {
|
|
234
|
+
clearBlurTimer();
|
|
235
|
+
const option = normalizedItems.find(o => o.value === selectedValue);
|
|
236
|
+
if (!option || option.disabled) return;
|
|
237
|
+
setQuery(option.label);
|
|
238
|
+
if (!isControlledValue) setInternalValue(option.value);
|
|
239
|
+
onValueChange?.(option.value, option);
|
|
240
|
+
setOpenState(false);
|
|
241
|
+
inputRef.current?.blur();
|
|
242
|
+
}, [clearBlurTimer, normalizedItems, setQuery, isControlledValue, onValueChange, setOpenState]);
|
|
243
|
+
const handleFocus = (0, _react.useCallback)(e => {
|
|
244
|
+
clearBlurTimer();
|
|
245
|
+
setIsFocused(true);
|
|
246
|
+
if (!isControlledOpen) setInternalOpen(true);
|
|
247
|
+
onFocus?.(e);
|
|
248
|
+
}, [clearBlurTimer, isControlledOpen, onFocus]);
|
|
249
|
+
const handleBlur = (0, _react.useCallback)(e => {
|
|
250
|
+
// Delay closing so a suggestion press (which blurs the input first
|
|
251
|
+
// on web) still registers before the list unmounts.
|
|
252
|
+
clearBlurTimer();
|
|
253
|
+
blurTimer.current = setTimeout(() => {
|
|
254
|
+
setIsFocused(false);
|
|
255
|
+
if (!isControlledOpen) setInternalOpen(false);
|
|
256
|
+
}, 120);
|
|
257
|
+
onBlur?.(e);
|
|
258
|
+
}, [clearBlurTimer, isControlledOpen, onBlur]);
|
|
259
|
+
|
|
260
|
+
// ---------------- Web outside-press to close ----------------
|
|
261
|
+
const rootRef = (0, _react.useRef)(null);
|
|
262
|
+
(0, _react.useEffect)(() => {
|
|
263
|
+
if (!IS_WEB || !isOpen) return;
|
|
264
|
+
const handler = e => {
|
|
265
|
+
const node = rootRef.current;
|
|
266
|
+
if (node?.contains && !node.contains(e.target)) {
|
|
267
|
+
clearBlurTimer();
|
|
268
|
+
setIsFocused(false);
|
|
269
|
+
if (!isControlledOpen) setInternalOpen(false);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
document.addEventListener('mousedown', handler);
|
|
273
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
274
|
+
}, [isOpen, isControlledOpen, clearBlurTimer]);
|
|
275
|
+
|
|
276
|
+
// ---------------- Styles ----------------
|
|
277
|
+
const labelTextStyle = {
|
|
278
|
+
color: tokens.labelColor,
|
|
279
|
+
fontFamily: tokens.labelFontFamily,
|
|
280
|
+
fontSize: tokens.labelFontSize,
|
|
281
|
+
lineHeight: tokens.labelLineHeight,
|
|
282
|
+
fontWeight: tokens.labelFontWeight
|
|
283
|
+
};
|
|
284
|
+
const requiredIndicatorStyle = {
|
|
285
|
+
...labelTextStyle,
|
|
286
|
+
color: '#d93d3d'
|
|
287
|
+
};
|
|
288
|
+
const wrapperStyle = {
|
|
289
|
+
gap: tokens.gap,
|
|
290
|
+
opacity: isDisabled ? 0.5 : 1,
|
|
291
|
+
width: '100%',
|
|
292
|
+
position: 'relative',
|
|
293
|
+
// Keep the dropdown above sibling content when it overflows the field.
|
|
294
|
+
zIndex: isOpen ? 1000 : undefined
|
|
295
|
+
};
|
|
296
|
+
const inputRowStyle = {
|
|
297
|
+
flexDirection: 'row',
|
|
298
|
+
alignItems: 'center',
|
|
299
|
+
backgroundColor: tokens.inputBackground,
|
|
300
|
+
borderColor: tokens.inputBorderColor,
|
|
301
|
+
borderWidth: tokens.inputBorderSize,
|
|
302
|
+
borderStyle: 'solid',
|
|
303
|
+
borderRadius: tokens.inputRadius,
|
|
304
|
+
paddingHorizontal: tokens.inputPaddingH,
|
|
305
|
+
paddingVertical: 0,
|
|
306
|
+
gap: tokens.inputGap,
|
|
307
|
+
minHeight: tokens.inputLineHeight,
|
|
308
|
+
width: '100%'
|
|
309
|
+
};
|
|
310
|
+
const inputTextStyles = {
|
|
311
|
+
flex: 1,
|
|
312
|
+
color: tokens.inputTextColor,
|
|
313
|
+
fontFamily: tokens.inputFontFamily,
|
|
314
|
+
fontSize: tokens.inputFontSize,
|
|
315
|
+
lineHeight: tokens.inputLineHeight,
|
|
316
|
+
fontWeight: tokens.inputFontWeight,
|
|
317
|
+
padding: 0,
|
|
318
|
+
margin: 0,
|
|
319
|
+
...(IS_WEB ? {
|
|
320
|
+
outlineStyle: 'none',
|
|
321
|
+
outlineWidth: 0,
|
|
322
|
+
outlineColor: 'transparent'
|
|
323
|
+
} : {})
|
|
324
|
+
};
|
|
325
|
+
const placeholderColor = '#888a8d';
|
|
326
|
+
const itemBaseTextStyle = {
|
|
327
|
+
flex: 1,
|
|
328
|
+
color: itemTextTokens.foreground,
|
|
329
|
+
fontFamily: itemTextTokens.fontFamily,
|
|
330
|
+
fontSize: itemTextTokens.fontSize,
|
|
331
|
+
lineHeight: itemTextTokens.lineHeight,
|
|
332
|
+
fontWeight: '400'
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// ---------------- Support text ----------------
|
|
336
|
+
const supportStatus = isInvalid ? 'Error' : 'Neutral';
|
|
337
|
+
const supportLabel = isInvalid && errorMessage ? errorMessage : supportText;
|
|
338
|
+
|
|
339
|
+
// ---------------- Accessibility ----------------
|
|
340
|
+
const resolvedA11yLabel = accessibilityLabel || label || placeholder || 'Search';
|
|
341
|
+
const a11yProps = {
|
|
342
|
+
accessibilityRole: IS_WEB ? undefined : 'search',
|
|
343
|
+
accessibilityLabel: resolvedA11yLabel,
|
|
344
|
+
accessibilityState: {
|
|
345
|
+
disabled: isDisabled,
|
|
346
|
+
expanded: isOpen
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
if (accessibilityHint) a11yProps.accessibilityHint = accessibilityHint;
|
|
350
|
+
|
|
351
|
+
// ---------------- Suggestion highlight ----------------
|
|
352
|
+
const renderHighlighted = (0, _react.useCallback)(optLabel => {
|
|
353
|
+
const trimmed = query.trim();
|
|
354
|
+
if (!highlightMatch || trimmed.length === 0) {
|
|
355
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
356
|
+
style: itemBaseTextStyle,
|
|
357
|
+
children: optLabel
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const idx = optLabel.toLowerCase().indexOf(trimmed.toLowerCase());
|
|
361
|
+
if (idx < 0) {
|
|
362
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
363
|
+
style: itemBaseTextStyle,
|
|
364
|
+
children: optLabel
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const before = optLabel.slice(0, idx);
|
|
368
|
+
const match = optLabel.slice(idx, idx + trimmed.length);
|
|
369
|
+
const after = optLabel.slice(idx + trimmed.length);
|
|
370
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
371
|
+
style: itemBaseTextStyle,
|
|
372
|
+
children: [before, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
373
|
+
style: {
|
|
374
|
+
fontWeight: '700'
|
|
375
|
+
},
|
|
376
|
+
children: match
|
|
377
|
+
}), after]
|
|
378
|
+
});
|
|
379
|
+
}, [query, highlightMatch, itemBaseTextStyle]);
|
|
380
|
+
|
|
381
|
+
// ---------------- Render ----------------
|
|
382
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
383
|
+
ref: rootRef,
|
|
384
|
+
style: [wrapperStyle, style],
|
|
385
|
+
pointerEvents: isDisabled ? 'none' : 'auto',
|
|
386
|
+
testID: testID,
|
|
387
|
+
children: [label != null && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
388
|
+
style: styles.labelRow,
|
|
389
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
390
|
+
style: labelTextStyle,
|
|
391
|
+
children: label
|
|
392
|
+
}), isRequired && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
393
|
+
style: requiredIndicatorStyle,
|
|
394
|
+
children: " *"
|
|
395
|
+
})]
|
|
396
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
397
|
+
style: styles.anchor,
|
|
398
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
399
|
+
style: [inputRowStyle, inputStyle],
|
|
400
|
+
onPress: () => inputRef.current?.focus(),
|
|
401
|
+
accessible: false,
|
|
402
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
403
|
+
ref: inputRef,
|
|
404
|
+
style: [inputTextStyles, inputTextStyle],
|
|
405
|
+
value: query,
|
|
406
|
+
onChangeText: handleChangeText,
|
|
407
|
+
onFocus: handleFocus,
|
|
408
|
+
onBlur: handleBlur,
|
|
409
|
+
placeholder: placeholder,
|
|
410
|
+
placeholderTextColor: placeholderColor,
|
|
411
|
+
editable: interactive,
|
|
412
|
+
autoCapitalize: "none",
|
|
413
|
+
autoComplete: "off",
|
|
414
|
+
autoCorrect: false,
|
|
415
|
+
...a11yProps,
|
|
416
|
+
...(IS_WEB ? {
|
|
417
|
+
accessibilityRole: 'search',
|
|
418
|
+
'aria-autocomplete': 'list',
|
|
419
|
+
'aria-expanded': isOpen
|
|
420
|
+
} : {})
|
|
421
|
+
})
|
|
422
|
+
}), isOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
423
|
+
style: [styles.popup, {
|
|
424
|
+
top: '100%',
|
|
425
|
+
marginTop: menuOffset
|
|
426
|
+
}]
|
|
427
|
+
// Keep taps from dismissing the keyboard before the
|
|
428
|
+
// item's press handler runs on native.
|
|
429
|
+
,
|
|
430
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Dropdown.default, {
|
|
431
|
+
modes: modes,
|
|
432
|
+
maxHeight: menuMaxHeight,
|
|
433
|
+
style: menuStyle,
|
|
434
|
+
accessibilityLabel: `${resolvedA11yLabel} suggestions`,
|
|
435
|
+
children: hasSuggestions ? suggestions.map(opt => {
|
|
436
|
+
const isSelected = opt.value === currentValue;
|
|
437
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Dropdown.DropdownItem, {
|
|
438
|
+
value: opt.value,
|
|
439
|
+
selected: isSelected,
|
|
440
|
+
disabled: opt.disabled ?? false,
|
|
441
|
+
onPress: handleSelect,
|
|
442
|
+
modes: modes,
|
|
443
|
+
children: renderItem ? renderItem(opt, {
|
|
444
|
+
query,
|
|
445
|
+
isSelected
|
|
446
|
+
}) : renderHighlighted(opt.label)
|
|
447
|
+
}, `sg-${opt.value}`);
|
|
448
|
+
}) : showEmpty && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
449
|
+
style: styles.emptyRow,
|
|
450
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
451
|
+
style: [itemBaseTextStyle, {
|
|
452
|
+
color: placeholderColor
|
|
453
|
+
}],
|
|
454
|
+
children: emptyMessage
|
|
455
|
+
})
|
|
456
|
+
})
|
|
457
|
+
})
|
|
458
|
+
})]
|
|
459
|
+
}), supportLabel != null && supportLabel !== '' && /*#__PURE__*/(0, _jsxRuntime.jsx)(_SupportText.default, {
|
|
460
|
+
label: supportLabel,
|
|
461
|
+
status: supportStatus,
|
|
462
|
+
modes: modes
|
|
463
|
+
})]
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
const styles = {
|
|
467
|
+
labelRow: {
|
|
468
|
+
flexDirection: 'row',
|
|
469
|
+
alignItems: 'baseline'
|
|
470
|
+
},
|
|
471
|
+
anchor: {
|
|
472
|
+
position: 'relative',
|
|
473
|
+
width: '100%',
|
|
474
|
+
zIndex: 1
|
|
475
|
+
},
|
|
476
|
+
popup: {
|
|
477
|
+
position: 'absolute',
|
|
478
|
+
left: 0,
|
|
479
|
+
right: 0,
|
|
480
|
+
zIndex: 1000
|
|
481
|
+
},
|
|
482
|
+
emptyRow: {
|
|
483
|
+
paddingHorizontal: 12,
|
|
484
|
+
paddingVertical: 12
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
var _default = exports.default = SuggestiveSearch;
|
|
@@ -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
|
const TEXT_ALIGN_MAP = {
|
|
@@ -20,7 +22,10 @@ function Text({
|
|
|
20
22
|
textAlign = 'Left',
|
|
21
23
|
modes = _reactUtils.EMPTY_MODES,
|
|
22
24
|
style,
|
|
23
|
-
numberOfLines
|
|
25
|
+
numberOfLines,
|
|
26
|
+
loading,
|
|
27
|
+
skeletonWidth,
|
|
28
|
+
skeletonLines
|
|
24
29
|
}) {
|
|
25
30
|
const foreground = (0, _figmaVariablesResolver.getVariableByName)('text/foreground', modes) ?? '#000000';
|
|
26
31
|
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('text/fontFamily', modes) ?? 'JioType';
|
|
@@ -28,6 +33,31 @@ function Text({
|
|
|
28
33
|
const fontWeight = (0, _figmaVariablesResolver.getVariableByName)('text/fontWeight', modes) ?? '500';
|
|
29
34
|
const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('text/lineHeight', modes) ?? 20;
|
|
30
35
|
const letterSpacing = (0, _figmaVariablesResolver.getVariableByName)('text/letterSpacing', modes) ?? -0.5;
|
|
36
|
+
|
|
37
|
+
// Skeleton short-circuit. The hook is always called (stable order); the
|
|
38
|
+
// surrounding group's `active` flag is the default, but an explicit
|
|
39
|
+
// `loading` prop overrides both directions.
|
|
40
|
+
const {
|
|
41
|
+
active: groupActive
|
|
42
|
+
} = (0, _SkeletonGroup.useSkeleton)();
|
|
43
|
+
const isLoading = loading ?? groupActive;
|
|
44
|
+
if (isLoading) {
|
|
45
|
+
const resolvedFontSize = fontSize;
|
|
46
|
+
const resolvedLineHeight = lineHeight;
|
|
47
|
+
const lines = Math.max(1, skeletonLines ?? numberOfLines ?? 1);
|
|
48
|
+
// Heuristic width based on the actual content length so the placeholder
|
|
49
|
+
// visually matches what's about to load. Capped to a reasonable lower
|
|
50
|
+
// bound so very short labels still produce a visible block.
|
|
51
|
+
const contentLength = typeof children === 'string' ? children.length : typeof text === 'string' ? text.length : 12;
|
|
52
|
+
const charWidth = resolvedFontSize * 0.55;
|
|
53
|
+
const computedWidth = Math.max(charWidth * 4, contentLength * charWidth);
|
|
54
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
|
|
55
|
+
kind: "text",
|
|
56
|
+
width: skeletonWidth ?? computedWidth,
|
|
57
|
+
height: resolvedLineHeight * lines,
|
|
58
|
+
modes: modes
|
|
59
|
+
});
|
|
60
|
+
}
|
|
31
61
|
const textStyle = {
|
|
32
62
|
color: foreground,
|
|
33
63
|
fontFamily: fontFamily,
|
|
@@ -118,6 +118,11 @@ function TextInput({
|
|
|
118
118
|
// Track focus state to hide placeholder when focused
|
|
119
119
|
const [isFocused, setIsFocused] = (0, _react.useState)(false);
|
|
120
120
|
const [isHovered, setIsHovered] = (0, _react.useState)(false);
|
|
121
|
+
// Ref to the underlying native input so a tap anywhere inside the Pressable
|
|
122
|
+
// wrapper can programmatically focus it. Without this, on Android the
|
|
123
|
+
// wrapping Pressable becomes the touch responder on the first tap and the
|
|
124
|
+
// native input only gains focus on the *second* tap.
|
|
125
|
+
const inputRef = (0, _react.useRef)(null);
|
|
121
126
|
|
|
122
127
|
// Resolve container tokens
|
|
123
128
|
const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('textInput/background', modes) || '#f5f5f5';
|
|
@@ -211,12 +216,22 @@ function TextInput({
|
|
|
211
216
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
212
217
|
style: [containerStyle, focusContainerStyle, hoverStyle, style],
|
|
213
218
|
onHoverIn: () => setIsHovered(true),
|
|
214
|
-
onHoverOut: () => setIsHovered(false)
|
|
219
|
+
onHoverOut: () => setIsHovered(false)
|
|
220
|
+
// Forward taps on the wrapper (padding, leading icon gutter, etc.) to the
|
|
221
|
+
// native input. This guarantees the keyboard opens on the FIRST tap on
|
|
222
|
+
// Android instead of requiring a second tap.
|
|
223
|
+
,
|
|
224
|
+
onPress: () => inputRef.current?.focus()
|
|
225
|
+
// The native input is the real accessible element; don't add a redundant
|
|
226
|
+
// focusable node for screen readers.
|
|
227
|
+
,
|
|
228
|
+
accessible: false,
|
|
215
229
|
children: [processedLeading && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
216
230
|
accessibilityElementsHidden: true,
|
|
217
231
|
importantForAccessibility: "no",
|
|
218
232
|
children: processedLeading
|
|
219
233
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
234
|
+
ref: inputRef,
|
|
220
235
|
accessibilityLabel: undefined,
|
|
221
236
|
accessibilityHint: accessibilityHint,
|
|
222
237
|
placeholder: displayPlaceholder,
|
|
@@ -40,13 +40,21 @@ function Title({
|
|
|
40
40
|
const gap = (0, _figmaVariablesResolver.getVariableByName)('title/gap', modes) || 8;
|
|
41
41
|
const labelColor = (0, _figmaVariablesResolver.getVariableByName)('title/label/color', modes) || '#0d0d0f';
|
|
42
42
|
const fontSize = (0, _figmaVariablesResolver.getVariableByName)('title/fontSize', modes) || 26;
|
|
43
|
-
const
|
|
43
|
+
const rawLineHeight = (0, _figmaVariablesResolver.getVariableByName)('title/lineHeight', modes) || 26;
|
|
44
|
+
// The Figma title tokens ship with `lineHeight === fontSize` (26/26). On
|
|
45
|
+
// native (especially Android) that produces a Text box exactly `lineHeight`
|
|
46
|
+
// tall, which clips descenders like p/g/y/q/j at the bottom — particularly
|
|
47
|
+
// noticeable with `numberOfLines={1}`. Clamp to ~1.2x fontSize so descenders
|
|
48
|
+
// always fit. When a downstream token already provides adequate leading,
|
|
49
|
+
// the original value is preserved.
|
|
50
|
+
const lineHeight = Math.max(rawLineHeight, Math.ceil(fontSize * 1.2));
|
|
44
51
|
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('title/fontFamily', modes) || 'System';
|
|
45
52
|
const fontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('title/fontWeight', modes) || 900;
|
|
46
53
|
const fontWeight = typeof fontWeightRaw === 'number' ? fontWeightRaw.toString() : fontWeightRaw;
|
|
47
54
|
const subtitleColor = (0, _figmaVariablesResolver.getVariableByName)('pageSubtitle/label/color', modes) || '#0d0d0f';
|
|
48
55
|
const subtitleFontSize = (0, _figmaVariablesResolver.getVariableByName)('pageSubtitle/fontSize', modes) || 14;
|
|
49
|
-
const
|
|
56
|
+
const subtitleRawLineHeight = (0, _figmaVariablesResolver.getVariableByName)('pageSubtitle/lineHeight', modes) || 18;
|
|
57
|
+
const subtitleLineHeight = Math.max(subtitleRawLineHeight, Math.ceil(subtitleFontSize * 1.2));
|
|
50
58
|
const subtitleFontFamily = (0, _figmaVariablesResolver.getVariableByName)('pageSubtitle/fontFamily', modes) || 'System';
|
|
51
59
|
const subtitleFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('pageSubtitle/fontWeight', modes) || 500;
|
|
52
60
|
const subtitleFontWeight = typeof subtitleFontWeightRaw === 'number' ? subtitleFontWeightRaw.toString() : subtitleFontWeightRaw;
|