@oxyhq/bloom 1.1.0 → 1.2.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.
- package/docs/media-flight.mdx +272 -0
- package/docs/migrating-the-media-gallery.mdx +103 -0
- package/docs/migrating-to-1.0.mdx +1 -1
- package/docs/zoomable-media-gallery.mdx +116 -0
- package/lib/commonjs/image-aspect-ratio-cache/aspect-ratio-cache.js +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/media-flight/MediaFlightLayer.js +151 -0
- package/lib/commonjs/media-flight/MediaFlightLayer.js.map +1 -0
- package/lib/commonjs/media-flight/MediaSurface.js +188 -0
- package/lib/commonjs/media-flight/MediaSurface.js.map +1 -0
- package/lib/commonjs/media-flight/constants.js +39 -0
- package/lib/commonjs/media-flight/constants.js.map +1 -0
- package/lib/commonjs/media-flight/expo-video-module.js +148 -0
- package/lib/commonjs/media-flight/expo-video-module.js.map +1 -0
- package/lib/commonjs/media-flight/index.js +102 -0
- package/lib/commonjs/media-flight/index.js.map +1 -0
- package/lib/commonjs/media-flight/store.js +457 -0
- package/lib/commonjs/media-flight/store.js.map +1 -0
- package/lib/commonjs/media-flight/types.js +6 -0
- package/lib/commonjs/media-flight/types.js.map +1 -0
- package/lib/commonjs/media-flight/use-media-flight.js +41 -0
- package/lib/commonjs/media-flight/use-media-flight.js.map +1 -0
- package/lib/commonjs/tab-bar/TabBarBase.js +1 -1
- package/lib/commonjs/{zoomable-image-gallery/ZoomableImageGallery.js → zoomable-media-gallery/ZoomableMediaGallery.js} +185 -131
- package/lib/commonjs/zoomable-media-gallery/ZoomableMediaGallery.js.map +1 -0
- package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.js.map +1 -1
- package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.js +5 -5
- package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.js.map +1 -1
- package/lib/commonjs/zoomable-media-gallery/types.js +6 -0
- package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/types.js.map +1 -1
- package/lib/module/image-aspect-ratio-cache/aspect-ratio-cache.js +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +2 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/media-flight/MediaFlightLayer.js +146 -0
- package/lib/module/media-flight/MediaFlightLayer.js.map +1 -0
- package/lib/module/media-flight/MediaSurface.js +180 -0
- package/lib/module/media-flight/MediaSurface.js.map +1 -0
- package/lib/module/media-flight/constants.js +35 -0
- package/lib/module/media-flight/constants.js.map +1 -0
- package/lib/module/media-flight/expo-video-module.js +142 -0
- package/lib/module/media-flight/expo-video-module.js.map +1 -0
- package/lib/module/media-flight/index.js +9 -0
- package/lib/module/media-flight/index.js.map +1 -0
- package/lib/module/media-flight/store.js +439 -0
- package/lib/module/media-flight/store.js.map +1 -0
- package/lib/module/media-flight/types.js +4 -0
- package/lib/module/media-flight/types.js.map +1 -0
- package/lib/module/media-flight/use-media-flight.js +36 -0
- package/lib/module/media-flight/use-media-flight.js.map +1 -0
- package/lib/module/tab-bar/TabBarBase.js +1 -1
- package/lib/module/{zoomable-image-gallery/ZoomableImageGallery.js → zoomable-media-gallery/ZoomableMediaGallery.js} +184 -130
- package/lib/module/zoomable-media-gallery/ZoomableMediaGallery.js.map +1 -0
- package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.js.map +1 -1
- package/lib/module/zoomable-media-gallery/index.js +5 -0
- package/lib/module/zoomable-media-gallery/index.js.map +1 -0
- package/lib/module/zoomable-media-gallery/types.js +4 -0
- package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/types.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/media-flight/MediaFlightLayer.d.ts +11 -0
- package/lib/typescript/commonjs/media-flight/MediaFlightLayer.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/MediaSurface.d.ts +106 -0
- package/lib/typescript/commonjs/media-flight/MediaSurface.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/constants.d.ts +18 -0
- package/lib/typescript/commonjs/media-flight/constants.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/expo-video-module.d.ts +103 -0
- package/lib/typescript/commonjs/media-flight/expo-video-module.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/index.d.ts +9 -0
- package/lib/typescript/commonjs/media-flight/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/store.d.ts +132 -0
- package/lib/typescript/commonjs/media-flight/store.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/types.d.ts +195 -0
- package/lib/typescript/commonjs/media-flight/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/media-flight/use-media-flight.d.ts +4 -0
- package/lib/typescript/commonjs/media-flight/use-media-flight.d.ts.map +1 -0
- package/lib/typescript/commonjs/zoomable-media-gallery/ZoomableMediaGallery.d.ts +5 -0
- package/lib/typescript/commonjs/zoomable-media-gallery/ZoomableMediaGallery.d.ts.map +1 -0
- package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-media-gallery/index.d.ts +4 -0
- package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/zoomable-media-gallery/types.d.ts +90 -0
- package/lib/typescript/commonjs/zoomable-media-gallery/types.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/media-flight/MediaFlightLayer.d.ts +11 -0
- package/lib/typescript/module/media-flight/MediaFlightLayer.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/MediaSurface.d.ts +106 -0
- package/lib/typescript/module/media-flight/MediaSurface.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/constants.d.ts +18 -0
- package/lib/typescript/module/media-flight/constants.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/expo-video-module.d.ts +103 -0
- package/lib/typescript/module/media-flight/expo-video-module.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/index.d.ts +9 -0
- package/lib/typescript/module/media-flight/index.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/store.d.ts +132 -0
- package/lib/typescript/module/media-flight/store.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/types.d.ts +195 -0
- package/lib/typescript/module/media-flight/types.d.ts.map +1 -0
- package/lib/typescript/module/media-flight/use-media-flight.d.ts +4 -0
- package/lib/typescript/module/media-flight/use-media-flight.d.ts.map +1 -0
- package/lib/typescript/module/zoomable-media-gallery/ZoomableMediaGallery.d.ts +5 -0
- package/lib/typescript/module/zoomable-media-gallery/ZoomableMediaGallery.d.ts.map +1 -0
- package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-media-gallery/index.d.ts +4 -0
- package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/index.d.ts.map +1 -1
- package/lib/typescript/module/zoomable-media-gallery/types.d.ts +90 -0
- package/lib/typescript/module/zoomable-media-gallery/types.d.ts.map +1 -0
- package/package.json +42 -8
- package/src/image-aspect-ratio-cache/aspect-ratio-cache.ts +1 -1
- package/src/index.ts +2 -1
- package/src/index.web.ts +2 -1
- package/src/media-flight/MediaFlightLayer.tsx +144 -0
- package/src/media-flight/MediaSurface.tsx +233 -0
- package/src/media-flight/constants.ts +32 -0
- package/src/media-flight/expo-video-module.ts +181 -0
- package/src/media-flight/index.ts +33 -0
- package/src/media-flight/store.ts +497 -0
- package/src/media-flight/types.ts +210 -0
- package/src/media-flight/use-media-flight.ts +42 -0
- package/src/tab-bar/TabBarBase.tsx +1 -1
- package/src/{zoomable-image-gallery/ZoomableImageGallery.tsx → zoomable-media-gallery/ZoomableMediaGallery.tsx} +192 -117
- package/src/zoomable-media-gallery/index.ts +11 -0
- package/src/zoomable-media-gallery/types.ts +100 -0
- package/docs/zoomable-image-gallery.mdx +0 -66
- package/lib/commonjs/zoomable-image-gallery/ZoomableImageGallery.js.map +0 -1
- package/lib/commonjs/zoomable-image-gallery/types.js +0 -2
- package/lib/module/zoomable-image-gallery/ZoomableImageGallery.js.map +0 -1
- package/lib/module/zoomable-image-gallery/index.js +0 -5
- package/lib/module/zoomable-image-gallery/index.js.map +0 -1
- package/lib/module/zoomable-image-gallery/types.js +0 -2
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts +0 -5
- package/lib/typescript/commonjs/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +0 -1
- package/lib/typescript/commonjs/zoomable-image-gallery/index.d.ts +0 -4
- package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts +0 -56
- package/lib/typescript/commonjs/zoomable-image-gallery/types.d.ts.map +0 -1
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts +0 -5
- package/lib/typescript/module/zoomable-image-gallery/ZoomableImageGallery.d.ts.map +0 -1
- package/lib/typescript/module/zoomable-image-gallery/index.d.ts +0 -4
- package/lib/typescript/module/zoomable-image-gallery/types.d.ts +0 -56
- package/lib/typescript/module/zoomable-image-gallery/types.d.ts.map +0 -1
- package/src/zoomable-image-gallery/index.ts +0 -9
- package/src/zoomable-image-gallery/types.ts +0 -59
- /package/lib/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.js +0 -0
- /package/lib/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.js +0 -0
- /package/lib/typescript/commonjs/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts +0 -0
- /package/lib/typescript/module/{zoomable-image-gallery → zoomable-media-gallery}/constants.d.ts +0 -0
- /package/src/{zoomable-image-gallery → zoomable-media-gallery}/constants.ts +0 -0
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = exports.
|
|
6
|
+
exports.default = exports.ZoomableMediaGallery = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
-
var _expoImage = require("expo-image");
|
|
10
9
|
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
11
10
|
var _reactNativeGestureHandler = require("react-native-gesture-handler");
|
|
12
11
|
var _tokens = require("../styles/tokens.js");
|
|
@@ -15,11 +14,43 @@ var _portal = require("../portal");
|
|
|
15
14
|
var _useInteractionState = require("../hooks/use-interaction-state.js");
|
|
16
15
|
var _index2 = require("../icons/index.js");
|
|
17
16
|
var _webViewStyle = require("../styles/web-view-style.js");
|
|
17
|
+
var _MediaSurface = require("../media-flight/MediaSurface.js");
|
|
18
18
|
var _index3 = require("../image-aspect-ratio-cache/index.js");
|
|
19
19
|
var _constants = require("./constants.js");
|
|
20
20
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
21
21
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The still-frame URI of an item: the image itself, or a video's poster.
|
|
24
|
+
* `undefined` for a video with no poster — there is nothing to probe, size
|
|
25
|
+
* against or show in the strip, and every caller below handles that.
|
|
26
|
+
*/
|
|
27
|
+
function posterUri(item) {
|
|
28
|
+
return item.kind === 'video' ? item.poster : item.uri;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The aspect ratio already known for an item, from the shared probe cache. */
|
|
32
|
+
function cachedRatio(item) {
|
|
33
|
+
const uri = posterUri(item);
|
|
34
|
+
return uri === undefined ? undefined : (0, _index3.getAspectRatio)(uri);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A stable React key. An image is identified by its URI; a video is not (its
|
|
39
|
+
* player is an object with no URL), which is why `GalleryVideo` carries an
|
|
40
|
+
* explicit `id`. The index is folded in so a post repeating one image still
|
|
41
|
+
* pages correctly.
|
|
42
|
+
*/
|
|
43
|
+
function mediaKey(item, index) {
|
|
44
|
+
return item.kind === 'video' ? `video-${item.id}-${index}` : `image-${item.uri}-${index}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* What the share sheet is handed. A poster is not what "share this video"
|
|
49
|
+
* means, so a video shares its own `shareUrl` or nothing at all.
|
|
50
|
+
*/
|
|
51
|
+
function shareUri(item) {
|
|
52
|
+
return item.kind === 'video' ? item.shareUrl : item.uri;
|
|
53
|
+
}
|
|
23
54
|
|
|
24
55
|
// Web-only interaction hints (no-ops on native): the zoom surfaces are
|
|
25
56
|
// tap-to-dismiss (`cursor: pointer`) and must not select text or drag the
|
|
@@ -62,7 +93,7 @@ function NavArrow({
|
|
|
62
93
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
63
94
|
onPress: onPress,
|
|
64
95
|
accessibilityRole: "button",
|
|
65
|
-
accessibilityLabel: isLeft ? 'Previous
|
|
96
|
+
accessibilityLabel: isLeft ? 'Previous item' : 'Next item',
|
|
66
97
|
hitSlop: 8,
|
|
67
98
|
onHoverIn: onHoverIn,
|
|
68
99
|
onHoverOut: onHoverOut,
|
|
@@ -77,7 +108,7 @@ function NavArrow({
|
|
|
77
108
|
});
|
|
78
109
|
}
|
|
79
110
|
/**
|
|
80
|
-
* Resolve `cornerRadius` against the size an
|
|
111
|
+
* Resolve `cornerRadius` against the size an item is actually rendered at, so
|
|
81
112
|
* `'circle'` stays a circle at every fitted size (and through the open/close
|
|
82
113
|
* animation, which scales this same box).
|
|
83
114
|
*/
|
|
@@ -86,26 +117,42 @@ function resolveCornerRadius(cornerRadius, fit) {
|
|
|
86
117
|
}
|
|
87
118
|
|
|
88
119
|
/**
|
|
89
|
-
* Fullscreen, swipeable
|
|
90
|
-
* measured-origin zoom transition
|
|
120
|
+
* Fullscreen, swipeable MEDIA viewer — images and consumer-owned videos in one
|
|
121
|
+
* pager — replicating the profile avatar's measured-origin zoom transition
|
|
122
|
+
* (`ZoomableAvatar`) for rectangular post media:
|
|
91
123
|
*
|
|
92
124
|
* - Open/close feel is identical to the avatar (same spring configs, web
|
|
93
125
|
* timing/easing, blur backdrop, and the measure-origin technique). The viewer
|
|
94
126
|
* renders through the Bloom `Portal` on BOTH platforms (RN's `Modal` is not
|
|
95
127
|
* used on native — on the New Architecture / Fabric Android its host views
|
|
96
128
|
* mount full-screen but never composite, leaving the viewer invisible).
|
|
97
|
-
* - The OPENING (tapped)
|
|
129
|
+
* - The OPENING (tapped) item animates from its measured rect to a centered,
|
|
98
130
|
* aspect-ratio-preserving fit within {@link FIT_FRACTION} of the screen. Once
|
|
99
131
|
* the open animation settles, a horizontal paging `ScrollView` mounts seeded at
|
|
100
|
-
* the tapped index so the user can swipe between every
|
|
132
|
+
* the tapped index so the user can swipe between every item in the post.
|
|
101
133
|
* - Gesture disambiguation: the pager owns horizontal swipes; a vertical-only
|
|
102
134
|
* `Gesture.Pan` (`activeOffsetY` + `failOffsetX`) owns drag-to-dismiss, so the
|
|
103
135
|
* two never fight.
|
|
136
|
+
*
|
|
137
|
+
* ## Video, and why it does not restart
|
|
138
|
+
*
|
|
139
|
+
* A video page is fed by a `VideoPlayer` the CONSUMER created and owns. Bloom
|
|
140
|
+
* never creates or destroys one: expo-video keeps the player OBJECT separate
|
|
141
|
+
* from the `VideoView` that shows it, and one player may feed several views, so
|
|
142
|
+
* handing the same player to this gallery moves a playing video into fullscreen
|
|
143
|
+
* without re-opening the stream. Exactly ONE view is mounted per opening — the
|
|
144
|
+
* active page's — while every other page and every strip tile renders the still
|
|
145
|
+
* (`MediaPoster`). The single unavoidable swap is the pre-pager open surface
|
|
146
|
+
* handing over to the pager's active page once the open animation settles.
|
|
147
|
+
*
|
|
148
|
+
* `expo-video` is an OPTIONAL peer loaded through `media-flight/expo-video-module`.
|
|
149
|
+
* Without it a video page degrades to its poster, once, with a dev warning.
|
|
104
150
|
*/
|
|
105
|
-
const
|
|
151
|
+
const ZoomableMediaGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
106
152
|
measureThumb,
|
|
107
153
|
cornerRadius = _constants.DEFAULT_CORNER_RADIUS,
|
|
108
|
-
indicatorVariant = 'dots'
|
|
154
|
+
indicatorVariant = 'dots',
|
|
155
|
+
videoControls = false
|
|
109
156
|
}, ref) => {
|
|
110
157
|
const {
|
|
111
158
|
width: SCREEN_WIDTH,
|
|
@@ -113,17 +160,17 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
113
160
|
} = (0, _reactNative.useWindowDimensions)();
|
|
114
161
|
const radiusFor = (0, _react.useCallback)(fit => resolveCornerRadius(cornerRadius, fit), [cornerRadius]);
|
|
115
162
|
const [isOpen, setIsOpen] = (0, _react.useState)(false);
|
|
116
|
-
// Once true, the swipeable pager is mounted and the single open-
|
|
163
|
+
// Once true, the swipeable pager is mounted and the single open-surface hidden.
|
|
117
164
|
const [pagerReady, setPagerReady] = (0, _react.useState)(false);
|
|
118
|
-
const [
|
|
165
|
+
const [items, setItems] = (0, _react.useState)([]);
|
|
119
166
|
const [activeIndex, setActiveIndex] = (0, _react.useState)(0);
|
|
120
|
-
// Whether the active
|
|
167
|
+
// Whether the active media is currently pinched/double-tapped past its base
|
|
121
168
|
// size. Drives the pan-while-zoomed gesture, disables the pager's horizontal
|
|
122
|
-
// paging + the dismiss drag so a pan moves the zoomed
|
|
169
|
+
// paging + the dismiss drag so a pan moves the zoomed media instead.
|
|
123
170
|
const [isZoomed, setIsZoomed] = (0, _react.useState)(false);
|
|
124
|
-
// Aspect ratio of the OPENING
|
|
171
|
+
// Aspect ratio of the OPENING item (drives the open-animation fit box).
|
|
125
172
|
const [openRatio, setOpenRatio] = (0, _react.useState)(_index3.DEFAULT_ASPECT_RATIO);
|
|
126
|
-
// Per-
|
|
173
|
+
// Per-item aspect ratios for the pager pages (index-aligned with `items`).
|
|
127
174
|
const [pageRatios, setPageRatios] = (0, _react.useState)({});
|
|
128
175
|
|
|
129
176
|
// Native `Share` is always available; web exposes a share button only when the
|
|
@@ -137,7 +184,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
137
184
|
const translateY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
138
185
|
const opacity = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
139
186
|
|
|
140
|
-
// Zoom transform of the active
|
|
187
|
+
// Zoom transform of the active media, kept SEPARATE from the open/close +
|
|
141
188
|
// dismiss-drag transform above (reusing those would collide two meanings).
|
|
142
189
|
// `zoom*` are the live/persisted values; `baseZoomScale` is captured at pinch
|
|
143
190
|
// start and `savedZoomTranslate*` at pan start for incremental bookkeeping.
|
|
@@ -148,14 +195,14 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
148
195
|
const savedZoomTranslateX = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
149
196
|
const savedZoomTranslateY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
150
197
|
|
|
151
|
-
// Origin (offset from screen center) of the tapped
|
|
198
|
+
// Origin (offset from screen center) of the tapped item.
|
|
152
199
|
const originX = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
153
200
|
const originY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
154
201
|
const startX = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
155
202
|
const startY = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
156
203
|
// Per-drag baseline captured at pan start (transient).
|
|
157
204
|
const startScale = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
158
|
-
// Persistent scale of the open-
|
|
205
|
+
// Persistent scale of the open-surface at its thumbnail origin (thumbWidth /
|
|
159
206
|
// fittedWidth). The open animation grows from this to 1; the close animation
|
|
160
207
|
// shrinks back to it. Never overwritten by the drag gesture.
|
|
161
208
|
const originScale = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
@@ -210,9 +257,9 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
210
257
|
setActiveIndex(prev => prev === next ? prev : next);
|
|
211
258
|
}, []);
|
|
212
259
|
|
|
213
|
-
// Snap the active
|
|
214
|
-
// all zoom bookkeeping. Called on every path that changes the active
|
|
215
|
-
// freshly-viewed
|
|
260
|
+
// Snap the active media back to its un-zoomed baseline (no animation) and
|
|
261
|
+
// clear all zoom bookkeeping. Called on every path that changes the active
|
|
262
|
+
// page so a freshly-viewed item always starts un-zoomed.
|
|
216
263
|
const resetZoom = (0, _react.useCallback)(() => {
|
|
217
264
|
zoomScale.value = 1;
|
|
218
265
|
zoomTranslateX.value = 0;
|
|
@@ -230,7 +277,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
230
277
|
}), [SCREEN_WIDTH, SCREEN_HEIGHT]);
|
|
231
278
|
|
|
232
279
|
// Largest width/height for `ratio` that fits inside `fitBox` without cropping
|
|
233
|
-
// (contain), never exceeding the
|
|
280
|
+
// (contain), never exceeding the media's own resolution: filling the viewport
|
|
234
281
|
// with a 512px avatar or a small thumbnail just renders it soft. `uri` is what
|
|
235
282
|
// lets us look the intrinsic size up; without it (not measured yet) the fit
|
|
236
283
|
// box wins and the clamp applies on the next frame.
|
|
@@ -253,18 +300,24 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
253
300
|
};
|
|
254
301
|
}, [fitBox]);
|
|
255
302
|
|
|
256
|
-
// Fitted size of the single open-
|
|
257
|
-
// the opened
|
|
258
|
-
// after swiping it tracks the viewed
|
|
259
|
-
// and flies back the
|
|
303
|
+
// Fitted size of the single open-surface for the CURRENT page. On open this is
|
|
304
|
+
// the opened item's fit (active index == opened index, ratio == `openRatio`);
|
|
305
|
+
// after swiping it tracks the viewed item so the collapse-on-dismiss renders
|
|
306
|
+
// and flies back the media actually on screen.
|
|
307
|
+
const activeItem = items[activeIndex];
|
|
260
308
|
const activeRatio = pageRatios[activeIndex] ?? openRatio;
|
|
261
|
-
const activeUri =
|
|
309
|
+
const activeUri = activeItem === undefined ? undefined : posterUri(activeItem);
|
|
262
310
|
const activeFit = (0, _react.useMemo)(() => fitForRatio(activeRatio, activeUri), [fitForRatio, activeRatio, activeUri]);
|
|
263
311
|
|
|
264
|
-
// Alt text (accessibility description) of the
|
|
312
|
+
// Alt text (accessibility description) of the item currently on screen, shown
|
|
265
313
|
// as a caption at the bottom of the viewer (Bluesky-style lightbox footer).
|
|
266
|
-
const activeAlt =
|
|
314
|
+
const activeAlt = activeItem?.alt?.trim() || undefined;
|
|
315
|
+
|
|
316
|
+
// Probe and cache the ratio of the page at `index`. A video with no poster has
|
|
317
|
+
// nothing to probe — it keeps whatever ratio the consumer declared, or the
|
|
318
|
+
// gallery default — so this is a no-op rather than a guess.
|
|
267
319
|
const ensureRatio = (0, _react.useCallback)((index, uri) => {
|
|
320
|
+
if (uri === undefined) return;
|
|
268
321
|
const cached = (0, _index3.getAspectRatio)(uri);
|
|
269
322
|
if (cached !== undefined) {
|
|
270
323
|
setPageRatios(prev => prev[index] === cached ? prev : {
|
|
@@ -292,7 +345,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
292
345
|
opacity.value = 0;
|
|
293
346
|
}, [opacity, scale, translateX, translateY]);
|
|
294
347
|
|
|
295
|
-
// Fly the (possibly dragged)
|
|
348
|
+
// Fly the (possibly dragged) media back toward `target` — the rect of the
|
|
296
349
|
// thumbnail currently being viewed — shrinking to its footprint. Uses the
|
|
297
350
|
// EXACT same close spring (native) / timing (web) as the avatar transition.
|
|
298
351
|
const flyBackTo = (0, _react.useCallback)(target => {
|
|
@@ -358,13 +411,13 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
358
411
|
// fly-back from an already-moving image.
|
|
359
412
|
if (dismissingRef.current) return;
|
|
360
413
|
dismissingRef.current = true;
|
|
361
|
-
// Collapse the pager back to the single open-
|
|
362
|
-
// one
|
|
414
|
+
// Collapse the pager back to the single open-surface so the fly-back
|
|
415
|
+
// animates one item (the current one) rather than the whole scrolled strip.
|
|
363
416
|
setPagerReady(false);
|
|
364
417
|
const index = activeIndexRef.current;
|
|
365
|
-
const current =
|
|
366
|
-
// Recompute the fly-back target from the CURRENT
|
|
367
|
-
// thumbnail + the same fitted box (`activeFit`) the open-
|
|
418
|
+
const current = items[index];
|
|
419
|
+
// Recompute the fly-back target from the CURRENT item: the live rect of its
|
|
420
|
+
// thumbnail + the same fitted box (`activeFit`) the open-surface is rendered
|
|
368
421
|
// at, using the same screen-center math as `open`. Falls back to a center
|
|
369
422
|
// fade-out when the thumbnail can't be measured.
|
|
370
423
|
if (measureThumb && current) {
|
|
@@ -384,7 +437,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
384
437
|
return;
|
|
385
438
|
}
|
|
386
439
|
fadeOutCenter();
|
|
387
|
-
}, [activeFit, fadeOutCenter, flyBackTo,
|
|
440
|
+
}, [activeFit, fadeOutCenter, flyBackTo, items, measureThumb, SCREEN_HEIGHT, SCREEN_WIDTH]);
|
|
388
441
|
|
|
389
442
|
// Reveal the swipeable pager once the open animation has settled. The index it
|
|
390
443
|
// lands on is `activeIndexRef` (set synchronously in `open`), seated by the
|
|
@@ -392,30 +445,32 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
392
445
|
const revealPager = (0, _react.useCallback)(() => {
|
|
393
446
|
setPagerReady(true);
|
|
394
447
|
}, []);
|
|
395
|
-
const open = (0, _react.useCallback)((
|
|
396
|
-
if (isOpen ||
|
|
448
|
+
const open = (0, _react.useCallback)((nextItems, index, rect) => {
|
|
449
|
+
if (isOpen || nextItems.length === 0) return;
|
|
397
450
|
dismissingRef.current = false;
|
|
398
|
-
const safeIndex = Math.min(Math.max(index, 0),
|
|
399
|
-
const target =
|
|
451
|
+
const safeIndex = Math.min(Math.max(index, 0), nextItems.length - 1);
|
|
452
|
+
const target = nextItems[safeIndex];
|
|
400
453
|
if (!target) return;
|
|
401
|
-
const
|
|
454
|
+
const targetUri = posterUri(target);
|
|
455
|
+
const knownRatio = target.aspectRatio ?? cachedRatio(target);
|
|
402
456
|
const ratio = knownRatio ?? _index3.DEFAULT_ASPECT_RATIO;
|
|
403
|
-
|
|
457
|
+
setItems(nextItems);
|
|
404
458
|
setActiveIndexBoth(safeIndex);
|
|
405
459
|
setOpenRatio(ratio);
|
|
406
460
|
// Seed every page whose ratio is already known (consumer-provided metadata
|
|
407
461
|
// or a previously-cached probe) so swiping never hits the same snap — only
|
|
408
|
-
// a genuinely-unknown
|
|
462
|
+
// a genuinely-unknown item falls through to `ensureRatio`'s async probe.
|
|
409
463
|
const initialRatios = {};
|
|
410
|
-
|
|
411
|
-
const r =
|
|
464
|
+
nextItems.forEach((item, i) => {
|
|
465
|
+
const r = item.aspectRatio ?? cachedRatio(item);
|
|
412
466
|
if (r !== undefined) initialRatios[i] = r;
|
|
413
467
|
});
|
|
414
468
|
setPageRatios(initialRatios);
|
|
415
469
|
|
|
416
|
-
// Resolve the opening ratio if it was not yet known, then re-fit.
|
|
417
|
-
|
|
418
|
-
|
|
470
|
+
// Resolve the opening ratio if it was not yet known, then re-fit. A video
|
|
471
|
+
// with no poster has nothing to probe and keeps the default.
|
|
472
|
+
if (knownRatio === undefined && targetUri !== undefined) {
|
|
473
|
+
void (0, _index3.fetchAspectRatio)(targetUri).then(resolved => {
|
|
419
474
|
setOpenRatio(resolved);
|
|
420
475
|
setPageRatios(prev => ({
|
|
421
476
|
...prev,
|
|
@@ -424,11 +479,11 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
424
479
|
});
|
|
425
480
|
}
|
|
426
481
|
|
|
427
|
-
// The open-
|
|
482
|
+
// The open-surface box is rendered at its FINAL fitted size; the open
|
|
428
483
|
// animation grows it from the thumbnail's footprint (scale < 1) up to 1,
|
|
429
484
|
// mirroring the avatar's small→big scale. The initial scale is the ratio
|
|
430
485
|
// of the thumbnail width to the fitted width.
|
|
431
|
-
const fitted = fitForRatio(ratio,
|
|
486
|
+
const fitted = fitForRatio(ratio, targetUri);
|
|
432
487
|
const centerX = SCREEN_WIDTH / 2;
|
|
433
488
|
const centerY = SCREEN_HEIGHT / 2;
|
|
434
489
|
if (rect && rect.width > 0) {
|
|
@@ -528,7 +583,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
528
583
|
opacity: opacity.value
|
|
529
584
|
}), [opacity]);
|
|
530
585
|
|
|
531
|
-
// The single open-
|
|
586
|
+
// The single open-surface animates from origin → fitted center.
|
|
532
587
|
const openImageStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
|
|
533
588
|
transform: [{
|
|
534
589
|
translateX: translateX.value
|
|
@@ -568,7 +623,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
568
623
|
// close fly-back target). Shared by `onMomentumScrollEnd` (native) and `onScroll`
|
|
569
624
|
// (web, where paging may not fire a reliable momentum-end).
|
|
570
625
|
const updateIndexFromOffset = (0, _react.useCallback)(offsetX => {
|
|
571
|
-
const lastIndex =
|
|
626
|
+
const lastIndex = items.length - 1;
|
|
572
627
|
if (lastIndex < 0) return;
|
|
573
628
|
const next = Math.min(Math.max(Math.round(offsetX / SCREEN_WIDTH), 0), lastIndex);
|
|
574
629
|
if (next === activeIndexRef.current) return;
|
|
@@ -576,16 +631,16 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
576
631
|
// the page scrolling away).
|
|
577
632
|
resetZoom();
|
|
578
633
|
setActiveIndexBoth(next);
|
|
579
|
-
const
|
|
580
|
-
if (
|
|
581
|
-
}, [ensureRatio,
|
|
634
|
+
const item = items[next];
|
|
635
|
+
if (item) ensureRatio(next, posterUri(item));
|
|
636
|
+
}, [ensureRatio, items, resetZoom, setActiveIndexBoth, SCREEN_WIDTH]);
|
|
582
637
|
|
|
583
638
|
// Programmatically page to `index` (arrow buttons, keyboard, thumbnail taps).
|
|
584
639
|
// Clamps into range and lets the existing scroll handlers own the index/ratio
|
|
585
640
|
// state-sync — this only triggers the scroll. No-op until the pager is mounted.
|
|
586
641
|
const pageTo = (0, _react.useCallback)(index => {
|
|
587
642
|
if (!pagerReady) return;
|
|
588
|
-
const lastIndex =
|
|
643
|
+
const lastIndex = items.length - 1;
|
|
589
644
|
if (lastIndex < 0) return;
|
|
590
645
|
const clamped = Math.min(Math.max(index, 0), lastIndex);
|
|
591
646
|
resetZoom();
|
|
@@ -594,7 +649,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
594
649
|
y: 0,
|
|
595
650
|
animated: true
|
|
596
651
|
});
|
|
597
|
-
}, [
|
|
652
|
+
}, [items.length, pagerReady, resetZoom, SCREEN_WIDTH]);
|
|
598
653
|
const onPagerScroll = (0, _react.useCallback)(event => {
|
|
599
654
|
updateIndexFromOffset(event.nativeEvent.contentOffset.x);
|
|
600
655
|
}, [updateIndexFromOffset]);
|
|
@@ -715,10 +770,12 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
715
770
|
// pinch + pan-while-zoomed.
|
|
716
771
|
const activePageGesture = (0, _react.useMemo)(() => _reactNativeGestureHandler.Gesture.Simultaneous(tapGesture, pinchGesture, panWhileZoomedGesture), [tapGesture, pinchGesture, panWhileZoomedGesture]);
|
|
717
772
|
|
|
718
|
-
// Share the active
|
|
719
|
-
// on supporting browsers (the button is only rendered where it exists).
|
|
773
|
+
// Share the active item's URI: the OS share sheet on native; the Web Share API
|
|
774
|
+
// on supporting browsers (the button is only rendered where it exists). A
|
|
775
|
+
// video shares its own `shareUrl`; its poster is not what "share this" means.
|
|
720
776
|
const handleShare = (0, _react.useCallback)(async () => {
|
|
721
|
-
const
|
|
777
|
+
const item = items[activeIndexRef.current];
|
|
778
|
+
const uri = item === undefined ? undefined : shareUri(item);
|
|
722
779
|
if (!uri) return;
|
|
723
780
|
try {
|
|
724
781
|
if (_reactNative.Platform.OS === 'web') {
|
|
@@ -736,9 +793,9 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
736
793
|
// A user-cancelled share sheet rejects (AbortError on web) — that's
|
|
737
794
|
// expected, not a failure. Anything else is a real error worth surfacing.
|
|
738
795
|
if (err instanceof Error && err.name === 'AbortError') return;
|
|
739
|
-
if (typeof console !== 'undefined' && console.error) console.error('
|
|
796
|
+
if (typeof console !== 'undefined' && console.error) console.error('Media share failed:', err);
|
|
740
797
|
}
|
|
741
|
-
}, [
|
|
798
|
+
}, [items]);
|
|
742
799
|
|
|
743
800
|
// Keyboard navigation (web only), scoped to the open lifetime like Dialog.web's
|
|
744
801
|
// Escape handler. Reads the live index from the ref so it need not re-subscribe
|
|
@@ -766,7 +823,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
766
823
|
style: _reactNative.StyleSheet.absoluteFill,
|
|
767
824
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index.Backdrop, {
|
|
768
825
|
onPress: handleDismiss,
|
|
769
|
-
accessibilityLabel: "Close
|
|
826
|
+
accessibilityLabel: "Close media viewer",
|
|
770
827
|
progress: opacity
|
|
771
828
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
|
|
772
829
|
gesture: panGesture,
|
|
@@ -776,23 +833,22 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
776
833
|
height: SCREEN_HEIGHT
|
|
777
834
|
}, webUserSelectNoneStyle],
|
|
778
835
|
pointerEvents: "box-none",
|
|
779
|
-
children: [!pagerReady && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
836
|
+
children: [!pagerReady && activeItem !== undefined && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
780
837
|
onPress: handleDismiss,
|
|
781
838
|
style: webPointerStyle,
|
|
782
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
783
|
-
|
|
784
|
-
uri: images[activeIndex]?.uri
|
|
785
|
-
},
|
|
786
|
-
contentFit: "contain",
|
|
787
|
-
style: [{
|
|
839
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
840
|
+
style: [styles.mediaBox, {
|
|
788
841
|
width: activeFit.width,
|
|
789
842
|
height: activeFit.height,
|
|
790
843
|
borderRadius: radiusFor(activeFit)
|
|
791
844
|
}, openImageStyle],
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
845
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MediaSurface.MediaSurface, {
|
|
846
|
+
content: activeItem,
|
|
847
|
+
contentFit: "contain",
|
|
848
|
+
nativeControls: videoControls,
|
|
849
|
+
accessibilityLabel: activeItem.alt,
|
|
850
|
+
style: _reactNative.StyleSheet.absoluteFill
|
|
851
|
+
})
|
|
796
852
|
})
|
|
797
853
|
}), pagerReady && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
798
854
|
style: [_reactNative.StyleSheet.absoluteFill, pagerContainerStyle],
|
|
@@ -815,73 +871,71 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
815
871
|
} : {}),
|
|
816
872
|
scrollEventThrottle: 16,
|
|
817
873
|
style: _reactNative.StyleSheet.absoluteFill,
|
|
818
|
-
children:
|
|
819
|
-
const
|
|
820
|
-
const
|
|
874
|
+
children: items.map((item, idx) => {
|
|
875
|
+
const uri = posterUri(item);
|
|
876
|
+
const ratio = pageRatios[idx] ?? cachedRatio(item) ?? item.aspectRatio ?? _index3.DEFAULT_ASPECT_RATIO;
|
|
877
|
+
const fit = fitForRatio(ratio, uri);
|
|
821
878
|
// Only the active page is zoomable + gesture-wrapped (double-tap
|
|
822
|
-
// / single-tap dismiss / pinch / pan).
|
|
823
|
-
//
|
|
824
|
-
//
|
|
879
|
+
// / single-tap dismiss / pinch / pan). It is also the ONLY page
|
|
880
|
+
// that mounts a real media surface: an off-screen page renders
|
|
881
|
+
// the still, because a second `VideoView` on the same player is
|
|
882
|
+
// exactly the duplicate surface this package exists to avoid.
|
|
883
|
+
// The active media handles its own tap-to-dismiss through the
|
|
884
|
+
// gesture, so it isn't wrapped in a Pressable.
|
|
825
885
|
if (idx === activeIndex) {
|
|
826
886
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
827
887
|
// The page fills the screen ON TOP of the backdrop (the
|
|
828
888
|
// pager below it needs pointer events to swipe), so a
|
|
829
|
-
// press on the empty area around the
|
|
889
|
+
// press on the empty area around the media can never
|
|
830
890
|
// reach the backdrop — the page dismisses it itself.
|
|
831
|
-
// Same outcome as tapping the
|
|
891
|
+
// Same outcome as tapping the media, which already
|
|
832
892
|
// dismisses through `singleTapDismissGesture`.
|
|
833
893
|
onPress: handleDismiss,
|
|
834
|
-
accessibilityLabel: "Close
|
|
894
|
+
accessibilityLabel: "Close media viewer",
|
|
835
895
|
style: [styles.page, {
|
|
836
896
|
width: SCREEN_WIDTH,
|
|
837
897
|
height: SCREEN_HEIGHT
|
|
838
898
|
}, webPointerStyle],
|
|
839
899
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
|
|
840
900
|
gesture: activePageGesture,
|
|
841
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
842
|
-
|
|
843
|
-
uri: img.uri
|
|
844
|
-
},
|
|
845
|
-
contentFit: "contain",
|
|
846
|
-
style: [{
|
|
901
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
902
|
+
style: [styles.mediaBox, {
|
|
847
903
|
width: fit.width,
|
|
848
904
|
height: fit.height,
|
|
849
905
|
borderRadius: radiusFor(fit)
|
|
850
906
|
}, zoomImageStyle],
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
907
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MediaSurface.MediaSurface, {
|
|
908
|
+
content: item,
|
|
909
|
+
contentFit: "contain",
|
|
910
|
+
nativeControls: videoControls,
|
|
911
|
+
accessibilityLabel: item.alt,
|
|
912
|
+
style: _reactNative.StyleSheet.absoluteFill
|
|
913
|
+
})
|
|
855
914
|
})
|
|
856
915
|
})
|
|
857
|
-
},
|
|
916
|
+
}, mediaKey(item, idx));
|
|
858
917
|
}
|
|
859
918
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
860
919
|
onPress: handleDismiss,
|
|
861
|
-
accessibilityLabel: "Close
|
|
920
|
+
accessibilityLabel: "Close media viewer",
|
|
862
921
|
style: [styles.page, {
|
|
863
922
|
width: SCREEN_WIDTH,
|
|
864
923
|
height: SCREEN_HEIGHT
|
|
865
924
|
}],
|
|
866
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
867
|
-
|
|
868
|
-
uri: img.uri
|
|
869
|
-
},
|
|
925
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MediaSurface.MediaPoster, {
|
|
926
|
+
content: item,
|
|
870
927
|
contentFit: "contain",
|
|
928
|
+
accessibilityLabel: item.alt,
|
|
871
929
|
style: {
|
|
872
930
|
width: fit.width,
|
|
873
931
|
height: fit.height,
|
|
874
932
|
borderRadius: radiusFor(fit)
|
|
875
|
-
}
|
|
876
|
-
transition: 0,
|
|
877
|
-
...(_reactNative.Platform.OS === 'web' ? {
|
|
878
|
-
draggable: false
|
|
879
|
-
} : {})
|
|
933
|
+
}
|
|
880
934
|
})
|
|
881
|
-
},
|
|
935
|
+
}, mediaKey(item, idx));
|
|
882
936
|
})
|
|
883
937
|
})
|
|
884
|
-
}), pagerReady &&
|
|
938
|
+
}), pagerReady && items.length > 1 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNativeReanimated.default.View, {
|
|
885
939
|
style: [styles.indicatorWrap, backdropStyle],
|
|
886
940
|
pointerEvents: "box-none",
|
|
887
941
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -889,47 +943,41 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
889
943
|
pointerEvents: "none",
|
|
890
944
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
891
945
|
style: styles.counterText,
|
|
892
|
-
children: `${activeIndex + 1} / ${
|
|
946
|
+
children: `${activeIndex + 1} / ${items.length}`
|
|
893
947
|
})
|
|
894
948
|
}), indicatorVariant === 'thumbnails' ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
|
|
895
949
|
horizontal: true,
|
|
896
950
|
showsHorizontalScrollIndicator: false,
|
|
897
951
|
style: styles.thumbStripScroll,
|
|
898
952
|
contentContainerStyle: styles.thumbStripContent,
|
|
899
|
-
children:
|
|
953
|
+
children: items.map((item, idx) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
900
954
|
onPress: () => pageTo(idx),
|
|
901
955
|
accessibilityRole: "button",
|
|
902
|
-
accessibilityLabel: `Go to
|
|
956
|
+
accessibilityLabel: `Go to item ${idx + 1} of ${items.length}`,
|
|
903
957
|
style: [styles.thumbTile, idx === activeIndex ? styles.thumbTileActive : styles.thumbTileInactive, webPointerStyle],
|
|
904
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
905
|
-
|
|
906
|
-
uri: img.uri
|
|
907
|
-
},
|
|
958
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MediaSurface.MediaPoster, {
|
|
959
|
+
content: item,
|
|
908
960
|
contentFit: "cover",
|
|
909
|
-
style: styles.thumbTileImage
|
|
910
|
-
transition: 0,
|
|
911
|
-
...(_reactNative.Platform.OS === 'web' ? {
|
|
912
|
-
draggable: false
|
|
913
|
-
} : {})
|
|
961
|
+
style: styles.thumbTileImage
|
|
914
962
|
})
|
|
915
|
-
}, `thumb-${
|
|
963
|
+
}, `thumb-${mediaKey(item, idx)}`))
|
|
916
964
|
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
917
965
|
style: styles.dotsRow,
|
|
918
966
|
pointerEvents: "none",
|
|
919
|
-
children:
|
|
967
|
+
children: items.map((item, idx) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
920
968
|
style: [styles.dot, idx === activeIndex ? styles.dotActive : styles.dotInactive]
|
|
921
|
-
}, `dot-${
|
|
969
|
+
}, `dot-${mediaKey(item, idx)}`))
|
|
922
970
|
})]
|
|
923
|
-
}), _reactNative.Platform.OS === 'web' && pagerReady &&
|
|
971
|
+
}), _reactNative.Platform.OS === 'web' && pagerReady && items.length > 1 && activeIndex > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(NavArrow, {
|
|
924
972
|
direction: "left",
|
|
925
973
|
onPress: () => pageTo(activeIndex - 1)
|
|
926
|
-
}), _reactNative.Platform.OS === 'web' && pagerReady &&
|
|
974
|
+
}), _reactNative.Platform.OS === 'web' && pagerReady && items.length > 1 && activeIndex < items.length - 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(NavArrow, {
|
|
927
975
|
direction: "right",
|
|
928
976
|
onPress: () => pageTo(activeIndex + 1)
|
|
929
977
|
}), canShare && pagerReady && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
930
978
|
onPress: handleShare,
|
|
931
979
|
accessibilityRole: "button",
|
|
932
|
-
accessibilityLabel: "Share
|
|
980
|
+
accessibilityLabel: "Share media",
|
|
933
981
|
hitSlop: 8,
|
|
934
982
|
style: [styles.shareButton, webPointerStyle],
|
|
935
983
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_index2.ArrowOutOfBox_Stroke2_Corner0_Rounded, {
|
|
@@ -937,7 +985,7 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
937
985
|
size: "md"
|
|
938
986
|
})
|
|
939
987
|
}), activeAlt ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
940
|
-
style: [styles.altCaptionWrap,
|
|
988
|
+
style: [styles.altCaptionWrap, items.length > 1 && styles.altCaptionWrapWithIndicator, backdropStyle],
|
|
941
989
|
pointerEvents: "none",
|
|
942
990
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
943
991
|
style: styles.altCaptionPill,
|
|
@@ -964,8 +1012,8 @@ const ZoomableImageGalleryInner = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
964
1012
|
children: renderContent()
|
|
965
1013
|
});
|
|
966
1014
|
});
|
|
967
|
-
|
|
968
|
-
const
|
|
1015
|
+
ZoomableMediaGalleryInner.displayName = 'ZoomableMediaGallery';
|
|
1016
|
+
const ZoomableMediaGallery = exports.ZoomableMediaGallery = ZoomableMediaGalleryInner;
|
|
969
1017
|
const styles = _reactNative.StyleSheet.create({
|
|
970
1018
|
modalContainer: {
|
|
971
1019
|
justifyContent: 'center',
|
|
@@ -1008,6 +1056,12 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
1008
1056
|
justifyContent: 'center',
|
|
1009
1057
|
alignItems: 'center'
|
|
1010
1058
|
},
|
|
1059
|
+
// The box the media is painted into. `overflow: hidden` is what makes the
|
|
1060
|
+
// box's corner radius clip a VIDEO — an image would round on its own, a
|
|
1061
|
+
// platform video surface will not.
|
|
1062
|
+
mediaBox: {
|
|
1063
|
+
overflow: 'hidden'
|
|
1064
|
+
},
|
|
1011
1065
|
indicatorWrap: {
|
|
1012
1066
|
position: 'absolute',
|
|
1013
1067
|
bottom: 48,
|
|
@@ -1143,5 +1197,5 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
1143
1197
|
lineHeight: 18
|
|
1144
1198
|
}
|
|
1145
1199
|
});
|
|
1146
|
-
var _default = exports.default =
|
|
1147
|
-
//# sourceMappingURL=
|
|
1200
|
+
var _default = exports.default = ZoomableMediaGallery;
|
|
1201
|
+
//# sourceMappingURL=ZoomableMediaGallery.js.map
|