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
@@ -57,4 +57,16 @@ Object.keys(_utils).forEach(function (key) {
57
57
  }
58
58
  });
59
59
  });
60
+ var _skeleton = require("./skeleton");
61
+ Object.keys(_skeleton).forEach(function (key) {
62
+ if (key === "default" || key === "__esModule") return;
63
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
64
+ if (key in exports && exports[key] === _skeleton[key]) return;
65
+ Object.defineProperty(exports, key, {
66
+ enumerable: true,
67
+ get: function () {
68
+ return _skeleton[key];
69
+ }
70
+ });
71
+ });
60
72
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
11
+ var _figmaVariablesResolver = require("../design-tokens/figma-variables-resolver");
12
+ var _shimmerTokens = require("./shimmer-tokens");
13
+ var _SkeletonGroup = require("./SkeletonGroup");
14
+ var _jsxRuntime = require("react/jsx-runtime");
15
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
16
+ // Solid-white overlay used by reduced-motion mode (also acts as the safe
17
+ // fallback when SVG can't render or when the box hasn't been measured yet).
18
+ // Allocated once at module scope so per-render allocation is zero.
19
+ const SOLID_OVERLAY_COLOR = 'rgba(255, 255, 255, 1)';
20
+
21
+ // `pointerEvents: 'none'` lives on the style (not the deprecated prop) so it
22
+ // works on both native and React Native Web without warnings.
23
+ const absoluteFillStyle = {
24
+ ..._reactNative.StyleSheet.absoluteFillObject,
25
+ overflow: 'hidden',
26
+ pointerEvents: 'none'
27
+ };
28
+ const solidOverlayStyle = {
29
+ ..._reactNative.StyleSheet.absoluteFillObject,
30
+ backgroundColor: SOLID_OVERLAY_COLOR
31
+ };
32
+
33
+ /**
34
+ * Atomic skeleton placeholder. Renders a base rectangle (background colour +
35
+ * radius per kind) plus an animated overlay that produces the shimmer.
36
+ *
37
+ * Two visual modes share the same engine:
38
+ *
39
+ * - Normal mode (gradient: true): a soft 135° band translates diagonally
40
+ * across the box end-to-end. Its alpha inside the moving band varies
41
+ * 33% → 100% → 33% via gradient stops. The sawtooth clock travels from
42
+ * fully off-screen (−overshoot) to fully off-screen (+overshoot) so the
43
+ * transparent gradient tails clear the box before the loop resets.
44
+ *
45
+ * - Reduced-motion mode (gradient: false): a solid white overlay covers the
46
+ * box and pulses opacity 33% → 100% → 33% in place, with no translation,
47
+ * ease-in-out timing and no stagger.
48
+ *
49
+ * Critical for cross-platform consistency:
50
+ *
51
+ * - The SVG gradient uses `gradientUnits="userSpaceOnUse"` with absolute
52
+ * pixel coordinates that have Δx === Δy. This guarantees the visual angle
53
+ * is exactly 45° in screen pixels (= 135° CSS) on ANY aspect ratio. With
54
+ * the default `objectBoundingBox` units, the gradient line is normalised
55
+ * to the box and tilts off-angle on non-square boxes.
56
+ *
57
+ * - The whole animated overlay sits inside a clipping parent (`overflow:
58
+ * hidden` + `borderRadius`). Translating an `Animated.View` is GPU-cheap
59
+ * on both iOS/Android (Reanimated UI thread) and on web (CSS transforms).
60
+ *
61
+ * - The SVG `id` for the gradient is per-instance (`useId`) so multiple
62
+ * skeletons on a web page don't collide on `url(#…)` resolution.
63
+ */
64
+ function SkeletonImpl({
65
+ kind = 'other',
66
+ width,
67
+ height,
68
+ style,
69
+ modes
70
+ }) {
71
+ const {
72
+ active,
73
+ reducedMotion,
74
+ clock
75
+ } = (0, _SkeletonGroup.useSkeleton)();
76
+ const index = (0, _SkeletonGroup.useStaggerIndex)();
77
+ const reactId = (0, _react.useId)();
78
+ // SVG ids must match `^[A-Za-z][A-Za-z0-9_.-]*$`; React's useId returns
79
+ // colons which are illegal in fragment refs, so strip them.
80
+ const gradientId = `jfsSkeletonGradient-${reactId.replace(/[^A-Za-z0-9_-]/g, '')}`;
81
+
82
+ // Measured pixel size of the base box. The moving shimmer geometry depends
83
+ // on real layout so we capture it via onLayout. Before the first layout
84
+ // pass W and H are 0 and we render no overlay (the next render after layout
85
+ // fills it in).
86
+ const [layout, setLayout] = (0, _react.useState)({
87
+ width: 0,
88
+ height: 0
89
+ });
90
+ const radiusTokenName = _shimmerTokens.SKELETON_TOKEN.radius[kind];
91
+ const radius = (0, _figmaVariablesResolver.getVariableByName)(radiusTokenName, modes ?? {}) ?? _shimmerTokens.SKELETON_FALLBACK.radius[kind];
92
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)(_shimmerTokens.SKELETON_TOKEN.background, modes ?? {}) ?? _shimmerTokens.SKELETON_FALLBACK.backgroundColor;
93
+ const spec = reducedMotion ? _shimmerTokens.SHIMMER.reduced : _shimmerTokens.SHIMMER.normal;
94
+ // Convert the per-item stagger into a phase offset in [0, 1) (relative to
95
+ // one cycle). The sawtooth clock means a phase shift simply slides the
96
+ // start point of each skeleton's sweep, producing the cascade.
97
+ const phaseOffset = spec.durationMs > 0 ? (0, _shimmerTokens.staggerDelayMs)(index, spec) / spec.durationMs : 0;
98
+ const baseStyle = (0, _react.useMemo)(() => ({
99
+ backgroundColor,
100
+ borderRadius: radius,
101
+ overflow: 'hidden',
102
+ ...(width !== undefined ? {
103
+ width
104
+ } : {}),
105
+ ...(height !== undefined ? {
106
+ height
107
+ } : {})
108
+ }), [backgroundColor, radius, width, height]);
109
+
110
+ // --- Moving-shimmer geometry --------------------------------------------
111
+ //
112
+ // Overlay is 2× the box diagonal so the gradient still covers the clipped
113
+ // area at extreme translations. Overshoot (travel beyond each corner) is
114
+ // derived from the gradient stop layout via `computeShimmerMotion`.
115
+ const W = layout.width;
116
+ const H = layout.height;
117
+ const diag = Math.sqrt(W * W + H * H);
118
+ const overlaySize = diag * 2;
119
+ const motion = (0, _react.useMemo)(() => (0, _shimmerTokens.computeShimmerMotion)(W, H, overlaySize, _shimmerTokens.SHIMMER_GRADIENT_STOPS), [W, H, overlaySize]);
120
+ const padX = motion?.padX ?? 0;
121
+ const padY = motion?.padY ?? 0;
122
+ const kStart = motion?.kStart ?? 0;
123
+ const kEnd = motion?.kEnd ?? 0;
124
+ const kTravel = kEnd - kStart;
125
+ const overlayAnimatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
126
+ const t = (clock.value + phaseOffset) % 1;
127
+ if (spec.gradient) {
128
+ if (kTravel === 0) {
129
+ // Pre-layout: keep the overlay invisible so we don't flash a
130
+ // mis-sized gradient before onLayout fires.
131
+ return {
132
+ transform: [{
133
+ translateX: 0
134
+ }, {
135
+ translateY: 0
136
+ }],
137
+ opacity: 0
138
+ };
139
+ }
140
+ // Linear sawtooth: t = 0 → fully before entry, t = 1 → fully after exit.
141
+ // Equivalent to normalised phase p = −O + t·(1 + 2O) where O is the
142
+ // gradient-derived overshoot fraction from `computeShimmerMotion`.
143
+ const k = kStart + t * kTravel;
144
+ return {
145
+ transform: [{
146
+ translateX: k
147
+ }, {
148
+ translateY: k
149
+ }],
150
+ opacity: 1
151
+ };
152
+ }
153
+ // Reduced motion: stationary solid overlay, opacity triangle wave.
154
+ const wave = t < 0.5 ? t * 2 : (1 - t) * 2;
155
+ const [lo, hi] = spec.opacityRange;
156
+ return {
157
+ transform: [{
158
+ translateX: 0
159
+ }, {
160
+ translateY: 0
161
+ }],
162
+ opacity: (0, _reactNativeReanimated.interpolate)(wave, [0, 1], [lo, hi])
163
+ };
164
+ }, [phaseOffset, kStart, kTravel, spec.gradient, spec.opacityRange[0], spec.opacityRange[1]]);
165
+ const gradientOverlayContainerStyle = (0, _react.useMemo)(() => ({
166
+ position: 'absolute',
167
+ left: -padX,
168
+ top: -padY,
169
+ width: overlaySize,
170
+ height: overlaySize,
171
+ pointerEvents: 'none'
172
+ }), [padX, padY, overlaySize]);
173
+ if (!active) {
174
+ // Render nothing when the group is inactive. This lets users sprinkle
175
+ // <Skeleton /> blocks unconditionally inside their loaded UI without
176
+ // having to manually gate them — only the active group flips them on.
177
+ return null;
178
+ }
179
+ const onLayout = e => {
180
+ const {
181
+ width: w,
182
+ height: h
183
+ } = e.nativeEvent.layout;
184
+ if (w !== layout.width || h !== layout.height) {
185
+ setLayout({
186
+ width: w,
187
+ height: h
188
+ });
189
+ }
190
+ };
191
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
192
+ style: [baseStyle, style],
193
+ accessibilityElementsHidden: true,
194
+ importantForAccessibility: "no-hide-descendants"
195
+ // Screen readers announce a loading state once via the group's owner;
196
+ // each individual skeleton is purely decorative.
197
+ ,
198
+ accessibilityRole: "none",
199
+ onLayout: onLayout,
200
+ children: spec.gradient ? layout.width > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
201
+ style: [gradientOverlayContainerStyle, overlayAnimatedStyle],
202
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeSvg.default, {
203
+ width: overlaySize,
204
+ height: overlaySize,
205
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Defs, {
206
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.LinearGradient, {
207
+ id: gradientId,
208
+ x1: 0,
209
+ y1: 0,
210
+ x2: overlaySize,
211
+ y2: overlaySize,
212
+ gradientUnits: "userSpaceOnUse",
213
+ children: _shimmerTokens.SHIMMER_GRADIENT_STOPS.map((stop, i) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Stop, {
214
+ offset: String(stop.offset),
215
+ stopColor: "#ffffff",
216
+ stopOpacity: stop.opacity
217
+ }, `${stop.offset}-${i}`))
218
+ })
219
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Rect, {
220
+ width: overlaySize,
221
+ height: overlaySize,
222
+ fill: `url(#${gradientId})`
223
+ })]
224
+ })
225
+ }) : null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
226
+ style: [absoluteFillStyle, overlayAnimatedStyle],
227
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
228
+ style: solidOverlayStyle
229
+ })
230
+ })
231
+ });
232
+ }
233
+ const Skeleton = /*#__PURE__*/_react.default.memo(SkeletonImpl);
234
+ var _default = exports.default = Skeleton;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SkeletonGroup = SkeletonGroup;
7
+ exports.useSkeleton = useSkeleton;
8
+ exports.useStaggerIndex = useStaggerIndex;
9
+ var _react = _interopRequireWildcard(require("react"));
10
+ var _reactNativeReanimated = require("react-native-reanimated");
11
+ var _shimmerTokens = require("./shimmer-tokens");
12
+ var _useReducedMotion = require("./useReducedMotion");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
+ /**
16
+ * Shape of the value carried by the SkeletonContext.
17
+ *
18
+ * The provider owns ONE Reanimated shared value per group; every child
19
+ * `<Skeleton>` reads it on the UI thread and computes its own opacity from
20
+ * `(clock + perItemPhaseOffset)`. That keeps per-skeleton work bounded to a
21
+ * single `useAnimatedStyle` and avoids per-block JS timers.
22
+ */
23
+
24
+ const SkeletonContext = /*#__PURE__*/(0, _react.createContext)(null);
25
+ /**
26
+ * Provider that flips an entire subtree into "loading" mode and supplies the
27
+ * shared shimmer clock + stagger index source to every descendant skeleton.
28
+ *
29
+ * Usage:
30
+ * ```tsx
31
+ * <SkeletonGroup loading={!data}>
32
+ * <Card>...JFS primitives auto-skeletonize...</Card>
33
+ * </SkeletonGroup>
34
+ * ```
35
+ *
36
+ * Nesting is supported — an inner `<SkeletonGroup>` fully overrides the
37
+ * outer one for its subtree (its own clock, its own index counter).
38
+ */
39
+ function SkeletonGroup({
40
+ loading,
41
+ reducedMotion: reducedMotionOverride,
42
+ children
43
+ }) {
44
+ const systemReducedMotion = (0, _useReducedMotion.useReducedMotion)();
45
+ const reducedMotion = reducedMotionOverride ?? systemReducedMotion;
46
+ const clock = (0, _reactNativeReanimated.useSharedValue)(0);
47
+
48
+ // The duration depends on whether we're in reduced-motion mode; we
49
+ // intentionally restart the loop when either `loading` or `reducedMotion`
50
+ // changes so the new timing takes effect immediately.
51
+ (0, _react.useEffect)(() => {
52
+ if (!loading) {
53
+ (0, _reactNativeReanimated.cancelAnimation)(clock);
54
+ clock.value = 0;
55
+ return;
56
+ }
57
+ const spec = reducedMotion ? _shimmerTokens.SHIMMER.reduced : _shimmerTokens.SHIMMER.normal;
58
+ const easing = reducedMotion ? _reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.ease) : _reactNativeReanimated.Easing.linear;
59
+ clock.value = 0;
60
+ // Sawtooth loop (third arg = false). One-way ramp 0 -> 1, reset, repeat.
61
+ // The moving shimmer band traverses the box on each 0 -> 1 ramp; with the
62
+ // band's gradient fully faded at both extremes of the sweep, the reset is
63
+ // visually imperceptible.
64
+ clock.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withTiming)(1, {
65
+ duration: spec.durationMs,
66
+ easing
67
+ }), -1, false);
68
+ return () => {
69
+ (0, _reactNativeReanimated.cancelAnimation)(clock);
70
+ };
71
+ }, [loading, reducedMotion, clock]);
72
+
73
+ // Stagger index counter — reset on each (re)mount so reorders and toggles
74
+ // get a fresh, deterministic ordering. The counter lives in a ref so
75
+ // React's commit phase doesn't shuffle it.
76
+ const indexRef = (0, _react.useRef)(0);
77
+ // Reset whenever loading flips on, so a freshly-displayed group cascades
78
+ // from index 0 again rather than picking up stale numbers.
79
+ (0, _react.useEffect)(() => {
80
+ if (loading) indexRef.current = 0;
81
+ }, [loading]);
82
+ const value = (0, _react.useMemo)(() => ({
83
+ active: loading,
84
+ reducedMotion,
85
+ clock,
86
+ nextIndex: () => {
87
+ const i = indexRef.current;
88
+ indexRef.current = i + 1;
89
+ return i;
90
+ }
91
+ }), [loading, reducedMotion, clock]);
92
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(SkeletonContext.Provider, {
93
+ value: value,
94
+ children: children
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Internal hook used by skeleton-aware primitives (and by `<Skeleton>`
100
+ * itself) to read the surrounding context.
101
+ *
102
+ * Always returns a non-null value: when called outside any provider, the
103
+ * returned object has `active: false` so the consumer falls through to its
104
+ * normal render path. The fallback clock is a no-op shared value so any
105
+ * direct `<Skeleton>` usage outside a group still has something safe to read.
106
+ */
107
+ function useSkeleton() {
108
+ const ctx = (0, _react.useContext)(SkeletonContext);
109
+ // Hook order must be stable — always allocate the fallback even when ctx
110
+ // exists; React only reads from `ctx` when it's non-null below.
111
+ const fallbackClock = (0, _reactNativeReanimated.useSharedValue)(0);
112
+ const fallbackIndexRef = (0, _react.useRef)(0);
113
+ const fallback = (0, _react.useMemo)(() => ({
114
+ active: false,
115
+ reducedMotion: false,
116
+ clock: fallbackClock,
117
+ nextIndex: () => {
118
+ const i = fallbackIndexRef.current;
119
+ fallbackIndexRef.current = i + 1;
120
+ return i;
121
+ }
122
+ }), [fallbackClock]);
123
+ return ctx ?? fallback;
124
+ }
125
+
126
+ /**
127
+ * Convenience wrapper around `useSkeleton().nextIndex()` that pins the
128
+ * returned value across re-renders. Each `<Skeleton>` calls this once on
129
+ * mount; subsequent renders re-use the same index from the closure.
130
+ */
131
+ function useStaggerIndex() {
132
+ const {
133
+ nextIndex
134
+ } = useSkeleton();
135
+ const indexRef = (0, _react.useRef)(null);
136
+ if (indexRef.current === null) {
137
+ indexRef.current = nextIndex();
138
+ }
139
+ return indexRef.current;
140
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "SHIMMER", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _shimmerTokens.SHIMMER;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "SKELETON_FALLBACK", {
13
+ enumerable: true,
14
+ get: function () {
15
+ return _shimmerTokens.SKELETON_FALLBACK;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "SKELETON_TOKEN", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _shimmerTokens.SKELETON_TOKEN;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "Skeleton", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _Skeleton.default;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "SkeletonGroup", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _SkeletonGroup.SkeletonGroup;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "useReducedMotion", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _useReducedMotion.useReducedMotion;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "useSkeleton", {
43
+ enumerable: true,
44
+ get: function () {
45
+ return _SkeletonGroup.useSkeleton;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "useStaggerIndex", {
49
+ enumerable: true,
50
+ get: function () {
51
+ return _SkeletonGroup.useStaggerIndex;
52
+ }
53
+ });
54
+ var _Skeleton = _interopRequireDefault(require("./Skeleton"));
55
+ var _SkeletonGroup = require("./SkeletonGroup");
56
+ var _useReducedMotion = require("./useReducedMotion");
57
+ var _shimmerTokens = require("./shimmer-tokens");
58
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SKELETON_TOKEN = exports.SKELETON_FALLBACK = exports.SHIMMER_GRADIENT_STOPS = exports.SHIMMER = void 0;
7
+ exports.computeShimmerMotion = computeShimmerMotion;
8
+ exports.gradientPeakOffset = gradientPeakOffset;
9
+ exports.gradientTransparentExtent = gradientTransparentExtent;
10
+ exports.staggerDelayMs = staggerDelayMs;
11
+ /**
12
+ * Single source of truth for the skeleton shimmer behaviour spec.
13
+ *
14
+ * The two modes mirror the documented design behaviour:
15
+ *
16
+ * Normal:
17
+ * - 1.5s linear sawtooth cycle (band sweeps end-to-end then resets)
18
+ * - 135° gradient (top-left -> bottom-right), fixed angle on any aspect
19
+ * ratio via SVG userSpaceOnUse coordinates
20
+ * - Band alpha animates 33% -> 100% -> 33% across the band core;
21
+ * transparent fades at the gradient edges hide the sawtooth reset
22
+ * - 50–100ms per-item stagger
23
+ *
24
+ * Reduced motion (system-level OS toggle):
25
+ * - 3.0s ease-in-out cycle
26
+ * - No translation — solid white overlay opacity pulses 33% -> 100% in
27
+ * place (`opacityRange` below is consumed in this path)
28
+ * - No gradient
29
+ * - No stagger
30
+ *
31
+ * Centralising the spec here keeps `Skeleton.tsx` purely presentational and
32
+ * lets us tune the behaviour in one place if design ever updates it.
33
+ */
34
+
35
+ const SHIMMER = exports.SHIMMER = {
36
+ normal: {
37
+ durationMs: 1500,
38
+ staggerMsRange: [50, 100],
39
+ gradient: true,
40
+ opacityRange: [0.33, 1.0]
41
+ },
42
+ reduced: {
43
+ durationMs: 3000,
44
+ staggerMsRange: [0, 0],
45
+ gradient: false,
46
+ opacityRange: [0.33, 1.0]
47
+ },
48
+ /**
49
+ * Gradient angle in degrees, measured the CSS way: 0deg points "up", 90deg
50
+ * "right", 135deg therefore points down-right (top-left corner to
51
+ * bottom-right corner).
52
+ */
53
+ gradientAngleDeg: 135,
54
+ /**
55
+ * Hard cap on the cumulative stagger delay so very long lists don't drift
56
+ * out forever; the wave wraps after this many ms.
57
+ */
58
+ maxStaggerMs: 600
59
+ };
60
+
61
+ /**
62
+ * Token names — referenced via `getVariableByName(...)` so the existing
63
+ * design-token resolver does its job (caching, mode resolution, etc.).
64
+ *
65
+ * The four tokens already live in the Figma export at
66
+ * `src/design-tokens/Coin Variables-variables-full.json`:
67
+ *
68
+ * - `bg/defaultSkeleton` -> base color for every skeleton block
69
+ * - `cornerRadius/defaultSkeleton` -> text & "other" (pill: 9999)
70
+ * - `cornerRadius/imageSkeleton` -> images (10)
71
+ * - `cornerRadius/badgeSkeleton` -> badges (4)
72
+ */
73
+ const SKELETON_TOKEN = exports.SKELETON_TOKEN = {
74
+ background: 'bg/defaultSkeleton',
75
+ radius: {
76
+ text: 'cornerRadius/defaultSkeleton',
77
+ image: 'cornerRadius/imageSkeleton',
78
+ badge: 'cornerRadius/badgeSkeleton',
79
+ other: 'cornerRadius/defaultSkeleton'
80
+ }
81
+ };
82
+
83
+ /**
84
+ * Fallback constants used when the token resolver is unavailable (tests,
85
+ * SSR, etc.). Match the current Figma values.
86
+ */
87
+ const SKELETON_FALLBACK = exports.SKELETON_FALLBACK = {
88
+ backgroundColor: 'rgb(245, 245, 246)',
89
+ radius: {
90
+ text: 9999,
91
+ image: 10,
92
+ badge: 4,
93
+ other: 9999
94
+ }
95
+ };
96
+
97
+ /**
98
+ * Compute a stable per-item delay from a 0-based registration index.
99
+ *
100
+ * We pick the midpoint of the documented range (75ms) so the cascade is
101
+ * deterministic for snapshot tests and visually identical between renders.
102
+ * The total delay is then wrapped at `maxStaggerMs` so deep lists don't
103
+ * drift past the cap.
104
+ */
105
+ function staggerDelayMs(index, mode) {
106
+ const [min, max] = mode.staggerMsRange;
107
+ if (max <= 0) return 0;
108
+ const step = (min + max) / 2;
109
+ return index * step % SHIMMER.maxStaggerMs;
110
+ }
111
+
112
+ /** One stop on the moving shimmer gradient (offset 0–1 along the 135° axis). */
113
+
114
+ /**
115
+ * Gradient stops for the normal-mode moving band. The peak sits at 0.5; fully
116
+ * transparent tails at 0 and 1. The 0.30 / 0.70 stops mark where the band
117
+ * reaches the documented 33 % alpha.
118
+ */
119
+ const SHIMMER_GRADIENT_STOPS = exports.SHIMMER_GRADIENT_STOPS = [{
120
+ offset: 0,
121
+ opacity: 0
122
+ }, {
123
+ offset: 0.30,
124
+ opacity: 0.33
125
+ }, {
126
+ offset: 0.50,
127
+ opacity: 1
128
+ }, {
129
+ offset: 0.70,
130
+ opacity: 0.33
131
+ }, {
132
+ offset: 1.0,
133
+ opacity: 0
134
+ }];
135
+
136
+ /** Offset (0–1) of the brightest point on the gradient line. */
137
+ function gradientPeakOffset(stops = SHIMMER_GRADIENT_STOPS) {
138
+ const peak = stops.find(s => s.opacity === 1);
139
+ return peak?.offset ?? 0.5;
140
+ }
141
+
142
+ /**
143
+ * How far the gradient extends from the peak to a fully transparent stop,
144
+ * expressed as a fraction of the gradient line (0–1). With the default stops
145
+ * this is 0.5 (peak at 0.5, transparent at 0 and 1).
146
+ *
147
+ * This value drives the overshoot: the band must travel this fraction of a
148
+ * full corner-to-corner sweep *beyond* each corner so the soft transparent
149
+ * tails fully clear the box before the sawtooth reset.
150
+ */
151
+ function gradientTransparentExtent(stops = SHIMMER_GRADIENT_STOPS) {
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
+ * Derive the moving-shimmer geometry from box size, overlay size, and the
159
+ * gradient stop layout.
160
+ *
161
+ * Coordinate model (135° / down-right):
162
+ * - Peak stripe world diagonal sum: (W + H) / 2 + 2k
163
+ * - Gradient offset 0 → 1 spans 2 × overlaySize in diagonal-sum units
164
+ * - Transparent tail beyond peak: fadeExtent × 2 × overlaySize / 2
165
+ * = fadeExtent × overlaySize in k units
166
+ *
167
+ * The sawtooth clock maps linearly kStart → kEnd so t = 0 and t = 1 both
168
+ * land with the entire gradient outside the box — no jitter on reset.
169
+ */
170
+ function computeShimmerMotion(width, height, overlaySize, stops = SHIMMER_GRADIENT_STOPS) {
171
+ if (width <= 0 || height <= 0 || overlaySize <= 0) return null;
172
+ const fadeExtent = gradientTransparentExtent(stops);
173
+ const cornerTravelK = (width + height) / 2;
174
+ const baseSweepHalfK = cornerTravelK / 2;
175
+ const fadeBeyondPeakK = fadeExtent * overlaySize;
176
+ const kStart = -baseSweepHalfK - fadeBeyondPeakK;
177
+ const kEnd = baseSweepHalfK + fadeBeyondPeakK;
178
+ const overshootFraction = cornerTravelK > 0 ? fadeBeyondPeakK / cornerTravelK : 0;
179
+ return {
180
+ overlaySize,
181
+ padX: (overlaySize - width) / 2,
182
+ padY: (overlaySize - height) / 2,
183
+ kStart,
184
+ kEnd,
185
+ overshootFraction,
186
+ cornerTravelK,
187
+ fadeBeyondPeakK
188
+ };
189
+ }