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
|
@@ -170,18 +170,34 @@ export function Dropdown({
|
|
|
170
170
|
const shadowOffsetX = parseInt(getVariableByName('dropdown/shadow/offsetX', modes), 10) || 0;
|
|
171
171
|
const shadowOffsetY = parseInt(getVariableByName('dropdown/shadow/offsetY', modes), 10) || 4;
|
|
172
172
|
const shadowBlur = parseInt(getVariableByName('dropdown/shadow/blur', modes), 10) || 16;
|
|
173
|
-
|
|
173
|
+
|
|
174
|
+
// Shadow lives on the OUTER view, which must NOT set `overflow: 'hidden'`.
|
|
175
|
+
// On native, clipping a view also clips its shadow (iOS clips the layer
|
|
176
|
+
// shadow; Android clips the elevation shadow), so the soft popup shadow
|
|
177
|
+
// that renders fine on web (CSS box-shadow paints outside the box) would
|
|
178
|
+
// get cut off. The rounded-corner clipping is moved to a separate inner
|
|
179
|
+
// view below.
|
|
180
|
+
//
|
|
181
|
+
// The `boxShadow` style prop (RN 0.76+ / react-native-web) is used as the
|
|
182
|
+
// single source of truth so the designed color/offset/blur are honored on
|
|
183
|
+
// iOS, Android AND web. We intentionally do NOT also set the legacy
|
|
184
|
+
// `shadow*` / `elevation` props: on the new architecture (and web) those
|
|
185
|
+
// are translated into a box-shadow internally, so combining them with an
|
|
186
|
+
// explicit `boxShadow` would stack two shadows. Android's legacy
|
|
187
|
+
// `elevation` is also undesirable here because it ignores the shadow color
|
|
188
|
+
// and only paints a generic gray shadow.
|
|
189
|
+
const shadowStyle = {
|
|
174
190
|
backgroundColor: background,
|
|
191
|
+
borderRadius: radius,
|
|
192
|
+
boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0px ${shadowColor}`
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Inner view carries the rounded corners + clipping so list/scroll content
|
|
196
|
+
// stays inside the radius without affecting the outer view's shadow.
|
|
197
|
+
const clipStyle = {
|
|
175
198
|
borderRadius: radius,
|
|
176
199
|
overflow: 'hidden',
|
|
177
|
-
|
|
178
|
-
shadowOffset: {
|
|
179
|
-
width: shadowOffsetX,
|
|
180
|
-
height: shadowOffsetY
|
|
181
|
-
},
|
|
182
|
-
shadowOpacity: 1,
|
|
183
|
-
shadowRadius: shadowBlur / 2,
|
|
184
|
-
elevation: 4
|
|
200
|
+
backgroundColor: background
|
|
185
201
|
};
|
|
186
202
|
const content = /*#__PURE__*/_jsx(View, {
|
|
187
203
|
style: {
|
|
@@ -190,17 +206,20 @@ export function Dropdown({
|
|
|
190
206
|
children: cloneChildrenWithModes(children, modes)
|
|
191
207
|
});
|
|
192
208
|
return /*#__PURE__*/_jsx(View, {
|
|
193
|
-
style: [
|
|
209
|
+
style: [shadowStyle, style],
|
|
194
210
|
accessibilityRole: "menu",
|
|
195
211
|
accessibilityLabel: accessibilityLabel || 'Dropdown menu',
|
|
196
|
-
children:
|
|
197
|
-
style:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
213
|
+
style: clipStyle,
|
|
214
|
+
children: maxHeight != null ? /*#__PURE__*/_jsx(ScrollView, {
|
|
215
|
+
style: {
|
|
216
|
+
maxHeight
|
|
217
|
+
},
|
|
218
|
+
showsVerticalScrollIndicator: true,
|
|
219
|
+
keyboardShouldPersistTaps: "handled",
|
|
220
|
+
children: content
|
|
221
|
+
}) : content
|
|
222
|
+
})
|
|
204
223
|
});
|
|
205
224
|
}
|
|
206
225
|
export default Dropdown;
|
|
@@ -184,6 +184,7 @@ function FullscreenModal({
|
|
|
184
184
|
heroMedia,
|
|
185
185
|
heroHeight = 420,
|
|
186
186
|
showClose = true,
|
|
187
|
+
closeOffsetY = 0,
|
|
187
188
|
onClose,
|
|
188
189
|
closeAccessibilityLabel = 'Close',
|
|
189
190
|
footer,
|
|
@@ -220,8 +221,8 @@ function FullscreenModal({
|
|
|
220
221
|
// SafeAreaProvider — every inset is 0, so the layout is unchanged.
|
|
221
222
|
const insets = useSafeAreaInsets();
|
|
222
223
|
const closeButtonInsetStyle = useMemo(() => ({
|
|
223
|
-
top: 12 + insets.top
|
|
224
|
-
}), [insets.top]);
|
|
224
|
+
top: 12 + insets.top + closeOffsetY
|
|
225
|
+
}), [insets.top, closeOffsetY]);
|
|
225
226
|
// Extend (not replace) the footer's token bottom padding by the bottom inset
|
|
226
227
|
// so the action button never sits under the system navigation area.
|
|
227
228
|
const footerInsetStyle = useMemo(() => {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { View, Text } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
|
+
import Avatar from '../Avatar/Avatar';
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
/**
|
|
10
|
+
* TestimonialsCard renders a compact, fixed-width card with a circular avatar,
|
|
11
|
+
* a bold title, and a body paragraph. It is typically used inside a horizontal
|
|
12
|
+
* carousel of customer testimonials.
|
|
13
|
+
*
|
|
14
|
+
* All styling values are resolved from Figma design tokens using the provided
|
|
15
|
+
* `modes`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <TestimonialsCard
|
|
20
|
+
* title="Aarav S."
|
|
21
|
+
* body="I was dreading renewing my car insurance, but JioFinance made it a breeze."
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function TestimonialsCard({
|
|
26
|
+
title = 'Title',
|
|
27
|
+
body = 'I was dreading renewing my car insurance, but JioFinance made it a breeze.',
|
|
28
|
+
modes = EMPTY_MODES,
|
|
29
|
+
style,
|
|
30
|
+
avatarProps,
|
|
31
|
+
accessibilityLabel
|
|
32
|
+
}) {
|
|
33
|
+
// Container tokens
|
|
34
|
+
const background = getVariableByName('testimonialsCard/background', modes) ?? '#ffffff';
|
|
35
|
+
const borderColor = getVariableByName('testimonialsCard/border/color', modes) ?? '#e8e8e8';
|
|
36
|
+
const radius = getVariableByName('testimonialsCard/radius', modes) ?? 8;
|
|
37
|
+
const gap = getVariableByName('testimonialsCard/gap', modes) ?? 8;
|
|
38
|
+
const paddingHorizontal = getVariableByName('testimonialsCard/padding/horizontal', modes) ?? 12;
|
|
39
|
+
const paddingVertical = getVariableByName('testimonialsCard/padding/vertical', modes) ?? 12;
|
|
40
|
+
|
|
41
|
+
// Title typography tokens
|
|
42
|
+
const titleColor = getVariableByName('testimonialsCard/subtitle/color', modes) ?? '#1a1c1f';
|
|
43
|
+
const titleFontSize = getVariableByName('testimonialsCard/title/fontSize', modes) ?? 14;
|
|
44
|
+
const titleFontFamily = getVariableByName('testimonialsCard/title/fontFamily', modes) ?? 'JioType Var';
|
|
45
|
+
const titleFontWeightRaw = getVariableByName('testimonialsCard/title/fontWeight', modes) ?? 700;
|
|
46
|
+
const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw;
|
|
47
|
+
|
|
48
|
+
// Body typography tokens
|
|
49
|
+
const bodyColor = getVariableByName('testimonialsCard/title/color', modes) ?? '#010101';
|
|
50
|
+
const bodyFontSize = getVariableByName('testimonialsCard/subtitle/fontSize', modes) ?? 12;
|
|
51
|
+
const bodyLineHeight = getVariableByName('testimonialsCard/subtitle/lineHeight', modes) ?? 16;
|
|
52
|
+
const bodyFontFamily = getVariableByName('testimonialsCard/subtitle/fontFamily', modes) ?? 'JioType Var';
|
|
53
|
+
const bodyFontWeightRaw = getVariableByName('testimonialsCard/subtitle/fontWeight', modes) ?? 400;
|
|
54
|
+
const bodyFontWeight = typeof bodyFontWeightRaw === 'number' ? bodyFontWeightRaw.toString() : bodyFontWeightRaw;
|
|
55
|
+
const containerStyle = {
|
|
56
|
+
width: 180,
|
|
57
|
+
backgroundColor: background,
|
|
58
|
+
borderColor: borderColor,
|
|
59
|
+
borderWidth: 1,
|
|
60
|
+
borderRadius: radius,
|
|
61
|
+
paddingHorizontal: paddingHorizontal,
|
|
62
|
+
paddingVertical: paddingVertical,
|
|
63
|
+
alignItems: 'flex-start',
|
|
64
|
+
gap: gap
|
|
65
|
+
};
|
|
66
|
+
const titleTextStyle = {
|
|
67
|
+
color: titleColor,
|
|
68
|
+
fontSize: titleFontSize,
|
|
69
|
+
lineHeight: bodyLineHeight,
|
|
70
|
+
fontFamily: titleFontFamily,
|
|
71
|
+
fontWeight: titleFontWeight,
|
|
72
|
+
width: '100%'
|
|
73
|
+
};
|
|
74
|
+
const bodyTextStyle = {
|
|
75
|
+
color: bodyColor,
|
|
76
|
+
fontSize: bodyFontSize,
|
|
77
|
+
lineHeight: bodyLineHeight,
|
|
78
|
+
fontFamily: bodyFontFamily,
|
|
79
|
+
fontWeight: bodyFontWeight,
|
|
80
|
+
width: '100%'
|
|
81
|
+
};
|
|
82
|
+
const avatarModes = {
|
|
83
|
+
...modes,
|
|
84
|
+
...(avatarProps?.modes || {})
|
|
85
|
+
};
|
|
86
|
+
const resolvedAccessibilityLabel = accessibilityLabel ?? `Testimonial${title ? ` from ${title}` : ''}${body ? `: ${body}` : ''}`;
|
|
87
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
88
|
+
style: [containerStyle, style],
|
|
89
|
+
accessibilityRole: "text",
|
|
90
|
+
accessibilityLabel: resolvedAccessibilityLabel,
|
|
91
|
+
children: [/*#__PURE__*/_jsx(Avatar, {
|
|
92
|
+
style: "Image",
|
|
93
|
+
modes: avatarModes,
|
|
94
|
+
...avatarProps
|
|
95
|
+
}), /*#__PURE__*/_jsxs(View, {
|
|
96
|
+
style: textContainerStyle,
|
|
97
|
+
children: [!!title && /*#__PURE__*/_jsx(Text, {
|
|
98
|
+
style: titleTextStyle,
|
|
99
|
+
accessibilityElementsHidden: true,
|
|
100
|
+
importantForAccessibility: "no-hide-descendants",
|
|
101
|
+
children: title
|
|
102
|
+
}), !!body && /*#__PURE__*/_jsx(Text, {
|
|
103
|
+
style: bodyTextStyle,
|
|
104
|
+
accessibilityElementsHidden: true,
|
|
105
|
+
importantForAccessibility: "no-hide-descendants",
|
|
106
|
+
children: body
|
|
107
|
+
})]
|
|
108
|
+
})]
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const textContainerStyle = {
|
|
112
|
+
width: '100%',
|
|
113
|
+
alignItems: 'flex-start',
|
|
114
|
+
gap: 4
|
|
115
|
+
};
|
|
116
|
+
export default /*#__PURE__*/React.memo(TestimonialsCard);
|
|
@@ -131,6 +131,7 @@ export { default as StatItem } from './StatItem/StatItem';
|
|
|
131
131
|
export { default as StatGroup } from './StatGroup/StatGroup';
|
|
132
132
|
export { default as StrengthIndicator } from './StrengthIndicator/StrengthIndicator';
|
|
133
133
|
export { default as SummaryTile } from './SummaryTile/SummaryTile';
|
|
134
|
+
export { default as TestimonialsCard } from './TestimonialsCard/TestimonialsCard';
|
|
134
135
|
export { default as Text } from './Text/Text';
|
|
135
136
|
export { default as SegmentedControl } from './SegmentedControl/SegmentedControl';
|
|
136
137
|
export { default as Toggle } from './Toggle/Toggle';
|