jfs-components 0.1.28 → 0.1.32

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 (53) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/lib/commonjs/components/CategoryCard/CategoryCard.js +264 -0
  3. package/lib/commonjs/components/CompareTable/CompareTable.js +247 -25
  4. package/lib/commonjs/components/ContentSheet/ContentSheet.js +91 -8
  5. package/lib/commonjs/components/CounterBadge/CounterBadge.js +78 -0
  6. package/lib/commonjs/components/FavoriteToggle/FavoriteToggle.js +180 -0
  7. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +287 -0
  8. package/lib/commonjs/components/MoneyValue/MoneyValue.js +179 -54
  9. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +7 -1
  10. package/lib/commonjs/components/Table/Table.js +27 -7
  11. package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +219 -0
  12. package/lib/commonjs/components/index.js +35 -0
  13. package/lib/commonjs/design-tokens/figma-modes.generated.js +3 -0
  14. package/lib/commonjs/icons/registry.js +1 -1
  15. package/lib/module/components/CategoryCard/CategoryCard.js +258 -0
  16. package/lib/module/components/CompareTable/CompareTable.js +247 -26
  17. package/lib/module/components/ContentSheet/ContentSheet.js +94 -11
  18. package/lib/module/components/CounterBadge/CounterBadge.js +73 -0
  19. package/lib/module/components/FavoriteToggle/FavoriteToggle.js +174 -0
  20. package/lib/module/components/HelloJioInput/HelloJioInput.js +281 -0
  21. package/lib/module/components/MoneyValue/MoneyValue.js +182 -57
  22. package/lib/module/components/PdpCcCard/PdpCcCard.js +7 -1
  23. package/lib/module/components/Table/Table.js +27 -7
  24. package/lib/module/components/ValueBackMetric/ValueBackMetric.js +214 -0
  25. package/lib/module/components/index.js +6 -1
  26. package/lib/module/design-tokens/figma-modes.generated.js +3 -0
  27. package/lib/module/icons/registry.js +1 -1
  28. package/lib/typescript/src/components/CategoryCard/CategoryCard.d.ts +72 -0
  29. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +36 -1
  30. package/lib/typescript/src/components/ContentSheet/ContentSheet.d.ts +20 -1
  31. package/lib/typescript/src/components/CounterBadge/CounterBadge.d.ts +19 -0
  32. package/lib/typescript/src/components/FavoriteToggle/FavoriteToggle.d.ts +66 -0
  33. package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +111 -0
  34. package/lib/typescript/src/components/MoneyValue/MoneyValue.d.ts +10 -1
  35. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +25 -1
  36. package/lib/typescript/src/components/Table/Table.d.ts +9 -1
  37. package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +86 -0
  38. package/lib/typescript/src/components/index.d.ts +5 -0
  39. package/lib/typescript/src/icons/registry.d.ts +1 -1
  40. package/package.json +1 -1
  41. package/src/components/CategoryCard/CategoryCard.tsx +404 -0
  42. package/src/components/CompareTable/CompareTable.tsx +324 -30
  43. package/src/components/ContentSheet/ContentSheet.tsx +120 -11
  44. package/src/components/CounterBadge/CounterBadge.tsx +95 -0
  45. package/src/components/FavoriteToggle/FavoriteToggle.tsx +243 -0
  46. package/src/components/HelloJioInput/HelloJioInput.tsx +513 -0
  47. package/src/components/MoneyValue/MoneyValue.tsx +216 -65
  48. package/src/components/PdpCcCard/PdpCcCard.tsx +36 -1
  49. package/src/components/Table/Table.tsx +29 -4
  50. package/src/components/ValueBackMetric/ValueBackMetric.tsx +298 -0
  51. package/src/components/index.ts +5 -0
  52. package/src/design-tokens/figma-modes.generated.ts +3 -0
  53. package/src/icons/registry.ts +1 -1
@@ -13,6 +13,18 @@ var _reactUtils = require("../../utils/react-utils");
13
13
  var _jsxRuntime = require("react/jsx-runtime");
14
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
15
  const IS_WEB = _reactNative.Platform.OS === 'web';
16
+
17
+ // Spring config — mirrors the Drawer's bottom pop-up spring so the two
18
+ // bottom-sheet surfaces share the exact same entrance feel (slight bounce,
19
+ // snappy). Keep in sync with `Drawer.tsx` SPRING_CONFIG.
20
+ const SPRING_CONFIG = {
21
+ damping: 32,
22
+ stiffness: 300,
23
+ mass: 1,
24
+ overshootClamping: false,
25
+ restDisplacementThreshold: 0.1,
26
+ restSpeedThreshold: 0.1
27
+ };
16
28
  function resolveContentSheetStyle(modes) {
17
29
  const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('contentSheet/bg', modes);
18
30
  const gap = (0, _figmaVariablesResolver.getVariableByName)('contentSheet/gap', modes);
@@ -96,6 +108,8 @@ function ContentSheet({
96
108
  keyboardSpacing = 0,
97
109
  safeAreaBottom = true,
98
110
  pinToBottom = true,
111
+ visible = true,
112
+ maxHeightPercent = 0.7,
99
113
  style,
100
114
  ...rest
101
115
  }) {
@@ -107,14 +121,31 @@ function ContentSheet({
107
121
  const resolved = (0, _react.useMemo)(() => resolveContentSheetStyle(modes), [modes]);
108
122
  const processedChildren = (0, _react.useMemo)(() => (0, _reactUtils.cloneChildrenWithModes)((0, _reactUtils.flattenChildren)(children), modes), [children, modes]);
109
123
  const paddingBottom = resolved.paddingBottom + (safeAreaBottom ? bottomInset : 0);
124
+
125
+ // Entrance / exit "pop up from bottom" animation — mirrors the Drawer. The
126
+ // sheet is bottom-anchored, so a positive `translateY` equal to the window
127
+ // height parks it fully below the bottom edge. Springing it back to `0`
128
+ // produces the same bottom pop-up the Drawer uses. The shared value is
129
+ // initialised off-screen so the very first mount pops in (no flash).
130
+ const {
131
+ height: windowHeight
132
+ } = (0, _reactNative.useWindowDimensions)();
133
+ const maxHeight = windowHeight * maxHeightPercent;
110
134
  const containerStyle = (0, _react.useMemo)(() => [pinToBottom ? pinnedStyle : null, resolved.container, {
135
+ maxHeight,
111
136
  paddingBottom
112
- }, style], [pinToBottom, resolved.container, paddingBottom, style]);
137
+ }, style], [pinToBottom, resolved.container, maxHeight, paddingBottom, style]);
138
+ const entrance = (0, _reactNativeReanimated.useSharedValue)(windowHeight);
139
+ (0, _react.useEffect)(() => {
140
+ entrance.value = (0, _reactNativeReanimated.withSpring)(visible ? 0 : windowHeight, SPRING_CONFIG);
141
+ }, [visible, windowHeight, entrance]);
113
142
 
114
143
  // Switching between the keyboard-aware and plain implementation is keyed off
115
144
  // `avoidKeyboard` (and platform). Because they are distinct component types,
116
145
  // React remounts on toggle, so the conditional `useAnimatedKeyboard` hook
117
- // inside `KeyboardAwareSheet` always runs in a stable hook order.
146
+ // inside `KeyboardAwareSheet` always runs in a stable hook order. Both
147
+ // branches receive the `entrance` shared value so the bottom pop-up plays in
148
+ // every configuration.
118
149
  const header = title ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
119
150
  style: resolved.headerStyle,
120
151
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
@@ -126,12 +157,16 @@ function ContentSheet({
126
157
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(KeyboardAwareSheet, {
127
158
  style: containerStyle,
128
159
  spacing: keyboardSpacing,
160
+ entrance: entrance,
161
+ maxHeight: maxHeight,
129
162
  ...rest,
130
163
  children: [header, processedChildren]
131
164
  });
132
165
  }
133
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
166
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(AnimatedSheet, {
134
167
  style: containerStyle,
168
+ entrance: entrance,
169
+ maxHeight: maxHeight,
135
170
  ...rest,
136
171
  children: [header, processedChildren]
137
172
  });
@@ -139,27 +174,75 @@ function ContentSheet({
139
174
  /**
140
175
  * Native-only wrapper that lifts the sheet by the live keyboard height. The
141
176
  * translation is computed on the UI thread from `useAnimatedKeyboard`, so the
142
- * sheet tracks the keyboard frame-for-frame without any JS work.
177
+ * sheet tracks the keyboard frame-for-frame without any JS work. The keyboard
178
+ * offset is summed with the entrance `translateY` (the bottom pop-up) inside a
179
+ * single `useAnimatedStyle` so both motions stay on the UI thread and never
180
+ * fight each other.
143
181
  */
144
182
  function KeyboardAwareSheet({
145
183
  style,
146
184
  spacing,
185
+ entrance,
186
+ maxHeight,
147
187
  children,
148
188
  ...rest
149
189
  }) {
150
190
  const keyboard = (0, _reactNativeReanimated.useAnimatedKeyboard)();
151
- const keyboardStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
191
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
152
192
  const height = keyboard.height.value;
193
+ const keyboardOffset = height > 0 ? -(height + spacing) : 0;
153
194
  return {
154
195
  transform: [{
155
- translateY: height > 0 ? -(height + spacing) : 0
196
+ translateY: entrance.value + keyboardOffset
156
197
  }]
157
198
  };
158
199
  });
159
200
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
160
- style: [style, keyboardStyle],
201
+ style: [style, animatedStyle],
161
202
  ...rest,
162
- children: children
203
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
204
+ style: {
205
+ maxHeight
206
+ },
207
+ bounces: false,
208
+ showsVerticalScrollIndicator: false,
209
+ keyboardShouldPersistTaps: "handled",
210
+ children: children
211
+ })
212
+ });
213
+ }
214
+
215
+ /**
216
+ * Entrance-only animated sheet (web, or when keyboard avoidance is disabled).
217
+ * Applies the same bottom pop-up `translateY` as `KeyboardAwareSheet`, minus
218
+ * the keyboard offset.
219
+ */
220
+ function AnimatedSheet({
221
+ style,
222
+ entrance,
223
+ maxHeight,
224
+ children,
225
+ ...rest
226
+ }) {
227
+ const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
228
+ return {
229
+ transform: [{
230
+ translateY: entrance.value
231
+ }]
232
+ };
233
+ });
234
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
235
+ style: [style, animatedStyle],
236
+ ...rest,
237
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
238
+ style: {
239
+ maxHeight
240
+ },
241
+ bounces: false,
242
+ showsVerticalScrollIndicator: false,
243
+ keyboardShouldPersistTaps: "handled",
244
+ children: children
245
+ })
163
246
  });
164
247
  }
165
248
  var _default = exports.default = ContentSheet;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
+ var _reactUtils = require("../../utils/react-utils");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ /**
14
+ * Counter Badge — a small token-driven pill that displays a value.
15
+ *
16
+ * All visual attributes resolve from the Figma `counterBadge/*` tokens via
17
+ * `getVariableByName`.
18
+ */
19
+ function CounterBadge({
20
+ value = '99',
21
+ modes = _reactUtils.EMPTY_MODES,
22
+ style,
23
+ ...rest
24
+ }) {
25
+ // Resolve token values (fall back to the Figma defaults for the single
26
+ // `Default` mode so the badge still renders if a token is missing).
27
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('counterBadge/background', modes) ?? '#ebebec';
28
+ const foreground = (0, _figmaVariablesResolver.getVariableByName)('counterBadge/foreground', modes) ?? '#0c0d10';
29
+ const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('counterBadge/fontFamily', modes) ?? 'JioType Var';
30
+ const fontWeightRaw = (0, _figmaVariablesResolver.getVariableByName)('counterBadge/fontWeight', modes) ?? 500;
31
+ const fontWeight = typeof fontWeightRaw === 'number' ? String(fontWeightRaw) : fontWeightRaw;
32
+ const fontSize = Number((0, _figmaVariablesResolver.getVariableByName)('counterBadge/fontSize', modes) ?? 8);
33
+ const lineHeight = Number((0, _figmaVariablesResolver.getVariableByName)('counterBadge/lineHeight', modes) ?? 8);
34
+ const paddingHorizontal = Number((0, _figmaVariablesResolver.getVariableByName)('counterBadge/padding/horizontal', modes) ?? 4);
35
+ // The Figma design pins vertical padding to 4px (matching horizontal), but the
36
+ // committed variables snapshot resolves `counterBadge/padding/vertical` to 5px
37
+ // (a stale export). Figma is the source of truth here, so hardcode 4px rather
38
+ // than trust the token, keeping the badge one pixel shorter to match the design.
39
+ const paddingVertical = 4;
40
+ const borderRadius = Number((0, _figmaVariablesResolver.getVariableByName)('counterBadge/radius', modes) ?? 999);
41
+ const containerStyle = {
42
+ backgroundColor,
43
+ paddingHorizontal,
44
+ paddingVertical,
45
+ borderRadius,
46
+ alignSelf: 'flex-start',
47
+ alignItems: 'center',
48
+ justifyContent: 'center',
49
+ ...(style || {})
50
+ };
51
+ const textStyle = {
52
+ color: foreground,
53
+ fontFamily,
54
+ fontWeight: fontWeight,
55
+ fontSize,
56
+ lineHeight,
57
+ textAlign: 'center'
58
+ };
59
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
60
+ style: containerStyle,
61
+ ...rest,
62
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
63
+ style: WRAP_STYLE,
64
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
65
+ style: textStyle,
66
+ children: value
67
+ })
68
+ })
69
+ });
70
+ }
71
+
72
+ // Figma `wrap` node (data-node-id 7701:10879): min-width 10, centered.
73
+ const WRAP_STYLE = {
74
+ minWidth: 10,
75
+ alignItems: 'center',
76
+ justifyContent: 'center'
77
+ };
78
+ var _default = exports.default = CounterBadge;
@@ -0,0 +1,180 @@
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 _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
10
+ var _Icon = _interopRequireDefault(require("../../icons/Icon"));
11
+ var _GlassFill = _interopRequireDefault(require("../../utils/GlassFill/GlassFill"));
12
+ var _webPlatformUtils = require("../../utils/web-platform-utils");
13
+ var _reactUtils = require("../../utils/react-utils");
14
+ var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
15
+ var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
16
+ var _jsxRuntime = require("react/jsx-runtime");
17
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
+ 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); }
19
+ const IS_WEB = _reactNative.Platform.OS === 'web';
20
+ const IS_IOS = _reactNative.Platform.OS === 'ios';
21
+ const PRESS_DELAY = IS_IOS ? 130 : 0;
22
+ const pressedOverlayStyle = {
23
+ opacity: 0.7
24
+ };
25
+ // The `favoriteToggle/*` tokens are not yet in the committed variables snapshot,
26
+ // so `getVariableByName` returns undefined and these Figma-node values are used
27
+ // as fallbacks. Once the token export is re-synced the resolver takes over.
28
+ function resolveTokens(modes, isActive) {
29
+ const width = Number((0, _figmaVariablesResolver.getVariableByName)('favoriteToggle/width', modes) ?? 29);
30
+ const height = Number((0, _figmaVariablesResolver.getVariableByName)('favoriteToggle/height', modes) ?? 29);
31
+ const radiusRaw = Number((0, _figmaVariablesResolver.getVariableByName)('favoriteToggle/icon/radius', modes) ?? 9999);
32
+ const iconSize = Number((0, _figmaVariablesResolver.getVariableByName)('favoriteToggle/icon/width', modes) ?? 20);
33
+ // 9999 is the design-token sentinel for "perfect circle".
34
+ const size = Math.max(width, height);
35
+ const borderRadius = radiusRaw >= 9999 ? size / 2 : radiusRaw;
36
+
37
+ // Background + icon color differ per variant. In Figma this is driven by the
38
+ // `isActive` collection (modes `'False'` / `'True'`), so we pass that mode
39
+ // through by its string name. Until the `color/favoriteToggle/*` tokens are
40
+ // exported the resolver returns undefined and the fallbacks below — which
41
+ // encode the two Figma variants directly — take over.
42
+ const isActiveMode = {
43
+ ...modes,
44
+ isActive: isActive ? 'True' : 'False'
45
+ };
46
+ const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('color/favoriteToggle/background/color', isActiveMode) ?? (isActive ? '#ffffff' : '#ffffff33');
47
+ const iconColor = (0, _figmaVariablesResolver.getVariableByName)('color/favoriteToggle/icon/color', isActiveMode) ?? (isActive ? '#cea15a' : '#ffffff');
48
+
49
+ // Figma `blur/minimal` (px radius) -> GlassFill's 0-100 intensity scale, the
50
+ // same mapping Badge's glass path uses.
51
+ const blurRaw = Number((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', modes) ?? 29);
52
+ const blurIntensity = Math.max(0, Math.min(100, Math.round(blurRaw)));
53
+ return {
54
+ size,
55
+ borderRadius,
56
+ backgroundColor,
57
+ iconColor,
58
+ iconSize,
59
+ blurIntensity
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Circular glass favorite (heart) toggle, mirroring the Figma `Favorite Toggle`
65
+ * component set's boolean `isActive` variant:
66
+ * - inactive: translucent frosted circle (`#ffffff33` over a `blur/minimal`
67
+ * backdrop) with a white heart.
68
+ * - active: solid white circle with a gold (`#cea15a`) heart.
69
+ * Both variants share the identical filled-heart glyph; only the background and
70
+ * icon colors change between states.
71
+ *
72
+ * Controlled or uncontrolled: pass `isActive` (+ `onChange`) to control it, or
73
+ * omit `isActive` and use `defaultActive` to let the component own the state.
74
+ *
75
+ * Visual attributes resolve from the `favoriteToggle/*` / `color/favoriteToggle/*`
76
+ * tokens via `getVariableByName`. Those tokens are not yet in the committed
77
+ * variables snapshot, so the component currently runs on the Figma-node values
78
+ * as fallbacks; once the tokens are exported (under the `isActive` collection,
79
+ * modes `False`/`True`) the resolver takes over.
80
+ */
81
+ function FavoriteToggle({
82
+ isActive: controlledActive,
83
+ defaultActive = false,
84
+ icon = 'ic_favorite',
85
+ onChange,
86
+ onPress,
87
+ modes = _reactUtils.EMPTY_MODES,
88
+ disabled = false,
89
+ accessibilityLabel,
90
+ accessibilityHint,
91
+ accessibilityState,
92
+ webAccessibilityProps,
93
+ style,
94
+ loading,
95
+ ...rest
96
+ }) {
97
+ // Controlled when `isActive` is supplied; otherwise own the state internally
98
+ // (mirrors the Toggle / SegmentedControl controlled-or-uncontrolled pattern).
99
+ const isControlled = controlledActive !== undefined;
100
+ const [internalActive, setInternalActive] = (0, _react.useState)(defaultActive);
101
+ const isActive = isControlled ? controlledActive : internalActive;
102
+ const tokens = (0, _react.useMemo)(() => resolveTokens(modes, isActive), [modes, isActive]);
103
+ const {
104
+ active: groupActive
105
+ } = (0, _SkeletonGroup.useSkeleton)();
106
+ const isLoading = loading ?? groupActive;
107
+ const handlePress = (0, _react.useCallback)(() => {
108
+ const next = !isActive;
109
+ if (!isControlled) {
110
+ setInternalActive(next);
111
+ }
112
+ onChange?.(next);
113
+ onPress?.();
114
+ }, [isActive, isControlled, onChange, onPress]);
115
+ const label = accessibilityLabel || 'Favorite';
116
+ const webProps = (0, _webPlatformUtils.usePressableWebSupport)({
117
+ restProps: rest,
118
+ onPress: disabled ? undefined : handlePress,
119
+ disabled,
120
+ accessibilityLabel: label,
121
+ webAccessibilityProps
122
+ });
123
+ const baseContainerStyle = {
124
+ width: tokens.size,
125
+ height: tokens.size,
126
+ borderRadius: tokens.borderRadius,
127
+ // Active is opaque white, so no glass is drawn and the background paints
128
+ // directly. Inactive keeps the container transparent so the GlassFill blur
129
+ // (clipped by overflow:hidden) shows through.
130
+ backgroundColor: isActive ? tokens.backgroundColor : 'transparent',
131
+ overflow: 'hidden',
132
+ alignItems: 'center',
133
+ justifyContent: 'center',
134
+ opacity: disabled ? 0.5 : 1
135
+ };
136
+
137
+ // Declared before the loading short-circuit so hook order stays stable.
138
+ const styleCallback = (0, _react.useCallback)(({
139
+ pressed
140
+ }) => [baseContainerStyle, style, pressed && !disabled ? pressedOverlayStyle : null], [baseContainerStyle, style, disabled]);
141
+ if (isLoading) {
142
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
143
+ kind: "other",
144
+ width: tokens.size,
145
+ height: tokens.size,
146
+ style: [{
147
+ borderRadius: tokens.borderRadius
148
+ }, style],
149
+ modes: modes
150
+ });
151
+ }
152
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
153
+ accessibilityRole: IS_WEB ? 'switch' : 'button',
154
+ accessibilityLabel: undefined,
155
+ accessibilityHint: accessibilityHint,
156
+ accessibilityState: {
157
+ disabled,
158
+ checked: isActive,
159
+ ...accessibilityState
160
+ },
161
+ onPress: disabled ? undefined : handlePress,
162
+ disabled: disabled,
163
+ unstable_pressDelay: PRESS_DELAY,
164
+ style: styleCallback,
165
+ ...webProps,
166
+ children: [!isActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_GlassFill.default, {
167
+ tint: "light",
168
+ intensity: tokens.blurIntensity,
169
+ overlayColor: tokens.backgroundColor,
170
+ androidTintWash: false
171
+ }) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
172
+ name: icon,
173
+ size: tokens.iconSize,
174
+ color: tokens.iconColor,
175
+ accessibilityElementsHidden: true,
176
+ importantForAccessibility: "no"
177
+ })]
178
+ });
179
+ }
180
+ var _default = exports.default = /*#__PURE__*/_react.default.memo(FavoriteToggle);