jfs-components 0.0.73 → 0.0.77

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 (134) hide show
  1. package/CHANGELOG.md +115 -6
  2. package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
  3. package/lib/commonjs/components/ActionFooter/ActionFooter.js +147 -82
  4. package/lib/commonjs/components/AppBar/AppBar.js +17 -11
  5. package/lib/commonjs/components/Avatar/Avatar.js +20 -0
  6. package/lib/commonjs/components/Badge/Badge.js +23 -0
  7. package/lib/commonjs/components/Button/Button.js +37 -0
  8. package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +18 -2
  9. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +40 -25
  10. package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
  11. package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
  12. package/lib/commonjs/components/FormField/FormField.js +328 -178
  13. package/lib/commonjs/components/IconButton/IconButton.js +20 -0
  14. package/lib/commonjs/components/Image/Image.js +26 -1
  15. package/lib/commonjs/components/LottieIntroBlock/LottieIntroBlock.js +150 -0
  16. package/lib/commonjs/components/LottiePlayer/LottiePlayer.js +116 -0
  17. package/lib/commonjs/components/LottiePlayer/LottiePlayer.web.js +82 -0
  18. package/lib/commonjs/components/LottiePlayer/loadNativeLottieView.js +74 -0
  19. package/lib/commonjs/components/LottiePlayer/loadWebLottieView.js +50 -0
  20. package/lib/commonjs/components/PageHero/PageHero.js +189 -0
  21. package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
  22. package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
  23. package/lib/commonjs/components/RechargeCard/RechargeCard.js +32 -17
  24. package/lib/commonjs/components/Text/Text.js +40 -3
  25. package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
  26. package/lib/commonjs/components/index.js +67 -0
  27. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  28. package/lib/commonjs/icons/Icon.js +16 -0
  29. package/lib/commonjs/icons/registry.js +1 -1
  30. package/lib/commonjs/index.js +12 -0
  31. package/lib/commonjs/skeleton/Skeleton.js +234 -0
  32. package/lib/commonjs/skeleton/SkeletonGroup.js +140 -0
  33. package/lib/commonjs/skeleton/index.js +58 -0
  34. package/lib/commonjs/skeleton/shimmer-tokens.js +189 -0
  35. package/lib/commonjs/skeleton/useReducedMotion.js +64 -0
  36. package/lib/module/components/AccountCard/AccountCard.js +241 -0
  37. package/lib/module/components/ActionFooter/ActionFooter.js +146 -82
  38. package/lib/module/components/AppBar/AppBar.js +17 -11
  39. package/lib/module/components/Avatar/Avatar.js +19 -0
  40. package/lib/module/components/Badge/Badge.js +23 -0
  41. package/lib/module/components/Button/Button.js +37 -0
  42. package/lib/module/components/CardBankAccount/CardBankAccount.js +17 -2
  43. package/lib/module/components/CheckboxItem/CheckboxItem.js +41 -26
  44. package/lib/module/components/Dropdown/Dropdown.js +206 -0
  45. package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
  46. package/lib/module/components/FormField/FormField.js +330 -180
  47. package/lib/module/components/IconButton/IconButton.js +20 -0
  48. package/lib/module/components/Image/Image.js +25 -1
  49. package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
  50. package/lib/module/components/LottiePlayer/LottiePlayer.js +111 -0
  51. package/lib/module/components/LottiePlayer/LottiePlayer.web.js +77 -0
  52. package/lib/module/components/LottiePlayer/loadNativeLottieView.js +69 -0
  53. package/lib/module/components/LottiePlayer/loadWebLottieView.js +45 -0
  54. package/lib/module/components/PageHero/PageHero.js +183 -0
  55. package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
  56. package/lib/module/components/PoweredByLabel/finvu.png +0 -0
  57. package/lib/module/components/RechargeCard/RechargeCard.js +33 -17
  58. package/lib/module/components/Text/Text.js +40 -3
  59. package/lib/module/components/Tooltip/Tooltip.js +34 -27
  60. package/lib/module/components/index.js +8 -1
  61. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  62. package/lib/module/icons/Icon.js +16 -0
  63. package/lib/module/icons/registry.js +1 -1
  64. package/lib/module/index.js +2 -1
  65. package/lib/module/skeleton/Skeleton.js +229 -0
  66. package/lib/module/skeleton/SkeletonGroup.js +133 -0
  67. package/lib/module/skeleton/index.js +6 -0
  68. package/lib/module/skeleton/shimmer-tokens.js +181 -0
  69. package/lib/module/skeleton/useReducedMotion.js +61 -0
  70. package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
  71. package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +26 -21
  72. package/lib/typescript/src/components/Avatar/Avatar.d.ts +7 -1
  73. package/lib/typescript/src/components/Badge/Badge.d.ts +7 -1
  74. package/lib/typescript/src/components/Button/Button.d.ts +8 -1
  75. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +9 -2
  76. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +18 -2
  77. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
  78. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
  79. package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
  80. package/lib/typescript/src/components/IconButton/IconButton.d.ts +7 -1
  81. package/lib/typescript/src/components/Image/Image.d.ts +8 -1
  82. package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
  83. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.d.ts +85 -0
  84. package/lib/typescript/src/components/LottiePlayer/LottiePlayer.web.d.ts +28 -0
  85. package/lib/typescript/src/components/LottiePlayer/loadNativeLottieView.d.ts +11 -0
  86. package/lib/typescript/src/components/LottiePlayer/loadWebLottieView.d.ts +11 -0
  87. package/lib/typescript/src/components/PageHero/PageHero.d.ts +79 -0
  88. package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
  89. package/lib/typescript/src/components/Text/Text.d.ts +31 -2
  90. package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
  91. package/lib/typescript/src/components/index.d.ts +8 -1
  92. package/lib/typescript/src/icons/Icon.d.ts +7 -1
  93. package/lib/typescript/src/icons/registry.d.ts +1 -1
  94. package/lib/typescript/src/index.d.ts +1 -0
  95. package/lib/typescript/src/skeleton/Skeleton.d.ts +60 -0
  96. package/lib/typescript/src/skeleton/SkeletonGroup.d.ts +78 -0
  97. package/lib/typescript/src/skeleton/index.d.ts +5 -0
  98. package/lib/typescript/src/skeleton/shimmer-tokens.d.ts +160 -0
  99. package/lib/typescript/src/skeleton/useReducedMotion.d.ts +15 -0
  100. package/package.json +11 -3
  101. package/src/components/AccountCard/AccountCard.tsx +376 -0
  102. package/src/components/ActionFooter/ActionFooter.tsx +152 -86
  103. package/src/components/AppBar/AppBar.tsx +25 -14
  104. package/src/components/Avatar/Avatar.tsx +26 -0
  105. package/src/components/Badge/Badge.tsx +27 -0
  106. package/src/components/Button/Button.tsx +40 -0
  107. package/src/components/CardBankAccount/CardBankAccount.tsx +29 -3
  108. package/src/components/CheckboxItem/CheckboxItem.tsx +65 -30
  109. package/src/components/Dropdown/Dropdown.tsx +331 -0
  110. package/src/components/DropdownInput/DropdownInput.tsx +819 -0
  111. package/src/components/FormField/FormField.tsx +542 -215
  112. package/src/components/IconButton/IconButton.tsx +27 -0
  113. package/src/components/Image/Image.tsx +25 -0
  114. package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
  115. package/src/components/LottiePlayer/LottiePlayer.tsx +145 -0
  116. package/src/components/LottiePlayer/LottiePlayer.web.tsx +94 -0
  117. package/src/components/LottiePlayer/loadNativeLottieView.tsx +87 -0
  118. package/src/components/LottiePlayer/loadWebLottieView.tsx +64 -0
  119. package/src/components/PageHero/PageHero.tsx +257 -0
  120. package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
  121. package/src/components/PoweredByLabel/finvu.png +0 -0
  122. package/src/components/RechargeCard/RechargeCard.tsx +32 -24
  123. package/src/components/Text/Text.tsx +78 -3
  124. package/src/components/Tooltip/Tooltip.tsx +50 -25
  125. package/src/components/index.ts +16 -1
  126. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  127. package/src/icons/Icon.tsx +17 -0
  128. package/src/icons/registry.ts +1 -1
  129. package/src/index.ts +1 -0
  130. package/src/skeleton/Skeleton.tsx +298 -0
  131. package/src/skeleton/SkeletonGroup.tsx +193 -0
  132. package/src/skeleton/index.ts +10 -0
  133. package/src/skeleton/shimmer-tokens.ts +221 -0
  134. package/src/skeleton/useReducedMotion.ts +72 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Single source of truth for the skeleton shimmer behaviour spec.
3
+ *
4
+ * The two modes mirror the documented design behaviour:
5
+ *
6
+ * Normal:
7
+ * - 1.5s linear sawtooth cycle (band sweeps end-to-end then resets)
8
+ * - 135° gradient (top-left -> bottom-right), fixed angle on any aspect
9
+ * ratio via SVG userSpaceOnUse coordinates
10
+ * - Band alpha animates 33% -> 100% -> 33% across the band core;
11
+ * transparent fades at the gradient edges hide the sawtooth reset
12
+ * - 50–100ms per-item stagger
13
+ *
14
+ * Reduced motion (system-level OS toggle):
15
+ * - 3.0s ease-in-out cycle
16
+ * - No translation — solid white overlay opacity pulses 33% -> 100% in
17
+ * place (`opacityRange` below is consumed in this path)
18
+ * - No gradient
19
+ * - No stagger
20
+ *
21
+ * Centralising the spec here keeps `Skeleton.tsx` purely presentational and
22
+ * lets us tune the behaviour in one place if design ever updates it.
23
+ */
24
+
25
+ export type SkeletonKind = 'text' | 'image' | 'badge' | 'other';
26
+
27
+ export interface ShimmerModeSpec {
28
+ durationMs: number;
29
+ /** Per-item delay range. Min/max in milliseconds. */
30
+ staggerMsRange: readonly [number, number];
31
+ gradient: boolean;
32
+ /** Opacity range applied to the moving "blob" overlay. */
33
+ opacityRange: readonly [number, number];
34
+ }
35
+
36
+ export const SHIMMER = {
37
+ normal: {
38
+ durationMs: 1500,
39
+ staggerMsRange: [50, 100] as const,
40
+ gradient: true,
41
+ opacityRange: [0.33, 1.0] as const,
42
+ } satisfies ShimmerModeSpec,
43
+ reduced: {
44
+ durationMs: 3000,
45
+ staggerMsRange: [0, 0] as const,
46
+ gradient: false,
47
+ opacityRange: [0.33, 1.0] as const,
48
+ } satisfies ShimmerModeSpec,
49
+ /**
50
+ * Gradient angle in degrees, measured the CSS way: 0deg points "up", 90deg
51
+ * "right", 135deg therefore points down-right (top-left corner to
52
+ * bottom-right corner).
53
+ */
54
+ gradientAngleDeg: 135,
55
+ /**
56
+ * Hard cap on the cumulative stagger delay so very long lists don't drift
57
+ * out forever; the wave wraps after this many ms.
58
+ */
59
+ maxStaggerMs: 600,
60
+ } as const
61
+
62
+ /**
63
+ * Token names — referenced via `getVariableByName(...)` so the existing
64
+ * design-token resolver does its job (caching, mode resolution, etc.).
65
+ *
66
+ * The four tokens already live in the Figma export at
67
+ * `src/design-tokens/Coin Variables-variables-full.json`:
68
+ *
69
+ * - `bg/defaultSkeleton` -> base color for every skeleton block
70
+ * - `cornerRadius/defaultSkeleton` -> text & "other" (pill: 9999)
71
+ * - `cornerRadius/imageSkeleton` -> images (10)
72
+ * - `cornerRadius/badgeSkeleton` -> badges (4)
73
+ */
74
+ export const SKELETON_TOKEN = {
75
+ background: 'bg/defaultSkeleton',
76
+ radius: {
77
+ text: 'cornerRadius/defaultSkeleton',
78
+ image: 'cornerRadius/imageSkeleton',
79
+ badge: 'cornerRadius/badgeSkeleton',
80
+ other: 'cornerRadius/defaultSkeleton',
81
+ } satisfies Record<SkeletonKind, string>,
82
+ } as const
83
+
84
+ /**
85
+ * Fallback constants used when the token resolver is unavailable (tests,
86
+ * SSR, etc.). Match the current Figma values.
87
+ */
88
+ export const SKELETON_FALLBACK = {
89
+ backgroundColor: 'rgb(245, 245, 246)',
90
+ radius: {
91
+ text: 9999,
92
+ image: 10,
93
+ badge: 4,
94
+ other: 9999,
95
+ } satisfies Record<SkeletonKind, number>,
96
+ } as const
97
+
98
+ /**
99
+ * Compute a stable per-item delay from a 0-based registration index.
100
+ *
101
+ * We pick the midpoint of the documented range (75ms) so the cascade is
102
+ * deterministic for snapshot tests and visually identical between renders.
103
+ * The total delay is then wrapped at `maxStaggerMs` so deep lists don't
104
+ * drift past the cap.
105
+ */
106
+ export function staggerDelayMs(index: number, mode: ShimmerModeSpec): number {
107
+ const [min, max] = mode.staggerMsRange
108
+ if (max <= 0) return 0
109
+ const step = (min + max) / 2
110
+ return (index * step) % SHIMMER.maxStaggerMs
111
+ }
112
+
113
+ /** One stop on the moving shimmer gradient (offset 0–1 along the 135° axis). */
114
+ export interface ShimmerGradientStop {
115
+ offset: number
116
+ opacity: number
117
+ }
118
+
119
+ /**
120
+ * Gradient stops for the normal-mode moving band. The peak sits at 0.5; fully
121
+ * transparent tails at 0 and 1. The 0.30 / 0.70 stops mark where the band
122
+ * reaches the documented 33 % alpha.
123
+ */
124
+ export const SHIMMER_GRADIENT_STOPS: readonly ShimmerGradientStop[] = [
125
+ { offset: 0, opacity: 0 },
126
+ { offset: 0.30, opacity: 0.33 },
127
+ { offset: 0.50, opacity: 1 },
128
+ { offset: 0.70, opacity: 0.33 },
129
+ { offset: 1.0, opacity: 0 },
130
+ ] as const
131
+
132
+ /** Offset (0–1) of the brightest point on the gradient line. */
133
+ export function gradientPeakOffset(
134
+ stops: readonly ShimmerGradientStop[] = SHIMMER_GRADIENT_STOPS,
135
+ ): number {
136
+ const peak = stops.find((s) => s.opacity === 1)
137
+ return peak?.offset ?? 0.5
138
+ }
139
+
140
+ /**
141
+ * How far the gradient extends from the peak to a fully transparent stop,
142
+ * expressed as a fraction of the gradient line (0–1). With the default stops
143
+ * this is 0.5 (peak at 0.5, transparent at 0 and 1).
144
+ *
145
+ * This value drives the overshoot: the band must travel this fraction of a
146
+ * full corner-to-corner sweep *beyond* each corner so the soft transparent
147
+ * tails fully clear the box before the sawtooth reset.
148
+ */
149
+ export function gradientTransparentExtent(
150
+ stops: readonly ShimmerGradientStop[] = SHIMMER_GRADIENT_STOPS,
151
+ ): number {
152
+ const peak = gradientPeakOffset(stops)
153
+ const transparent = stops.filter((s) => s.opacity === 0)
154
+ if (transparent.length === 0) return 0.5
155
+ return Math.max(...transparent.map((s) => Math.abs(s.offset - peak)))
156
+ }
157
+
158
+ export interface ShimmerMotionGeometry {
159
+ /** Square overlay side length (gradient is painted on this). */
160
+ overlaySize: number
161
+ /** Padding to centre the overlay on the box. */
162
+ padX: number
163
+ padY: number
164
+ /** Translation (k, k) when the band is fully off-screen before entry. */
165
+ kStart: number
166
+ /** Translation (k, k) when the band is fully off-screen after exit. */
167
+ kEnd: number
168
+ /**
169
+ * Normalised overshoot on each side of the corner-to-corner sweep. A value
170
+ * of 0.5 means the travel extends 50 % past each corner; at reset the
171
+ * clock jumps from `1 + overshoot` to `-overshoot` while the box still
172
+ * shows only the base skeleton colour.
173
+ */
174
+ overshootFraction: number
175
+ /** k distance from peak-at-TL to peak-at-BR. */
176
+ cornerTravelK: number
177
+ /** k distance from peak to fully transparent tail on the gradient. */
178
+ fadeBeyondPeakK: number
179
+ }
180
+
181
+ /**
182
+ * Derive the moving-shimmer geometry from box size, overlay size, and the
183
+ * gradient stop layout.
184
+ *
185
+ * Coordinate model (135° / down-right):
186
+ * - Peak stripe world diagonal sum: (W + H) / 2 + 2k
187
+ * - Gradient offset 0 → 1 spans 2 × overlaySize in diagonal-sum units
188
+ * - Transparent tail beyond peak: fadeExtent × 2 × overlaySize / 2
189
+ * = fadeExtent × overlaySize in k units
190
+ *
191
+ * The sawtooth clock maps linearly kStart → kEnd so t = 0 and t = 1 both
192
+ * land with the entire gradient outside the box — no jitter on reset.
193
+ */
194
+ export function computeShimmerMotion(
195
+ width: number,
196
+ height: number,
197
+ overlaySize: number,
198
+ stops: readonly ShimmerGradientStop[] = SHIMMER_GRADIENT_STOPS,
199
+ ): ShimmerMotionGeometry | null {
200
+ if (width <= 0 || height <= 0 || overlaySize <= 0) return null
201
+
202
+ const fadeExtent = gradientTransparentExtent(stops)
203
+ const cornerTravelK = (width + height) / 2
204
+ const baseSweepHalfK = cornerTravelK / 2
205
+ const fadeBeyondPeakK = fadeExtent * overlaySize
206
+ const kStart = -baseSweepHalfK - fadeBeyondPeakK
207
+ const kEnd = baseSweepHalfK + fadeBeyondPeakK
208
+ const overshootFraction =
209
+ cornerTravelK > 0 ? fadeBeyondPeakK / cornerTravelK : 0
210
+
211
+ return {
212
+ overlaySize,
213
+ padX: (overlaySize - width) / 2,
214
+ padY: (overlaySize - height) / 2,
215
+ kStart,
216
+ kEnd,
217
+ overshootFraction,
218
+ cornerTravelK,
219
+ fadeBeyondPeakK,
220
+ }
221
+ }
@@ -0,0 +1,72 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { AccessibilityInfo, Platform } from 'react-native'
3
+
4
+ /**
5
+ * Cross-platform "prefers reduced motion" hook.
6
+ *
7
+ * - Native: reads `AccessibilityInfo.isReduceMotionEnabled()` and subscribes
8
+ * to `reduceMotionChanged` events so the value stays live as the user
9
+ * toggles the OS setting.
10
+ * - Web: uses `window.matchMedia('(prefers-reduced-motion: reduce)')`,
11
+ * subscribing to its `change` event.
12
+ * - Anywhere either API is missing: returns `false` (no reduction).
13
+ *
14
+ * The hook never throws — every native API access is defensively guarded so
15
+ * the skeleton system stays safe in tests, SSR, and constrained sandboxes.
16
+ */
17
+ export function useReducedMotion(): boolean {
18
+ const [reduced, setReduced] = useState<boolean>(false)
19
+
20
+ useEffect(() => {
21
+ let cancelled = false
22
+
23
+ if (Platform.OS === 'web') {
24
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
25
+ return
26
+ }
27
+ const mql = window.matchMedia('(prefers-reduced-motion: reduce)')
28
+ const update = (matches: boolean) => {
29
+ if (!cancelled) setReduced(matches)
30
+ }
31
+ update(mql.matches)
32
+ const listener = (e: MediaQueryListEvent) => update(e.matches)
33
+ if (typeof mql.addEventListener === 'function') {
34
+ mql.addEventListener('change', listener)
35
+ return () => {
36
+ cancelled = true
37
+ mql.removeEventListener('change', listener)
38
+ }
39
+ }
40
+ const legacyMql = mql as MediaQueryList & {
41
+ addListener?: (l: (e: MediaQueryListEvent) => void) => void;
42
+ removeListener?: (l: (e: MediaQueryListEvent) => void) => void;
43
+ }
44
+ legacyMql.addListener?.(listener)
45
+ return () => {
46
+ cancelled = true
47
+ legacyMql.removeListener?.(listener)
48
+ }
49
+ }
50
+
51
+ if (typeof AccessibilityInfo?.isReduceMotionEnabled === 'function') {
52
+ AccessibilityInfo.isReduceMotionEnabled()
53
+ .then((value: boolean) => {
54
+ if (!cancelled) setReduced(!!value)
55
+ })
56
+ .catch(() => {})
57
+ }
58
+ const sub =
59
+ typeof AccessibilityInfo?.addEventListener === 'function'
60
+ ? AccessibilityInfo.addEventListener('reduceMotionChanged', (value: boolean) => {
61
+ if (!cancelled) setReduced(!!value)
62
+ })
63
+ : null
64
+
65
+ return () => {
66
+ cancelled = true
67
+ sub?.remove?.()
68
+ }
69
+ }, [])
70
+
71
+ return reduced
72
+ }