zkit-ui 1.0.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 (267) hide show
  1. package/README.md +267 -0
  2. package/dist/ComponentLibProvider.d.ts +15 -0
  3. package/dist/ComponentLibProvider.d.ts.map +1 -0
  4. package/dist/ComponentLibProvider.js +18 -0
  5. package/dist/assets/animations/spinner.json +1 -0
  6. package/dist/assets/icons/check.svg +4 -0
  7. package/dist/assets/icons/toast/error.webp +0 -0
  8. package/dist/assets/icons/toast/success.webp +0 -0
  9. package/dist/assets/icons/toast/warning.webp +0 -0
  10. package/dist/config.d.ts +16 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +42 -0
  13. package/dist/i18n/I18nProvider.d.ts +11 -0
  14. package/dist/i18n/I18nProvider.d.ts.map +1 -0
  15. package/dist/i18n/I18nProvider.js +84 -0
  16. package/dist/i18n/locale.d.ts +7 -0
  17. package/dist/i18n/locale.d.ts.map +1 -0
  18. package/dist/i18n/locale.js +87 -0
  19. package/dist/i18n/locales/de.d.ts +51 -0
  20. package/dist/i18n/locales/de.d.ts.map +1 -0
  21. package/dist/i18n/locales/de.js +62 -0
  22. package/dist/i18n/locales/en-US.d.ts +51 -0
  23. package/dist/i18n/locales/en-US.d.ts.map +1 -0
  24. package/dist/i18n/locales/en-US.js +62 -0
  25. package/dist/i18n/locales/index.d.ts +9 -0
  26. package/dist/i18n/locales/index.d.ts.map +1 -0
  27. package/dist/i18n/locales/index.js +23 -0
  28. package/dist/i18n/locales/ja.d.ts +51 -0
  29. package/dist/i18n/locales/ja.d.ts.map +1 -0
  30. package/dist/i18n/locales/ja.js +62 -0
  31. package/dist/i18n/locales/zh-CN.d.ts +51 -0
  32. package/dist/i18n/locales/zh-CN.d.ts.map +1 -0
  33. package/dist/i18n/locales/zh-CN.js +62 -0
  34. package/dist/i18n/locales/zh-TW.d.ts +51 -0
  35. package/dist/i18n/locales/zh-TW.d.ts.map +1 -0
  36. package/dist/i18n/locales/zh-TW.js +62 -0
  37. package/dist/i18n/types.d.ts +11 -0
  38. package/dist/i18n/types.d.ts.map +1 -0
  39. package/dist/i18n/types.js +2 -0
  40. package/dist/i18n/useI18n.d.ts +2 -0
  41. package/dist/i18n/useI18n.d.ts.map +1 -0
  42. package/dist/i18n/useI18n.js +41 -0
  43. package/dist/index.d.ts +57 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +90 -0
  46. package/dist/services/ActionDialogService/ActionDialog.d.ts +4 -0
  47. package/dist/services/ActionDialogService/ActionDialog.d.ts.map +1 -0
  48. package/dist/services/ActionDialogService/ActionDialog.js +531 -0
  49. package/dist/services/ActionDialogService/index.d.ts +4 -0
  50. package/dist/services/ActionDialogService/index.d.ts.map +1 -0
  51. package/dist/services/ActionDialogService/index.js +8 -0
  52. package/dist/services/ActionDialogService/service.d.ts +7 -0
  53. package/dist/services/ActionDialogService/service.d.ts.map +1 -0
  54. package/dist/services/ActionDialogService/service.js +309 -0
  55. package/dist/services/ActionDialogService/shared.d.ts +17 -0
  56. package/dist/services/ActionDialogService/shared.d.ts.map +1 -0
  57. package/dist/services/ActionDialogService/shared.js +167 -0
  58. package/dist/services/ActionDialogService/types.d.ts +160 -0
  59. package/dist/services/ActionDialogService/types.d.ts.map +1 -0
  60. package/dist/services/ActionDialogService/types.js +2 -0
  61. package/dist/services/CardToastService/index.d.ts +115 -0
  62. package/dist/services/CardToastService/index.d.ts.map +1 -0
  63. package/dist/services/CardToastService/index.js +899 -0
  64. package/dist/services/ImagePreviewService/index.d.ts +179 -0
  65. package/dist/services/ImagePreviewService/index.d.ts.map +1 -0
  66. package/dist/services/ImagePreviewService/index.js +1383 -0
  67. package/dist/services/LoadingService/index.d.ts +146 -0
  68. package/dist/services/LoadingService/index.d.ts.map +1 -0
  69. package/dist/services/LoadingService/index.js +827 -0
  70. package/dist/services/PermissionPurposeDialogService/index.d.ts +99 -0
  71. package/dist/services/PermissionPurposeDialogService/index.d.ts.map +1 -0
  72. package/dist/services/PermissionPurposeDialogService/index.js +360 -0
  73. package/dist/services/PickerService/index.d.ts +128 -0
  74. package/dist/services/PickerService/index.d.ts.map +1 -0
  75. package/dist/services/PickerService/index.js +250 -0
  76. package/dist/theme/ThemeProvider.d.ts +15 -0
  77. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  78. package/dist/theme/ThemeProvider.js +61 -0
  79. package/dist/theme/defaultTheme.d.ts +3 -0
  80. package/dist/theme/defaultTheme.d.ts.map +1 -0
  81. package/dist/theme/defaultTheme.js +28 -0
  82. package/dist/theme/mergeTheme.d.ts +3 -0
  83. package/dist/theme/mergeTheme.d.ts.map +1 -0
  84. package/dist/theme/mergeTheme.js +19 -0
  85. package/dist/theme/types.d.ts +17 -0
  86. package/dist/theme/types.d.ts.map +1 -0
  87. package/dist/theme/types.js +2 -0
  88. package/dist/theme/useTheme.d.ts +2 -0
  89. package/dist/theme/useTheme.d.ts.map +1 -0
  90. package/dist/theme/useTheme.js +46 -0
  91. package/dist/ui/Accordion/index.d.ts +99 -0
  92. package/dist/ui/Accordion/index.d.ts.map +1 -0
  93. package/dist/ui/Accordion/index.js +777 -0
  94. package/dist/ui/AddressCascader/index.d.ts +58 -0
  95. package/dist/ui/AddressCascader/index.d.ts.map +1 -0
  96. package/dist/ui/AddressCascader/index.js +710 -0
  97. package/dist/ui/BetweenTime/index.d.ts +82 -0
  98. package/dist/ui/BetweenTime/index.d.ts.map +1 -0
  99. package/dist/ui/BetweenTime/index.js +702 -0
  100. package/dist/ui/Button/index.d.ts +93 -0
  101. package/dist/ui/Button/index.d.ts.map +1 -0
  102. package/dist/ui/Button/index.js +832 -0
  103. package/dist/ui/Checkbox/index.d.ts +103 -0
  104. package/dist/ui/Checkbox/index.d.ts.map +1 -0
  105. package/dist/ui/Checkbox/index.js +694 -0
  106. package/dist/ui/DatePicker/index.d.ts +5 -0
  107. package/dist/ui/DatePicker/index.d.ts.map +1 -0
  108. package/dist/ui/DatePicker/index.js +157 -0
  109. package/dist/ui/DatePicker/types.d.ts +99 -0
  110. package/dist/ui/DatePicker/types.d.ts.map +1 -0
  111. package/dist/ui/DatePicker/types.js +2 -0
  112. package/dist/ui/DatePicker/utils.d.ts +32 -0
  113. package/dist/ui/DatePicker/utils.d.ts.map +1 -0
  114. package/dist/ui/DatePicker/utils.js +316 -0
  115. package/dist/ui/LinkedScroll/index.d.ts +78 -0
  116. package/dist/ui/LinkedScroll/index.d.ts.map +1 -0
  117. package/dist/ui/LinkedScroll/index.js +470 -0
  118. package/dist/ui/LoadingSpinner/index.d.ts +8 -0
  119. package/dist/ui/LoadingSpinner/index.d.ts.map +1 -0
  120. package/dist/ui/LoadingSpinner/index.js +122 -0
  121. package/dist/ui/Picker/actionBar.d.ts +14 -0
  122. package/dist/ui/Picker/actionBar.d.ts.map +1 -0
  123. package/dist/ui/Picker/actionBar.js +28 -0
  124. package/dist/ui/Picker/index.d.ts +5 -0
  125. package/dist/ui/Picker/index.d.ts.map +1 -0
  126. package/dist/ui/Picker/index.js +412 -0
  127. package/dist/ui/Picker/types.d.ts +80 -0
  128. package/dist/ui/Picker/types.d.ts.map +1 -0
  129. package/dist/ui/Picker/types.js +2 -0
  130. package/dist/ui/Picker/utils.d.ts +27 -0
  131. package/dist/ui/Picker/utils.d.ts.map +1 -0
  132. package/dist/ui/Picker/utils.js +182 -0
  133. package/dist/ui/Radio/index.d.ts +95 -0
  134. package/dist/ui/Radio/index.d.ts.map +1 -0
  135. package/dist/ui/Radio/index.js +633 -0
  136. package/dist/ui/Sheet/index.d.ts +12 -0
  137. package/dist/ui/Sheet/index.d.ts.map +1 -0
  138. package/dist/ui/Sheet/index.js +832 -0
  139. package/dist/ui/Sheet/nativeBottom.d.ts +52 -0
  140. package/dist/ui/Sheet/nativeBottom.d.ts.map +1 -0
  141. package/dist/ui/Sheet/nativeBottom.js +442 -0
  142. package/dist/ui/Sheet/nativeBottom.web.d.ts +18 -0
  143. package/dist/ui/Sheet/nativeBottom.web.d.ts.map +1 -0
  144. package/dist/ui/Sheet/nativeBottom.web.js +46 -0
  145. package/dist/ui/Sheet/slots.d.ts +5 -0
  146. package/dist/ui/Sheet/slots.d.ts.map +1 -0
  147. package/dist/ui/Sheet/slots.js +40 -0
  148. package/dist/ui/Sheet/types.d.ts +131 -0
  149. package/dist/ui/Sheet/types.d.ts.map +1 -0
  150. package/dist/ui/Sheet/types.js +2 -0
  151. package/dist/ui/SliderCaptcha/index.d.ts +57 -0
  152. package/dist/ui/SliderCaptcha/index.d.ts.map +1 -0
  153. package/dist/ui/SliderCaptcha/index.js +586 -0
  154. package/dist/ui/Switch/index.d.ts +72 -0
  155. package/dist/ui/Switch/index.d.ts.map +1 -0
  156. package/dist/ui/Switch/index.js +671 -0
  157. package/dist/ui/Text/index.d.ts +41 -0
  158. package/dist/ui/Text/index.d.ts.map +1 -0
  159. package/dist/ui/Text/index.js +325 -0
  160. package/dist/ui/TextInput/TextInputPrimitive.d.ts +8 -0
  161. package/dist/ui/TextInput/TextInputPrimitive.d.ts.map +1 -0
  162. package/dist/ui/TextInput/TextInputPrimitive.js +54 -0
  163. package/dist/ui/TextInput/index.d.ts +89 -0
  164. package/dist/ui/TextInput/index.d.ts.map +1 -0
  165. package/dist/ui/TextInput/index.js +545 -0
  166. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts +39 -0
  167. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.d.ts.map +1 -0
  168. package/dist/ui/WheelColumn/ZKitWheelPickerNativeComponent.js +60 -0
  169. package/dist/ui/WheelColumn/constants.d.ts +2 -0
  170. package/dist/ui/WheelColumn/constants.d.ts.map +1 -0
  171. package/dist/ui/WheelColumn/constants.js +4 -0
  172. package/dist/ui/WheelColumn/index.d.ts +59 -0
  173. package/dist/ui/WheelColumn/index.d.ts.map +1 -0
  174. package/dist/ui/WheelColumn/index.js +547 -0
  175. package/ios/ZKitWheelPicker.h +18 -0
  176. package/ios/ZKitWheelPicker.mm +230 -0
  177. package/ios/ZKitWheelPickerLabel.h +10 -0
  178. package/ios/ZKitWheelPickerLabel.mm +31 -0
  179. package/ios/ZKitWheelPickerManager.h +5 -0
  180. package/ios/ZKitWheelPickerManager.mm +53 -0
  181. package/package.json +75 -0
  182. package/react-native.config.js +10 -0
  183. package/src/ComponentLibProvider.tsx +56 -0
  184. package/src/assets/animations/spinner.json +1 -0
  185. package/src/assets/icons/check.svg +4 -0
  186. package/src/assets/icons/toast/error.webp +0 -0
  187. package/src/assets/icons/toast/success.webp +0 -0
  188. package/src/assets/icons/toast/warning.webp +0 -0
  189. package/src/config.ts +65 -0
  190. package/src/i18n/I18nProvider.tsx +69 -0
  191. package/src/i18n/locale.ts +108 -0
  192. package/src/i18n/locales/de.ts +67 -0
  193. package/src/i18n/locales/en-US.ts +67 -0
  194. package/src/i18n/locales/index.ts +19 -0
  195. package/src/i18n/locales/ja.ts +67 -0
  196. package/src/i18n/locales/zh-CN.ts +67 -0
  197. package/src/i18n/locales/zh-TW.ts +67 -0
  198. package/src/i18n/types.ts +21 -0
  199. package/src/i18n/useI18n.ts +7 -0
  200. package/src/index.ts +395 -0
  201. package/src/services/ActionDialogService/ActionDialog.tsx +756 -0
  202. package/src/services/ActionDialogService/README.md +146 -0
  203. package/src/services/ActionDialogService/index.tsx +39 -0
  204. package/src/services/ActionDialogService/service.tsx +355 -0
  205. package/src/services/ActionDialogService/shared.ts +204 -0
  206. package/src/services/ActionDialogService/types.ts +197 -0
  207. package/src/services/CardToastService/README.md +128 -0
  208. package/src/services/CardToastService/index.tsx +1284 -0
  209. package/src/services/ImagePreviewService/README.md +67 -0
  210. package/src/services/ImagePreviewService/index.tsx +2148 -0
  211. package/src/services/LoadingService/README.md +128 -0
  212. package/src/services/LoadingService/index.tsx +1113 -0
  213. package/src/services/PermissionPurposeDialogService/README.md +80 -0
  214. package/src/services/PermissionPurposeDialogService/index.tsx +449 -0
  215. package/src/services/PickerService/README.md +154 -0
  216. package/src/services/PickerService/index.tsx +410 -0
  217. package/src/theme/ThemeProvider.tsx +40 -0
  218. package/src/theme/defaultTheme.ts +30 -0
  219. package/src/theme/mergeTheme.ts +16 -0
  220. package/src/theme/types.ts +36 -0
  221. package/src/theme/useTheme.ts +10 -0
  222. package/src/types/svg.d.ts +9 -0
  223. package/src/types/vant-area-data.d.ts +19 -0
  224. package/src/ui/Accordion/README.md +67 -0
  225. package/src/ui/Accordion/index.tsx +1221 -0
  226. package/src/ui/AddressCascader/README.md +144 -0
  227. package/src/ui/AddressCascader/index.tsx +1084 -0
  228. package/src/ui/BetweenTime/README.md +490 -0
  229. package/src/ui/BetweenTime/index.tsx +1001 -0
  230. package/src/ui/Button/README.md +186 -0
  231. package/src/ui/Button/index.tsx +1356 -0
  232. package/src/ui/Checkbox/README.md +211 -0
  233. package/src/ui/Checkbox/index.tsx +1231 -0
  234. package/src/ui/DatePicker/README.md +117 -0
  235. package/src/ui/DatePicker/index.tsx +295 -0
  236. package/src/ui/DatePicker/types.ts +115 -0
  237. package/src/ui/DatePicker/utils.ts +405 -0
  238. package/src/ui/LinkedScroll/README.md +55 -0
  239. package/src/ui/LinkedScroll/index.tsx +809 -0
  240. package/src/ui/LoadingSpinner/index.tsx +104 -0
  241. package/src/ui/Picker/README.md +190 -0
  242. package/src/ui/Picker/actionBar.tsx +70 -0
  243. package/src/ui/Picker/index.tsx +694 -0
  244. package/src/ui/Picker/types.ts +94 -0
  245. package/src/ui/Picker/utils.ts +239 -0
  246. package/src/ui/Radio/README.md +207 -0
  247. package/src/ui/Radio/index.tsx +1117 -0
  248. package/src/ui/Sheet/README.md +82 -0
  249. package/src/ui/Sheet/index.tsx +1163 -0
  250. package/src/ui/Sheet/nativeBottom.tsx +683 -0
  251. package/src/ui/Sheet/nativeBottom.web.tsx +40 -0
  252. package/src/ui/Sheet/slots.tsx +75 -0
  253. package/src/ui/Sheet/types.ts +195 -0
  254. package/src/ui/SliderCaptcha/README.md +61 -0
  255. package/src/ui/SliderCaptcha/index.tsx +816 -0
  256. package/src/ui/Switch/README.md +143 -0
  257. package/src/ui/Switch/index.tsx +1092 -0
  258. package/src/ui/Text/README.md +157 -0
  259. package/src/ui/Text/index.tsx +450 -0
  260. package/src/ui/TextInput/README.md +197 -0
  261. package/src/ui/TextInput/TextInputPrimitive.tsx +44 -0
  262. package/src/ui/TextInput/index.tsx +942 -0
  263. package/src/ui/WheelColumn/README.md +104 -0
  264. package/src/ui/WheelColumn/ZKitWheelPickerNativeComponent.tsx +68 -0
  265. package/src/ui/WheelColumn/constants.ts +1 -0
  266. package/src/ui/WheelColumn/index.tsx +826 -0
  267. package/zkit-ui.podspec +19 -0
@@ -0,0 +1,1383 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ImagePreview = exports.imagePreview = void 0;
37
+ exports.ImagePreviewProvider = ImagePreviewProvider;
38
+ const jsx_runtime_1 = require("react/jsx-runtime");
39
+ /**
40
+ * @file ImagePreviewService
41
+ *
42
+ * A full-screen image preview primitive plus a global imperative service.
43
+ * The declarative component owns the controlled/uncontrolled state model;
44
+ * the service only schedules one preview instance through ImagePreviewProvider.
45
+ */
46
+ const React = __importStar(require("react"));
47
+ const vector_icons_1 = require("@expo/vector-icons");
48
+ const react_native_1 = require("react-native");
49
+ const react_native_reanimated_1 = __importStar(require("react-native-reanimated"));
50
+ const react_native_gesture_handler_1 = require("react-native-gesture-handler");
51
+ const expo_image_1 = require("expo-image");
52
+ const react_native_safe_area_context_1 = require("react-native-safe-area-context");
53
+ const react_native_worklets_1 = require("react-native-worklets");
54
+ const zkit_tools_1 = require("zkit-tools");
55
+ const useI18n_1 = require("../../i18n/useI18n");
56
+ const useTheme_1 = require("../../theme/useTheme");
57
+ const LoadingSpinner_1 = require("../../ui/LoadingSpinner");
58
+ const Text_1 = require("../../ui/Text");
59
+ const MIN_SCALE = 1;
60
+ const MAX_SCALE = 5;
61
+ const DOUBLE_TAP_SCALE = 2.5;
62
+ const SCALE_EPSILON = 0.02;
63
+ const TRANSLATE_EPSILON = (0, zkit_tools_1.wp)(0.5);
64
+ const VELOCITY_THRESHOLD = 520;
65
+ const DISMISS_VELOCITY_THRESHOLD = 900;
66
+ const MODE_LOCK_DISTANCE = (0, zkit_tools_1.wp)(8);
67
+ const EDGE_RESISTANCE = 0.34;
68
+ const TAP_SUPPRESSION_RELEASE_DURATION = 320;
69
+ const DEFAULT_RENDER_AHEAD = 1;
70
+ const MAX_RENDER_AHEAD = 2;
71
+ const DEFAULT_Z_INDEX = 9999;
72
+ const CLOSE_BUTTON_SIDE = (0, zkit_tools_1.wp)(40);
73
+ const CLOSE_BUTTON_LEFT = (0, zkit_tools_1.wp)(14);
74
+ const CLOSE_BUTTON_TOP = (0, zkit_tools_1.wp)(8);
75
+ const CLOSE_ICON_SIZE = (0, zkit_tools_1.wp)(22);
76
+ const CONTROL_HIT_SLOP = (0, zkit_tools_1.wp)(12);
77
+ const CONTROL_RADIUS = (0, zkit_tools_1.wp)(20);
78
+ const BOTTOM_CHROME_OFFSET = (0, zkit_tools_1.wp)(24);
79
+ const BOTTOM_CHROME_GAP = (0, zkit_tools_1.wp)(10);
80
+ const COUNTER_MIN_HEIGHT = (0, zkit_tools_1.wp)(28);
81
+ const COUNTER_PADDING_HORIZONTAL = (0, zkit_tools_1.wp)(12);
82
+ const COUNTER_RADIUS = (0, zkit_tools_1.wp)(14);
83
+ const COUNTER_FONT_SIZE = 13;
84
+ const COUNTER_LINE_HEIGHT = 18;
85
+ const ERROR_CARD_MAX_WIDTH = (0, zkit_tools_1.wp)(260);
86
+ const ERROR_CARD_PADDING_HORIZONTAL = (0, zkit_tools_1.wp)(16);
87
+ const ERROR_CARD_PADDING_VERTICAL = (0, zkit_tools_1.wp)(14);
88
+ const ERROR_CARD_RADIUS = (0, zkit_tools_1.wp)(14);
89
+ const ERROR_ICON_SIZE = (0, zkit_tools_1.wp)(24);
90
+ const ERROR_ICON_MARGIN_BOTTOM = (0, zkit_tools_1.wp)(8);
91
+ const ERROR_TEXT_SIZE = 13;
92
+ const ERROR_TEXT_LINE_HEIGHT = 18;
93
+ const LOADING_SPINNER_SIZE = (0, zkit_tools_1.wp)(42);
94
+ const SPRING_CONFIG = { damping: 28, stiffness: 280, mass: 0.62 };
95
+ const ENTER_TIMING = {
96
+ duration: 220,
97
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
98
+ reduceMotion: react_native_reanimated_1.ReduceMotion.System,
99
+ };
100
+ const EXIT_TIMING = {
101
+ duration: 180,
102
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
103
+ reduceMotion: react_native_reanimated_1.ReduceMotion.System,
104
+ };
105
+ const RESET_TIMING = {
106
+ duration: 220,
107
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
108
+ reduceMotion: react_native_reanimated_1.ReduceMotion.System,
109
+ };
110
+ const CHROME_TIMING = {
111
+ duration: 160,
112
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.cubic),
113
+ reduceMotion: react_native_reanimated_1.ReduceMotion.System,
114
+ };
115
+ const ANDROID_REMOTE_ACCEPT_HEADER = 'image/jpeg,image/png,image/webp,*/*';
116
+ function isFiniteNumber(value) {
117
+ return typeof value === 'number' && Number.isFinite(value);
118
+ }
119
+ function clampNumber(value, min, max) {
120
+ return Math.min(Math.max(value, min), max);
121
+ }
122
+ function normalizeIndex(index, total) {
123
+ if (total <= 0)
124
+ return 0;
125
+ if (!isFiniteNumber(index))
126
+ return 0;
127
+ return clampNumber(Math.round(index), 0, total - 1);
128
+ }
129
+ function clampRenderAhead(value) {
130
+ if (!isFiniteNumber(value))
131
+ return DEFAULT_RENDER_AHEAD;
132
+ return clampNumber(Math.round(value), 0, MAX_RENDER_AHEAD);
133
+ }
134
+ function clampV(value, min, max) {
135
+ 'worklet';
136
+ return Math.min(Math.max(value, min), max);
137
+ }
138
+ function rubberClampV(value, min, max) {
139
+ 'worklet';
140
+ if (value < min)
141
+ return min + (value - min) * EDGE_RESISTANCE;
142
+ if (value > max)
143
+ return max + (value - max) * EDGE_RESISTANCE;
144
+ return value;
145
+ }
146
+ function rubberClampAxis(value, max) {
147
+ 'worklet';
148
+ if (max <= TRANSLATE_EPSILON)
149
+ return 0;
150
+ return rubberClampV(value, -max, max);
151
+ }
152
+ function getViewportPanBounds(nextScale, viewportWidth, viewportHeight) {
153
+ 'worklet';
154
+ const delta = Math.max(0, nextScale - MIN_SCALE);
155
+ return {
156
+ maxX: (viewportWidth * delta) / 2,
157
+ maxY: (viewportHeight * delta) / 2,
158
+ };
159
+ }
160
+ function hasOwnKey(value, key) {
161
+ return Object.prototype.hasOwnProperty.call(value, key);
162
+ }
163
+ function isSharedRefLike(value) {
164
+ return hasOwnKey(value, 'nativeRefType');
165
+ }
166
+ function isImageDescriptor(image) {
167
+ if (typeof image !== 'object' || image === null || Array.isArray(image))
168
+ return false;
169
+ if (isSharedRefLike(image))
170
+ return false;
171
+ return (hasOwnKey(image, 'accessibilityLabel') ||
172
+ hasOwnKey(image, 'alt') ||
173
+ hasOwnKey(image, 'blurhash') ||
174
+ hasOwnKey(image, 'cacheKey') ||
175
+ hasOwnKey(image, 'headers') ||
176
+ hasOwnKey(image, 'height') ||
177
+ hasOwnKey(image, 'id') ||
178
+ hasOwnKey(image, 'source') ||
179
+ hasOwnKey(image, 'thumbhash') ||
180
+ hasOwnKey(image, 'uri') ||
181
+ hasOwnKey(image, 'width'));
182
+ }
183
+ function getSourceUri(source) {
184
+ if (typeof source === 'string')
185
+ return source;
186
+ if (typeof source === 'number' || source == null)
187
+ return undefined;
188
+ if (Array.isArray(source)) {
189
+ for (const item of source) {
190
+ const uri = getSourceUri(item);
191
+ if (uri)
192
+ return uri;
193
+ }
194
+ return undefined;
195
+ }
196
+ return source.uri;
197
+ }
198
+ function isRemoteUri(uri) {
199
+ return !!uri && /^https?:\/\//i.test(uri);
200
+ }
201
+ function normalizeImageSourceObject(source) {
202
+ if (react_native_1.Platform.OS !== 'android' || !isRemoteUri(source.uri))
203
+ return source;
204
+ const headers = source.headers;
205
+ if ((headers === null || headers === void 0 ? void 0 : headers.Accept) || (headers === null || headers === void 0 ? void 0 : headers.accept))
206
+ return source;
207
+ return {
208
+ ...source,
209
+ headers: {
210
+ ...headers,
211
+ Accept: ANDROID_REMOTE_ACCEPT_HEADER,
212
+ },
213
+ };
214
+ }
215
+ function normalizeRenderableSource(source) {
216
+ if (typeof source === 'string') {
217
+ return normalizeImageSourceObject({ uri: source });
218
+ }
219
+ if (Array.isArray(source)) {
220
+ return source.map((item) => typeof item === 'string'
221
+ ? normalizeImageSourceObject({ uri: item })
222
+ : normalizeImageSourceObject(item));
223
+ }
224
+ if (typeof source === 'object' && source !== null && !isSharedRefLike(source)) {
225
+ return normalizeImageSourceObject(source);
226
+ }
227
+ return source;
228
+ }
229
+ function getSourceSize(source) {
230
+ if (!source || typeof source === 'string' || typeof source === 'number')
231
+ return null;
232
+ if (Array.isArray(source)) {
233
+ for (const item of source) {
234
+ const size = getSourceSize(item);
235
+ if (size)
236
+ return size;
237
+ }
238
+ return null;
239
+ }
240
+ const sourceObject = source;
241
+ const width = sourceObject.width;
242
+ const height = sourceObject.height;
243
+ if (isFiniteNumber(width) && isFiniteNumber(height) && width > 0 && height > 0) {
244
+ return { width, height };
245
+ }
246
+ return null;
247
+ }
248
+ function isRenderableSource(source) {
249
+ if (source == null)
250
+ return false;
251
+ if (typeof source === 'string')
252
+ return source.trim().length > 0;
253
+ if (Array.isArray(source))
254
+ return source.length > 0;
255
+ return true;
256
+ }
257
+ function createSourceFromDescriptor(image) {
258
+ if (image.source !== undefined)
259
+ return image.source;
260
+ const { accessibilityLabel, alt, id, source: _source, ...source } = image;
261
+ return source;
262
+ }
263
+ function getImageKey(source, image, index) {
264
+ var _a, _b, _c, _d;
265
+ if (isImageDescriptor(image) && image.id)
266
+ return image.id;
267
+ if (typeof source === 'string' || typeof source === 'number')
268
+ return String(source);
269
+ if (Array.isArray(source)) {
270
+ const firstKey = source
271
+ .map((item) => getSourceUri(item))
272
+ .find((uri) => !!uri);
273
+ return firstKey !== null && firstKey !== void 0 ? firstKey : `image-${index}`;
274
+ }
275
+ const sourceObject = source;
276
+ return ((_d = (_c = (_b = (_a = sourceObject.cacheKey) !== null && _a !== void 0 ? _a : sourceObject.uri) !== null && _b !== void 0 ? _b : sourceObject.blurhash) !== null && _c !== void 0 ? _c : sourceObject.thumbhash) !== null && _d !== void 0 ? _d : `image-${index}`);
277
+ }
278
+ function normalizeImage(image, index) {
279
+ const rawSource = isImageDescriptor(image) ? createSourceFromDescriptor(image) : image;
280
+ if (!isRenderableSource(rawSource))
281
+ return null;
282
+ const source = normalizeRenderableSource(rawSource);
283
+ const size = getSourceSize(source);
284
+ const descriptor = isImageDescriptor(image) ? image : null;
285
+ return {
286
+ key: getImageKey(rawSource, image, index),
287
+ source,
288
+ uri: getSourceUri(rawSource),
289
+ width: size === null || size === void 0 ? void 0 : size.width,
290
+ height: size === null || size === void 0 ? void 0 : size.height,
291
+ alt: descriptor === null || descriptor === void 0 ? void 0 : descriptor.alt,
292
+ accessibilityLabel: descriptor === null || descriptor === void 0 ? void 0 : descriptor.accessibilityLabel,
293
+ raw: image,
294
+ };
295
+ }
296
+ function normalizeImages(images) {
297
+ return images
298
+ .map((image, index) => normalizeImage(image, index))
299
+ .filter((image) => image != null);
300
+ }
301
+ function resolveInteractions(interactions) {
302
+ var _a, _b, _c, _d, _e, _f;
303
+ return {
304
+ tapBehavior: (_a = interactions === null || interactions === void 0 ? void 0 : interactions.tapBehavior) !== null && _a !== void 0 ? _a : 'none',
305
+ pinchToZoom: (_b = interactions === null || interactions === void 0 ? void 0 : interactions.pinchToZoom) !== null && _b !== void 0 ? _b : true,
306
+ doubleTapToZoom: (_c = interactions === null || interactions === void 0 ? void 0 : interactions.doubleTapToZoom) !== null && _c !== void 0 ? _c : true,
307
+ swipeToChange: (_d = interactions === null || interactions === void 0 ? void 0 : interactions.swipeToChange) !== null && _d !== void 0 ? _d : true,
308
+ swipeDownToClose: (_e = interactions === null || interactions === void 0 ? void 0 : interactions.swipeDownToClose) !== null && _e !== void 0 ? _e : true,
309
+ backToClose: (_f = interactions === null || interactions === void 0 ? void 0 : interactions.backToClose) !== null && _f !== void 0 ? _f : true,
310
+ };
311
+ }
312
+ function createOpenMeta(reason, value, images) {
313
+ var _a;
314
+ return {
315
+ reason,
316
+ value,
317
+ image: (_a = images[value]) !== null && _a !== void 0 ? _a : null,
318
+ total: images.length,
319
+ };
320
+ }
321
+ function createChangeMeta(reason, previousValue, value, images) {
322
+ var _a;
323
+ return {
324
+ reason,
325
+ previousValue,
326
+ value,
327
+ image: (_a = images[value]) !== null && _a !== void 0 ? _a : null,
328
+ total: images.length,
329
+ };
330
+ }
331
+ function createResult(reason, value, images) {
332
+ var _a;
333
+ const normalizedValue = normalizeIndex(value, images.length);
334
+ return {
335
+ reason,
336
+ value: normalizedValue,
337
+ image: (_a = images[normalizedValue]) !== null && _a !== void 0 ? _a : null,
338
+ total: images.length,
339
+ };
340
+ }
341
+ function getPrefetchUris(images, value, mode) {
342
+ var _a;
343
+ if (mode === false || images.length <= 1)
344
+ return [];
345
+ const indexes = mode === 'all'
346
+ ? images.map((_, index) => index)
347
+ : [value - 1, value + 1].filter((index) => index >= 0 && index < images.length);
348
+ const seen = new Set();
349
+ const uris = [];
350
+ for (const index of indexes) {
351
+ const uri = (_a = images[index]) === null || _a === void 0 ? void 0 : _a.uri;
352
+ if (!uri || seen.has(uri))
353
+ continue;
354
+ seen.add(uri);
355
+ uris.push(uri);
356
+ }
357
+ return uris;
358
+ }
359
+ class ImagePreviewServiceClass {
360
+ constructor() {
361
+ this.controller = null;
362
+ this.currentRequest = null;
363
+ this.requestId = 0;
364
+ }
365
+ _bind(controller) {
366
+ this.controller = controller;
367
+ }
368
+ open(options) {
369
+ const images = normalizeImages(options.images);
370
+ const id = ++this.requestId;
371
+ let resolveResult = () => { };
372
+ const result = new Promise((resolve) => {
373
+ resolveResult = resolve;
374
+ });
375
+ const request = {
376
+ ...options,
377
+ id,
378
+ index: normalizeIndex(options.index, images.length),
379
+ result,
380
+ resolve: resolveResult,
381
+ settled: false,
382
+ };
383
+ const handle = {
384
+ id,
385
+ result,
386
+ close: (reason = 'api') => {
387
+ var _a;
388
+ if (((_a = this.currentRequest) === null || _a === void 0 ? void 0 : _a.id) === id) {
389
+ this.close(reason);
390
+ }
391
+ },
392
+ setValue: (value) => {
393
+ var _a;
394
+ if (((_a = this.currentRequest) === null || _a === void 0 ? void 0 : _a.id) === id) {
395
+ this.setValue(value);
396
+ }
397
+ },
398
+ getSnapshot: () => this.getSnapshot(),
399
+ };
400
+ if (this.currentRequest && !this.currentRequest.settled) {
401
+ this.finishRequest(this.currentRequest, 'replace');
402
+ }
403
+ this.currentRequest = request;
404
+ if (!images.length) {
405
+ this.finishRequest(request, 'empty');
406
+ return handle;
407
+ }
408
+ if (!this.controller) {
409
+ console.warn('[imagePreview] ImagePreviewProvider is not mounted.');
410
+ this.finishRequest(request, 'unmount');
411
+ return handle;
412
+ }
413
+ this.controller.open(request);
414
+ return handle;
415
+ }
416
+ close(reason = 'api') {
417
+ if (!this.currentRequest || this.currentRequest.settled)
418
+ return;
419
+ if (!this.controller) {
420
+ this.finishRequest(this.currentRequest, reason);
421
+ return;
422
+ }
423
+ this.controller.close(reason);
424
+ }
425
+ setValue(value) {
426
+ var _a;
427
+ (_a = this.controller) === null || _a === void 0 ? void 0 : _a.setValue(value);
428
+ }
429
+ getSnapshot() {
430
+ var _a, _b;
431
+ return ((_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.getSnapshot()) !== null && _b !== void 0 ? _b : {
432
+ open: false,
433
+ value: 0,
434
+ image: null,
435
+ total: 0,
436
+ });
437
+ }
438
+ _complete(id, result) {
439
+ var _a;
440
+ if (((_a = this.currentRequest) === null || _a === void 0 ? void 0 : _a.id) !== id)
441
+ return;
442
+ this.finishRequest(this.currentRequest, result.reason, result);
443
+ }
444
+ _dispose(id) {
445
+ var _a;
446
+ if (((_a = this.currentRequest) === null || _a === void 0 ? void 0 : _a.id) !== id || this.currentRequest.settled)
447
+ return;
448
+ this.finishRequest(this.currentRequest, 'unmount');
449
+ }
450
+ finishRequest(request, reason, result) {
451
+ var _a, _b;
452
+ if (request.settled)
453
+ return;
454
+ request.settled = true;
455
+ const finalResult = result !== null && result !== void 0 ? result : createResult(reason, request.index, normalizeImages(request.images));
456
+ (_a = request.onClose) === null || _a === void 0 ? void 0 : _a.call(request, finalResult);
457
+ request.resolve(finalResult);
458
+ if (((_b = this.currentRequest) === null || _b === void 0 ? void 0 : _b.id) === request.id) {
459
+ this.currentRequest = null;
460
+ }
461
+ }
462
+ }
463
+ exports.imagePreview = new ImagePreviewServiceClass();
464
+ function CloseButton({ color, backgroundColor, label, onPress, }) {
465
+ return ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { accessibilityLabel: label, accessibilityRole: "button", hitSlop: CONTROL_HIT_SLOP, onPress: onPress, style: ({ pressed }) => [
466
+ styles.closeButton,
467
+ { backgroundColor, opacity: pressed ? 0.72 : 1 },
468
+ ], children: (0, jsx_runtime_1.jsx)(vector_icons_1.Feather, { color: color, name: "x", size: CLOSE_ICON_SIZE }) }));
469
+ }
470
+ function ZoomableImage({ active, contentFit, currentIndexSV, dismissible, image, imageAccessibilityLabel, imageProps, imageStyle, index, interactions, labels, onImageError, onImageLoad, onPageChange, onRequestClose, onSingleTap, pageTranslateX, totalCount, viewport, backdropOpacity, colors, }) {
471
+ var _a, _b;
472
+ const [failed, setFailed] = React.useState(false);
473
+ const [loading, setLoading] = React.useState(true);
474
+ const imageCachePolicy = (_a = imageProps === null || imageProps === void 0 ? void 0 : imageProps.cachePolicy) !== null && _a !== void 0 ? _a : 'memory-disk';
475
+ const imagePriority = (_b = imageProps === null || imageProps === void 0 ? void 0 : imageProps.priority) !== null && _b !== void 0 ? _b : (active ? 'high' : 'low');
476
+ const viewportStyle = React.useMemo(() => ({ width: viewport.width, height: viewport.height }), [viewport.height, viewport.width]);
477
+ const scale = (0, react_native_reanimated_1.useSharedValue)(1);
478
+ const savedScale = (0, react_native_reanimated_1.useSharedValue)(1);
479
+ const translateX = (0, react_native_reanimated_1.useSharedValue)(0);
480
+ const translateY = (0, react_native_reanimated_1.useSharedValue)(0);
481
+ const savedTranslateX = (0, react_native_reanimated_1.useSharedValue)(0);
482
+ const savedTranslateY = (0, react_native_reanimated_1.useSharedValue)(0);
483
+ const dismissY = (0, react_native_reanimated_1.useSharedValue)(0);
484
+ const mode = (0, react_native_reanimated_1.useSharedValue)('none');
485
+ const tapSuppression = (0, react_native_reanimated_1.useSharedValue)(0);
486
+ const pinchActive = (0, react_native_reanimated_1.useSharedValue)(0);
487
+ const gestureFocalX = (0, react_native_reanimated_1.useSharedValue)(0);
488
+ const gestureFocalY = (0, react_native_reanimated_1.useSharedValue)(0);
489
+ const panOffsetX = (0, react_native_reanimated_1.useSharedValue)(0);
490
+ const panOffsetY = (0, react_native_reanimated_1.useSharedValue)(0);
491
+ const gestureBoundX = (0, react_native_reanimated_1.useSharedValue)(0);
492
+ const gestureBoundY = (0, react_native_reanimated_1.useSharedValue)(0);
493
+ React.useEffect(() => {
494
+ setFailed(false);
495
+ setLoading(true);
496
+ }, [image.key]);
497
+ React.useEffect(() => {
498
+ if (mode.value !== 'none')
499
+ return;
500
+ const bounds = getViewportPanBounds(scale.value, viewport.width, viewport.height);
501
+ const clamped = {
502
+ x: clampV(translateX.value, -bounds.maxX, bounds.maxX),
503
+ y: clampV(translateY.value, -bounds.maxY, bounds.maxY),
504
+ };
505
+ translateX.value = (0, react_native_reanimated_1.withSpring)(clamped.x, SPRING_CONFIG);
506
+ translateY.value = (0, react_native_reanimated_1.withSpring)(clamped.y, SPRING_CONFIG);
507
+ savedTranslateX.value = clamped.x;
508
+ savedTranslateY.value = clamped.y;
509
+ // eslint-disable-next-line react-hooks/exhaustive-deps
510
+ }, [viewport.height, viewport.width]);
511
+ const handleImageLoadStart = React.useCallback(() => {
512
+ setFailed(false);
513
+ setLoading(true);
514
+ }, []);
515
+ const handleImageLoad = React.useCallback((event) => {
516
+ setLoading(false);
517
+ onImageLoad === null || onImageLoad === void 0 ? void 0 : onImageLoad(event, { value: index, image });
518
+ }, [image, index, onImageLoad]);
519
+ const handleImageError = React.useCallback((event) => {
520
+ setFailed(true);
521
+ setLoading(false);
522
+ onImageError === null || onImageError === void 0 ? void 0 : onImageError(event, { value: index, image });
523
+ }, [image, index, onImageError]);
524
+ const resetImageTransform = () => {
525
+ 'worklet';
526
+ (0, react_native_reanimated_1.cancelAnimation)(scale);
527
+ (0, react_native_reanimated_1.cancelAnimation)(translateX);
528
+ (0, react_native_reanimated_1.cancelAnimation)(translateY);
529
+ scale.value = (0, react_native_reanimated_1.withTiming)(MIN_SCALE, RESET_TIMING);
530
+ savedScale.value = MIN_SCALE;
531
+ translateX.value = (0, react_native_reanimated_1.withTiming)(0, RESET_TIMING);
532
+ translateY.value = (0, react_native_reanimated_1.withTiming)(0, RESET_TIMING);
533
+ savedTranslateX.value = 0;
534
+ savedTranslateY.value = 0;
535
+ };
536
+ const blockTap = () => {
537
+ 'worklet';
538
+ (0, react_native_reanimated_1.cancelAnimation)(tapSuppression);
539
+ tapSuppression.value = 1;
540
+ };
541
+ const releaseTap = () => {
542
+ 'worklet';
543
+ tapSuppression.value = (0, react_native_reanimated_1.withTiming)(0, { duration: TAP_SUPPRESSION_RELEASE_DURATION });
544
+ };
545
+ const settleImageTransform = () => {
546
+ 'worklet';
547
+ (0, react_native_reanimated_1.cancelAnimation)(scale);
548
+ (0, react_native_reanimated_1.cancelAnimation)(translateX);
549
+ (0, react_native_reanimated_1.cancelAnimation)(translateY);
550
+ const currentScale = scale.value;
551
+ let targetScale = clampV(currentScale, MIN_SCALE, MAX_SCALE);
552
+ if (targetScale <= MIN_SCALE + SCALE_EPSILON) {
553
+ targetScale = MIN_SCALE;
554
+ if (Math.abs(currentScale - targetScale) > SCALE_EPSILON) {
555
+ scale.value = (0, react_native_reanimated_1.withSpring)(targetScale, SPRING_CONFIG);
556
+ }
557
+ else {
558
+ scale.value = targetScale;
559
+ }
560
+ if (Math.abs(translateX.value) > TRANSLATE_EPSILON) {
561
+ translateX.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG);
562
+ }
563
+ else {
564
+ translateX.value = 0;
565
+ }
566
+ if (Math.abs(translateY.value) > TRANSLATE_EPSILON) {
567
+ translateY.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG);
568
+ }
569
+ else {
570
+ translateY.value = 0;
571
+ }
572
+ savedScale.value = targetScale;
573
+ savedTranslateX.value = 0;
574
+ savedTranslateY.value = 0;
575
+ return;
576
+ }
577
+ const bounds = getViewportPanBounds(targetScale, viewport.width, viewport.height);
578
+ const clamped = {
579
+ x: clampV(translateX.value, -bounds.maxX, bounds.maxX),
580
+ y: clampV(translateY.value, -bounds.maxY, bounds.maxY),
581
+ };
582
+ if (Math.abs(currentScale - targetScale) > SCALE_EPSILON) {
583
+ scale.value = (0, react_native_reanimated_1.withSpring)(targetScale, SPRING_CONFIG);
584
+ }
585
+ else {
586
+ scale.value = targetScale;
587
+ }
588
+ if (Math.abs(translateX.value - clamped.x) > TRANSLATE_EPSILON) {
589
+ translateX.value = (0, react_native_reanimated_1.withSpring)(clamped.x, SPRING_CONFIG);
590
+ }
591
+ else {
592
+ translateX.value = clamped.x;
593
+ }
594
+ if (Math.abs(translateY.value - clamped.y) > TRANSLATE_EPSILON) {
595
+ translateY.value = (0, react_native_reanimated_1.withSpring)(clamped.y, SPRING_CONFIG);
596
+ }
597
+ else {
598
+ translateY.value = clamped.y;
599
+ }
600
+ savedScale.value = targetScale;
601
+ savedTranslateX.value = clamped.x;
602
+ savedTranslateY.value = clamped.y;
603
+ };
604
+ (0, react_native_reanimated_1.useAnimatedReaction)(() => currentIndexSV.value, (curr, prev) => {
605
+ if (prev !== null && prev === index && curr !== index) {
606
+ resetImageTransform();
607
+ dismissY.value = 0;
608
+ mode.value = 'none';
609
+ }
610
+ });
611
+ const singleTap = react_native_gesture_handler_1.Gesture.Tap()
612
+ .enabled(active && interactions.tapBehavior !== 'none')
613
+ .numberOfTaps(1)
614
+ .maxDuration(220)
615
+ .onEnd(() => {
616
+ 'worklet';
617
+ if (currentIndexSV.value !== index)
618
+ return;
619
+ if (mode.value !== 'none')
620
+ return;
621
+ if (tapSuppression.value > 0.01)
622
+ return;
623
+ if (scale.value > 1.05)
624
+ return;
625
+ if (interactions.tapBehavior === 'close') {
626
+ if (dismissible)
627
+ (0, react_native_worklets_1.scheduleOnRN)(onRequestClose, 'tap');
628
+ return;
629
+ }
630
+ (0, react_native_worklets_1.scheduleOnRN)(onSingleTap);
631
+ });
632
+ const doubleTap = react_native_gesture_handler_1.Gesture.Tap()
633
+ .enabled(active && interactions.doubleTapToZoom)
634
+ .numberOfTaps(2)
635
+ .maxDuration(300)
636
+ .onEnd((event) => {
637
+ 'worklet';
638
+ if (currentIndexSV.value !== index)
639
+ return;
640
+ if (mode.value !== 'none')
641
+ return;
642
+ if (tapSuppression.value > 0.01)
643
+ return;
644
+ (0, react_native_reanimated_1.cancelAnimation)(scale);
645
+ (0, react_native_reanimated_1.cancelAnimation)(translateX);
646
+ (0, react_native_reanimated_1.cancelAnimation)(translateY);
647
+ if (scale.value > 1.05) {
648
+ resetImageTransform();
649
+ }
650
+ else {
651
+ const targetScale = Math.min(DOUBLE_TAP_SCALE, MAX_SCALE);
652
+ const focalX = event.x - viewport.width / 2;
653
+ const focalY = event.y - viewport.height / 2;
654
+ const bounds = getViewportPanBounds(targetScale, viewport.width, viewport.height);
655
+ const targetX = clampV(-focalX * (targetScale - MIN_SCALE), -bounds.maxX, bounds.maxX);
656
+ const targetY = clampV(-focalY * (targetScale - MIN_SCALE), -bounds.maxY, bounds.maxY);
657
+ scale.value = (0, react_native_reanimated_1.withTiming)(targetScale, RESET_TIMING);
658
+ savedScale.value = targetScale;
659
+ translateX.value = (0, react_native_reanimated_1.withTiming)(targetX, RESET_TIMING);
660
+ translateY.value = (0, react_native_reanimated_1.withTiming)(targetY, RESET_TIMING);
661
+ savedTranslateX.value = targetX;
662
+ savedTranslateY.value = targetY;
663
+ }
664
+ });
665
+ const pinch = react_native_gesture_handler_1.Gesture.Pinch()
666
+ .enabled(active && interactions.pinchToZoom)
667
+ .onStart((event) => {
668
+ 'worklet';
669
+ if (currentIndexSV.value !== index)
670
+ return;
671
+ pinchActive.value = 1;
672
+ blockTap();
673
+ mode.value = 'pinch';
674
+ (0, react_native_reanimated_1.cancelAnimation)(scale);
675
+ (0, react_native_reanimated_1.cancelAnimation)(translateX);
676
+ (0, react_native_reanimated_1.cancelAnimation)(translateY);
677
+ (0, react_native_reanimated_1.cancelAnimation)(pageTranslateX);
678
+ (0, react_native_reanimated_1.cancelAnimation)(dismissY);
679
+ pageTranslateX.value = 0;
680
+ dismissY.value = 0;
681
+ backdropOpacity.value = (0, react_native_reanimated_1.withSpring)(1, SPRING_CONFIG);
682
+ savedScale.value = scale.value;
683
+ savedTranslateX.value = translateX.value;
684
+ savedTranslateY.value = translateY.value;
685
+ gestureFocalX.value = event.focalX - viewport.width / 2;
686
+ gestureFocalY.value = event.focalY - viewport.height / 2;
687
+ })
688
+ .onUpdate((event) => {
689
+ 'worklet';
690
+ if (currentIndexSV.value !== index)
691
+ return;
692
+ if (event.numberOfPointers < 2)
693
+ return;
694
+ const nextScale = clampV(savedScale.value * event.scale, MIN_SCALE * 0.5, MAX_SCALE + MIN_SCALE);
695
+ scale.value = nextScale;
696
+ translateX.value =
697
+ savedTranslateX.value + (savedScale.value - nextScale) * gestureFocalX.value;
698
+ translateY.value =
699
+ savedTranslateY.value + (savedScale.value - nextScale) * gestureFocalY.value;
700
+ })
701
+ .onEnd(() => {
702
+ 'worklet';
703
+ if (currentIndexSV.value !== index) {
704
+ pinchActive.value = 0;
705
+ return;
706
+ }
707
+ settleImageTransform();
708
+ mode.value = 'none';
709
+ pinchActive.value = 0;
710
+ releaseTap();
711
+ })
712
+ .onFinalize(() => {
713
+ 'worklet';
714
+ if (pinchActive.value > 0 || mode.value === 'pinch') {
715
+ settleImageTransform();
716
+ mode.value = 'none';
717
+ }
718
+ pinchActive.value = 0;
719
+ releaseTap();
720
+ });
721
+ const pan = react_native_gesture_handler_1.Gesture.Pan()
722
+ .enabled(active &&
723
+ (interactions.pinchToZoom ||
724
+ interactions.doubleTapToZoom ||
725
+ interactions.swipeToChange ||
726
+ interactions.swipeDownToClose))
727
+ .minPointers(1)
728
+ .minDistance(1)
729
+ .averageTouches(true)
730
+ .onStart((event) => {
731
+ 'worklet';
732
+ if (currentIndexSV.value !== index)
733
+ return;
734
+ blockTap();
735
+ (0, react_native_reanimated_1.cancelAnimation)(translateX);
736
+ (0, react_native_reanimated_1.cancelAnimation)(translateY);
737
+ (0, react_native_reanimated_1.cancelAnimation)(pageTranslateX);
738
+ (0, react_native_reanimated_1.cancelAnimation)(dismissY);
739
+ (0, react_native_reanimated_1.cancelAnimation)(backdropOpacity);
740
+ savedTranslateX.value = translateX.value;
741
+ savedTranslateY.value = translateY.value;
742
+ panOffsetX.value = 0;
743
+ panOffsetY.value = 0;
744
+ const bounds = getViewportPanBounds(scale.value, viewport.width, viewport.height);
745
+ gestureBoundX.value = bounds.maxX;
746
+ gestureBoundY.value = bounds.maxY;
747
+ pageTranslateX.value = 0;
748
+ dismissY.value = 0;
749
+ backdropOpacity.value = (0, react_native_reanimated_1.withSpring)(1, SPRING_CONFIG);
750
+ if (event.numberOfPointers >= 2 || pinchActive.value > 0 || mode.value === 'pinch') {
751
+ mode.value = 'none';
752
+ }
753
+ else {
754
+ mode.value = scale.value > MIN_SCALE + SCALE_EPSILON ? 'drag' : 'undecided';
755
+ }
756
+ })
757
+ .onUpdate((event) => {
758
+ 'worklet';
759
+ if (currentIndexSV.value !== index)
760
+ return;
761
+ if (pinchActive.value > 0) {
762
+ panOffsetX.value = event.translationX;
763
+ panOffsetY.value = event.translationY;
764
+ return;
765
+ }
766
+ if (mode.value === 'none') {
767
+ savedTranslateX.value = translateX.value;
768
+ savedTranslateY.value = translateY.value;
769
+ panOffsetX.value = event.translationX;
770
+ panOffsetY.value = event.translationY;
771
+ const bounds = getViewportPanBounds(scale.value, viewport.width, viewport.height);
772
+ gestureBoundX.value = bounds.maxX;
773
+ gestureBoundY.value = bounds.maxY;
774
+ mode.value = scale.value > MIN_SCALE + SCALE_EPSILON ? 'drag' : 'undecided';
775
+ return;
776
+ }
777
+ const deltaX = event.translationX - panOffsetX.value;
778
+ const deltaY = event.translationY - panOffsetY.value;
779
+ if (mode.value === 'undecided') {
780
+ const absX = Math.abs(deltaX);
781
+ const absY = Math.abs(deltaY);
782
+ if (absX <= MODE_LOCK_DISTANCE && absY <= MODE_LOCK_DISTANCE)
783
+ return;
784
+ if (absX >= absY) {
785
+ if (!interactions.swipeToChange)
786
+ return;
787
+ mode.value = 'swipe';
788
+ }
789
+ else {
790
+ if (!dismissible || !interactions.swipeDownToClose)
791
+ return;
792
+ mode.value = 'dismiss';
793
+ }
794
+ }
795
+ if (mode.value === 'drag') {
796
+ const nextX = savedTranslateX.value + deltaX;
797
+ const nextY = savedTranslateY.value + deltaY;
798
+ translateX.value = rubberClampAxis(nextX, gestureBoundX.value);
799
+ translateY.value = rubberClampAxis(nextY, gestureBoundY.value);
800
+ }
801
+ else if (mode.value === 'swipe') {
802
+ let tx = deltaX;
803
+ const canNext = currentIndexSV.value < totalCount - 1;
804
+ const canPrev = currentIndexSV.value > 0;
805
+ if ((!canPrev && tx > 0) || (!canNext && tx < 0)) {
806
+ tx *= EDGE_RESISTANCE;
807
+ }
808
+ pageTranslateX.value = tx;
809
+ }
810
+ else if (mode.value === 'dismiss') {
811
+ const dragY = Math.max(0, deltaY);
812
+ dismissY.value = dragY;
813
+ const progress = dragY / (viewport.height * 0.42);
814
+ backdropOpacity.value = (0, react_native_reanimated_1.interpolate)(progress, [0, 1], [1, 0.1], react_native_reanimated_1.Extrapolation.CLAMP);
815
+ }
816
+ })
817
+ .onEnd((event) => {
818
+ 'worklet';
819
+ if (currentIndexSV.value !== index)
820
+ return;
821
+ if (pinchActive.value > 0 || mode.value === 'pinch') {
822
+ mode.value = 'none';
823
+ return;
824
+ }
825
+ const deltaX = event.translationX - panOffsetX.value;
826
+ const deltaY = event.translationY - panOffsetY.value;
827
+ if (mode.value === 'drag') {
828
+ settleImageTransform();
829
+ releaseTap();
830
+ }
831
+ else if (mode.value === 'swipe') {
832
+ const canNext = currentIndexSV.value < totalCount - 1;
833
+ const canPrev = currentIndexSV.value > 0;
834
+ const swipeThreshold = viewport.width * 0.23;
835
+ const goNext = canNext && (deltaX < -swipeThreshold || event.velocityX < -VELOCITY_THRESHOLD);
836
+ const goPrev = canPrev && (deltaX > swipeThreshold || event.velocityX > VELOCITY_THRESHOLD);
837
+ if (goNext) {
838
+ const target = currentIndexSV.value + 1;
839
+ pageTranslateX.value = (0, react_native_reanimated_1.withSpring)(-viewport.width, SPRING_CONFIG, (finished) => {
840
+ 'worklet';
841
+ if (finished) {
842
+ currentIndexSV.value = target;
843
+ pageTranslateX.value = 0;
844
+ releaseTap();
845
+ (0, react_native_worklets_1.scheduleOnRN)(onPageChange, target);
846
+ }
847
+ });
848
+ }
849
+ else if (goPrev) {
850
+ const target = currentIndexSV.value - 1;
851
+ pageTranslateX.value = (0, react_native_reanimated_1.withSpring)(viewport.width, SPRING_CONFIG, (finished) => {
852
+ 'worklet';
853
+ if (finished) {
854
+ currentIndexSV.value = target;
855
+ pageTranslateX.value = 0;
856
+ releaseTap();
857
+ (0, react_native_worklets_1.scheduleOnRN)(onPageChange, target);
858
+ }
859
+ });
860
+ }
861
+ else {
862
+ pageTranslateX.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG, (finished) => {
863
+ 'worklet';
864
+ if (finished)
865
+ releaseTap();
866
+ });
867
+ }
868
+ }
869
+ else if (mode.value === 'dismiss') {
870
+ const dismissThreshold = viewport.height * 0.12;
871
+ if (deltaY > dismissThreshold || event.velocityY > DISMISS_VELOCITY_THRESHOLD) {
872
+ dismissY.value = (0, react_native_reanimated_1.withTiming)(viewport.height, EXIT_TIMING);
873
+ backdropOpacity.value = (0, react_native_reanimated_1.withTiming)(0, EXIT_TIMING, () => {
874
+ (0, react_native_worklets_1.scheduleOnRN)(onRequestClose, 'swipeDown');
875
+ });
876
+ }
877
+ else {
878
+ dismissY.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG);
879
+ backdropOpacity.value = (0, react_native_reanimated_1.withSpring)(1, SPRING_CONFIG);
880
+ releaseTap();
881
+ }
882
+ }
883
+ else {
884
+ releaseTap();
885
+ }
886
+ mode.value = 'none';
887
+ })
888
+ .onFinalize(() => {
889
+ 'worklet';
890
+ if (pinchActive.value > 0 || mode.value === 'pinch')
891
+ return;
892
+ if (mode.value === 'none')
893
+ return;
894
+ if (mode.value === 'drag') {
895
+ settleImageTransform();
896
+ }
897
+ else if (mode.value === 'swipe') {
898
+ pageTranslateX.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG);
899
+ }
900
+ else if (mode.value === 'dismiss') {
901
+ dismissY.value = (0, react_native_reanimated_1.withSpring)(0, SPRING_CONFIG);
902
+ backdropOpacity.value = (0, react_native_reanimated_1.withSpring)(1, SPRING_CONFIG);
903
+ }
904
+ mode.value = 'none';
905
+ releaseTap();
906
+ });
907
+ const tapGestures = react_native_gesture_handler_1.Gesture.Exclusive(doubleTap, singleTap);
908
+ const composed = react_native_gesture_handler_1.Gesture.Simultaneous(tapGestures, react_native_gesture_handler_1.Gesture.Simultaneous(pinch, pan));
909
+ const pageStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
910
+ const pageX = (index - currentIndexSV.value) * viewport.width + pageTranslateX.value;
911
+ return {
912
+ opacity: backdropOpacity.value,
913
+ transform: [
914
+ { translateX: pageX + translateX.value },
915
+ { translateY: translateY.value + dismissY.value },
916
+ { scale: scale.value },
917
+ ],
918
+ };
919
+ });
920
+ return ((0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: composed, children: (0, jsx_runtime_1.jsxs)(react_native_reanimated_1.default.View, { pointerEvents: active ? 'auto' : 'none', style: [styles.imagePage, viewportStyle, pageStyle], children: [(0, jsx_runtime_1.jsx)(expo_image_1.Image, { ...imageProps, accessibilityLabel: imageAccessibilityLabel, alt: imageAccessibilityLabel, cachePolicy: imageCachePolicy, contentFit: contentFit, onError: handleImageError, onLoad: handleImageLoad, onLoadStart: handleImageLoadStart, priority: imagePriority, recyclingKey: image.key, source: image.source, style: [viewportStyle, imageStyle] }), loading && !failed && ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: styles.loadingState, children: (0, jsx_runtime_1.jsx)(LoadingSpinner_1.LoadingSpinner, { color: colors.loading, size: LOADING_SPINNER_SIZE, speed: 1.8 }) })), failed && ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: styles.errorState, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.errorCard, { backgroundColor: colors.errorBackground }], children: [(0, jsx_runtime_1.jsx)(vector_icons_1.Feather, { color: colors.errorText, name: "image", size: ERROR_ICON_SIZE, style: styles.errorIcon }), (0, jsx_runtime_1.jsx)(Text_1.Text, { align: "center", color: colors.errorText, lineHeight: ERROR_TEXT_LINE_HEIGHT, size: ERROR_TEXT_SIZE, weight: "medium", children: labels.loadFailed })] }) }))] }) }));
921
+ }
922
+ function ImagePreviewOverlay({ accessibilityLabel, closeButton, colors, contentFit, counter, disabled, dismissible, imageProps, imageStyle, images, interactions, labels, modalProps, onChange, onCloseComplete, onImageError, onImageLoad, onOpenChange, open, prefetch, prefetchCachePolicy, renderAhead, renderFooter, renderHeader, renderOverlay, rootStyle, testID, value, }) {
923
+ var _a, _b;
924
+ const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
925
+ const window = (0, react_native_1.useWindowDimensions)();
926
+ const [mounted, setMounted] = React.useState(false);
927
+ const [closing, setClosing] = React.useState(false);
928
+ const [renderIndex, setRenderIndex] = React.useState(() => normalizeIndex(value, images.length));
929
+ const [chromeVisible, setChromeVisible] = React.useState(true);
930
+ const closeReasonRef = React.useRef('api');
931
+ const closingRef = React.useRef(false);
932
+ const imagesRef = React.useRef(images);
933
+ const onCloseCompleteRef = React.useRef(onCloseComplete);
934
+ const renderIndexRef = React.useRef(renderIndex);
935
+ imagesRef.current = images;
936
+ onCloseCompleteRef.current = onCloseComplete;
937
+ renderIndexRef.current = renderIndex;
938
+ const backdropOpacity = (0, react_native_reanimated_1.useSharedValue)(0);
939
+ const chromeOpacity = (0, react_native_reanimated_1.useSharedValue)(1);
940
+ const pageTranslateX = (0, react_native_reanimated_1.useSharedValue)(0);
941
+ const currentIndexSV = (0, react_native_reanimated_1.useSharedValue)(normalizeIndex(value, images.length));
942
+ const viewport = React.useMemo(() => ({ width: window.width, height: window.height }), [window.height, window.width]);
943
+ const total = images.length;
944
+ const normalizedValue = normalizeIndex(value, total);
945
+ const currentImage = (_b = (_a = images[renderIndex]) !== null && _a !== void 0 ? _a : images[normalizedValue]) !== null && _b !== void 0 ? _b : null;
946
+ const setChromeVisibleAnimated = React.useCallback((nextVisible) => {
947
+ setChromeVisible(nextVisible);
948
+ chromeOpacity.value = (0, react_native_reanimated_1.withTiming)(nextVisible ? 1 : 0, CHROME_TIMING);
949
+ }, [chromeOpacity]);
950
+ const requestClose = React.useCallback((reason) => {
951
+ if (disabled)
952
+ return;
953
+ if (!dismissible &&
954
+ reason !== 'api' &&
955
+ reason !== 'empty' &&
956
+ reason !== 'replace' &&
957
+ reason !== 'unmount') {
958
+ return;
959
+ }
960
+ if (closingRef.current)
961
+ return;
962
+ closeReasonRef.current = reason;
963
+ onOpenChange(false, createOpenMeta(reason, renderIndexRef.current, images));
964
+ }, [disabled, dismissible, images, onOpenChange]);
965
+ const finishClose = React.useCallback(() => {
966
+ const result = createResult(closeReasonRef.current, renderIndexRef.current, imagesRef.current);
967
+ setMounted(false);
968
+ setClosing(false);
969
+ onCloseCompleteRef.current(result);
970
+ }, []);
971
+ const jumpToValue = React.useCallback((nextValue, reason) => {
972
+ const target = normalizeIndex(nextValue, total);
973
+ const previousValue = renderIndexRef.current;
974
+ if (target === previousValue)
975
+ return;
976
+ currentIndexSV.value = target;
977
+ pageTranslateX.value = 0;
978
+ setRenderIndex(target);
979
+ onChange(target, reason);
980
+ }, [currentIndexSV, onChange, pageTranslateX, total]);
981
+ const renderContext = React.useMemo(() => ({
982
+ open,
983
+ value: renderIndex,
984
+ image: currentImage,
985
+ total,
986
+ close: (reason = 'api') => requestClose(reason),
987
+ setValue: (nextValue) => jumpToValue(nextValue, 'api'),
988
+ }), [currentImage, jumpToValue, open, renderIndex, requestClose, total]);
989
+ React.useEffect(() => {
990
+ if (open && total > 0) {
991
+ const nextValue = normalizeIndex(value, total);
992
+ closingRef.current = false;
993
+ closeReasonRef.current = 'api';
994
+ setClosing(false);
995
+ setChromeVisible(true);
996
+ chromeOpacity.value = 1;
997
+ currentIndexSV.value = nextValue;
998
+ pageTranslateX.value = 0;
999
+ setRenderIndex(nextValue);
1000
+ setMounted(true);
1001
+ backdropOpacity.value = (0, react_native_reanimated_1.withTiming)(1, ENTER_TIMING);
1002
+ }
1003
+ else if (mounted) {
1004
+ closingRef.current = true;
1005
+ setClosing(true);
1006
+ backdropOpacity.value = (0, react_native_reanimated_1.withTiming)(0, EXIT_TIMING, (finished) => {
1007
+ if (finished) {
1008
+ (0, react_native_worklets_1.scheduleOnRN)(finishClose);
1009
+ }
1010
+ });
1011
+ }
1012
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1013
+ }, [open, total]);
1014
+ React.useEffect(() => {
1015
+ if (!open || total <= 0)
1016
+ return;
1017
+ const nextValue = normalizeIndex(value, total);
1018
+ currentIndexSV.value = nextValue;
1019
+ pageTranslateX.value = 0;
1020
+ setRenderIndex(nextValue);
1021
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1022
+ }, [value, total]);
1023
+ React.useEffect(() => {
1024
+ if (!mounted || prefetch === false)
1025
+ return;
1026
+ const uris = getPrefetchUris(images, renderIndex, prefetch !== null && prefetch !== void 0 ? prefetch : 'adjacent');
1027
+ if (!uris.length)
1028
+ return;
1029
+ expo_image_1.Image.prefetch(uris, prefetchCachePolicy).catch(() => { });
1030
+ }, [images, mounted, prefetch, prefetchCachePolicy, renderIndex]);
1031
+ (0, react_native_reanimated_1.useAnimatedReaction)(() => currentIndexSV.value, (nextValue) => {
1032
+ (0, react_native_worklets_1.scheduleOnRN)(setRenderIndex, nextValue);
1033
+ });
1034
+ const handlePageChange = React.useCallback((nextValue) => {
1035
+ setRenderIndex(nextValue);
1036
+ onChange(nextValue, 'swipe');
1037
+ }, [onChange]);
1038
+ const handleImageLoad = React.useCallback((event, meta) => {
1039
+ onImageLoad === null || onImageLoad === void 0 ? void 0 : onImageLoad(event, meta);
1040
+ }, [onImageLoad]);
1041
+ const handleImageError = React.useCallback((event, meta) => {
1042
+ if (meta.value === renderIndexRef.current && typeof __DEV__ !== 'undefined' && __DEV__) {
1043
+ console.warn('[imagePreview] Failed to load image', {
1044
+ error: event.error,
1045
+ uri: meta.image.uri,
1046
+ });
1047
+ }
1048
+ onImageError === null || onImageError === void 0 ? void 0 : onImageError(event, meta);
1049
+ }, [onImageError]);
1050
+ const handleSingleTap = React.useCallback(() => {
1051
+ if (interactions.tapBehavior !== 'toggleChrome')
1052
+ return;
1053
+ setChromeVisibleAnimated(!chromeVisible);
1054
+ }, [chromeVisible, interactions.tapBehavior, setChromeVisibleAnimated]);
1055
+ const handleModalRequestClose = React.useCallback(() => {
1056
+ if (!interactions.backToClose)
1057
+ return;
1058
+ requestClose('back');
1059
+ }, [interactions.backToClose, requestClose]);
1060
+ const handleAccessibilityEscape = React.useCallback(() => {
1061
+ requestClose('back');
1062
+ }, [requestClose]);
1063
+ const backdropStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
1064
+ opacity: backdropOpacity.value,
1065
+ }));
1066
+ const chromeStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => ({
1067
+ opacity: backdropOpacity.value * chromeOpacity.value,
1068
+ }));
1069
+ if (!mounted)
1070
+ return null;
1071
+ const lower = Math.max(0, renderIndex - renderAhead);
1072
+ const upper = Math.min(total - 1, renderIndex + renderAhead);
1073
+ const renderRange = [];
1074
+ for (let index = lower; index <= upper; index += 1) {
1075
+ renderRange.push(index);
1076
+ }
1077
+ const topOffset = insets.top + CLOSE_BUTTON_TOP;
1078
+ const bottomOffset = insets.bottom + BOTTOM_CHROME_OFFSET;
1079
+ const showCloseButton = closeButton && dismissible && !disabled;
1080
+ const showCounter = counter && total > 1;
1081
+ const chromePointerEvents = chromeVisible && !closing ? 'box-none' : 'none';
1082
+ return ((0, jsx_runtime_1.jsx)(react_native_1.Modal, { animationType: "none", hardwareAccelerated: true, onRequestClose: handleModalRequestClose, presentationStyle: "overFullScreen", statusBarTranslucent: true, supportedOrientations: [
1083
+ 'portrait',
1084
+ 'portrait-upside-down',
1085
+ 'landscape',
1086
+ 'landscape-left',
1087
+ 'landscape-right',
1088
+ ], transparent: true, visible: mounted, ...modalProps, children: (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureHandlerRootView, { style: styles.modalRoot, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { accessibilityLabel: accessibilityLabel !== null && accessibilityLabel !== void 0 ? accessibilityLabel : labels.preview, accessibilityViewIsModal: true, onAccessibilityEscape: handleAccessibilityEscape, pointerEvents: closing ? 'none' : 'auto', style: [styles.root, { zIndex: DEFAULT_Z_INDEX }, rootStyle], testID: testID, children: [(0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { pointerEvents: "none", style: [styles.backdrop, { backgroundColor: colors.backdrop }, backdropStyle] }), renderRange.map((index) => {
1089
+ var _a, _b;
1090
+ const image = images[index];
1091
+ if (!image)
1092
+ return null;
1093
+ const imageAccessibilityLabel = (_b = (_a = image.accessibilityLabel) !== null && _a !== void 0 ? _a : image.alt) !== null && _b !== void 0 ? _b : labels.image(index + 1, total);
1094
+ return ((0, jsx_runtime_1.jsx)(ZoomableImage, { active: index === renderIndex, backdropOpacity: backdropOpacity, colors: colors, contentFit: contentFit, currentIndexSV: currentIndexSV, dismissible: dismissible && !disabled, image: image, imageAccessibilityLabel: imageAccessibilityLabel, imageProps: imageProps, imageStyle: imageStyle, index: index, interactions: interactions, labels: labels, onImageError: handleImageError, onImageLoad: handleImageLoad, onPageChange: handlePageChange, onRequestClose: requestClose, onSingleTap: handleSingleTap, pageTranslateX: pageTranslateX, totalCount: total, viewport: viewport }, image.key));
1095
+ }), renderOverlay && ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "box-none", style: styles.overlaySlot, children: renderOverlay(renderContext) })), renderHeader && ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { pointerEvents: chromePointerEvents, style: [styles.headerSlot, { top: topOffset }, chromeStyle], children: renderHeader(renderContext) })), showCloseButton && ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { pointerEvents: chromeVisible && !closing ? 'auto' : 'none', style: [styles.closeButtonWrap, { top: topOffset }, chromeStyle], children: (0, jsx_runtime_1.jsx)(CloseButton, { backgroundColor: colors.controlBackground, color: colors.controlForeground, label: labels.close, onPress: () => requestClose('closeButton') }) })), (renderFooter || showCounter) && ((0, jsx_runtime_1.jsxs)(react_native_reanimated_1.default.View, { pointerEvents: chromePointerEvents, style: [styles.bottomChrome, { bottom: bottomOffset }, chromeStyle], children: [renderFooter && (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.footerSlot, children: renderFooter(renderContext) }), showCounter && ((0, jsx_runtime_1.jsx)(react_native_1.View, { accessibilityLabel: labels.counter(renderIndex + 1, total), pointerEvents: "none", style: [
1096
+ styles.counterPill,
1097
+ { backgroundColor: colors.counterBackground },
1098
+ ], children: (0, jsx_runtime_1.jsxs)(Text_1.Text, { color: colors.counterText, lineHeight: COUNTER_LINE_HEIGHT, size: COUNTER_FONT_SIZE, tabularNumbers: true, weight: "semibold", children: [renderIndex + 1, " / ", total] }) }))] }))] }) }) }));
1099
+ }
1100
+ const ImagePreviewRoot = React.forwardRef(function ImagePreview({ accessibilityLabel, closeButton = true, colors, contentFit = 'contain', counter = true, defaultOpen = false, defaultValue = 0, disabled = false, dismissible = true, imageProps, imageStyle, images, interactions, labels, modalProps, onChange, onClose, onImageError, onImageLoad, onOpenChange, open, prefetch = false, prefetchCachePolicy = 'memory-disk', renderAhead, renderFooter, renderHeader, renderOverlay, rootStyle, testID, value, }, ref) {
1101
+ const theme = (0, useTheme_1.useTheme)();
1102
+ const { t } = (0, useI18n_1.useI18n)();
1103
+ const normalizedImages = React.useMemo(() => normalizeImages(images), [images]);
1104
+ const total = normalizedImages.length;
1105
+ const isOpenControlled = open !== undefined;
1106
+ const isValueControlled = value !== undefined;
1107
+ const [innerOpen, setInnerOpen] = React.useState(defaultOpen);
1108
+ const [innerValue, setInnerValue] = React.useState(() => normalizeIndex(defaultValue, total));
1109
+ const actualOpen = isOpenControlled ? !!open : innerOpen;
1110
+ const actualValue = normalizeIndex(isValueControlled ? value : innerValue, total);
1111
+ const closeReasonRef = React.useRef('api');
1112
+ const closeRequestedRef = React.useRef(false);
1113
+ const previousOpenRef = React.useRef(actualOpen);
1114
+ const valueRef = React.useRef(actualValue);
1115
+ valueRef.current = actualValue;
1116
+ const resolvedInteractions = React.useMemo(() => resolveInteractions(interactions), [interactions]);
1117
+ const resolvedColors = React.useMemo(() => {
1118
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1119
+ return ({
1120
+ backdrop: (_a = colors === null || colors === void 0 ? void 0 : colors.backdrop) !== null && _a !== void 0 ? _a : '#000000',
1121
+ controlBackground: (_b = colors === null || colors === void 0 ? void 0 : colors.controlBackground) !== null && _b !== void 0 ? _b : 'rgba(17, 24, 39, 0.62)',
1122
+ controlForeground: (_c = colors === null || colors === void 0 ? void 0 : colors.controlForeground) !== null && _c !== void 0 ? _c : theme.colors.onPrimary,
1123
+ counterBackground: (_d = colors === null || colors === void 0 ? void 0 : colors.counterBackground) !== null && _d !== void 0 ? _d : 'rgba(17, 24, 39, 0.58)',
1124
+ counterText: (_e = colors === null || colors === void 0 ? void 0 : colors.counterText) !== null && _e !== void 0 ? _e : theme.colors.onPrimary,
1125
+ errorBackground: (_f = colors === null || colors === void 0 ? void 0 : colors.errorBackground) !== null && _f !== void 0 ? _f : 'rgba(17, 24, 39, 0.72)',
1126
+ errorText: (_g = colors === null || colors === void 0 ? void 0 : colors.errorText) !== null && _g !== void 0 ? _g : theme.colors.onPrimary,
1127
+ loading: (_h = colors === null || colors === void 0 ? void 0 : colors.loading) !== null && _h !== void 0 ? _h : theme.colors.onPrimary,
1128
+ });
1129
+ }, [
1130
+ colors === null || colors === void 0 ? void 0 : colors.backdrop,
1131
+ colors === null || colors === void 0 ? void 0 : colors.controlBackground,
1132
+ colors === null || colors === void 0 ? void 0 : colors.controlForeground,
1133
+ colors === null || colors === void 0 ? void 0 : colors.counterBackground,
1134
+ colors === null || colors === void 0 ? void 0 : colors.counterText,
1135
+ colors === null || colors === void 0 ? void 0 : colors.errorBackground,
1136
+ colors === null || colors === void 0 ? void 0 : colors.errorText,
1137
+ colors === null || colors === void 0 ? void 0 : colors.loading,
1138
+ theme.colors.onPrimary,
1139
+ ]);
1140
+ const resolvedLabels = React.useMemo(() => {
1141
+ var _a, _b, _c, _d, _e;
1142
+ return ({
1143
+ preview: (_a = labels === null || labels === void 0 ? void 0 : labels.preview) !== null && _a !== void 0 ? _a : t('imagePreview.preview'),
1144
+ close: (_b = labels === null || labels === void 0 ? void 0 : labels.close) !== null && _b !== void 0 ? _b : t('imagePreview.close'),
1145
+ loadFailed: (_c = labels === null || labels === void 0 ? void 0 : labels.loadFailed) !== null && _c !== void 0 ? _c : t('imagePreview.loadFailed'),
1146
+ counter: (_d = labels === null || labels === void 0 ? void 0 : labels.counter) !== null && _d !== void 0 ? _d : ((current, imageTotal) => t('imagePreview.counter', { current, total: imageTotal })),
1147
+ image: (_e = labels === null || labels === void 0 ? void 0 : labels.image) !== null && _e !== void 0 ? _e : ((current, imageTotal) => t('imagePreview.image', { current, total: imageTotal })),
1148
+ });
1149
+ }, [labels, t]);
1150
+ const notifyOpenChange = React.useCallback((nextOpen, meta) => {
1151
+ if (!nextOpen) {
1152
+ closeReasonRef.current = meta.reason;
1153
+ closeRequestedRef.current = true;
1154
+ }
1155
+ if (!isOpenControlled)
1156
+ setInnerOpen(nextOpen);
1157
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(nextOpen, meta);
1158
+ }, [isOpenControlled, onOpenChange]);
1159
+ const notifyChange = React.useCallback((nextValue, reason) => {
1160
+ const normalizedValue = normalizeIndex(nextValue, total);
1161
+ const previousValue = valueRef.current;
1162
+ if (normalizedValue === previousValue)
1163
+ return;
1164
+ valueRef.current = normalizedValue;
1165
+ if (!isValueControlled)
1166
+ setInnerValue(normalizedValue);
1167
+ onChange === null || onChange === void 0 ? void 0 : onChange(normalizedValue, createChangeMeta(reason, previousValue, normalizedValue, normalizedImages));
1168
+ }, [isValueControlled, normalizedImages, onChange, total]);
1169
+ const close = React.useCallback((reason = 'api') => {
1170
+ notifyOpenChange(false, createOpenMeta(reason, valueRef.current, normalizedImages));
1171
+ }, [normalizedImages, notifyOpenChange]);
1172
+ React.useImperativeHandle(ref, () => ({
1173
+ close,
1174
+ setValue: (nextValue) => notifyChange(nextValue, 'api'),
1175
+ getValue: () => valueRef.current,
1176
+ }), [close, notifyChange]);
1177
+ React.useEffect(() => {
1178
+ if (actualOpen) {
1179
+ closeReasonRef.current = 'api';
1180
+ closeRequestedRef.current = false;
1181
+ }
1182
+ else if (previousOpenRef.current && !actualOpen && !closeRequestedRef.current) {
1183
+ closeReasonRef.current = 'api';
1184
+ }
1185
+ previousOpenRef.current = actualOpen;
1186
+ }, [actualOpen]);
1187
+ React.useEffect(() => {
1188
+ if (actualOpen && total === 0) {
1189
+ notifyOpenChange(false, createOpenMeta('empty', 0, normalizedImages));
1190
+ }
1191
+ }, [actualOpen, normalizedImages, notifyOpenChange, total]);
1192
+ React.useEffect(() => {
1193
+ if (isValueControlled)
1194
+ return;
1195
+ const nextValue = normalizeIndex(innerValue, total);
1196
+ if (nextValue !== innerValue)
1197
+ setInnerValue(nextValue);
1198
+ }, [innerValue, isValueControlled, total]);
1199
+ const handleCloseComplete = React.useCallback((result) => {
1200
+ onClose === null || onClose === void 0 ? void 0 : onClose({
1201
+ ...result,
1202
+ reason: closeReasonRef.current,
1203
+ });
1204
+ }, [onClose]);
1205
+ return ((0, jsx_runtime_1.jsx)(ImagePreviewOverlay, { accessibilityLabel: accessibilityLabel, closeButton: closeButton, colors: resolvedColors, contentFit: contentFit, counter: counter, disabled: disabled, dismissible: dismissible, imageProps: imageProps, imageStyle: imageStyle, images: normalizedImages, interactions: resolvedInteractions, labels: resolvedLabels, modalProps: modalProps, onChange: notifyChange, onCloseComplete: handleCloseComplete, onImageError: onImageError, onImageLoad: onImageLoad, onOpenChange: notifyOpenChange, open: actualOpen && total > 0, prefetch: prefetch, prefetchCachePolicy: prefetchCachePolicy, renderAhead: clampRenderAhead(renderAhead), renderFooter: renderFooter, renderHeader: renderHeader, renderOverlay: renderOverlay, rootStyle: rootStyle, testID: testID, value: actualValue }));
1206
+ });
1207
+ ImagePreviewRoot.displayName = 'ImagePreview';
1208
+ exports.ImagePreview = React.memo(ImagePreviewRoot);
1209
+ exports.ImagePreview.displayName = 'ImagePreview';
1210
+ function ImagePreviewProvider({ children }) {
1211
+ const [state, setState] = React.useState({
1212
+ open: false,
1213
+ request: null,
1214
+ value: 0,
1215
+ closeReason: 'api',
1216
+ });
1217
+ const stateRef = React.useRef(state);
1218
+ stateRef.current = state;
1219
+ React.useEffect(() => {
1220
+ const controller = {
1221
+ open: (request) => {
1222
+ setState({
1223
+ open: true,
1224
+ request,
1225
+ value: request.index,
1226
+ closeReason: 'api',
1227
+ });
1228
+ },
1229
+ close: (reason) => {
1230
+ setState((prev) => ({ ...prev, open: false, closeReason: reason }));
1231
+ },
1232
+ setValue: (nextValue) => {
1233
+ setState((prev) => {
1234
+ var _a, _b;
1235
+ return ({
1236
+ ...prev,
1237
+ value: normalizeIndex(nextValue, normalizeImages((_b = (_a = prev.request) === null || _a === void 0 ? void 0 : _a.images) !== null && _b !== void 0 ? _b : []).length),
1238
+ });
1239
+ });
1240
+ },
1241
+ getSnapshot: () => {
1242
+ var _a, _b, _c;
1243
+ const current = stateRef.current;
1244
+ const images = normalizeImages((_b = (_a = current.request) === null || _a === void 0 ? void 0 : _a.images) !== null && _b !== void 0 ? _b : []);
1245
+ const value = normalizeIndex(current.value, images.length);
1246
+ return {
1247
+ open: current.open,
1248
+ value,
1249
+ image: (_c = images[value]) !== null && _c !== void 0 ? _c : null,
1250
+ total: images.length,
1251
+ };
1252
+ },
1253
+ };
1254
+ exports.imagePreview._bind(controller);
1255
+ return () => {
1256
+ exports.imagePreview._bind(null);
1257
+ const request = stateRef.current.request;
1258
+ if (request)
1259
+ exports.imagePreview._dispose(request.id);
1260
+ };
1261
+ }, []);
1262
+ const request = state.request;
1263
+ const handleOpenChange = React.useCallback((nextOpen, meta) => {
1264
+ var _a;
1265
+ (_a = request === null || request === void 0 ? void 0 : request.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(request, nextOpen, meta);
1266
+ if (!nextOpen) {
1267
+ setState((prev) => ({
1268
+ ...prev,
1269
+ open: false,
1270
+ closeReason: meta.reason,
1271
+ }));
1272
+ }
1273
+ }, [request]);
1274
+ const handleChange = React.useCallback((nextValue, meta) => {
1275
+ var _a;
1276
+ setState((prev) => ({ ...prev, value: nextValue }));
1277
+ (_a = request === null || request === void 0 ? void 0 : request.onChange) === null || _a === void 0 ? void 0 : _a.call(request, nextValue, meta);
1278
+ }, [request]);
1279
+ const handleClose = React.useCallback((result) => {
1280
+ if (!request)
1281
+ return;
1282
+ const finalResult = {
1283
+ ...result,
1284
+ reason: stateRef.current.closeReason,
1285
+ };
1286
+ exports.imagePreview._complete(request.id, finalResult);
1287
+ setState((prev) => {
1288
+ var _a;
1289
+ return ((_a = prev.request) === null || _a === void 0 ? void 0 : _a.id) === request.id
1290
+ ? { open: false, request: null, value: 0, closeReason: 'api' }
1291
+ : prev;
1292
+ });
1293
+ }, [request]);
1294
+ let previewNode = null;
1295
+ if (request) {
1296
+ const { index: _index, onChange: _onChange, onClose: _onClose, onOpenChange: _onOpenChange, ...previewProps } = request;
1297
+ previewNode = ((0, jsx_runtime_1.jsx)(exports.ImagePreview, { ...previewProps, onChange: handleChange, onClose: handleClose, onOpenChange: handleOpenChange, open: state.open, value: state.value }, request.id));
1298
+ }
1299
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [children, previewNode] }));
1300
+ }
1301
+ const styles = react_native_1.StyleSheet.create({
1302
+ modalRoot: {
1303
+ flex: 1,
1304
+ },
1305
+ root: {
1306
+ flex: 1,
1307
+ width: '100%',
1308
+ height: '100%',
1309
+ elevation: react_native_1.Platform.OS === 'android' ? 100 : 0,
1310
+ overflow: 'hidden',
1311
+ },
1312
+ backdrop: {
1313
+ ...react_native_1.StyleSheet.absoluteFillObject,
1314
+ },
1315
+ imagePage: {
1316
+ position: 'absolute',
1317
+ justifyContent: 'center',
1318
+ alignItems: 'center',
1319
+ overflow: 'hidden',
1320
+ zIndex: 1,
1321
+ },
1322
+ overlaySlot: {
1323
+ ...react_native_1.StyleSheet.absoluteFillObject,
1324
+ zIndex: 5,
1325
+ },
1326
+ headerSlot: {
1327
+ position: 'absolute',
1328
+ left: 0,
1329
+ right: 0,
1330
+ zIndex: 12,
1331
+ },
1332
+ closeButtonWrap: {
1333
+ position: 'absolute',
1334
+ left: CLOSE_BUTTON_LEFT,
1335
+ zIndex: 14,
1336
+ },
1337
+ closeButton: {
1338
+ width: CLOSE_BUTTON_SIDE,
1339
+ height: CLOSE_BUTTON_SIDE,
1340
+ borderRadius: CONTROL_RADIUS,
1341
+ alignItems: 'center',
1342
+ justifyContent: 'center',
1343
+ },
1344
+ bottomChrome: {
1345
+ position: 'absolute',
1346
+ left: 0,
1347
+ right: 0,
1348
+ alignItems: 'center',
1349
+ gap: BOTTOM_CHROME_GAP,
1350
+ zIndex: 12,
1351
+ },
1352
+ footerSlot: {
1353
+ alignSelf: 'stretch',
1354
+ },
1355
+ counterPill: {
1356
+ minHeight: COUNTER_MIN_HEIGHT,
1357
+ paddingHorizontal: COUNTER_PADDING_HORIZONTAL,
1358
+ borderRadius: COUNTER_RADIUS,
1359
+ alignItems: 'center',
1360
+ justifyContent: 'center',
1361
+ },
1362
+ errorState: {
1363
+ ...react_native_1.StyleSheet.absoluteFillObject,
1364
+ alignItems: 'center',
1365
+ justifyContent: 'center',
1366
+ },
1367
+ loadingState: {
1368
+ ...react_native_1.StyleSheet.absoluteFillObject,
1369
+ alignItems: 'center',
1370
+ justifyContent: 'center',
1371
+ zIndex: 3,
1372
+ },
1373
+ errorCard: {
1374
+ maxWidth: ERROR_CARD_MAX_WIDTH,
1375
+ paddingHorizontal: ERROR_CARD_PADDING_HORIZONTAL,
1376
+ paddingVertical: ERROR_CARD_PADDING_VERTICAL,
1377
+ borderRadius: ERROR_CARD_RADIUS,
1378
+ alignItems: 'center',
1379
+ },
1380
+ errorIcon: {
1381
+ marginBottom: ERROR_ICON_MARGIN_BOTTOM,
1382
+ },
1383
+ });