jfs-components 0.0.99 → 0.1.0
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 +10 -0
- package/lib/commonjs/components/Dropdown/Dropdown.js +37 -18
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +3 -2
- package/lib/commonjs/components/TestimonialsCard/TestimonialsCard.js +121 -0
- package/lib/commonjs/components/index.js +7 -0
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Dropdown/Dropdown.js +37 -18
- package/lib/module/components/FullscreenModal/FullscreenModal.js +3 -2
- package/lib/module/components/TestimonialsCard/TestimonialsCard.js +116 -0
- package/lib/module/components/index.js +1 -0
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +7 -1
- package/lib/typescript/src/components/TestimonialsCard/TestimonialsCard.d.ts +51 -0
- package/lib/typescript/src/components/index.d.ts +1 -0
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Dropdown/Dropdown.tsx +38 -18
- package/src/components/FullscreenModal/FullscreenModal.tsx +9 -2
- package/src/components/TestimonialsCard/TestimonialsCard.tsx +162 -0
- package/src/components/index.ts +1 -0
- package/src/icons/registry.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [0.1.0] - 2026-06-08
|
|
8
|
+
|
|
9
|
+
- Added `TestimonialsCard` — compact avatar + title + body card for testimonial carousels.
|
|
10
|
+
- `Drawer` — programmatic ref API (`expand` / `collapse` / `toggle` / `getState`), controlled `state` + `onStateChange`, touch-overlay and gesture-loop fixes.
|
|
11
|
+
- `FullscreenModal` — safe-area-aware footer and close button; new `closeOffsetY` prop.
|
|
12
|
+
- `Dropdown` — `boxShadow`-based popup shadow with inner clip view (fixes clipped shadow on native).
|
|
13
|
+
- `Spinner` / `Skeleton` — shimmer overlay uses explicit absolute positioning instead of `absoluteFillObject`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
7
17
|
## [0.0.95] - 2026-06-04
|
|
8
18
|
|
|
9
19
|
- Added `Icon` — token-driven design-system icon primitive (`iconName`, `source`, `children` slot); exported from the package barrel.
|
|
@@ -178,18 +178,34 @@ function Dropdown({
|
|
|
178
178
|
const shadowOffsetX = parseInt((0, _figmaVariablesResolver.getVariableByName)('dropdown/shadow/offsetX', modes), 10) || 0;
|
|
179
179
|
const shadowOffsetY = parseInt((0, _figmaVariablesResolver.getVariableByName)('dropdown/shadow/offsetY', modes), 10) || 4;
|
|
180
180
|
const shadowBlur = parseInt((0, _figmaVariablesResolver.getVariableByName)('dropdown/shadow/blur', modes), 10) || 16;
|
|
181
|
-
|
|
181
|
+
|
|
182
|
+
// Shadow lives on the OUTER view, which must NOT set `overflow: 'hidden'`.
|
|
183
|
+
// On native, clipping a view also clips its shadow (iOS clips the layer
|
|
184
|
+
// shadow; Android clips the elevation shadow), so the soft popup shadow
|
|
185
|
+
// that renders fine on web (CSS box-shadow paints outside the box) would
|
|
186
|
+
// get cut off. The rounded-corner clipping is moved to a separate inner
|
|
187
|
+
// view below.
|
|
188
|
+
//
|
|
189
|
+
// The `boxShadow` style prop (RN 0.76+ / react-native-web) is used as the
|
|
190
|
+
// single source of truth so the designed color/offset/blur are honored on
|
|
191
|
+
// iOS, Android AND web. We intentionally do NOT also set the legacy
|
|
192
|
+
// `shadow*` / `elevation` props: on the new architecture (and web) those
|
|
193
|
+
// are translated into a box-shadow internally, so combining them with an
|
|
194
|
+
// explicit `boxShadow` would stack two shadows. Android's legacy
|
|
195
|
+
// `elevation` is also undesirable here because it ignores the shadow color
|
|
196
|
+
// and only paints a generic gray shadow.
|
|
197
|
+
const shadowStyle = {
|
|
182
198
|
backgroundColor: background,
|
|
199
|
+
borderRadius: radius,
|
|
200
|
+
boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0px ${shadowColor}`
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Inner view carries the rounded corners + clipping so list/scroll content
|
|
204
|
+
// stays inside the radius without affecting the outer view's shadow.
|
|
205
|
+
const clipStyle = {
|
|
183
206
|
borderRadius: radius,
|
|
184
207
|
overflow: 'hidden',
|
|
185
|
-
|
|
186
|
-
shadowOffset: {
|
|
187
|
-
width: shadowOffsetX,
|
|
188
|
-
height: shadowOffsetY
|
|
189
|
-
},
|
|
190
|
-
shadowOpacity: 1,
|
|
191
|
-
shadowRadius: shadowBlur / 2,
|
|
192
|
-
elevation: 4
|
|
208
|
+
backgroundColor: background
|
|
193
209
|
};
|
|
194
210
|
const content = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
195
211
|
style: {
|
|
@@ -198,17 +214,20 @@ function Dropdown({
|
|
|
198
214
|
children: (0, _reactUtils.cloneChildrenWithModes)(children, modes)
|
|
199
215
|
});
|
|
200
216
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
201
|
-
style: [
|
|
217
|
+
style: [shadowStyle, style],
|
|
202
218
|
accessibilityRole: "menu",
|
|
203
219
|
accessibilityLabel: accessibilityLabel || 'Dropdown menu',
|
|
204
|
-
children:
|
|
205
|
-
style:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
221
|
+
style: clipStyle,
|
|
222
|
+
children: maxHeight != null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
|
|
223
|
+
style: {
|
|
224
|
+
maxHeight
|
|
225
|
+
},
|
|
226
|
+
showsVerticalScrollIndicator: true,
|
|
227
|
+
keyboardShouldPersistTaps: "handled",
|
|
228
|
+
children: content
|
|
229
|
+
}) : content
|
|
230
|
+
})
|
|
212
231
|
});
|
|
213
232
|
}
|
|
214
233
|
var _default = exports.default = Dropdown;
|
|
@@ -189,6 +189,7 @@ function FullscreenModal({
|
|
|
189
189
|
heroMedia,
|
|
190
190
|
heroHeight = 420,
|
|
191
191
|
showClose = true,
|
|
192
|
+
closeOffsetY = 0,
|
|
192
193
|
onClose,
|
|
193
194
|
closeAccessibilityLabel = 'Close',
|
|
194
195
|
footer,
|
|
@@ -225,8 +226,8 @@ function FullscreenModal({
|
|
|
225
226
|
// SafeAreaProvider — every inset is 0, so the layout is unchanged.
|
|
226
227
|
const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
227
228
|
const closeButtonInsetStyle = (0, _react.useMemo)(() => ({
|
|
228
|
-
top: 12 + insets.top
|
|
229
|
-
}), [insets.top]);
|
|
229
|
+
top: 12 + insets.top + closeOffsetY
|
|
230
|
+
}), [insets.top, closeOffsetY]);
|
|
230
231
|
// Extend (not replace) the footer's token bottom padding by the bottom inset
|
|
231
232
|
// so the action button never sits under the system navigation area.
|
|
232
233
|
const footerInsetStyle = (0, _react.useMemo)(() => {
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(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 _Avatar = _interopRequireDefault(require("../Avatar/Avatar"));
|
|
12
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
/**
|
|
15
|
+
* TestimonialsCard renders a compact, fixed-width card with a circular avatar,
|
|
16
|
+
* a bold title, and a body paragraph. It is typically used inside a horizontal
|
|
17
|
+
* carousel of customer testimonials.
|
|
18
|
+
*
|
|
19
|
+
* All styling values are resolved from Figma design tokens using the provided
|
|
20
|
+
* `modes`.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <TestimonialsCard
|
|
25
|
+
* title="Aarav S."
|
|
26
|
+
* body="I was dreading renewing my car insurance, but JioFinance made it a breeze."
|
|
27
|
+
* />
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function TestimonialsCard({
|
|
31
|
+
title = 'Title',
|
|
32
|
+
body = 'I was dreading renewing my car insurance, but JioFinance made it a breeze.',
|
|
33
|
+
modes = _reactUtils.EMPTY_MODES,
|
|
34
|
+
style,
|
|
35
|
+
avatarProps,
|
|
36
|
+
accessibilityLabel
|
|
37
|
+
}) {
|
|
38
|
+
// Container tokens
|
|
39
|
+
const background = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/background', modes) ?? '#ffffff';
|
|
40
|
+
const borderColor = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/border/color', modes) ?? '#e8e8e8';
|
|
41
|
+
const radius = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/radius', modes) ?? 8;
|
|
42
|
+
const gap = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/gap', modes) ?? 8;
|
|
43
|
+
const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/padding/horizontal', modes) ?? 12;
|
|
44
|
+
const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/padding/vertical', modes) ?? 12;
|
|
45
|
+
|
|
46
|
+
// Title typography tokens
|
|
47
|
+
const titleColor = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/subtitle/color', modes) ?? '#1a1c1f';
|
|
48
|
+
const titleFontSize = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/title/fontSize', modes) ?? 14;
|
|
49
|
+
const titleFontFamily = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/title/fontFamily', modes) ?? 'JioType Var';
|
|
50
|
+
const titleFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/title/fontWeight', modes) ?? 700;
|
|
51
|
+
const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw;
|
|
52
|
+
|
|
53
|
+
// Body typography tokens
|
|
54
|
+
const bodyColor = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/title/color', modes) ?? '#010101';
|
|
55
|
+
const bodyFontSize = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/subtitle/fontSize', modes) ?? 12;
|
|
56
|
+
const bodyLineHeight = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/subtitle/lineHeight', modes) ?? 16;
|
|
57
|
+
const bodyFontFamily = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/subtitle/fontFamily', modes) ?? 'JioType Var';
|
|
58
|
+
const bodyFontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('testimonialsCard/subtitle/fontWeight', modes) ?? 400;
|
|
59
|
+
const bodyFontWeight = typeof bodyFontWeightRaw === 'number' ? bodyFontWeightRaw.toString() : bodyFontWeightRaw;
|
|
60
|
+
const containerStyle = {
|
|
61
|
+
width: 180,
|
|
62
|
+
backgroundColor: background,
|
|
63
|
+
borderColor: borderColor,
|
|
64
|
+
borderWidth: 1,
|
|
65
|
+
borderRadius: radius,
|
|
66
|
+
paddingHorizontal: paddingHorizontal,
|
|
67
|
+
paddingVertical: paddingVertical,
|
|
68
|
+
alignItems: 'flex-start',
|
|
69
|
+
gap: gap
|
|
70
|
+
};
|
|
71
|
+
const titleTextStyle = {
|
|
72
|
+
color: titleColor,
|
|
73
|
+
fontSize: titleFontSize,
|
|
74
|
+
lineHeight: bodyLineHeight,
|
|
75
|
+
fontFamily: titleFontFamily,
|
|
76
|
+
fontWeight: titleFontWeight,
|
|
77
|
+
width: '100%'
|
|
78
|
+
};
|
|
79
|
+
const bodyTextStyle = {
|
|
80
|
+
color: bodyColor,
|
|
81
|
+
fontSize: bodyFontSize,
|
|
82
|
+
lineHeight: bodyLineHeight,
|
|
83
|
+
fontFamily: bodyFontFamily,
|
|
84
|
+
fontWeight: bodyFontWeight,
|
|
85
|
+
width: '100%'
|
|
86
|
+
};
|
|
87
|
+
const avatarModes = {
|
|
88
|
+
...modes,
|
|
89
|
+
...(avatarProps?.modes || {})
|
|
90
|
+
};
|
|
91
|
+
const resolvedAccessibilityLabel = accessibilityLabel ?? `Testimonial${title ? ` from ${title}` : ''}${body ? `: ${body}` : ''}`;
|
|
92
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
93
|
+
style: [containerStyle, style],
|
|
94
|
+
accessibilityRole: "text",
|
|
95
|
+
accessibilityLabel: resolvedAccessibilityLabel,
|
|
96
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Avatar.default, {
|
|
97
|
+
style: "Image",
|
|
98
|
+
modes: avatarModes,
|
|
99
|
+
...avatarProps
|
|
100
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
101
|
+
style: textContainerStyle,
|
|
102
|
+
children: [!!title && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
103
|
+
style: titleTextStyle,
|
|
104
|
+
accessibilityElementsHidden: true,
|
|
105
|
+
importantForAccessibility: "no-hide-descendants",
|
|
106
|
+
children: title
|
|
107
|
+
}), !!body && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
108
|
+
style: bodyTextStyle,
|
|
109
|
+
accessibilityElementsHidden: true,
|
|
110
|
+
importantForAccessibility: "no-hide-descendants",
|
|
111
|
+
children: body
|
|
112
|
+
})]
|
|
113
|
+
})]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const textContainerStyle = {
|
|
117
|
+
width: '100%',
|
|
118
|
+
alignItems: 'flex-start',
|
|
119
|
+
gap: 4
|
|
120
|
+
};
|
|
121
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(TestimonialsCard);
|
|
@@ -723,6 +723,12 @@ Object.defineProperty(exports, "Tabs", {
|
|
|
723
723
|
return _Tabs.default;
|
|
724
724
|
}
|
|
725
725
|
});
|
|
726
|
+
Object.defineProperty(exports, "TestimonialsCard", {
|
|
727
|
+
enumerable: true,
|
|
728
|
+
get: function () {
|
|
729
|
+
return _TestimonialsCard.default;
|
|
730
|
+
}
|
|
731
|
+
});
|
|
726
732
|
Object.defineProperty(exports, "Text", {
|
|
727
733
|
enumerable: true,
|
|
728
734
|
get: function () {
|
|
@@ -981,6 +987,7 @@ var _StatItem = _interopRequireDefault(require("./StatItem/StatItem"));
|
|
|
981
987
|
var _StatGroup = _interopRequireDefault(require("./StatGroup/StatGroup"));
|
|
982
988
|
var _StrengthIndicator = _interopRequireDefault(require("./StrengthIndicator/StrengthIndicator"));
|
|
983
989
|
var _SummaryTile = _interopRequireDefault(require("./SummaryTile/SummaryTile"));
|
|
990
|
+
var _TestimonialsCard = _interopRequireDefault(require("./TestimonialsCard/TestimonialsCard"));
|
|
984
991
|
var _Text = _interopRequireDefault(require("./Text/Text"));
|
|
985
992
|
var _SegmentedControl = _interopRequireDefault(require("./SegmentedControl/SegmentedControl"));
|
|
986
993
|
var _Toggle = _interopRequireDefault(require("./Toggle/Toggle"));
|