jfs-components 0.0.70 → 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 (43) hide show
  1. package/lib/commonjs/components/CardAdvisory/CardAdvisory.js +203 -0
  2. package/lib/commonjs/components/CardCTA/CardCTA.js +198 -16
  3. package/lib/commonjs/components/CircularProgressBar/CircularProgressBar.js +147 -0
  4. package/lib/commonjs/components/CircularProgressBarDoted/CircularProgressBarDoted.js +258 -0
  5. package/lib/commonjs/components/CircularRating/CircularRating.js +161 -0
  6. package/lib/commonjs/components/Gauge/Gauge.js +223 -0
  7. package/lib/commonjs/components/ListGroup/ListGroup.js +3 -1
  8. package/lib/commonjs/components/Nudge/Nudge.js +179 -87
  9. package/lib/commonjs/components/index.js +35 -0
  10. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  11. package/lib/commonjs/icons/registry.js +1 -1
  12. package/lib/module/components/CardAdvisory/CardAdvisory.js +197 -0
  13. package/lib/module/components/CardCTA/CardCTA.js +199 -17
  14. package/lib/module/components/CircularProgressBar/CircularProgressBar.js +141 -0
  15. package/lib/module/components/CircularProgressBarDoted/CircularProgressBarDoted.js +253 -0
  16. package/lib/module/components/CircularRating/CircularRating.js +155 -0
  17. package/lib/module/components/Gauge/Gauge.js +217 -0
  18. package/lib/module/components/ListGroup/ListGroup.js +3 -1
  19. package/lib/module/components/Nudge/Nudge.js +178 -87
  20. package/lib/module/components/index.js +5 -0
  21. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  22. package/lib/module/icons/registry.js +1 -1
  23. package/lib/typescript/src/components/CardAdvisory/CardAdvisory.d.ts +49 -0
  24. package/lib/typescript/src/components/CardCTA/CardCTA.d.ts +16 -1
  25. package/lib/typescript/src/components/CircularProgressBar/CircularProgressBar.d.ts +27 -0
  26. package/lib/typescript/src/components/CircularProgressBarDoted/CircularProgressBarDoted.d.ts +48 -0
  27. package/lib/typescript/src/components/CircularRating/CircularRating.d.ts +49 -0
  28. package/lib/typescript/src/components/Gauge/Gauge.d.ts +53 -0
  29. package/lib/typescript/src/components/Nudge/Nudge.d.ts +14 -11
  30. package/lib/typescript/src/components/index.d.ts +6 -1
  31. package/lib/typescript/src/icons/registry.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/components/CardAdvisory/CardAdvisory.tsx +283 -0
  34. package/src/components/CardCTA/CardCTA.tsx +236 -13
  35. package/src/components/CircularProgressBar/CircularProgressBar.tsx +190 -0
  36. package/src/components/CircularProgressBarDoted/CircularProgressBarDoted.tsx +357 -0
  37. package/src/components/CircularRating/CircularRating.tsx +241 -0
  38. package/src/components/Gauge/Gauge.tsx +303 -0
  39. package/src/components/ListGroup/ListGroup.tsx +3 -1
  40. package/src/components/Nudge/Nudge.tsx +222 -82
  41. package/src/components/index.ts +6 -1
  42. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  43. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ type CardAdvisoryBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
4
+ export type CardAdvisoryProps = CardAdvisoryBaseProps & {
5
+ /** Main heading text. */
6
+ title?: string;
7
+ /** Description shown below the heading. */
8
+ description?: string;
9
+ /** Progress score displayed in the circular progress bar. */
10
+ value?: number;
11
+ /** Optional formatted score label. */
12
+ valueLabel?: string;
13
+ /** Show the info icon beside the title. */
14
+ showInfoIcon?: boolean;
15
+ /** Show the bottom advisory nudge. */
16
+ showNudge?: boolean;
17
+ /** Body text for the default nudge. */
18
+ nudgeBody?: string;
19
+ /** Button label for the default nudge. */
20
+ nudgeButtonLabel?: string;
21
+ /** Callback for the default nudge button. */
22
+ onPressNudgeButton?: () => void;
23
+ /** Optional slot replacing the title info icon. Receives `modes` recursively. */
24
+ titleEndSlot?: React.ReactNode;
25
+ /** Optional slot replacing the circular progress bar. Receives `modes` recursively. */
26
+ progressSlot?: React.ReactNode;
27
+ /** Optional slot replacing the bottom nudge. Receives `modes` recursively. */
28
+ nudgeSlot?: React.ReactNode;
29
+ /** Design token modes forwarded to token lookups and child components. */
30
+ modes?: Record<string, any>;
31
+ /** Optional container style override. */
32
+ style?: StyleProp<ViewStyle>;
33
+ /** Optional main content row style override. */
34
+ mainContentStyle?: StyleProp<ViewStyle>;
35
+ /** Optional title text style override. */
36
+ titleStyle?: StyleProp<TextStyle>;
37
+ /** Optional description text style override. */
38
+ descriptionStyle?: StyleProp<TextStyle>;
39
+ /** Optional progress wrapper style override. */
40
+ progressStyle?: StyleProp<ViewStyle>;
41
+ /** Optional nudge style override. */
42
+ nudgeStyle?: StyleProp<ViewStyle>;
43
+ /** Accessibility label for the full card. */
44
+ accessibilityLabel?: string;
45
+ };
46
+ declare function CardAdvisory({ title, description, value, valueLabel, showInfoIcon, showNudge, nudgeBody, nudgeButtonLabel, onPressNudgeButton, titleEndSlot, progressSlot, nudgeSlot, modes: propModes, style, mainContentStyle, titleStyle, descriptionStyle, progressStyle, nudgeStyle, accessibilityLabel, ...rest }: CardAdvisoryProps): import("react/jsx-runtime").JSX.Element;
47
+ declare const _default: React.MemoExoticComponent<typeof CardAdvisory>;
48
+ export default _default;
49
+ //# sourceMappingURL=CardAdvisory.d.ts.map
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
  import { type ViewStyle, type StyleProp } from 'react-native';
3
+ export type CardCTAType = 'cta' | 'rating';
3
4
  export type CardCTAProps = {
5
+ /** Visual layout variant */
6
+ type?: CardCTAType;
4
7
  /** Title text */
5
8
  title?: string;
6
9
  /** Body / subtitle text */
@@ -11,15 +14,27 @@ export type CardCTAProps = {
11
14
  buttonLabel?: string;
12
15
  /** Callback for the default Button press */
13
16
  onPressButton?: () => void;
17
+ /** Label shown in the rating badge */
18
+ ratingLabel?: string;
19
+ /** Show like/dislike actions in the rating footer */
20
+ showRatingActions?: boolean;
21
+ /** Callback for the default like action */
22
+ onPressLike?: () => void;
23
+ /** Callback for the default dislike action */
24
+ onPressDislike?: () => void;
14
25
  /** Mode configuration for design token resolution */
15
26
  modes?: Record<string, any>;
16
27
  /** Slot: replaces the default icon area (right side) */
17
28
  iconSlot?: React.ReactNode;
18
29
  /** Slot: replaces the default Button */
19
30
  buttonSlot?: React.ReactNode;
31
+ /** Slot: replaces the default rating badge */
32
+ ratingBadgeSlot?: React.ReactNode;
33
+ /** Slot: replaces the default like/dislike action group */
34
+ ratingActionsSlot?: React.ReactNode;
20
35
  /** Container style overrides */
21
36
  style?: StyleProp<ViewStyle>;
22
37
  };
23
- declare function CardCTA({ title, body, iconName, buttonLabel, onPressButton, modes: propModes, iconSlot, buttonSlot, style, }: CardCTAProps): import("react/jsx-runtime").JSX.Element;
38
+ declare function CardCTA({ type, title, body, iconName, buttonLabel, onPressButton, ratingLabel, showRatingActions, onPressLike, onPressDislike, modes: propModes, iconSlot, buttonSlot, ratingBadgeSlot, ratingActionsSlot, style, }: CardCTAProps): import("react/jsx-runtime").JSX.Element;
24
39
  export default CardCTA;
25
40
  //# sourceMappingURL=CardCTA.d.ts.map
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ type CircularProgressBarBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
4
+ export type CircularProgressBarState = 'Active' | 'Inactive';
5
+ export type CircularProgressBarProps = CircularProgressBarBaseProps & {
6
+ /** Current progress value. Clamped between 0 and 100. */
7
+ value?: number;
8
+ /** Active shows progress and value; inactive shows the track and minus icon. */
9
+ state?: CircularProgressBarState | boolean;
10
+ /** Optional formatted value shown in the active state. */
11
+ valueLabel?: string;
12
+ /** Design token modes forwarded to token lookups. */
13
+ modes?: Record<string, any>;
14
+ /** Container style override. */
15
+ style?: StyleProp<ViewStyle>;
16
+ /** Track stroke style override. */
17
+ trackStyle?: StyleProp<ViewStyle>;
18
+ /** Progress stroke style override. */
19
+ progressStyle?: StyleProp<ViewStyle>;
20
+ /** Value text style override. */
21
+ valueStyle?: StyleProp<TextStyle>;
22
+ /** Accessibility label for the whole progress component. */
23
+ accessibilityLabel?: string;
24
+ };
25
+ declare function CircularProgressBar({ value, state, valueLabel, modes: propModes, style, trackStyle, progressStyle, valueStyle, accessibilityLabel, ...rest }: CircularProgressBarProps): import("react/jsx-runtime").JSX.Element;
26
+ export default CircularProgressBar;
27
+ //# sourceMappingURL=CircularProgressBar.d.ts.map
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ type CircularProgressBarDotedBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
4
+ export type CircularProgressBarDotedProps = CircularProgressBarDotedBaseProps & {
5
+ /** Progress value. Clamped between 0 and 100. */
6
+ value?: number;
7
+ /** Number of dots in the ring. */
8
+ dotCount?: number;
9
+ /** Small label above the score. */
10
+ label?: string;
11
+ /** Text below the score. */
12
+ tierLabel?: string;
13
+ /** Show or hide the chevron after the tier label. */
14
+ showChevron?: boolean;
15
+ /** Called when the component is pressed. */
16
+ onPress?: () => void;
17
+ /** Called when the tier row is pressed. */
18
+ onTierPress?: () => void;
19
+ /** Design token modes forwarded to token lookups and slot children. */
20
+ modes?: Record<string, any>;
21
+ /** Slot rendered in the center of the dotted ring. Receives `modes` recursively. */
22
+ children?: React.ReactNode;
23
+ /** Container style override. */
24
+ style?: StyleProp<ViewStyle>;
25
+ /** Ring wrapper style override. */
26
+ ringStyle?: StyleProp<ViewStyle>;
27
+ /** Track dot style override. */
28
+ trackDotStyle?: StyleProp<ViewStyle>;
29
+ /** Progress dot style override. */
30
+ progressDotStyle?: StyleProp<ViewStyle>;
31
+ /** Center content style override. */
32
+ contentStyle?: StyleProp<ViewStyle>;
33
+ /** Score tier stack style override. */
34
+ scoreTierStyle?: StyleProp<ViewStyle>;
35
+ /** Score trend row style override. */
36
+ scoreTrendStyle?: StyleProp<ViewStyle>;
37
+ /** Label text style override. */
38
+ labelStyle?: StyleProp<TextStyle>;
39
+ /** Score text style override. */
40
+ scoreLabelStyle?: StyleProp<TextStyle>;
41
+ /** Tier text style override. */
42
+ tierLabelStyle?: StyleProp<TextStyle>;
43
+ /** Accessibility label for the whole progress component. */
44
+ accessibilityLabel?: string;
45
+ };
46
+ declare function CircularProgressBarDoted({ value, dotCount, label, tierLabel, showChevron, onPress, onTierPress, modes: propModes, children, style, ringStyle, trackDotStyle, progressDotStyle, contentStyle, scoreTierStyle, scoreTrendStyle, labelStyle, scoreLabelStyle, tierLabelStyle, accessibilityLabel, onLayout, ...rest }: CircularProgressBarDotedProps): import("react/jsx-runtime").JSX.Element;
47
+ export default CircularProgressBarDoted;
48
+ //# sourceMappingURL=CircularProgressBarDoted.d.ts.map
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ type CircularRatingBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
4
+ export type CircularRatingProps = CircularRatingBaseProps & {
5
+ /** Rating value. Clamped by CircularProgressBarDoted between 0 and 100. */
6
+ value?: number;
7
+ /** Number of dots rendered around the rating ring. */
8
+ dotCount?: number;
9
+ /** Small label above the score. */
10
+ label?: string;
11
+ /** Text below the score. */
12
+ tierLabel?: string;
13
+ /** Footer timestamp/copy shown below the rating. */
14
+ footerText?: string;
15
+ /** Show the footer info icon. */
16
+ showFooterIcon?: boolean;
17
+ /** Show the bottom inline nudge. */
18
+ showNudge?: boolean;
19
+ /** Body text for the default bottom nudge. */
20
+ nudgeBody?: string;
21
+ /** Button label for the default bottom nudge. */
22
+ nudgeButtonLabel?: string;
23
+ /** Called when the nudge button is pressed. */
24
+ onPressNudgeButton?: () => void;
25
+ /** Called when the rating tier row is pressed. */
26
+ onTierPress?: () => void;
27
+ /** Optional footer slot. Receives `modes` recursively. */
28
+ footerSlot?: React.ReactNode;
29
+ /** Optional nudge slot. Receives `modes` recursively. */
30
+ nudgeSlot?: React.ReactNode;
31
+ /** Design token modes forwarded to token lookups and child components. */
32
+ modes?: Record<string, any>;
33
+ /** Optional container style overrides. */
34
+ style?: StyleProp<ViewStyle>;
35
+ /** Optional rating ring wrapper style overrides. */
36
+ ratingStyle?: StyleProp<ViewStyle>;
37
+ /** Optional footer row style overrides. */
38
+ footerStyle?: StyleProp<ViewStyle>;
39
+ /** Optional footer text style overrides. */
40
+ footerTextStyle?: StyleProp<TextStyle>;
41
+ /** Optional nudge style overrides. */
42
+ nudgeStyle?: StyleProp<ViewStyle>;
43
+ /** Accessibility label for the whole component. */
44
+ accessibilityLabel?: string;
45
+ };
46
+ declare function CircularRating({ value, dotCount, label, tierLabel, footerText, showFooterIcon, showNudge, nudgeBody, nudgeButtonLabel, onPressNudgeButton, onTierPress, footerSlot, nudgeSlot, modes: propModes, style, ratingStyle, footerStyle, footerTextStyle, nudgeStyle, accessibilityLabel, ...rest }: CircularRatingProps): import("react/jsx-runtime").JSX.Element;
47
+ declare const _default: React.MemoExoticComponent<typeof CircularRating>;
48
+ export default _default;
49
+ //# sourceMappingURL=CircularRating.d.ts.map
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { View, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
3
+ import { type SupportTextProps } from '../SupportText/SupportText';
4
+ type GaugeBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>;
5
+ export type GaugeProps = GaugeBaseProps & {
6
+ /** Current gauge value. Interpreted against `min` and `max`. */
7
+ value?: number;
8
+ /** Lower bound used to normalize progress. */
9
+ min?: number;
10
+ /** Upper bound used to normalize progress. */
11
+ max?: number;
12
+ /** Optional formatted value shown in the default readout. */
13
+ valueLabel?: string;
14
+ /** Heading above the arc. */
15
+ title?: string;
16
+ /** Caption below the arc. */
17
+ caption?: string;
18
+ /** Support text shown in the default readout. */
19
+ supportText?: string;
20
+ /** Status passed to the default SupportText component. */
21
+ supportTextStatus?: SupportTextProps['status'];
22
+ /** Hides the heading while keeping the rest of the layout intact. */
23
+ showTitle?: boolean;
24
+ /** Hides the caption while keeping the rest of the layout intact. */
25
+ showCaption?: boolean;
26
+ /** Hides default support text when no custom readout slot is provided. */
27
+ showSupportText?: boolean;
28
+ /** Design token modes forwarded to token lookups and slot children. */
29
+ modes?: Record<string, any>;
30
+ /** Slot rendered in the center of the gauge arc. Receives `modes` recursively. */
31
+ children?: React.ReactNode;
32
+ /** Container style override. */
33
+ style?: StyleProp<ViewStyle>;
34
+ /** Arc wrapper style override. */
35
+ arcStyle?: StyleProp<ViewStyle>;
36
+ /** Readout container style override. */
37
+ readoutStyle?: StyleProp<ViewStyle>;
38
+ /** Title text style override. */
39
+ titleStyle?: StyleProp<TextStyle>;
40
+ /** Value text style override. */
41
+ valueStyle?: StyleProp<TextStyle>;
42
+ /** Caption text style override. */
43
+ captionStyle?: StyleProp<TextStyle>;
44
+ /** Track stroke style override. */
45
+ trackStyle?: StyleProp<ViewStyle>;
46
+ /** Progress stroke style override. */
47
+ progressStyle?: StyleProp<ViewStyle>;
48
+ /** Accessibility label for the whole gauge. */
49
+ accessibilityLabel?: string;
50
+ };
51
+ declare function Gauge({ value, min, max, valueLabel, title, caption, supportText, supportTextStatus, showTitle, showCaption, showSupportText, modes: propModes, children, style, arcStyle, readoutStyle, titleStyle, valueStyle, captionStyle, trackStyle, progressStyle, accessibilityLabel, ...rest }: GaugeProps): import("react/jsx-runtime").JSX.Element;
52
+ export default Gauge;
53
+ //# sourceMappingURL=Gauge.d.ts.map
@@ -1,31 +1,34 @@
1
1
  import React from 'react';
2
2
  import { type ViewStyle, type StyleProp } from 'react-native';
3
+ export type NudgeType = 'stacked-prominent' | 'stacked-detailed' | 'inline-compact';
3
4
  export type NudgeProps = {
4
5
  /**
5
- * Controls the layout variant.
6
- * - "Default": horizontal layout with optional start icon and content (title/body/button or children slot)
7
- * - "Variant2": vertical layout with header (icon + title) and children slot below
6
+ * Controls the layout type.
7
+ * - "stacked-prominent": icon + title/body/button content
8
+ * - "inline-compact": icon + body/button in one row
9
+ * - "stacked-detailed": header + children detail slot
8
10
  */
9
- variant?: 'Default' | 'Variant2';
11
+ type?: NudgeType;
10
12
  /** Title text displayed in the nudge */
11
13
  title?: string;
12
- /** Body text displayed below the title (Default variant only, when no children are provided) */
14
+ /** Body text displayed in prominent and compact types when no children are provided */
13
15
  body?: string;
14
- /** Label for the default button (Default variant only, when no children are provided) */
16
+ /** Label for the default button when no buttonSlot is provided */
15
17
  buttonLabel?: string;
16
18
  /** Callback for the default button press */
17
19
  onPressButton?: () => void;
18
- /** Custom button slot (Default variant only, overrides buttonLabel/onPressButton) */
20
+ /** Custom button slot, overrides buttonLabel/onPressButton */
19
21
  buttonSlot?: React.ReactNode;
20
- /** Optional leading slot for an icon/element. Pass null or omit to hide. */
21
- startSlot?: React.ReactNode;
22
- /** Content slot — overrides the default title/body/button content */
22
+ /** Optional leading slot. Omit for the token-driven sparkle icon, pass null/false to hide. */
23
+ startSlot?: React.ReactNode | false;
24
+ /** Content slot — overrides default content, or provides detailed list content */
23
25
  children?: React.ReactNode;
24
26
  /** Mode configuration for design token resolution */
25
27
  modes?: Record<string, any>;
26
28
  /** Optional container style overrides */
27
29
  style?: StyleProp<ViewStyle>;
28
30
  };
29
- declare function Nudge({ variant, title, body, buttonLabel, onPressButton, buttonSlot, startSlot, children, modes: propModes, style, }: NudgeProps): import("react/jsx-runtime").JSX.Element;
31
+ declare function NudgeImpl({ type, title, body, buttonLabel, onPressButton, buttonSlot, startSlot, children, modes: propModes, style, }: NudgeProps): import("react/jsx-runtime").JSX.Element;
32
+ declare const Nudge: React.MemoExoticComponent<typeof NudgeImpl>;
30
33
  export default Nudge;
31
34
  //# sourceMappingURL=Nudge.d.ts.map
@@ -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';
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-04-23T10:31:30.721Z
7
+ * Generated: 2026-04-30T09:22:52.462Z
8
8
  */
9
9
  export declare const iconRegistry: Record<string, {
10
10
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfs-components",
3
- "version": "0.0.70",
3
+ "version": "0.0.71",
4
4
  "description": "React Native Jio Finance Components Library",
5
5
  "author": "sunshuaiqi@gmail.com",
6
6
  "license": "MIT",
@@ -0,0 +1,283 @@
1
+ import React, { useMemo } from 'react'
2
+ import {
3
+ Text,
4
+ View,
5
+ type StyleProp,
6
+ type TextStyle,
7
+ type ViewStyle,
8
+ } from 'react-native'
9
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+ import { useTokens } from '../../design-tokens/JFSThemeProvider'
11
+ import Icon from '../../icons/Icon'
12
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils'
13
+ import CircularProgressBar from '../CircularProgressBar/CircularProgressBar'
14
+ import Nudge from '../Nudge/Nudge'
15
+
16
+ type CardAdvisoryBaseProps = Omit<React.ComponentProps<typeof View>, 'children' | 'style'>
17
+
18
+ export type CardAdvisoryProps = CardAdvisoryBaseProps & {
19
+ /** Main heading text. */
20
+ title?: string
21
+ /** Description shown below the heading. */
22
+ description?: string
23
+ /** Progress score displayed in the circular progress bar. */
24
+ value?: number
25
+ /** Optional formatted score label. */
26
+ valueLabel?: string
27
+ /** Show the info icon beside the title. */
28
+ showInfoIcon?: boolean
29
+ /** Show the bottom advisory nudge. */
30
+ showNudge?: boolean
31
+ /** Body text for the default nudge. */
32
+ nudgeBody?: string
33
+ /** Button label for the default nudge. */
34
+ nudgeButtonLabel?: string
35
+ /** Callback for the default nudge button. */
36
+ onPressNudgeButton?: () => void
37
+ /** Optional slot replacing the title info icon. Receives `modes` recursively. */
38
+ titleEndSlot?: React.ReactNode
39
+ /** Optional slot replacing the circular progress bar. Receives `modes` recursively. */
40
+ progressSlot?: React.ReactNode
41
+ /** Optional slot replacing the bottom nudge. Receives `modes` recursively. */
42
+ nudgeSlot?: React.ReactNode
43
+ /** Design token modes forwarded to token lookups and child components. */
44
+ modes?: Record<string, any>
45
+ /** Optional container style override. */
46
+ style?: StyleProp<ViewStyle>
47
+ /** Optional main content row style override. */
48
+ mainContentStyle?: StyleProp<ViewStyle>
49
+ /** Optional title text style override. */
50
+ titleStyle?: StyleProp<TextStyle>
51
+ /** Optional description text style override. */
52
+ descriptionStyle?: StyleProp<TextStyle>
53
+ /** Optional progress wrapper style override. */
54
+ progressStyle?: StyleProp<ViewStyle>
55
+ /** Optional nudge style override. */
56
+ nudgeStyle?: StyleProp<ViewStyle>
57
+ /** Accessibility label for the full card. */
58
+ accessibilityLabel?: string
59
+ }
60
+
61
+ interface CardAdvisoryTokens {
62
+ containerStyle: ViewStyle
63
+ mainContentStyle: ViewStyle
64
+ contentStyle: ViewStyle
65
+ headerStyle: ViewStyle
66
+ titleStyle: TextStyle
67
+ descriptionStyle: TextStyle
68
+ iconColor: string
69
+ iconSize: number
70
+ }
71
+
72
+ const toNumber = (value: unknown, fallback: number) => {
73
+ if (typeof value === 'number') {
74
+ return Number.isFinite(value) ? value : fallback
75
+ }
76
+
77
+ if (typeof value === 'string') {
78
+ const parsed = Number(value)
79
+ return Number.isFinite(parsed) ? parsed : fallback
80
+ }
81
+
82
+ return fallback
83
+ }
84
+
85
+ const toFontWeight = (value: unknown, fallback: TextStyle['fontWeight']) => {
86
+ if (typeof value === 'number') {
87
+ return String(value) as TextStyle['fontWeight']
88
+ }
89
+
90
+ if (typeof value === 'string') {
91
+ return value as TextStyle['fontWeight']
92
+ }
93
+
94
+ return fallback
95
+ }
96
+
97
+ function resolveCardAdvisoryTokens(modes: Record<string, any>): CardAdvisoryTokens {
98
+ const width = toNumber(getVariableByName('cardAdvisory/width', modes), 360)
99
+ const gap = toNumber(getVariableByName('cardAdvisory/gap', modes), 16)
100
+ const paddingHorizontal = toNumber(getVariableByName('cardAdvisory/padding/horizontal', modes), 16)
101
+ const paddingVertical = toNumber(getVariableByName('cardAdvisory/padding/vertical', modes), 12)
102
+ const radius = toNumber(getVariableByName('cardAdvisory/radius', modes), 0)
103
+ const background = getVariableByName('cardAdvisory/background', modes) || '#ffffff'
104
+
105
+ const mainContentGap = toNumber(getVariableByName('cardAdvisory/mainContent/gap', modes), 16)
106
+ const contentGap = toNumber(getVariableByName('cardAdvisory/content/gap', modes), 6)
107
+ const headerGap = toNumber(getVariableByName('cardAdvisory/header/gap', modes), 8)
108
+
109
+ const titleColor = getVariableByName('cardAdvisory/title/foreground', modes) || '#0d0d0f'
110
+ const titleFontSize = toNumber(getVariableByName('cardAdvisory/title/fontSize', modes), 26)
111
+ const titleFontFamily = getVariableByName('cardAdvisory/title/fontFamily', modes) || 'JioType Var'
112
+ const titleLineHeight = toNumber(getVariableByName('cardAdvisory/title/lineHeight', modes), 26)
113
+ const titleFontWeight = toFontWeight(getVariableByName('cardAdvisory/title/fontWeight', modes), '900')
114
+ const titleDescenderAllowance = Math.ceil(titleFontSize * 0.16)
115
+
116
+ const descriptionColor = getVariableByName('cardAdvisory/description/foreground', modes) || '#24262b'
117
+ const descriptionFontSize = toNumber(getVariableByName('cardAdvisory/description/fontSize', modes), 12)
118
+ const descriptionFontFamily = getVariableByName('cardAdvisory/description/fontFamily', modes) || 'JioType Var'
119
+ const descriptionLineHeight = toNumber(getVariableByName('cardAdvisory/description/lineHeight', modes), 16)
120
+ const descriptionFontWeight = toFontWeight(
121
+ getVariableByName('cardAdvisory/description/fontWeight', modes),
122
+ '500'
123
+ )
124
+
125
+ return {
126
+ containerStyle: {
127
+ alignItems: 'flex-start',
128
+ backgroundColor: background as string,
129
+ borderRadius: radius,
130
+ gap,
131
+ overflow: 'hidden',
132
+ paddingHorizontal,
133
+ paddingVertical,
134
+ width,
135
+ },
136
+ mainContentStyle: {
137
+ alignItems: 'flex-start',
138
+ flexDirection: 'row',
139
+ gap: mainContentGap,
140
+ width: '100%',
141
+ },
142
+ contentStyle: {
143
+ alignItems: 'flex-start',
144
+ flex: 1,
145
+ gap: contentGap,
146
+ minWidth: 1,
147
+ },
148
+ headerStyle: {
149
+ alignItems: 'center',
150
+ flexDirection: 'row',
151
+ gap: headerGap,
152
+ width: '100%',
153
+ },
154
+ titleStyle: {
155
+ color: titleColor as string,
156
+ fontFamily: titleFontFamily as string,
157
+ fontSize: titleFontSize,
158
+ fontWeight: titleFontWeight,
159
+ lineHeight: titleLineHeight,
160
+ marginBottom: -titleDescenderAllowance,
161
+ paddingBottom: titleDescenderAllowance,
162
+ },
163
+ descriptionStyle: {
164
+ color: descriptionColor as string,
165
+ fontFamily: descriptionFontFamily as string,
166
+ fontSize: descriptionFontSize,
167
+ fontWeight: descriptionFontWeight,
168
+ lineHeight: descriptionLineHeight,
169
+ width: '100%',
170
+ },
171
+ iconColor: (getVariableByName('cardAdvisory/icon/color', modes) || '#1a1c1f') as string,
172
+ iconSize: toNumber(getVariableByName('cardAdvisory/icon/size', modes), 18),
173
+ }
174
+ }
175
+
176
+ function CardAdvisory({
177
+ title = 'Spending',
178
+ description = 'Track your spending habits and stay within your budget.',
179
+ value = 70,
180
+ valueLabel,
181
+ showInfoIcon = true,
182
+ showNudge = true,
183
+ nudgeBody = 'Data confidence is low, add more accounts for better insights.',
184
+ nudgeButtonLabel = 'Button',
185
+ onPressNudgeButton,
186
+ titleEndSlot,
187
+ progressSlot,
188
+ nudgeSlot,
189
+ modes: propModes = EMPTY_MODES,
190
+ style,
191
+ mainContentStyle,
192
+ titleStyle,
193
+ descriptionStyle,
194
+ progressStyle,
195
+ nudgeStyle,
196
+ accessibilityLabel,
197
+ ...rest
198
+ }: CardAdvisoryProps) {
199
+ const { modes: globalModes } = useTokens()
200
+ const modes = useMemo(
201
+ () => (globalModes === EMPTY_MODES && propModes === EMPTY_MODES
202
+ ? EMPTY_MODES
203
+ : { ...globalModes, ...propModes }),
204
+ [globalModes, propModes]
205
+ )
206
+ const tokens = useMemo(() => resolveCardAdvisoryTokens(modes), [modes])
207
+
208
+ const processedTitleEndSlot = useMemo(() => {
209
+ if (!titleEndSlot) return null
210
+ const processed = cloneChildrenWithModes(React.Children.toArray(titleEndSlot), modes)
211
+ return processed.length === 1 ? processed[0] : processed
212
+ }, [titleEndSlot, modes])
213
+
214
+ const processedProgressSlot = useMemo(() => {
215
+ if (!progressSlot) return null
216
+ const processed = cloneChildrenWithModes(React.Children.toArray(progressSlot), modes)
217
+ return processed.length === 1 ? processed[0] : processed
218
+ }, [progressSlot, modes])
219
+
220
+ const processedNudgeSlot = useMemo(() => {
221
+ if (!nudgeSlot) return null
222
+ const processed = cloneChildrenWithModes(React.Children.toArray(nudgeSlot), modes)
223
+ return processed.length === 1 ? processed[0] : processed
224
+ }, [nudgeSlot, modes])
225
+
226
+ const defaultAccessibilityLabel =
227
+ accessibilityLabel ?? `${title}. ${description}. ${Math.round(value)} out of 100. ${nudgeBody}`
228
+
229
+ return (
230
+ <View
231
+ accessibilityLabel={defaultAccessibilityLabel}
232
+ style={[tokens.containerStyle, style]}
233
+ {...rest}
234
+ >
235
+ <View style={[tokens.mainContentStyle, mainContentStyle]}>
236
+ <View style={tokens.contentStyle}>
237
+ <View style={tokens.headerStyle}>
238
+ <Text numberOfLines={1} style={[tokens.titleStyle, titleStyle]}>
239
+ {title}
240
+ </Text>
241
+ {processedTitleEndSlot || (showInfoIcon ? (
242
+ <Icon
243
+ name="ic_info"
244
+ size={tokens.iconSize}
245
+ color={tokens.iconColor}
246
+ accessibilityElementsHidden={true}
247
+ importantForAccessibility="no"
248
+ />
249
+ ) : null)}
250
+ </View>
251
+ <Text style={[tokens.descriptionStyle, descriptionStyle]}>
252
+ {description}
253
+ </Text>
254
+ </View>
255
+
256
+ {processedProgressSlot || (
257
+ <CircularProgressBar
258
+ state="Active"
259
+ value={value}
260
+ modes={modes}
261
+ style={progressStyle}
262
+ {...(valueLabel ? { valueLabel } : {})}
263
+ />
264
+ )}
265
+ </View>
266
+
267
+ {showNudge ? (
268
+ processedNudgeSlot || (
269
+ <Nudge
270
+ type="inline-compact"
271
+ body={nudgeBody}
272
+ buttonLabel={nudgeButtonLabel}
273
+ modes={modes}
274
+ style={[{ width: '100%' }, nudgeStyle]}
275
+ {...(onPressNudgeButton ? { onPressButton: onPressNudgeButton } : {})}
276
+ />
277
+ )
278
+ ) : null}
279
+ </View>
280
+ )
281
+ }
282
+
283
+ export default React.memo(CardAdvisory)