zkit-ui 2.0.5 → 2.0.6
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/README.md +1 -1
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryLayoutSupport.java → NativeImagePreviewLayoutSupport.java} +12 -12
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryMediaItem.java → NativeImagePreviewMediaItem.java} +2 -2
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewModule.kt +43 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryPagerAdapter.java → NativeImagePreviewPagerAdapter.java} +18 -18
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySharedElementGeometry.java → NativeImagePreviewSharedElementGeometry.java} +2 -2
- package/android/src/main/java/cn/fontree/zkit/ui/preview/NativeImagePreviewSharedElementNames.java +39 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySharedElementState.java → NativeImagePreviewSharedElementState.java} +4 -4
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GallerySourceViewRegistry.java → NativeImagePreviewSourceViewRegistry.java} +2 -2
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryTransitionSnapshotView.java → NativeImagePreviewTransitionSnapshotView.java} +3 -3
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryVideoPlayerView.java → NativeImagePreviewVideoPlayerView.java} +7 -7
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GaleriaView.kt → NativeImagePreviewView.kt} +18 -18
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerOverlayView.java → NativeImagePreviewViewerOverlayView.java} +49 -49
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerPageView.java → NativeImagePreviewViewerPageView.java} +27 -27
- package/android/src/main/java/cn/fontree/zkit/ui/preview/{GalleryViewerTransitionController.java → NativeImagePreviewViewerTransitionController.java} +22 -22
- package/dist/ui/NativeImagePreview/NativeImagePreview.js +6 -6
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts +1 -0
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.d.ts.map +1 -1
- package/dist/ui/NativeImagePreview/NativeImagePreview.web.js +105 -11
- package/dist/ui/NativeImagePreview/context.d.ts +1 -1
- package/dist/ui/NativeImagePreview/context.d.ts.map +1 -1
- package/dist/ui/NativeImagePreview/types.d.ts +3 -1
- package/dist/ui/NativeImagePreview/types.d.ts.map +1 -1
- package/dist/ui/NativeImagePreview/utils.d.ts.map +1 -1
- package/dist/ui/NativeImagePreview/utils.js +1 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts +20 -0
- package/dist/ui/NativeImagePreview/web/geometry.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/geometry.js +121 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts +6 -0
- package/dist/ui/NativeImagePreview/web/media.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/media.js +90 -0
- package/dist/ui/NativeImagePreview/web/styles.css +137 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts +15 -0
- package/dist/ui/NativeImagePreview/web/transition.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/transition.js +77 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts +74 -0
- package/dist/ui/NativeImagePreview/web/types.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/types.js +3 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts +104 -0
- package/dist/ui/NativeImagePreview/web/viewer.d.ts.map +1 -0
- package/dist/ui/NativeImagePreview/web/viewer.js +894 -0
- package/expo-module.config.json +2 -2
- package/ios/{GalleryViewer/GaleriaImageLoader.swift → NativeImagePreview/NativeImagePreviewImageLoader.swift} +7 -7
- package/ios/NativeImagePreview/NativeImagePreviewImageViewInteraction.swift +245 -0
- package/ios/{GalleryViewer/GaleriaLayoutSupport.swift → NativeImagePreview/NativeImagePreviewLayoutSupport.swift} +19 -19
- package/ios/NativeImagePreview/NativeImagePreviewMedia.swift +28 -0
- package/ios/NativeImagePreview/NativeImagePreviewSourceViewRegistry.swift +80 -0
- package/ios/{GalleryViewer/GaleriaVideoPlayerView.swift → NativeImagePreview/NativeImagePreviewVideoPlayerView.swift} +1 -1
- package/ios/{GalleryViewer/GaleriaViewerConfiguration.swift → NativeImagePreview/NativeImagePreviewViewerConfiguration.swift} +7 -7
- package/ios/{GalleryViewer/GaleriaViewerController.swift → NativeImagePreview/NativeImagePreviewViewerController.swift} +36 -36
- package/ios/{GalleryViewer/GaleriaViewerPageView.swift → NativeImagePreview/NativeImagePreviewViewerPageView.swift} +33 -33
- package/ios/{GalleryViewer/GaleriaViewerTransitionCoordinator.swift → NativeImagePreview/NativeImagePreviewViewerTransitionCoordinator.swift} +16 -16
- package/ios/{GaleriaModule.swift → NativeImagePreviewModule.swift} +4 -4
- package/ios/{GaleriaView.swift → NativeImagePreviewView.swift} +26 -27
- package/package.json +18 -12
- package/src/services/ImagePreviewService/README.md +1 -1
- package/src/types.d.ts +1 -0
- package/src/ui/NativeImagePreview/NativeImagePreview.tsx +7 -7
- package/src/ui/NativeImagePreview/NativeImagePreview.web.tsx +175 -11
- package/src/ui/NativeImagePreview/README.md +3 -3
- package/src/ui/NativeImagePreview/context.tsx +1 -1
- package/src/ui/NativeImagePreview/types.ts +3 -1
- package/src/ui/NativeImagePreview/utils.ts +1 -0
- package/src/ui/NativeImagePreview/web/geometry.ts +134 -0
- package/src/ui/NativeImagePreview/web/media.ts +91 -0
- package/src/ui/NativeImagePreview/web/styles.css +137 -0
- package/src/ui/NativeImagePreview/web/transition.ts +111 -0
- package/src/ui/NativeImagePreview/web/types.ts +88 -0
- package/src/ui/NativeImagePreview/web/viewer.ts +1024 -0
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GaleriaModule.kt +0 -43
- package/android/src/main/java/cn/fontree/zkit/ui/preview/GallerySharedElementNames.java +0 -39
- package/ios/GalleryViewer/GaleriaImageViewInteraction.swift +0 -245
- package/ios/GalleryViewer/GaleriaMedia.swift +0 -28
- package/ios/GalleryViewer/GaleriaSourceViewRegistry.swift +0 -80
- /package/android/src/main/res/layout/{galeria_player_view.xml → native_image_preview_player_view.xml} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
2
|
import UIKit
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class NativeImagePreviewView: ExpoView {
|
|
5
5
|
private var childImageView: UIImageView?
|
|
6
6
|
private weak var registeredAnchorImageView: UIImageView?
|
|
7
|
-
private var
|
|
7
|
+
private var registeredPreviewGroupId: String?
|
|
8
8
|
private var registeredIndex: Int?
|
|
9
9
|
|
|
10
10
|
deinit {
|
|
@@ -57,11 +57,10 @@ class GaleriaView: ExpoView {
|
|
|
57
57
|
#endif
|
|
58
58
|
|
|
59
59
|
#if RCT_NEW_ARCH_ENABLED
|
|
60
|
-
//
|
|
61
|
-
// Cleanup gesture recognizers from the image view to work with fabric view recycling
|
|
60
|
+
// Cleanup gesture recognizers from the image view to work with Fabric view recycling.
|
|
62
61
|
override func unmountChildComponentView(_ childComponentView: UIView, index: Int) {
|
|
63
62
|
unregisterCurrentAnchor()
|
|
64
|
-
childImageView?.
|
|
63
|
+
childImageView?.removeNativeImagePreviewViewerInteraction()
|
|
65
64
|
childImageView = nil
|
|
66
65
|
super.unmountChildComponentView(childComponentView, index: index)
|
|
67
66
|
}
|
|
@@ -71,7 +70,7 @@ class GaleriaView: ExpoView {
|
|
|
71
70
|
var urls: [String]?
|
|
72
71
|
var items: [[String: Any]]?
|
|
73
72
|
var initialIndex: Int?
|
|
74
|
-
var
|
|
73
|
+
var previewGroupId: String?
|
|
75
74
|
var closeIconName: String?
|
|
76
75
|
var rightNavItemIconName: String?
|
|
77
76
|
let onPressRightNavItemIcon = EventDispatcher()
|
|
@@ -99,7 +98,7 @@ class GaleriaView: ExpoView {
|
|
|
99
98
|
) {
|
|
100
99
|
let configuration = buildViewerConfiguration()
|
|
101
100
|
|
|
102
|
-
let imageItems: [
|
|
101
|
+
let imageItems: [NativeImagePreviewMediaItem] = items.compactMap { item in
|
|
103
102
|
guard let type = item["type"] as? String else { return nil }
|
|
104
103
|
|
|
105
104
|
if type == "video", let urlStr = item["url"] as? String, let url = URL(string: urlStr) {
|
|
@@ -107,25 +106,25 @@ class GaleriaView: ExpoView {
|
|
|
107
106
|
if let posterStr = item["poster"] as? String {
|
|
108
107
|
posterUrl = URL(string: posterStr)
|
|
109
108
|
}
|
|
110
|
-
return
|
|
109
|
+
return NativeImagePreviewMediaItem.video(url: url, poster: posterUrl)
|
|
111
110
|
} else if type == "image", let urlStr = item["url"] as? String {
|
|
112
111
|
if urlStr.hasPrefix("http://") || urlStr.hasPrefix("https://") || urlStr.hasPrefix("file://") {
|
|
113
112
|
if let url = URL(string: urlStr) {
|
|
114
|
-
return
|
|
113
|
+
return NativeImagePreviewMediaItem.imageURL(url, placeholder: nil)
|
|
115
114
|
}
|
|
116
115
|
} else {
|
|
117
|
-
return
|
|
116
|
+
return NativeImagePreviewMediaItem.imageURL(URL(fileURLWithPath: urlStr), placeholder: nil)
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
return nil
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
let datasource =
|
|
124
|
-
childImage.
|
|
122
|
+
let datasource = NativeImagePreviewArrayDataSource(items: imageItems)
|
|
123
|
+
childImage.setupNativeImagePreviewViewer(
|
|
125
124
|
dataSource: datasource,
|
|
126
125
|
initialIndex: initialIndex,
|
|
127
126
|
configuration: configuration,
|
|
128
|
-
|
|
127
|
+
previewGroupId: previewGroupId)
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
|
|
@@ -143,11 +142,11 @@ class GaleriaView: ExpoView {
|
|
|
143
142
|
return URL(fileURLWithPath: string)
|
|
144
143
|
}
|
|
145
144
|
|
|
146
|
-
childImage.
|
|
145
|
+
childImage.setupNativeImagePreviewViewer(
|
|
147
146
|
urls: urlObjects,
|
|
148
147
|
initialIndex: initialIndex,
|
|
149
148
|
configuration: configuration,
|
|
150
|
-
|
|
149
|
+
previewGroupId: previewGroupId)
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
private func setupViewerWithSingleImage(_ childImage: UIImageView) {
|
|
@@ -157,11 +156,11 @@ class GaleriaView: ExpoView {
|
|
|
157
156
|
}
|
|
158
157
|
let configuration = buildViewerConfiguration()
|
|
159
158
|
|
|
160
|
-
childImage.
|
|
159
|
+
childImage.setupNativeImagePreviewViewer(images: [img], configuration: configuration, previewGroupId: previewGroupId)
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
private func buildViewerConfiguration() ->
|
|
164
|
-
var configuration =
|
|
162
|
+
private func buildViewerConfiguration() -> NativeImagePreviewViewerConfiguration {
|
|
163
|
+
var configuration = NativeImagePreviewViewerConfiguration(theme: theme.toViewerTheme())
|
|
165
164
|
let iconColor = theme.iconColor()
|
|
166
165
|
|
|
167
166
|
if let closeIconName = closeIconName,
|
|
@@ -191,7 +190,7 @@ class GaleriaView: ExpoView {
|
|
|
191
190
|
private func registerCurrentAnchorIfNeeded() {
|
|
192
191
|
guard
|
|
193
192
|
window != nil,
|
|
194
|
-
let
|
|
193
|
+
let previewGroupId,
|
|
195
194
|
let initialIndex,
|
|
196
195
|
let childImage = getChildImageView()
|
|
197
196
|
else {
|
|
@@ -202,30 +201,30 @@ class GaleriaView: ExpoView {
|
|
|
202
201
|
if
|
|
203
202
|
let registeredAnchorImageView,
|
|
204
203
|
(registeredAnchorImageView !== childImage
|
|
205
|
-
||
|
|
204
|
+
|| registeredPreviewGroupId != previewGroupId
|
|
206
205
|
|| registeredIndex != initialIndex)
|
|
207
206
|
{
|
|
208
207
|
unregisterCurrentAnchor()
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
|
|
210
|
+
NativeImagePreviewSourceViewRegistry.shared.register(
|
|
212
211
|
childImage,
|
|
213
|
-
|
|
212
|
+
previewGroupId: previewGroupId,
|
|
214
213
|
index: initialIndex)
|
|
215
214
|
registeredAnchorImageView = childImage
|
|
216
|
-
|
|
215
|
+
registeredPreviewGroupId = previewGroupId
|
|
217
216
|
registeredIndex = initialIndex
|
|
218
217
|
}
|
|
219
218
|
|
|
220
219
|
private func unregisterCurrentAnchor() {
|
|
221
220
|
if let registeredAnchorImageView = registeredAnchorImageView {
|
|
222
|
-
|
|
221
|
+
NativeImagePreviewSourceViewRegistry.shared.unregister(
|
|
223
222
|
registeredAnchorImageView,
|
|
224
|
-
|
|
223
|
+
previewGroupId: registeredPreviewGroupId,
|
|
225
224
|
index: registeredIndex)
|
|
226
225
|
}
|
|
227
226
|
registeredAnchorImageView = nil
|
|
228
|
-
|
|
227
|
+
registeredPreviewGroupId = nil
|
|
229
228
|
registeredIndex = nil
|
|
230
229
|
}
|
|
231
230
|
}
|
|
@@ -234,7 +233,7 @@ enum Theme: String, Enumerable {
|
|
|
234
233
|
case dark
|
|
235
234
|
case light
|
|
236
235
|
|
|
237
|
-
func toViewerTheme() ->
|
|
236
|
+
func toViewerTheme() -> NativeImagePreviewViewerTheme {
|
|
238
237
|
switch self {
|
|
239
238
|
case .dark:
|
|
240
239
|
return .dark
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zkit-ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -170,7 +170,10 @@
|
|
|
170
170
|
"./assets/*": "./dist/assets/*",
|
|
171
171
|
"./package.json": "./package.json"
|
|
172
172
|
},
|
|
173
|
-
"sideEffects":
|
|
173
|
+
"sideEffects": [
|
|
174
|
+
"*.css",
|
|
175
|
+
"**/*.css"
|
|
176
|
+
],
|
|
174
177
|
"repository": {
|
|
175
178
|
"type": "git",
|
|
176
179
|
"url": "git+https://github.com/Concur-max/zkit.git",
|
|
@@ -181,7 +184,8 @@
|
|
|
181
184
|
"url": "https://github.com/Concur-max/zkit/issues"
|
|
182
185
|
},
|
|
183
186
|
"files": [
|
|
184
|
-
"android",
|
|
187
|
+
"android/build.gradle",
|
|
188
|
+
"android/src",
|
|
185
189
|
"dist",
|
|
186
190
|
"expo-module.config.json",
|
|
187
191
|
"src",
|
|
@@ -193,6 +197,14 @@
|
|
|
193
197
|
"publishConfig": {
|
|
194
198
|
"registry": "https://registry.npmjs.org/"
|
|
195
199
|
},
|
|
200
|
+
"scripts": {
|
|
201
|
+
"build": "node scripts/build.cjs",
|
|
202
|
+
"verify:entrypoints": "node scripts/verify-entrypoints.cjs",
|
|
203
|
+
"verify": "pnpm run build && pnpm run verify:entrypoints",
|
|
204
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
205
|
+
"prepack": "pnpm run build",
|
|
206
|
+
"prepublishOnly": "pnpm run build"
|
|
207
|
+
},
|
|
196
208
|
"dependencies": {
|
|
197
209
|
"@vant/area-data": "^2.1.0",
|
|
198
210
|
"dayjs": "^1.11.13",
|
|
@@ -219,6 +231,7 @@
|
|
|
219
231
|
},
|
|
220
232
|
"devDependencies": {
|
|
221
233
|
"@expo/vector-icons": "^15.1.1",
|
|
234
|
+
"@lodev09/react-native-true-sheet": "workspace:*",
|
|
222
235
|
"@shopify/flash-list": "^2.3.1",
|
|
223
236
|
"@types/react": "~19.1.17",
|
|
224
237
|
"dayjs": "^1.11.13",
|
|
@@ -235,13 +248,6 @@
|
|
|
235
248
|
"react-native-svg": "^15.12.1",
|
|
236
249
|
"react-native-worklets": "0.5.1",
|
|
237
250
|
"sonner-native": "^0.22.2",
|
|
238
|
-
"typescript": "~5.9.3"
|
|
239
|
-
"@lodev09/react-native-true-sheet": "3.10.0"
|
|
240
|
-
},
|
|
241
|
-
"scripts": {
|
|
242
|
-
"build": "node scripts/build.cjs",
|
|
243
|
-
"verify:entrypoints": "node scripts/verify-entrypoints.cjs",
|
|
244
|
-
"verify": "pnpm run build && pnpm run verify:entrypoints",
|
|
245
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
251
|
+
"typescript": "~5.9.3"
|
|
246
252
|
}
|
|
247
|
-
}
|
|
253
|
+
}
|
|
@@ -77,7 +77,7 @@ import { NativeImagePreview } from 'zkit-ui/native-image-preview';
|
|
|
77
77
|
两者不要混用成同一个本地封装:
|
|
78
78
|
|
|
79
79
|
- `ImagePreview`:普通全屏预览,支持声明式 `open/value` 和命令式 `imagePreview.open()`。
|
|
80
|
-
- `NativeImagePreview
|
|
80
|
+
- `NativeImagePreview`:共享转场预览,需要包住真实缩略图节点,iOS / Android 走原生 viewer,Web 走 H5 viewer。
|
|
81
81
|
|
|
82
82
|
## 手动确认点
|
|
83
83
|
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module '*.css';
|
|
@@ -25,7 +25,7 @@ type NativePreviewViewProps = {
|
|
|
25
25
|
children: React.ReactElement;
|
|
26
26
|
index: number;
|
|
27
27
|
style?: StyleProp<ViewStyle>;
|
|
28
|
-
|
|
28
|
+
previewGroupId: string;
|
|
29
29
|
items: readonly NativeImagePreviewNativeItem[];
|
|
30
30
|
theme: 'dark' | 'light';
|
|
31
31
|
edgeToEdge: boolean;
|
|
@@ -39,7 +39,7 @@ const NativePreviewView = requireNativeView<NativePreviewViewProps>(
|
|
|
39
39
|
|
|
40
40
|
const ANDROID_EDGE_TO_EDGE = Platform.OS === 'android' ? isEdgeToEdge() : false;
|
|
41
41
|
|
|
42
|
-
function
|
|
42
|
+
function createPreviewGroupId() {
|
|
43
43
|
return `zkit-native-image-preview-${Date.now().toString(36)}-${Math.random()
|
|
44
44
|
.toString(36)
|
|
45
45
|
.slice(2, 10)}`;
|
|
@@ -55,9 +55,9 @@ const NativeImagePreviewRoot = React.memo(function NativeImagePreview({
|
|
|
55
55
|
onChange,
|
|
56
56
|
}: NativeImagePreviewProps) {
|
|
57
57
|
const systemColorScheme = useColorScheme();
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
60
|
-
|
|
58
|
+
const previewGroupIdRef = React.useRef<string | null>(null);
|
|
59
|
+
if (previewGroupIdRef.current == null) {
|
|
60
|
+
previewGroupIdRef.current = createPreviewGroupId();
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const resolvedItems = React.useMemo(
|
|
@@ -71,7 +71,7 @@ const NativeImagePreviewRoot = React.memo(function NativeImagePreview({
|
|
|
71
71
|
const resolvedColorScheme = resolveNativeImagePreviewColorScheme(colorScheme, systemColorScheme);
|
|
72
72
|
const contextValue = React.useMemo(
|
|
73
73
|
() => ({
|
|
74
|
-
|
|
74
|
+
previewGroupId: previewGroupIdRef.current!,
|
|
75
75
|
items: resolvedItems,
|
|
76
76
|
nativeItems,
|
|
77
77
|
colorScheme: resolvedColorScheme,
|
|
@@ -129,7 +129,7 @@ const NativeImagePreviewItem = React.memo(function NativeImagePreviewItem({
|
|
|
129
129
|
<NativePreviewView
|
|
130
130
|
closeIconName={context.nativeProps?.iosCloseIconName}
|
|
131
131
|
edgeToEdge={resolvedEdgeToEdge}
|
|
132
|
-
|
|
132
|
+
previewGroupId={context.previewGroupId}
|
|
133
133
|
index={clampPreviewIndex(index, context.nativeItems.length)}
|
|
134
134
|
items={context.nativeItems}
|
|
135
135
|
onIndexChange={handleIndexChange}
|
|
@@ -1,35 +1,199 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
3
|
+
import './web/styles.css';
|
|
4
|
+
|
|
1
5
|
import * as React from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
6
|
+
import { View, useColorScheme } from 'react-native';
|
|
7
|
+
import { clampPreviewIndex, normalizeNativeImagePreviewItems, resolveNativeImagePreviewColorScheme } from './utils';
|
|
3
8
|
import type {
|
|
9
|
+
NativeImagePreviewChangeMeta,
|
|
4
10
|
NativeImagePreviewComponent,
|
|
5
11
|
NativeImagePreviewItemProps,
|
|
6
12
|
NativeImagePreviewProps,
|
|
13
|
+
NativeImagePreviewResolvedItem,
|
|
7
14
|
} from './types';
|
|
15
|
+
import { createNativeImagePreviewWeb } from './web/viewer';
|
|
16
|
+
import type {
|
|
17
|
+
NativeImagePreviewWebChangeMeta,
|
|
18
|
+
NativeImagePreviewWebController,
|
|
19
|
+
NativeImagePreviewWebMediaItem,
|
|
20
|
+
} from './web/types';
|
|
8
21
|
|
|
9
|
-
|
|
22
|
+
type SourceElement = HTMLElement | null;
|
|
10
23
|
|
|
11
|
-
|
|
12
|
-
|
|
24
|
+
type NativeImagePreviewWebContextValue = {
|
|
25
|
+
disabled: boolean;
|
|
26
|
+
items: readonly NativeImagePreviewResolvedItem[];
|
|
27
|
+
open: (
|
|
28
|
+
index: number,
|
|
29
|
+
onItemChange?: (value: number, meta: NativeImagePreviewChangeMeta) => void
|
|
30
|
+
) => void;
|
|
31
|
+
sourceElementsRef: { current: SourceElement[] };
|
|
32
|
+
};
|
|
13
33
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
const NativeImagePreviewWebContext =
|
|
35
|
+
React.createContext<NativeImagePreviewWebContextValue | null>(null);
|
|
36
|
+
|
|
37
|
+
const WebView = View as unknown as React.ElementType;
|
|
38
|
+
|
|
39
|
+
function toWebPreviewItems(
|
|
40
|
+
items: readonly NativeImagePreviewResolvedItem[]
|
|
41
|
+
): NativeImagePreviewWebMediaItem[] {
|
|
42
|
+
return items.map(({ alt, height, id, poster, type, url, width }) => ({
|
|
43
|
+
alt: alt ?? id,
|
|
44
|
+
height,
|
|
45
|
+
id,
|
|
46
|
+
poster,
|
|
47
|
+
type,
|
|
48
|
+
url,
|
|
49
|
+
width,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function findPreviewSourceElement(element: SourceElement): SourceElement {
|
|
54
|
+
if (!element) return null;
|
|
55
|
+
if (element instanceof HTMLImageElement || element instanceof HTMLVideoElement) {
|
|
56
|
+
return element;
|
|
57
|
+
}
|
|
58
|
+
return element.querySelector('img, video') ?? element;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function toChangeMeta(
|
|
62
|
+
meta: NativeImagePreviewWebChangeMeta,
|
|
63
|
+
items: readonly NativeImagePreviewResolvedItem[]
|
|
64
|
+
): NativeImagePreviewChangeMeta {
|
|
65
|
+
return {
|
|
66
|
+
reason: meta.reason,
|
|
67
|
+
item: items[meta.index] ?? null,
|
|
68
|
+
};
|
|
18
69
|
}
|
|
19
70
|
|
|
20
71
|
const NativeImagePreviewRoot = React.memo(function NativeImagePreview({
|
|
21
72
|
children,
|
|
73
|
+
colorScheme = 'dark',
|
|
74
|
+
disabled = false,
|
|
75
|
+
items,
|
|
76
|
+
onChange,
|
|
22
77
|
}: NativeImagePreviewProps) {
|
|
23
|
-
|
|
78
|
+
const systemColorScheme = useColorScheme();
|
|
79
|
+
const controllerRef = React.useRef<NativeImagePreviewWebController | null>(null);
|
|
80
|
+
const sourceElementsRef = React.useRef<SourceElement[]>([]);
|
|
81
|
+
const resolvedItems = React.useMemo(
|
|
82
|
+
() => normalizeNativeImagePreviewItems(items),
|
|
83
|
+
[items]
|
|
84
|
+
);
|
|
85
|
+
const webItems = React.useMemo(
|
|
86
|
+
() => toWebPreviewItems(resolvedItems),
|
|
87
|
+
[resolvedItems]
|
|
88
|
+
);
|
|
89
|
+
const resolvedColorScheme = resolveNativeImagePreviewColorScheme(colorScheme, systemColorScheme);
|
|
90
|
+
|
|
91
|
+
React.useEffect(() => {
|
|
92
|
+
const controller = createNativeImagePreviewWeb();
|
|
93
|
+
controllerRef.current = controller;
|
|
94
|
+
|
|
95
|
+
return () => {
|
|
96
|
+
controller.destroy();
|
|
97
|
+
controllerRef.current = null;
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
24
100
|
|
|
25
|
-
|
|
101
|
+
const open = React.useCallback<NativeImagePreviewWebContextValue['open']>(
|
|
102
|
+
(index, onItemChange) => {
|
|
103
|
+
const controller = controllerRef.current;
|
|
104
|
+
if (disabled || !controller || webItems.length === 0) return;
|
|
105
|
+
|
|
106
|
+
const value = clampPreviewIndex(index, webItems.length);
|
|
107
|
+
const getSourceElement = (nextIndex: number) =>
|
|
108
|
+
findPreviewSourceElement(sourceElementsRef.current[nextIndex] ?? null);
|
|
109
|
+
|
|
110
|
+
void controller.open({
|
|
111
|
+
getSourceElement,
|
|
112
|
+
index: value,
|
|
113
|
+
items: webItems,
|
|
114
|
+
objectFit: 'cover',
|
|
115
|
+
sourceElement: getSourceElement(value),
|
|
116
|
+
sourceImageSize: resolvedItems[value]
|
|
117
|
+
? {
|
|
118
|
+
width: resolvedItems[value].width ?? 0,
|
|
119
|
+
height: resolvedItems[value].height ?? 0,
|
|
120
|
+
}
|
|
121
|
+
: null,
|
|
122
|
+
sourceObjectFit: 'cover',
|
|
123
|
+
sourceVisibility: 'hidden',
|
|
124
|
+
theme: resolvedColorScheme,
|
|
125
|
+
onChange: (nextValue, meta) => {
|
|
126
|
+
const changeMeta = toChangeMeta(meta, resolvedItems);
|
|
127
|
+
onChange?.(nextValue, changeMeta);
|
|
128
|
+
onItemChange?.(nextValue, changeMeta);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
[disabled, onChange, resolvedColorScheme, resolvedItems, webItems]
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const contextValue = React.useMemo<NativeImagePreviewWebContextValue>(
|
|
136
|
+
() => ({
|
|
137
|
+
disabled,
|
|
138
|
+
items: resolvedItems,
|
|
139
|
+
open,
|
|
140
|
+
sourceElementsRef,
|
|
141
|
+
}),
|
|
142
|
+
[disabled, open, resolvedItems]
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<NativeImagePreviewWebContext.Provider value={contextValue}>
|
|
147
|
+
{children}
|
|
148
|
+
</NativeImagePreviewWebContext.Provider>
|
|
149
|
+
);
|
|
26
150
|
});
|
|
27
151
|
|
|
28
152
|
const NativeImagePreviewItem = React.memo(function NativeImagePreviewItem({
|
|
29
153
|
children,
|
|
154
|
+
disabled = false,
|
|
155
|
+
index,
|
|
156
|
+
onChange,
|
|
30
157
|
style,
|
|
31
158
|
}: NativeImagePreviewItemProps) {
|
|
32
|
-
|
|
159
|
+
const context = React.useContext(NativeImagePreviewWebContext);
|
|
160
|
+
const sourceRef = React.useCallback(
|
|
161
|
+
(node: SourceElement) => {
|
|
162
|
+
if (!context) return;
|
|
163
|
+
context.sourceElementsRef.current[index] = node;
|
|
164
|
+
},
|
|
165
|
+
[context, index]
|
|
166
|
+
);
|
|
167
|
+
const itemDisabled = !context || context.disabled || disabled || context.items.length === 0;
|
|
168
|
+
|
|
169
|
+
const open = React.useCallback(() => {
|
|
170
|
+
if (itemDisabled) return;
|
|
171
|
+
context.open(index, onChange);
|
|
172
|
+
}, [context, index, itemDisabled, onChange]);
|
|
173
|
+
|
|
174
|
+
const handleKeyDown = React.useCallback(
|
|
175
|
+
(event: React.KeyboardEvent) => {
|
|
176
|
+
if (itemDisabled) return;
|
|
177
|
+
if (event.key !== 'Enter' && event.key !== ' ') return;
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
open();
|
|
180
|
+
},
|
|
181
|
+
[itemDisabled, open]
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<WebView
|
|
186
|
+
ref={sourceRef}
|
|
187
|
+
aria-label={itemDisabled ? undefined : 'Open image preview'}
|
|
188
|
+
onClick={itemDisabled ? undefined : open}
|
|
189
|
+
onKeyDown={itemDisabled ? undefined : handleKeyDown}
|
|
190
|
+
role={itemDisabled ? undefined : 'button'}
|
|
191
|
+
style={style}
|
|
192
|
+
tabIndex={itemDisabled ? undefined : 0}
|
|
193
|
+
>
|
|
194
|
+
{children}
|
|
195
|
+
</WebView>
|
|
196
|
+
);
|
|
33
197
|
});
|
|
34
198
|
|
|
35
199
|
export const NativeImagePreview = Object.assign(NativeImagePreviewRoot, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NativeImagePreview
|
|
2
2
|
|
|
3
|
-
`NativeImagePreview`
|
|
3
|
+
`NativeImagePreview` 是强调真实缩略图锚点与共享转场体验的图片预览组件。它和普通 `ImagePreview` 服务分工不同:`ImagePreview` 适合命令式打开图片;`NativeImagePreview` 适合从真实缩略图自然过渡到全屏 viewer,并在 iOS / Android 上走原生手势、缩放、滑动和拖拽关闭路径,在 Web 上走 H5 共享转场 viewer。
|
|
4
4
|
|
|
5
5
|
## 适用场景
|
|
6
6
|
|
|
@@ -95,14 +95,14 @@ import { imagePreview } from 'zkit-ui/image-preview';
|
|
|
95
95
|
imagePreview.open({ images, index: 0 });
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
`ImagePreview` 是普通全屏预览服务,适合没有稳定缩略图锚点的命令式场景。`NativeImagePreview`
|
|
98
|
+
`ImagePreview` 是普通全屏预览服务,适合没有稳定缩略图锚点的命令式场景。`NativeImagePreview` 是声明式共享转场组件,必须就地包住真实图片节点。两者不要混成同一个封装,否则很容易把共享转场退化成普通弹层。
|
|
99
99
|
|
|
100
100
|
## 平台行为
|
|
101
101
|
|
|
102
102
|
- iOS / Android:使用原生 viewer 和真实图片锚点,主打共享转场、缩放、滑动和拖拽关闭。
|
|
103
|
+
- Web:使用 H5 viewer 和真实 DOM 图片锚点,支持共享转场、缩放、滑动、视频播放和拖拽关闭。
|
|
103
104
|
- 首次接入或变更原生依赖后需要重新构建 native app;仅刷新 JS bundle 不会注册新的原生 ViewManager。
|
|
104
105
|
- 原生模块未注册时不会降级到 `ImagePreview`。这是 native 接入或构建错误,需要重新安装依赖并重建 native app。
|
|
105
|
-
- Web:仅保留子节点渲染,不提供预览能力,也不会模拟普通 `ImagePreview`。
|
|
106
106
|
|
|
107
107
|
## 手动确认点
|
|
108
108
|
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
} from './types';
|
|
9
9
|
|
|
10
10
|
export type NativeImagePreviewContextValue = {
|
|
11
|
-
|
|
11
|
+
previewGroupId: string;
|
|
12
12
|
items: readonly NativeImagePreviewResolvedItem[];
|
|
13
13
|
nativeItems: readonly NativeImagePreviewNativeItem[];
|
|
14
14
|
colorScheme: NativeImagePreviewResolvedColorScheme;
|
|
@@ -15,6 +15,7 @@ export type NativeImagePreviewSource = ImageSourcePropType | string;
|
|
|
15
15
|
|
|
16
16
|
export type NativeImagePreviewItemDescriptor = {
|
|
17
17
|
id?: string;
|
|
18
|
+
alt?: string;
|
|
18
19
|
type?: NativeImagePreviewMediaType;
|
|
19
20
|
url?: string;
|
|
20
21
|
source?: NativeImagePreviewSource;
|
|
@@ -34,6 +35,7 @@ export type NativeImagePreviewResolvedItem = {
|
|
|
34
35
|
poster?: string;
|
|
35
36
|
width?: number;
|
|
36
37
|
height?: number;
|
|
38
|
+
alt?: string;
|
|
37
39
|
raw: NativeImagePreviewItem;
|
|
38
40
|
};
|
|
39
41
|
|
|
@@ -46,7 +48,7 @@ export type NativeImagePreviewNativeItem = {
|
|
|
46
48
|
height?: number;
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
export type NativeImagePreviewChangeReason = 'swipe';
|
|
51
|
+
export type NativeImagePreviewChangeReason = 'api' | 'swipe';
|
|
50
52
|
|
|
51
53
|
export type NativeImagePreviewChangeEventPayload = {
|
|
52
54
|
currentIndex: number;
|