jfs-components 0.0.69 → 0.0.71

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
  3. package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
  4. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +147 -0
  5. package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +258 -0
  6. package/lib/commonjs/components/CircularRating/CircularRating.js +161 -0
  7. package/lib/commonjs/components/Gauge/Gauge.js +223 -0
  8. package/lib/commonjs/components/ListGroup/ListGroup.js +3 -1
  9. package/lib/commonjs/components/MediaCard/GlassFill.js +62 -0
  10. package/lib/commonjs/components/MediaCard/GlassFill.web.js +48 -0
  11. package/lib/commonjs/components/MediaCard/MediaCard.js +28 -31
  12. package/lib/commonjs/components/Nudge/Nudge.js +179 -87
  13. package/lib/commonjs/components/index.js +35 -0
  14. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  15. package/lib/commonjs/icons/registry.js +1 -1
  16. package/lib/module/components/CardAdvisory/CardAdvisory.js +197 -0
  17. package/lib/module/components/CardCTA/CardCTA.js +199 -17
  18. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +141 -0
  19. package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +253 -0
  20. package/lib/module/components/CircularRating/CircularRating.js +155 -0
  21. package/lib/module/components/Gauge/Gauge.js +217 -0
  22. package/lib/module/components/ListGroup/ListGroup.js +3 -1
  23. package/lib/module/components/MediaCard/GlassFill.js +57 -0
  24. package/lib/module/components/MediaCard/GlassFill.web.js +43 -0
  25. package/lib/module/components/MediaCard/MediaCard.js +29 -32
  26. package/lib/module/components/Nudge/Nudge.js +178 -87
  27. package/lib/module/components/index.js +5 -0
  28. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  29. package/lib/module/icons/registry.js +1 -1
  30. package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +49 -0
  31. package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +16 -1
  32. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +27 -0
  33. package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +48 -0
  34. package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +49 -0
  35. package/lib/typescript/src/components/Gauge/Gauge.d.ts +53 -0
  36. package/lib/typescript/src/components/MediaCard/GlassFill.d.ts +47 -0
  37. package/lib/typescript/src/components/MediaCard/GlassFill.web.d.ts +20 -0
  38. package/lib/typescript/src/components/MediaCard/MediaCard.d.ts +17 -13
  39. package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
  40. package/lib/typescript/src/components/index.d.ts +6 -1
  41. package/lib/typescript/src/icons/registry.d.ts +1 -1
  42. package/package.json +3 -2
  43. package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
  44. package/src/components/CardCTA/CardCTA.tsx +236 -13
  45. package/src/components/CircularProgressBar/CircularProgressBar.tsx +190 -0
  46. package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +357 -0
  47. package/src/components/CircularRating/CircularRating.tsx +241 -0
  48. package/src/components/Gauge/Gauge.tsx +303 -0
  49. package/src/components/ListGroup/ListGroup.tsx +3 -1
  50. package/src/components/MediaCard/GlassFill.tsx +89 -0
  51. package/src/components/MediaCard/GlassFill.web.tsx +53 -0
  52. package/src/components/MediaCard/MediaCard.tsx +29 -48
  53. package/src/components/Nudge/Nudge.tsx +222 -82
  54. package/src/components/index.ts +6 -1
  55. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  56. package/src/icons/registry.ts +1 -1
@@ -1,8 +1,8 @@
1
1
  import React, { createContext, useContext } from 'react'
2
- import { View, Text, StyleSheet, type ViewStyle, type TextStyle, type StyleProp, type ImageSourcePropType, Platform } from 'react-native'
3
- import { BlurView, type BlurTint } from 'expo-blur'
2
+ import { View, Text, StyleSheet, type ViewStyle, type TextStyle, type StyleProp, type ImageSourcePropType } from 'react-native'
4
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
5
4
  import Image from '../Image/Image'
5
+ import GlassFill, { type GlassTint } from './GlassFill'
6
6
  import { EMPTY_MODES } from '../../utils/react-utils'
7
7
 
8
8
  const MediaCardContext = createContext<{ modes?: Record<string, any> }>({})
@@ -162,20 +162,24 @@ export function Title({ children, style, modes: propModes }: { children?: React.
162
162
  * Glass Footer — pinned to the bottom of the card, **always** on top of the
163
163
  * Header (`zIndex: 2`).
164
164
  *
165
- * Glass implementation (April 2026 best practice for RN/Expo):
166
- * - **iOS:** `expo-blur`'s `BlurView` renders a native `UIVisualEffectView`,
167
- * so this is a real OS-level live blur of whatever's underneath. We pick
168
- * `tint` from the Figma "Contrast Context" mode (`'dark'` / `'light'`)
169
- * and a moderate intensity that matches the Figma `blur/minimal` token.
170
- * - **Android:** the same `BlurView` with `experimentalBlurMethod="dimezisBlurView"`
171
- * enables the hardware-accelerated `RenderEffect` blur on Android 12+.
172
- * On older Android, expo-blur cleanly degrades to a tinted scrim — we
173
- * layer a subtle noise/grain overlay on top so the surface still reads
174
- * as "frosted glass" instead of a flat color.
175
- * - **Web:** `BlurView` on web is implemented as `backdrop-filter: blur()`,
176
- * which already worked in the previous version. Same component, same API.
165
+ * Glass implementation:
166
+ * - **iOS / Android:** `<GlassFill>` (this folder) wraps
167
+ * `@react-native-community/blur`'s `BlurView`. iOS gets a real
168
+ * `UIVisualEffectView` (live OS blur); Android gets the community
169
+ * `RealtimeBlurView` with a token-driven tinted scrim fallback for
170
+ * devices where realtime blur is unavailable.
171
+ * - **Web:** the platform-extension file `GlassFill.web.tsx` renders a
172
+ * translucent View with `backdrop-filter: blur()` Metro picks the
173
+ * correct file automatically, so the web bundle never imports the
174
+ * native-only blur module.
177
175
  *
178
- * Tokens still drive the tint color, blur radius and inner spacing.
176
+ * Why we don't use `expo-blur`: it requires Expo Modules autolinking on the
177
+ * consumer side (`use_expo_modules!` / `ExpoModulesPackage`), which silently
178
+ * breaks bare React Native apps that just install this library and run
179
+ * `pod install`. `@react-native-community/blur` is a regular RN native
180
+ * module — autolinking handles it with no additional setup.
181
+ *
182
+ * Tokens still drive the tint color, blur intensity and inner spacing.
179
183
  */
180
184
  export function Footer({ children, style, modes: propModes }: { children?: React.ReactNode; style?: StyleProp<ViewStyle>; modes?: Record<string, any> }) {
181
185
  const context = useContext(MediaCardContext)
@@ -186,10 +190,14 @@ export function Footer({ children, style, modes: propModes }: { children?: React
186
190
  const paddingVertical = parseFloat(getVariableByName('cardMedia/footer/padding/vertical', modes) || '12')
187
191
 
188
192
  // Figma tokens:
189
- // blur/minimal/background -> tint laid over the native blur
190
- // blur/minimal -> blur radius (px). expo-blur takes a 0-100
191
- // "intensity" instead of px; we map roughly:
192
- // intensity clamp(radius * 1.7, 0, 100).
193
+ // blur/minimal/background -> tint laid over the live blur, also used
194
+ // as the iOS reduced-transparency fallback.
195
+ // blur/minimal -> blur radius (px). The community BlurView
196
+ // uses `blurAmount` (~0-32). `GlassFill`
197
+ // accepts a 0-100 "intensity" (kept compat
198
+ // with the previous expo-blur scale) and
199
+ // maps it internally — here we convert the
200
+ // token's radius to that intensity scale.
193
201
  const glassBgColor = getVariableByName('blur/minimal/background', modes) || '#1414174a'
194
202
  const blurRadius = parseFloat(getVariableByName('blur/minimal', modes) || '29')
195
203
  const intensity = Math.max(0, Math.min(100, Math.round(blurRadius * 1.7)))
@@ -197,7 +205,7 @@ export function Footer({ children, style, modes: propModes }: { children?: React
197
205
  // Pick the iOS/Android material tint from "Contrast Context" mode so the
198
206
  // glass adapts to dark/light backgrounds the same way the Figma tokens do.
199
207
  const contrast = (modes['Contrast Context'] || 'on dark') as string
200
- const tint: BlurTint = contrast === 'on light' ? 'light' : 'dark'
208
+ const tint: GlassTint = contrast === 'on light' ? 'light' : 'dark'
201
209
 
202
210
  return (
203
211
  <View
@@ -216,34 +224,7 @@ export function Footer({ children, style, modes: propModes }: { children?: React
216
224
  ]}
217
225
  pointerEvents="box-none"
218
226
  >
219
- {/* Native live blur. On Android pre-12 expo-blur falls back to a
220
- tinted scrim automatically; on web it's a backdrop-filter. */}
221
- <BlurView
222
- style={StyleSheet.absoluteFill}
223
- tint={tint}
224
- intensity={intensity}
225
- experimentalBlurMethod="dimezisBlurView"
226
- />
227
-
228
- {/* Token-driven tint laid on top of the live blur — keeps the
229
- Figma color signature regardless of platform blur quality. */}
230
- <View style={[StyleSheet.absoluteFill, { backgroundColor: glassBgColor }]} />
231
-
232
- {/* Subtle noise/grain on Android only, to compensate for the
233
- lower-fidelity blur — purely additive, no behavior change.
234
- On iOS/web the native blur already has natural texture. */}
235
- {Platform.OS === 'android' ? (
236
- <View
237
- style={[
238
- StyleSheet.absoluteFill,
239
- {
240
- backgroundColor: 'rgba(255,255,255,0.03)',
241
- opacity: 0.6,
242
- },
243
- ]}
244
- pointerEvents="none"
245
- />
246
- ) : null}
227
+ <GlassFill tint={tint} intensity={intensity} overlayColor={glassBgColor} />
247
228
 
248
229
  <View
249
230
  style={{
@@ -1,30 +1,34 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import { View, Text, type ViewStyle, type TextStyle, type StyleProp } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import { useTokens } from '../../design-tokens/JFSThemeProvider'
5
5
  import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils'
6
6
  import Button from '../Button/Button'
7
+ import Icon from '../../icons/Icon'
8
+
9
+ export type NudgeType = 'stacked-prominent' | 'stacked-detailed' | 'inline-compact'
7
10
 
8
11
  export type NudgeProps = {
9
12
  /**
10
- * Controls the layout variant.
11
- * - "Default": horizontal layout with optional start icon and content (title/body/button or children slot)
12
- * - "Variant2": vertical layout with header (icon + title) and children slot below
13
+ * Controls the layout type.
14
+ * - "stacked-prominent": icon + title/body/button content
15
+ * - "inline-compact": icon + body/button in one row
16
+ * - "stacked-detailed": header + children detail slot
13
17
  */
14
- variant?: 'Default' | 'Variant2';
18
+ type?: NudgeType;
15
19
  /** Title text displayed in the nudge */
16
20
  title?: string;
17
- /** Body text displayed below the title (Default variant only, when no children are provided) */
21
+ /** Body text displayed in prominent and compact types when no children are provided */
18
22
  body?: string;
19
- /** Label for the default button (Default variant only, when no children are provided) */
23
+ /** Label for the default button when no buttonSlot is provided */
20
24
  buttonLabel?: string;
21
25
  /** Callback for the default button press */
22
26
  onPressButton?: () => void;
23
- /** Custom button slot (Default variant only, overrides buttonLabel/onPressButton) */
27
+ /** Custom button slot, overrides buttonLabel/onPressButton */
24
28
  buttonSlot?: React.ReactNode;
25
- /** Optional leading slot for an icon/element. Pass null or omit to hide. */
26
- startSlot?: React.ReactNode;
27
- /** Content slot — overrides the default title/body/button content */
29
+ /** Optional leading slot. Omit for the token-driven sparkle icon, pass null/false to hide. */
30
+ startSlot?: React.ReactNode | false;
31
+ /** Content slot — overrides default content, or provides detailed list content */
28
32
  children?: React.ReactNode;
29
33
  /** Mode configuration for design token resolution */
30
34
  modes?: Record<string, any>;
@@ -32,21 +36,32 @@ export type NudgeProps = {
32
36
  style?: StyleProp<ViewStyle>;
33
37
  };
34
38
 
35
- function Nudge({
36
- variant = 'Default',
37
- title = 'Split payment',
38
- body = 'Split this transaction into installments',
39
- buttonLabel = 'Button',
40
- onPressButton,
41
- buttonSlot,
42
- startSlot,
43
- children,
44
- modes: propModes = EMPTY_MODES,
45
- style,
46
- }: NudgeProps) {
47
- const { modes: globalModes } = useTokens()
48
- const modes = { ...globalModes, ...propModes }
39
+ interface NudgeTokens {
40
+ containerBaseStyle: ViewStyle;
41
+ prominentContainerStyle: ViewStyle;
42
+ compactContainerStyle: ViewStyle;
43
+ detailedContainerStyle: ViewStyle;
44
+ contentStyle: ViewStyle;
45
+ compactOuterContentStyle: ViewStyle;
46
+ compactContentWrapStyle: ViewStyle;
47
+ textWrapStyle: ViewStyle;
48
+ compactTextWrapStyle: ViewStyle;
49
+ headerStyle: ViewStyle;
50
+ detailSlotStyle: ViewStyle;
51
+ titleTextStyle: TextStyle;
52
+ bodyTextStyle: TextStyle;
53
+ iconColor: string;
54
+ iconSize: number;
55
+ startSlotGap: number;
56
+ }
57
+
58
+ function toFontWeight(value: unknown, fallback: TextStyle['fontWeight']): TextStyle['fontWeight'] {
59
+ if (typeof value === 'number') return value.toString() as TextStyle['fontWeight']
60
+ if (typeof value === 'string') return value as TextStyle['fontWeight']
61
+ return fallback
62
+ }
49
63
 
64
+ function resolveNudgeTokens(modes: Record<string, any>): NudgeTokens {
50
65
  const background = getVariableByName('nudge/background', modes) || '#f5f5f5'
51
66
  const radius = getVariableByName('nudge/radius', modes) || 12
52
67
  const paddingH = getVariableByName('nudge/padding/horizontal', modes) || 12
@@ -55,96 +70,221 @@ function Nudge({
55
70
 
56
71
  const titleColor = getVariableByName('nudge/title/color', modes) || '#0d0d0f'
57
72
  const titleFontSize = getVariableByName('nudge/title/fontSize', modes) || 14
58
- const titleFontFamily = getVariableByName('nudge/title/fontFamily', modes) || 'JioType Var'
73
+ const titleFontFamily = getVariableByName('nudge/title/fontFamily', modes) || 'System'
59
74
  const titleLineHeight = getVariableByName('nudge/title/lineHeight', modes) || 15
60
- const titleFontWeightRaw = getVariableByName('nudge/title/fontWeight', modes) || 700
61
- const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw
75
+ const titleFontWeight = toFontWeight(getVariableByName('nudge/title/fontWeight', modes), '700')
62
76
 
63
77
  const bodyColor = getVariableByName('nudge/body/color', modes) || '#1a1c1f'
64
78
  const bodyFontSize = getVariableByName('nudge/body/fontSize', modes) || 12
65
- const bodyFontFamily = getVariableByName('nudge/body/fontFamily', modes) || 'JioType Var'
79
+ const bodyFontFamily = getVariableByName('nudge/body/fontFamily', modes) || 'System'
66
80
  const bodyLineHeight = getVariableByName('nudge/body/lineHeight', modes) || 16
67
- const bodyFontWeightRaw = getVariableByName('nudge/body/fontWeight', modes) || 500
68
- const bodyFontWeight = typeof bodyFontWeightRaw === 'number' ? bodyFontWeightRaw.toString() : bodyFontWeightRaw
81
+ const bodyFontWeight = toFontWeight(getVariableByName('nudge/body/fontWeight', modes), '500')
69
82
 
70
83
  const textGap = getVariableByName('nudge/text/gap', modes) || 4
71
84
  const contentGap = getVariableByName('nudge/content/gap', modes) || 8
72
85
  const contentMinHeight = getVariableByName('nudge/content/minHeight', modes) || 20
86
+ const startSlotGap = getVariableByName('nudge/startSlot/gap', modes) || 4
73
87
 
74
- const containerStyle: ViewStyle = {
75
- backgroundColor: background,
76
- borderRadius: radius,
77
- paddingHorizontal: paddingH,
78
- paddingVertical: paddingV,
79
- gap,
80
- overflow: 'hidden',
81
- ...(variant === 'Variant2'
82
- ? { flexDirection: 'column', alignItems: 'flex-start' }
83
- : { flexDirection: 'row', alignItems: 'flex-start' }),
88
+ return {
89
+ containerBaseStyle: {
90
+ backgroundColor: background as string,
91
+ borderRadius: radius as number,
92
+ paddingHorizontal: paddingH as number,
93
+ paddingVertical: paddingV as number,
94
+ gap: gap as number,
95
+ overflow: 'hidden',
96
+ },
97
+ prominentContainerStyle: {
98
+ flexDirection: 'row',
99
+ alignItems: 'flex-start',
100
+ },
101
+ compactContainerStyle: {
102
+ flexDirection: 'row',
103
+ alignItems: 'center',
104
+ },
105
+ detailedContainerStyle: {
106
+ flexDirection: 'column',
107
+ alignItems: 'flex-start',
108
+ },
109
+ contentStyle: {
110
+ flex: 1,
111
+ minWidth: 1,
112
+ minHeight: contentMinHeight as number,
113
+ justifyContent: 'center',
114
+ overflow: 'hidden',
115
+ },
116
+ compactOuterContentStyle: {
117
+ flex: 1,
118
+ minWidth: 1,
119
+ alignSelf: 'stretch',
120
+ justifyContent: 'center',
121
+ },
122
+ compactContentWrapStyle: {
123
+ flexDirection: 'row',
124
+ alignItems: 'center',
125
+ gap: contentGap as number,
126
+ width: '100%',
127
+ },
128
+ textWrapStyle: {
129
+ gap: textGap as number,
130
+ alignItems: 'flex-start',
131
+ width: '100%',
132
+ },
133
+ compactTextWrapStyle: {
134
+ flex: 1,
135
+ minWidth: 1,
136
+ alignItems: 'flex-start',
137
+ },
138
+ headerStyle: {
139
+ flexDirection: 'row',
140
+ alignItems: 'center',
141
+ gap: gap as number,
142
+ width: '100%',
143
+ },
144
+ detailSlotStyle: {
145
+ gap: getVariableByName('slot/gap', modes) || 8,
146
+ width: '100%',
147
+ },
148
+ titleTextStyle: {
149
+ color: titleColor as string,
150
+ fontSize: titleFontSize as number,
151
+ fontFamily: titleFontFamily as string,
152
+ lineHeight: titleLineHeight as number,
153
+ fontWeight: titleFontWeight,
154
+ },
155
+ bodyTextStyle: {
156
+ color: bodyColor as string,
157
+ fontSize: bodyFontSize as number,
158
+ fontFamily: bodyFontFamily as string,
159
+ lineHeight: bodyLineHeight as number,
160
+ fontWeight: bodyFontWeight,
161
+ },
162
+ iconColor: (getVariableByName('appearance/nudge/icon/color', modes) || '#5d00b5') as string,
163
+ iconSize: (getVariableByName('nudge/icon/size', modes) || 20) as number,
164
+ startSlotGap: startSlotGap as number,
84
165
  }
166
+ }
85
167
 
86
- const titleStyle: TextStyle = {
87
- color: titleColor,
88
- fontSize: titleFontSize,
89
- fontFamily: titleFontFamily,
90
- lineHeight: titleLineHeight,
91
- fontWeight: titleFontWeight,
92
- }
168
+ function NudgeImpl({
169
+ type = 'stacked-prominent',
170
+ title = 'Split payment',
171
+ body = 'Split this transaction into installments',
172
+ buttonLabel = 'Button',
173
+ onPressButton,
174
+ buttonSlot,
175
+ startSlot,
176
+ children,
177
+ modes: propModes = EMPTY_MODES,
178
+ style,
179
+ }: NudgeProps) {
180
+ const { modes: globalModes } = useTokens()
181
+ const modes = useMemo(
182
+ () => (globalModes === EMPTY_MODES && propModes === EMPTY_MODES
183
+ ? EMPTY_MODES
184
+ : { ...globalModes, ...propModes }),
185
+ [globalModes, propModes]
186
+ )
93
187
 
94
- const bodyStyle: TextStyle = {
95
- color: bodyColor,
96
- fontSize: bodyFontSize,
97
- fontFamily: bodyFontFamily,
98
- lineHeight: bodyLineHeight,
99
- fontWeight: bodyFontWeight,
100
- }
188
+ const tokens = useMemo(() => resolveNudgeTokens(modes), [modes])
189
+
190
+ const startSlotElement = useMemo(() => {
191
+ if (startSlot === null || startSlot === false) return null
192
+
193
+ if (startSlot !== undefined) {
194
+ const processed = cloneChildrenWithModes(React.Children.toArray(startSlot), modes)
195
+ return processed.length === 1 ? processed[0] : processed
196
+ }
197
+
198
+ return (
199
+ <Icon
200
+ name="ic_ai_sparkle"
201
+ size={tokens.iconSize}
202
+ color={tokens.iconColor}
203
+ accessibilityElementsHidden={true}
204
+ importantForAccessibility="no"
205
+ />
206
+ )
207
+ }, [startSlot, modes, tokens.iconColor, tokens.iconSize])
208
+
209
+ const startSlotWrapper = startSlotElement ? (
210
+ <View style={{ gap: tokens.startSlotGap, alignItems: 'center' }}>
211
+ {startSlotElement}
212
+ </View>
213
+ ) : null
101
214
 
102
- const processedStartSlot = startSlot
103
- ? cloneChildrenWithModes(React.Children.toArray(startSlot), modes)
104
- : null
215
+ const processedChildren = useMemo(() => {
216
+ if (!children) return null
217
+ const processed = cloneChildrenWithModes(React.Children.toArray(children), modes)
218
+ return processed.length === 1 ? processed[0] : processed
219
+ }, [children, modes])
105
220
 
106
- const startSlotElement = processedStartSlot && processedStartSlot.length > 0
107
- ? (processedStartSlot.length === 1 ? processedStartSlot[0] : processedStartSlot)
108
- : null
221
+ const buttonElement = buttonSlot
222
+ ? cloneChildrenWithModes(React.Children.toArray(buttonSlot), modes)
223
+ : (
224
+ <Button
225
+ label={buttonLabel}
226
+ modes={modes}
227
+ {...(onPressButton ? { onPress: onPressButton } : {})}
228
+ />
229
+ )
109
230
 
110
- if (variant === 'Variant2') {
231
+ if (type === 'stacked-detailed') {
111
232
  return (
112
- <View style={[containerStyle, style]}>
113
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, width: '100%' }}>
114
- {startSlotElement}
115
- <Text style={[titleStyle, { flex: 1 }]}>{title}</Text>
233
+ <View style={[tokens.containerBaseStyle, tokens.detailedContainerStyle, style]}>
234
+ <View style={tokens.headerStyle}>
235
+ {startSlotWrapper}
236
+ <Text style={[tokens.titleTextStyle, { flex: 1 }]}>{title}</Text>
116
237
  </View>
117
238
 
118
- {children ? (
119
- cloneChildrenWithModes(React.Children.toArray(children), modes)
239
+ {processedChildren ? (
240
+ <View style={tokens.detailSlotStyle}>
241
+ {processedChildren}
242
+ </View>
120
243
  ) : null}
121
244
  </View>
122
245
  )
123
246
  }
124
247
 
125
- const defaultContent = (
126
- <View style={{ gap: contentGap, alignItems: 'flex-start', width: '100%' }}>
127
- <View style={{ gap: textGap, alignItems: 'flex-start', width: '100%' }}>
128
- <Text style={titleStyle}>{title}</Text>
129
- <Text style={bodyStyle}>{body}</Text>
248
+ if (type === 'inline-compact') {
249
+ return (
250
+ <View style={[tokens.containerBaseStyle, tokens.compactContainerStyle, style]}>
251
+ {startSlotWrapper}
252
+
253
+ <View style={tokens.compactOuterContentStyle}>
254
+ {processedChildren || (
255
+ <View style={tokens.compactContentWrapStyle}>
256
+ <View style={tokens.compactTextWrapStyle}>
257
+ <Text style={tokens.bodyTextStyle}>{body}</Text>
258
+ </View>
259
+ {buttonElement}
260
+ </View>
261
+ )}
262
+ </View>
263
+ </View>
264
+ )
265
+ }
266
+
267
+ const prominentContent = (
268
+ <View style={{ gap: tokens.compactContentWrapStyle.gap as number, alignItems: 'flex-start', width: '100%' }}>
269
+ <View style={tokens.textWrapStyle}>
270
+ <Text style={tokens.titleTextStyle}>{title}</Text>
271
+ <Text style={tokens.bodyTextStyle}>{body}</Text>
130
272
  </View>
131
- {buttonSlot
132
- ? cloneChildrenWithModes(React.Children.toArray(buttonSlot), modes)
133
- : <Button label={buttonLabel} onPress={onPressButton} modes={modes} />}
273
+ {buttonElement}
134
274
  </View>
135
275
  )
136
276
 
137
277
  return (
138
- <View style={[containerStyle, style]}>
139
- {startSlotElement}
278
+ <View style={[tokens.containerBaseStyle, tokens.prominentContainerStyle, style]}>
279
+ {startSlotWrapper}
140
280
 
141
- <View style={{ flex: 1, minWidth: 1, minHeight: contentMinHeight, justifyContent: 'center', overflow: 'hidden' }}>
142
- {children
143
- ? cloneChildrenWithModes(React.Children.toArray(children), modes)
144
- : defaultContent}
281
+ <View style={tokens.contentStyle}>
282
+ {processedChildren || prominentContent}
145
283
  </View>
146
284
  </View>
147
285
  )
148
286
  }
149
287
 
288
+ const Nudge = React.memo(NudgeImpl)
289
+
150
290
  export default Nudge
@@ -7,6 +7,7 @@ export { default as BottomNav } from './BottomNav/BottomNav';
7
7
  export { default as BottomNavItem } from './BottomNavItem/BottomNavItem';
8
8
  export { default as Button, type ButtonProps } from './Button/Button';
9
9
  export { default as Card } from './Card/Card';
10
+ export { default as CardAdvisory, type CardAdvisoryProps } from './CardAdvisory/CardAdvisory'
10
11
  export { default as Carousel } from './Carousel/Carousel';
11
12
  export type { CarouselProps, CarouselItemProps, PaginationProps } from './Carousel/Carousel';
12
13
  export { default as Checkbox, type CheckboxProps } from './Checkbox/Checkbox';
@@ -14,12 +15,16 @@ export { default as CardFeedback, type CardFeedbackProps } from './CardFeedback/
14
15
  export { default as Disclaimer } from './Disclaimer/Disclaimer';
15
16
  export { default as Divider, type DividerProps, type DividerDirection } from './Divider/Divider';
16
17
  export { default as Drawer } from './Drawer/Drawer';
17
- export { default as CardCTA, type CardCTAProps } from './CardCTA/CardCTA';
18
+ export { default as CardCTA, type CardCTAProps, type CardCTAType } from './CardCTA/CardCTA'
18
19
  export { default as DebitCard, type DebitCardProps } from './DebitCard/DebitCard';
19
20
  export { default as FilterBar } from './FilterBar/FilterBar';
20
21
  export { default as Form, type FormProps } from './Form/Form';
21
22
  export { useFormContext } from './Form/Form';
22
23
  export { default as FormField, type FormFieldProps, type FormFieldType } from './FormField/FormField';
24
+ export { default as CircularProgressBar, type CircularProgressBarProps } from './CircularProgressBar/CircularProgressBar'
25
+ export { default as CircularProgressBarDoted, type CircularProgressBarDotedProps } from './CircularProgressBarDoted/CircularProgressBarDoted'
26
+ export { default as CircularRating, type CircularRatingProps } from './CircularRating/CircularRating'
27
+ export { default as Gauge, type GaugeProps } from './Gauge/Gauge';
23
28
  export { default as HoldingsCard, type HoldingsCardProps } from './HoldingsCard/HoldingsCard';
24
29
  export { default as HStack, type HStackProps } from './HStack/HStack';
25
30
  export { default as IconButton } from './IconButton/IconButton';