@retray-dev/ui-kit 9.3.1 → 10.1.0

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 (39) hide show
  1. package/COMPONENTS.md +136 -22
  2. package/CONSUMER.md +48 -8
  3. package/FONTS.md +54 -13
  4. package/README.md +40 -3
  5. package/dist/Accordion.d.mts +1 -1
  6. package/dist/Accordion.d.ts +1 -1
  7. package/dist/Accordion.js +2 -2
  8. package/dist/Accordion.mjs +1 -1
  9. package/dist/ConfirmDialog.d.mts +6 -1
  10. package/dist/ConfirmDialog.d.ts +6 -1
  11. package/dist/ConfirmDialog.js +44 -14
  12. package/dist/ConfirmDialog.mjs +1 -1
  13. package/dist/ImageViewer.js +282 -141
  14. package/dist/ImageViewer.mjs +3 -1
  15. package/dist/Sheet.js +16 -13
  16. package/dist/Sheet.mjs +1 -1
  17. package/dist/Switch.js +40 -17
  18. package/dist/Switch.mjs +1 -1
  19. package/dist/{chunk-O3HA6TYM.mjs → chunk-DJ7RN37L.mjs} +2 -2
  20. package/dist/{chunk-FZZLPJ6B.mjs → chunk-KZL5VTYK.mjs} +43 -14
  21. package/dist/{chunk-QKH5ZOD5.mjs → chunk-WF2XDFRK.mjs} +40 -17
  22. package/dist/{chunk-Z4BVUWW6.mjs → chunk-WOEYDUJZ.mjs} +19 -31
  23. package/dist/{chunk-PFZTM6D5.mjs → chunk-Y2NS74WS.mjs} +9 -7
  24. package/dist/fonts.d.mts +39 -31
  25. package/dist/fonts.d.ts +39 -31
  26. package/dist/fonts.js +34 -39
  27. package/dist/fonts.mjs +35 -34
  28. package/dist/index.js +119 -76
  29. package/dist/index.mjs +5 -5
  30. package/package.json +3 -1
  31. package/scripts/build-apk.sh +84 -0
  32. package/scripts/copy-fonts.js +90 -0
  33. package/scripts/test-consumer-fonts.sh +82 -0
  34. package/src/components/Accordion/Accordion.tsx +7 -3
  35. package/src/components/ConfirmDialog/ConfirmDialog.tsx +61 -23
  36. package/src/components/ImageViewer/ImageViewer.tsx +25 -30
  37. package/src/components/Sheet/Sheet.tsx +10 -9
  38. package/src/components/Switch/Switch.tsx +30 -17
  39. package/src/fonts.ts +59 -40
@@ -1,21 +1,22 @@
1
1
  'use strict';
2
2
 
3
- var React4 = require('react');
3
+ var React5 = require('react');
4
4
  var reactNative = require('react-native');
5
5
  var reactNativeGestureHandler = require('react-native-gesture-handler');
6
6
  var Animated2 = require('react-native-reanimated');
7
7
  var reactNativeSafeAreaContext = require('react-native-safe-area-context');
8
+ var reactNativeSizeMatters = require('react-native-size-matters');
8
9
  var AntDesign = require('@expo/vector-icons/AntDesign');
9
10
  var Entypo = require('@expo/vector-icons/Entypo');
10
11
  var Feather = require('@expo/vector-icons/Feather');
11
12
  var FontAwesome5 = require('@expo/vector-icons/FontAwesome5');
12
13
  var MaterialIcons = require('@expo/vector-icons/MaterialIcons');
13
14
  var Ionicons = require('@expo/vector-icons/Ionicons');
14
- var reactNativeSizeMatters = require('react-native-size-matters');
15
+ var pressto = require('pressto');
15
16
 
16
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
17
18
 
18
- var React4__default = /*#__PURE__*/_interopDefault(React4);
19
+ var React5__default = /*#__PURE__*/_interopDefault(React5);
19
20
  var Animated2__default = /*#__PURE__*/_interopDefault(Animated2);
20
21
  var AntDesign__default = /*#__PURE__*/_interopDefault(AntDesign);
21
22
  var Entypo__default = /*#__PURE__*/_interopDefault(Entypo);
@@ -30,46 +31,69 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
30
31
  if (typeof require !== "undefined") return require.apply(this, arguments);
31
32
  throw Error('Dynamic require of "' + x + '" is not supported');
32
33
  });
33
- var glyphMapOf = (mod) => mod.glyphMap ?? {};
34
- var ALL_FAMILIES = [
35
- { name: "Ionicons", component: Ionicons__default.default, getGlyphMap: () => glyphMapOf(Ionicons__default.default) },
36
- { name: "MaterialIcons", component: MaterialIcons__default.default, getGlyphMap: () => glyphMapOf(MaterialIcons__default.default) },
37
- { name: "FontAwesome5", component: FontAwesome5__default.default, getGlyphMap: () => glyphMapOf(FontAwesome5__default.default) },
38
- { name: "Entypo", component: Entypo__default.default, getGlyphMap: () => glyphMapOf(Entypo__default.default) },
39
- { name: "AntDesign", component: AntDesign__default.default, getGlyphMap: () => glyphMapOf(AntDesign__default.default) },
40
- { name: "Feather", component: Feather__default.default, getGlyphMap: () => glyphMapOf(Feather__default.default) }
41
- ];
42
- var activeFamilies = ALL_FAMILIES;
43
- var resolvedCache = null;
44
- function buildCache() {
45
- const cache = /* @__PURE__ */ new Map();
46
- for (const family of activeFamilies) {
47
- const glyphMap = family.getGlyphMap();
48
- for (const iconName of Object.keys(glyphMap)) {
49
- cache.set(iconName, family);
34
+ var _haptics = null;
35
+ var _hapticsLoaded = false;
36
+ async function getHaptics() {
37
+ if (reactNative.Platform.OS === "web") return null;
38
+ if (!_hapticsLoaded) {
39
+ _hapticsLoaded = true;
40
+ try {
41
+ _haptics = await import('expo-haptics');
42
+ } catch {
43
+ _haptics = null;
50
44
  }
51
45
  }
52
- return cache;
46
+ return _haptics;
53
47
  }
54
- function resolveFamily(name) {
55
- if (!resolvedCache) {
56
- resolvedCache = buildCache();
48
+ var _pulsar = null;
49
+ var _pulsarChecked = false;
50
+ var _pulsarAvailable = false;
51
+ function isPulsarNativeRegistered() {
52
+ try {
53
+ const g = globalThis;
54
+ if (typeof g.__turboModuleProxy === "function") {
55
+ return g.__turboModuleProxy("RNPulsar") != null;
56
+ }
57
+ return reactNative.NativeModules?.RNPulsar != null;
58
+ } catch {
59
+ return false;
57
60
  }
58
- return resolvedCache.get(name) ?? null;
59
61
  }
60
- function Icon({ name, size, color, family }) {
61
- let resolved = null;
62
- if (family) {
63
- resolved = ALL_FAMILIES.find((f) => f.name === family) ?? null;
64
- } else {
65
- resolved = resolveFamily(name);
62
+ function getPulsar() {
63
+ if (reactNative.Platform.OS === "web") return null;
64
+ if (!_pulsarChecked) {
65
+ _pulsarChecked = true;
66
+ try {
67
+ if (isPulsarNativeRegistered()) {
68
+ _pulsar = __require("react-native-pulsar");
69
+ _pulsarAvailable = true;
70
+ }
71
+ } catch {
72
+ _pulsar = null;
73
+ _pulsarAvailable = false;
74
+ }
66
75
  }
67
- if (!resolved) return null;
68
- const Component = resolved.component;
69
- return React4__default.default.createElement(Component, { name, size, color });
76
+ return _pulsarAvailable ? _pulsar : null;
70
77
  }
71
- function renderIcon(name, size, color) {
72
- return React4__default.default.createElement(Icon, { name, size, color });
78
+ function selectionAsync() {
79
+ if (reactNative.Platform.OS === "web") return;
80
+ getHaptics().then((h) => {
81
+ if (h) {
82
+ h.selectionAsync();
83
+ } else {
84
+ getPulsar()?.Presets.System.selection();
85
+ }
86
+ });
87
+ }
88
+ function impactLight() {
89
+ if (reactNative.Platform.OS === "web") return;
90
+ getHaptics().then((h) => {
91
+ if (h) {
92
+ h.impactAsync(h.ImpactFeedbackStyle.Light);
93
+ } else {
94
+ getPulsar()?.Presets.System.impactLight();
95
+ }
96
+ });
73
97
  }
74
98
 
75
99
  // src/theme/colorUtils.ts
@@ -179,12 +203,12 @@ function deriveColors(t, scheme) {
179
203
  }
180
204
 
181
205
  // src/theme/ThemeProvider.tsx
182
- var ThemeContext = React4.createContext({
206
+ var ThemeContext = React5.createContext({
183
207
  colors: deriveColors(defaultLight, "light"),
184
208
  colorScheme: "light"
185
209
  });
186
210
  function useTheme() {
187
- const context = React4.useContext(ThemeContext);
211
+ const context = React5.useContext(ThemeContext);
188
212
  if (!context) {
189
213
  throw new Error("useTheme must be used within a ThemeProvider");
190
214
  }
@@ -193,6 +217,48 @@ function useTheme() {
193
217
  var isWeb = reactNative.Platform.OS === "web";
194
218
  var s = isWeb ? (n) => n : reactNativeSizeMatters.scale;
195
219
  var vs = isWeb ? (n) => n : reactNativeSizeMatters.verticalScale;
220
+ var ms = isWeb ? (n, _factor) => n : reactNativeSizeMatters.moderateScale;
221
+ var glyphMapOf = (mod) => mod.glyphMap ?? {};
222
+ var ALL_FAMILIES = [
223
+ { name: "Ionicons", component: Ionicons__default.default, getGlyphMap: () => glyphMapOf(Ionicons__default.default) },
224
+ { name: "MaterialIcons", component: MaterialIcons__default.default, getGlyphMap: () => glyphMapOf(MaterialIcons__default.default) },
225
+ { name: "FontAwesome5", component: FontAwesome5__default.default, getGlyphMap: () => glyphMapOf(FontAwesome5__default.default) },
226
+ { name: "Entypo", component: Entypo__default.default, getGlyphMap: () => glyphMapOf(Entypo__default.default) },
227
+ { name: "AntDesign", component: AntDesign__default.default, getGlyphMap: () => glyphMapOf(AntDesign__default.default) },
228
+ { name: "Feather", component: Feather__default.default, getGlyphMap: () => glyphMapOf(Feather__default.default) }
229
+ ];
230
+ var activeFamilies = ALL_FAMILIES;
231
+ var resolvedCache = null;
232
+ function buildCache() {
233
+ const cache = /* @__PURE__ */ new Map();
234
+ for (const family of activeFamilies) {
235
+ const glyphMap = family.getGlyphMap();
236
+ for (const iconName of Object.keys(glyphMap)) {
237
+ cache.set(iconName, family);
238
+ }
239
+ }
240
+ return cache;
241
+ }
242
+ function resolveFamily(name) {
243
+ if (!resolvedCache) {
244
+ resolvedCache = buildCache();
245
+ }
246
+ return resolvedCache.get(name) ?? null;
247
+ }
248
+ function Icon({ name, size, color, family }) {
249
+ let resolved = null;
250
+ if (family) {
251
+ resolved = ALL_FAMILIES.find((f) => f.name === family) ?? null;
252
+ } else {
253
+ resolved = resolveFamily(name);
254
+ }
255
+ if (!resolved) return null;
256
+ const Component = resolved.component;
257
+ return React5__default.default.createElement(Component, { name, size, color });
258
+ }
259
+ function renderIcon(name, size, color) {
260
+ return React5__default.default.createElement(Icon, { name, size, color });
261
+ }
196
262
  var SPRINGS = {
197
263
  /** Settled transitions for moving indicators — Tabs pill, Switch thumb. */
198
264
  glide: { stiffness: 380, damping: 38, mass: 1 }};
@@ -204,78 +270,165 @@ var SPRINGS = {
204
270
  /** Quick ease-in for collapsing. */
205
271
  collapse: Animated2.Easing.in(Animated2.Easing.ease)
206
272
  });
207
- var _haptics = null;
208
- var _hapticsLoaded = false;
209
- async function getHaptics() {
210
- if (reactNative.Platform.OS === "web") return null;
211
- if (!_hapticsLoaded) {
212
- _hapticsLoaded = true;
213
- try {
214
- _haptics = await import('expo-haptics');
215
- } catch {
216
- _haptics = null;
217
- }
218
- }
219
- return _haptics;
220
- }
221
- var _pulsar = null;
222
- var _pulsarChecked = false;
223
- var _pulsarAvailable = false;
224
- function isPulsarNativeRegistered() {
225
- try {
226
- const g = globalThis;
227
- if (typeof g.__turboModuleProxy === "function") {
228
- return g.__turboModuleProxy("RNPulsar") != null;
229
- }
230
- return reactNative.NativeModules?.RNPulsar != null;
231
- } catch {
232
- return false;
233
- }
273
+ var PRESS_SCALE = {
274
+ button: 0.95,
275
+ card: 0.98,
276
+ row: 0.97,
277
+ chip: 0.94
278
+ };
279
+ var PressableButton = pressto.createAnimatedPressable((progress) => {
280
+ "worklet";
281
+ const scale2 = 1 - (1 - PRESS_SCALE.button) * progress;
282
+ return { transform: [{ scale: scale2 }] };
283
+ });
284
+ pressto.createAnimatedPressable((progress) => {
285
+ "worklet";
286
+ const scale2 = 1 - (1 - PRESS_SCALE.card) * progress;
287
+ return { transform: [{ scale: scale2 }] };
288
+ });
289
+ pressto.createAnimatedPressable((progress) => {
290
+ "worklet";
291
+ const scale2 = 1 - (1 - PRESS_SCALE.row) * progress;
292
+ return { transform: [{ scale: scale2 }] };
293
+ });
294
+ pressto.createAnimatedPressable((progress) => {
295
+ "worklet";
296
+ const scale2 = 1 - (1 - PRESS_SCALE.chip) * progress;
297
+ return { transform: [{ scale: scale2 }] };
298
+ });
299
+ pressto.createAnimatedPressable((progress) => {
300
+ "worklet";
301
+ const scale2 = 1 - (1 - PRESS_SCALE.button) * progress;
302
+ return { transform: [{ scale: scale2 }] };
303
+ });
304
+
305
+ // src/components/IconButton/IconButton.tsx
306
+ var sizeMap = {
307
+ // AUDIT FIX: sm was 32pt — below Apple HIG 44pt minimum touch target.
308
+ sm: { container: s(44), icon: 18 },
309
+ md: { container: s(44), icon: 20 },
310
+ lg: { container: s(52), icon: 24 }
311
+ };
312
+ function IconButtonBase({
313
+ iconName,
314
+ icon,
315
+ iconColor,
316
+ variant = "primary",
317
+ size = "md",
318
+ loading = false,
319
+ badge,
320
+ disabled,
321
+ style,
322
+ onPress,
323
+ accessibilityLabel,
324
+ accessibilityHint
325
+ }) {
326
+ const { colors } = useTheme();
327
+ const isDisabled = disabled || loading;
328
+ const handlePress = () => {
329
+ impactLight();
330
+ onPress?.();
331
+ };
332
+ const containerVariantStyle = {
333
+ primary: { backgroundColor: colors.primary },
334
+ secondary: { backgroundColor: colors.surface },
335
+ outline: { backgroundColor: "transparent", borderWidth: 1.5, borderColor: colors.border },
336
+ text: { backgroundColor: "transparent" },
337
+ destructive: { backgroundColor: colors.destructive }
338
+ }[variant];
339
+ const defaultIconColor = {
340
+ primary: colors.primaryForeground,
341
+ secondary: colors.foreground,
342
+ outline: colors.foreground,
343
+ text: colors.foreground,
344
+ destructive: colors.destructiveForeground
345
+ }[variant];
346
+ const spinnerColor = variant === "destructive" ? colors.destructiveForeground : variant === "primary" ? colors.primaryForeground : colors.foreground;
347
+ const { container: containerSize, icon: iconSize } = sizeMap[size];
348
+ const resolvedIcon = iconName ? renderIcon(iconName, iconSize, iconColor ?? defaultIconColor) : icon;
349
+ const showBadge = badge !== void 0 && badge !== false && badge !== 0;
350
+ const badgeCount = typeof badge === "number" ? Math.min(badge, 99) : null;
351
+ const showCount = typeof badge === "number" && badge > 0;
352
+ return /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: styles.wrapper }, /* @__PURE__ */ React5__default.default.createElement(
353
+ PressableButton,
354
+ {
355
+ style: [
356
+ styles.base,
357
+ containerVariantStyle,
358
+ { width: containerSize, height: containerSize },
359
+ isDisabled && styles.disabled,
360
+ style
361
+ ],
362
+ enabled: !isDisabled,
363
+ onPress: handlePress,
364
+ rippleColor: "transparent",
365
+ touchSoundDisabled: true,
366
+ activateOnHover: true,
367
+ accessibilityRole: "button",
368
+ accessibilityLabel: accessibilityLabel ?? iconName ?? "icon button",
369
+ accessibilityHint,
370
+ accessibilityState: { disabled: isDisabled, busy: loading }
371
+ },
372
+ loading ? /* @__PURE__ */ React5__default.default.createElement(reactNative.ActivityIndicator, { size: "small", color: spinnerColor }) : resolvedIcon
373
+ ), showBadge && /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: [
374
+ styles.badge,
375
+ { backgroundColor: colors.primary },
376
+ showCount ? styles.badgeCount : styles.badgeDot
377
+ ] }, showCount && /* @__PURE__ */ React5__default.default.createElement(reactNative.Text, { style: [styles.badgeText, { color: colors.primaryForeground }] }, badgeCount)));
234
378
  }
235
- function getPulsar() {
236
- if (reactNative.Platform.OS === "web") return null;
237
- if (!_pulsarChecked) {
238
- _pulsarChecked = true;
239
- try {
240
- if (isPulsarNativeRegistered()) {
241
- _pulsar = __require("react-native-pulsar");
242
- _pulsarAvailable = true;
243
- }
244
- } catch {
245
- _pulsar = null;
246
- _pulsarAvailable = false;
247
- }
379
+ var IconButton = React5__default.default.memo(IconButtonBase);
380
+ var styles = reactNative.StyleSheet.create({
381
+ wrapper: {
382
+ alignSelf: "flex-start"
383
+ },
384
+ base: {
385
+ borderRadius: 9999,
386
+ alignItems: "center",
387
+ justifyContent: "center"
388
+ },
389
+ disabled: {
390
+ opacity: 0.45
391
+ },
392
+ badge: {
393
+ position: "absolute",
394
+ top: -2,
395
+ right: -2,
396
+ alignItems: "center",
397
+ justifyContent: "center"
398
+ },
399
+ badgeDot: {
400
+ width: 8,
401
+ height: 8,
402
+ borderRadius: 9999
403
+ },
404
+ badgeCount: {
405
+ minWidth: 16,
406
+ height: 16,
407
+ borderRadius: 9999,
408
+ paddingHorizontal: 3
409
+ },
410
+ badgeText: {
411
+ fontFamily: "Sohne-Bold",
412
+ fontSize: ms(9),
413
+ lineHeight: 14
248
414
  }
249
- return _pulsarAvailable ? _pulsar : null;
250
- }
251
- function selectionAsync() {
252
- if (reactNative.Platform.OS === "web") return;
253
- getHaptics().then((h) => {
254
- if (h) {
255
- h.selectionAsync();
256
- } else {
257
- getPulsar()?.Presets.System.selection();
258
- }
259
- });
260
- }
261
-
262
- // src/components/PagerDots/PagerDots.tsx
415
+ });
263
416
  function Dot({ active, size, activeColor, inactiveColor, onPress, index, total }) {
264
417
  const progress = Animated2.useSharedValue(active ? 1 : 0);
265
- React4.useEffect(() => {
418
+ React5.useEffect(() => {
266
419
  progress.value = Animated2.withSpring(active ? 1 : 0, SPRINGS.glide);
267
420
  }, [active, progress]);
268
421
  const animatedStyle = Animated2.useAnimatedStyle(() => ({
269
422
  width: size + progress.value * size * 1.5,
270
423
  backgroundColor: Animated2.interpolateColor(progress.value, [0, 1], [inactiveColor, activeColor])
271
424
  }));
272
- const dot = /* @__PURE__ */ React4__default.default.createElement(Animated2__default.default.View, { style: [{ height: size, borderRadius: size / 2 }, animatedStyle] });
425
+ const dot = /* @__PURE__ */ React5__default.default.createElement(Animated2__default.default.View, { style: [{ height: size, borderRadius: size / 2 }, animatedStyle] });
273
426
  if (!onPress) return dot;
274
427
  const handlePress = () => {
275
428
  selectionAsync();
276
429
  onPress();
277
430
  };
278
- return /* @__PURE__ */ React4__default.default.createElement(
431
+ return /* @__PURE__ */ React5__default.default.createElement(
279
432
  reactNative.TouchableOpacity,
280
433
  {
281
434
  onPress: handlePress,
@@ -324,14 +477,14 @@ function PagerDots({
324
477
  onDotPress(activeIndex + 1);
325
478
  }
326
479
  };
327
- return /* @__PURE__ */ React4__default.default.createElement(
480
+ return /* @__PURE__ */ React5__default.default.createElement(
328
481
  reactNative.View,
329
482
  {
330
- style: [styles.container, { gap: s(spacing) }, style],
483
+ style: [styles2.container, { gap: s(spacing) }, style],
331
484
  accessibilityRole: "adjustable",
332
485
  accessibilityLabel: `Page ${activeIndex + 1} of ${count}`
333
486
  },
334
- hasControls && /* @__PURE__ */ React4__default.default.createElement(
487
+ hasControls && /* @__PURE__ */ React5__default.default.createElement(
335
488
  reactNative.TouchableOpacity,
336
489
  {
337
490
  onPress: handlePrevious,
@@ -341,11 +494,11 @@ function PagerDots({
341
494
  accessibilityRole: "button",
342
495
  accessibilityLabel: "Previous page",
343
496
  hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
344
- style: [styles.controlBtn, !canGoPrev && styles.controlBtnDisabled]
497
+ style: [styles2.controlBtn, !canGoPrev && styles2.controlBtnDisabled]
345
498
  },
346
499
  renderIcon("chevron-left", s(18), canGoPrev ? colors.foreground : colors.foregroundMuted)
347
500
  ),
348
- /* @__PURE__ */ React4__default.default.createElement(reactNative.View, { style: [styles.dotsRow, { gap: s(spacing) }] }, Array.from({ length: count }).map((_, i) => /* @__PURE__ */ React4__default.default.createElement(
501
+ /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: [styles2.dotsRow, { gap: s(spacing) }] }, Array.from({ length: count }).map((_, i) => /* @__PURE__ */ React5__default.default.createElement(
349
502
  Dot,
350
503
  {
351
504
  key: i,
@@ -358,7 +511,7 @@ function PagerDots({
358
511
  onPress: onDotPress ? () => onDotPress(i) : void 0
359
512
  }
360
513
  ))),
361
- hasControls && /* @__PURE__ */ React4__default.default.createElement(
514
+ hasControls && /* @__PURE__ */ React5__default.default.createElement(
362
515
  reactNative.TouchableOpacity,
363
516
  {
364
517
  onPress: handleNext,
@@ -368,13 +521,13 @@ function PagerDots({
368
521
  accessibilityRole: "button",
369
522
  accessibilityLabel: "Next page",
370
523
  hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
371
- style: [styles.controlBtn, !canGoNext && styles.controlBtnDisabled]
524
+ style: [styles2.controlBtn, !canGoNext && styles2.controlBtnDisabled]
372
525
  },
373
526
  renderIcon("chevron-right", s(18), canGoNext ? colors.foreground : colors.foregroundMuted)
374
527
  )
375
528
  );
376
529
  }
377
- var styles = reactNative.StyleSheet.create({
530
+ var styles2 = reactNative.StyleSheet.create({
378
531
  container: {
379
532
  flexDirection: "row",
380
533
  alignItems: "center",
@@ -402,7 +555,7 @@ function ZoomableImage({ source, width, height, onZoomChange }) {
402
555
  const translateY = Animated2.useSharedValue(0);
403
556
  const savedX = Animated2.useSharedValue(0);
404
557
  const savedY = Animated2.useSharedValue(0);
405
- const reportZoom = React4.useCallback((zoomed) => onZoomChange(zoomed), [onZoomChange]);
558
+ const reportZoom = React5.useCallback((zoomed) => onZoomChange(zoomed), [onZoomChange]);
406
559
  const reset = () => {
407
560
  "worklet";
408
561
  scale2.value = Animated2.withTiming(1);
@@ -448,22 +601,17 @@ function ZoomableImage({ source, width, height, onZoomChange }) {
448
601
  { scale: scale2.value }
449
602
  ]
450
603
  }));
451
- return /* @__PURE__ */ React4__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composed }, /* @__PURE__ */ React4__default.default.createElement(Animated2__default.default.View, { style: [{ width, height }, styles2.imageWrap] }, /* @__PURE__ */ React4__default.default.createElement(
452
- Animated2__default.default.Image,
453
- {
454
- source,
455
- style: [{ width, height }, animatedStyle],
456
- resizeMode: "contain"
457
- }
458
- )));
604
+ return /* @__PURE__ */ React5__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: composed }, /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: [{ width, height }, styles3.imageWrap], collapsable: false }, /* @__PURE__ */ React5__default.default.createElement(Animated2__default.default.View, { style: [{ width, height }, animatedStyle] }, /* @__PURE__ */ React5__default.default.createElement(reactNative.Image, { source, style: { width, height }, resizeMode: "contain" }))));
459
605
  }
460
606
  function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
461
- const { width, height } = reactNative.useWindowDimensions();
607
+ const window = reactNative.useWindowDimensions();
608
+ const width = window.width > 0 ? window.width : reactNative.Dimensions.get("window").width;
609
+ const height = window.height > 0 ? window.height : reactNative.Dimensions.get("window").height;
462
610
  const insets = reactNativeSafeAreaContext.useSafeAreaInsets();
463
- const [index, setIndex] = React4.useState(initialIndex);
464
- const [pagingEnabled, setPagingEnabled] = React4.useState(true);
465
- const scrollRef = React4__default.default.useRef(null);
466
- React4__default.default.useEffect(() => {
611
+ const [index, setIndex] = React5.useState(initialIndex);
612
+ const [pagingEnabled, setPagingEnabled] = React5.useState(true);
613
+ const scrollRef = React5__default.default.useRef(null);
614
+ React5__default.default.useEffect(() => {
467
615
  if (!visible) return;
468
616
  const handle = requestAnimationFrame(() => {
469
617
  setIndex(initialIndex);
@@ -474,7 +622,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
474
622
  }, [visible, initialIndex, width]);
475
623
  const dragY = Animated2.useSharedValue(0);
476
624
  const DISMISS_THRESHOLD = height * 0.18;
477
- const closeViewer = React4.useCallback(() => onClose(), [onClose]);
625
+ const closeViewer = React5.useCallback(() => onClose(), [onClose]);
478
626
  const swipeDown = reactNativeGestureHandler.Gesture.Pan().enabled(pagingEnabled).activeOffsetY(12).failOffsetX([-16, 16]).onUpdate((e) => {
479
627
  dragY.value = Math.max(0, e.translationY);
480
628
  }).onEnd((e) => {
@@ -484,7 +632,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
484
632
  dragY.value = Animated2.withTiming(0);
485
633
  }
486
634
  });
487
- React4__default.default.useEffect(() => {
635
+ React5__default.default.useEffect(() => {
488
636
  if (visible) dragY.value = 0;
489
637
  }, [visible, dragY]);
490
638
  const dismissStyle = Animated2.useAnimatedStyle(() => ({
@@ -501,7 +649,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
501
649
  scrollRef.current?.scrollTo({ x: page * width, animated: true });
502
650
  setIndex(page);
503
651
  };
504
- return /* @__PURE__ */ React4__default.default.createElement(reactNative.Modal, { visible, transparent: false, animationType: "fade", onRequestClose: onClose, statusBarTranslucent: true }, /* @__PURE__ */ React4__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles2.root }, /* @__PURE__ */ React4__default.default.createElement(Animated2__default.default.View, { style: [styles2.backdrop, backdropStyle], pointerEvents: "none" }), /* @__PURE__ */ React4__default.default.createElement(Animated2__default.default.View, { style: [styles2.container, dismissStyle] }, /* @__PURE__ */ React4__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: swipeDown }, /* @__PURE__ */ React4__default.default.createElement(Animated2__default.default.View, { style: styles2.root }, /* @__PURE__ */ React4__default.default.createElement(
652
+ return /* @__PURE__ */ React5__default.default.createElement(reactNative.Modal, { visible, transparent: false, animationType: "fade", onRequestClose: onClose, statusBarTranslucent: true }, /* @__PURE__ */ React5__default.default.createElement(reactNativeGestureHandler.GestureHandlerRootView, { style: styles3.root }, /* @__PURE__ */ React5__default.default.createElement(Animated2__default.default.View, { style: [styles3.backdrop, backdropStyle], pointerEvents: "none" }), /* @__PURE__ */ React5__default.default.createElement(Animated2__default.default.View, { style: [styles3.container, dismissStyle] }, /* @__PURE__ */ React5__default.default.createElement(reactNativeGestureHandler.GestureDetector, { gesture: swipeDown }, /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: styles3.root, collapsable: false }, /* @__PURE__ */ React5__default.default.createElement(
505
653
  reactNative.ScrollView,
506
654
  {
507
655
  ref: scrollRef,
@@ -512,7 +660,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
512
660
  onMomentumScrollEnd: onMomentumEnd,
513
661
  bounces: false
514
662
  },
515
- images.map((source, i) => /* @__PURE__ */ React4__default.default.createElement(
663
+ images.map((source, i) => /* @__PURE__ */ React5__default.default.createElement(
516
664
  ZoomableImage,
517
665
  {
518
666
  key: i,
@@ -522,19 +670,18 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
522
670
  onZoomChange: (zoomed) => setPagingEnabled(!zoomed)
523
671
  }
524
672
  ))
525
- ))), /* @__PURE__ */ React4__default.default.createElement(
526
- reactNative.TouchableOpacity,
673
+ ))), /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: [styles3.closeButtonWrapper, { top: insets.top + vs(8) }] }, /* @__PURE__ */ React5__default.default.createElement(
674
+ IconButton,
527
675
  {
528
- style: [styles2.closeButton, { top: insets.top + vs(8) }],
676
+ iconName: "x",
677
+ size: "md",
678
+ variant: "text",
679
+ style: { backgroundColor: "rgba(255,255,255,0.18)" },
680
+ iconColor: "#fff",
529
681
  onPress: onClose,
530
- activeOpacity: 0.7,
531
- touchSoundDisabled: true,
532
- accessibilityRole: "button",
533
- accessibilityLabel: "Close",
534
- hitSlop: { top: 12, bottom: 12, left: 12, right: 12 }
535
- },
536
- renderIcon("x", 26, "#fff")
537
- ), images.length > 1 ? /* @__PURE__ */ React4__default.default.createElement(reactNative.View, { style: [styles2.dots, { bottom: insets.bottom + vs(16) }], pointerEvents: "box-none" }, /* @__PURE__ */ React4__default.default.createElement(
682
+ accessibilityLabel: "Close"
683
+ }
684
+ )), images.length > 1 ? /* @__PURE__ */ React5__default.default.createElement(reactNative.View, { style: [styles3.dots, { bottom: insets.bottom + vs(16) }], pointerEvents: "box-none" }, /* @__PURE__ */ React5__default.default.createElement(
538
685
  PagerDots,
539
686
  {
540
687
  count: images.length,
@@ -545,7 +692,7 @@ function ImageViewer({ images, visible, onClose, initialIndex = 0 }) {
545
692
  }
546
693
  )) : null)));
547
694
  }
548
- var styles2 = reactNative.StyleSheet.create({
695
+ var styles3 = reactNative.StyleSheet.create({
549
696
  root: {
550
697
  flex: 1
551
698
  },
@@ -561,15 +708,9 @@ var styles2 = reactNative.StyleSheet.create({
561
708
  justifyContent: "center",
562
709
  overflow: "hidden"
563
710
  },
564
- closeButton: {
711
+ closeButtonWrapper: {
565
712
  position: "absolute",
566
- right: s(12),
567
- width: s(40),
568
- height: s(40),
569
- borderRadius: s(20),
570
- backgroundColor: "rgba(0,0,0,0.4)",
571
- alignItems: "center",
572
- justifyContent: "center"
713
+ right: s(12)
573
714
  },
574
715
  dots: {
575
716
  position: "absolute",
@@ -1,5 +1,7 @@
1
- export { ImageViewer } from './chunk-Z4BVUWW6.mjs';
1
+ export { ImageViewer } from './chunk-WOEYDUJZ.mjs';
2
2
  import './chunk-4K625MVM.mjs';
3
+ import './chunk-3U4SSNWP.mjs';
4
+ import './chunk-3DKJ2GIC.mjs';
3
5
  import './chunk-EJ7ZPXOH.mjs';
4
6
  import './chunk-DVK4G2GT.mjs';
5
7
  import './chunk-T7XZ7H7Y.mjs';