@vxrn/native 1.12.8-1774390675831
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/.turbo/turbo-build.log +2 -0
- package/android/build.gradle +32 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/dev/vxrn/native/VxrnNativeModule.kt +117 -0
- package/android/src/main/java/dev/vxrn/native/VxrnNativePackage.kt +16 -0
- package/dist/cjs/color/android.dynamic.types.cjs +16 -0
- package/dist/cjs/color/android.dynamic.types.native.js +19 -0
- package/dist/cjs/color/android.dynamic.types.native.js.map +1 -0
- package/dist/cjs/color/android.material.types.cjs +16 -0
- package/dist/cjs/color/android.material.types.native.js +19 -0
- package/dist/cjs/color/android.material.types.native.js.map +1 -0
- package/dist/cjs/color/index.cjs +66 -0
- package/dist/cjs/color/index.native.js +69 -0
- package/dist/cjs/color/index.native.js.map +1 -0
- package/dist/cjs/color/ios.types.cjs +16 -0
- package/dist/cjs/color/ios.types.native.js +19 -0
- package/dist/cjs/color/ios.types.native.js.map +1 -0
- package/dist/cjs/color/materialColor.cjs +32 -0
- package/dist/cjs/color/materialColor.native.js +43 -0
- package/dist/cjs/color/materialColor.native.js.map +1 -0
- package/dist/cjs/index.cjs +37 -0
- package/dist/cjs/index.native.js +40 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/menu/index.cjs +28 -0
- package/dist/cjs/menu/index.native.js +47 -0
- package/dist/cjs/menu/index.native.js.map +1 -0
- package/dist/cjs/menu/types.cjs +16 -0
- package/dist/cjs/menu/types.native.js +19 -0
- package/dist/cjs/menu/types.native.js.map +1 -0
- package/dist/cjs/split-view/index.cjs +26 -0
- package/dist/cjs/split-view/index.native.js +29 -0
- package/dist/cjs/split-view/index.native.js.map +1 -0
- package/dist/cjs/split-view/split-view.cjs +80 -0
- package/dist/cjs/split-view/split-view.native.js +88 -0
- package/dist/cjs/split-view/split-view.native.js.map +1 -0
- package/dist/cjs/toolbar/index.cjs +32 -0
- package/dist/cjs/toolbar/index.native.js +60 -0
- package/dist/cjs/toolbar/index.native.js.map +1 -0
- package/dist/cjs/toolbar/types.cjs +16 -0
- package/dist/cjs/toolbar/types.native.js +19 -0
- package/dist/cjs/toolbar/types.native.js.map +1 -0
- package/dist/cjs/zoom/index.cjs +36 -0
- package/dist/cjs/zoom/index.native.js +65 -0
- package/dist/cjs/zoom/index.native.js.map +1 -0
- package/dist/cjs/zoom/types.cjs +16 -0
- package/dist/cjs/zoom/types.native.js +19 -0
- package/dist/cjs/zoom/types.native.js.map +1 -0
- package/dist/esm/color/android.dynamic.types.mjs +2 -0
- package/dist/esm/color/android.dynamic.types.mjs.map +1 -0
- package/dist/esm/color/android.dynamic.types.native.js +2 -0
- package/dist/esm/color/android.dynamic.types.native.js.map +1 -0
- package/dist/esm/color/android.material.types.mjs +2 -0
- package/dist/esm/color/android.material.types.mjs.map +1 -0
- package/dist/esm/color/android.material.types.native.js +2 -0
- package/dist/esm/color/android.material.types.native.js.map +1 -0
- package/dist/esm/color/index.mjs +42 -0
- package/dist/esm/color/index.mjs.map +1 -0
- package/dist/esm/color/index.native.js +42 -0
- package/dist/esm/color/index.native.js.map +1 -0
- package/dist/esm/color/ios.types.mjs +2 -0
- package/dist/esm/color/ios.types.mjs.map +1 -0
- package/dist/esm/color/ios.types.native.js +2 -0
- package/dist/esm/color/ios.types.native.js.map +1 -0
- package/dist/esm/color/materialColor.mjs +8 -0
- package/dist/esm/color/materialColor.mjs.map +1 -0
- package/dist/esm/color/materialColor.native.js +16 -0
- package/dist/esm/color/materialColor.native.js.map +1 -0
- package/dist/esm/index.mjs +7 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +7 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/menu/index.mjs +5 -0
- package/dist/esm/menu/index.mjs.map +1 -0
- package/dist/esm/menu/index.native.js +21 -0
- package/dist/esm/menu/index.native.js.map +1 -0
- package/dist/esm/menu/types.mjs +2 -0
- package/dist/esm/menu/types.mjs.map +1 -0
- package/dist/esm/menu/types.native.js +2 -0
- package/dist/esm/menu/types.native.js.map +1 -0
- package/dist/esm/split-view/index.mjs +3 -0
- package/dist/esm/split-view/index.mjs.map +1 -0
- package/dist/esm/split-view/index.native.js +3 -0
- package/dist/esm/split-view/index.native.js.map +1 -0
- package/dist/esm/split-view/split-view.mjs +46 -0
- package/dist/esm/split-view/split-view.mjs.map +1 -0
- package/dist/esm/split-view/split-view.native.js +51 -0
- package/dist/esm/split-view/split-view.native.js.map +1 -0
- package/dist/esm/toolbar/index.mjs +8 -0
- package/dist/esm/toolbar/index.mjs.map +1 -0
- package/dist/esm/toolbar/index.native.js +33 -0
- package/dist/esm/toolbar/index.native.js.map +1 -0
- package/dist/esm/toolbar/types.mjs +2 -0
- package/dist/esm/toolbar/types.mjs.map +1 -0
- package/dist/esm/toolbar/types.native.js +2 -0
- package/dist/esm/toolbar/types.native.js.map +1 -0
- package/dist/esm/zoom/index.mjs +11 -0
- package/dist/esm/zoom/index.mjs.map +1 -0
- package/dist/esm/zoom/index.native.js +37 -0
- package/dist/esm/zoom/index.native.js.map +1 -0
- package/dist/esm/zoom/types.mjs +2 -0
- package/dist/esm/zoom/types.mjs.map +1 -0
- package/dist/esm/zoom/types.native.js +2 -0
- package/dist/esm/zoom/types.native.js.map +1 -0
- package/ios/Menu/MenuActionView.swift +339 -0
- package/ios/Menu/VxrnMenuActionManager.m +47 -0
- package/ios/Toolbar/FontUtils.swift +46 -0
- package/ios/Toolbar/ToolbarHostView.swift +138 -0
- package/ios/Toolbar/ToolbarItemView.swift +334 -0
- package/ios/Toolbar/VxrnToolbarHostManager.m +20 -0
- package/ios/Toolbar/VxrnToolbarItemManager.m +41 -0
- package/ios/VxrnNative-Bridging-Header.h +7 -0
- package/ios/VxrnNative.podspec +31 -0
- package/ios/ZoomTransition/VxrnZoomAlignmentManager.m +22 -0
- package/ios/ZoomTransition/VxrnZoomEnablerManager.m +23 -0
- package/ios/ZoomTransition/VxrnZoomSourceManager.m +24 -0
- package/ios/ZoomTransition/ZoomTransition.swift +384 -0
- package/package.json +82 -0
- package/react-native.config.js +14 -0
- package/src/color/android.dynamic.types.ts +384 -0
- package/src/color/android.material.types.ts +291 -0
- package/src/color/index.native.ts +75 -0
- package/src/color/index.ts +75 -0
- package/src/color/ios.types.ts +156 -0
- package/src/color/materialColor.native.ts +15 -0
- package/src/color/materialColor.ts +7 -0
- package/src/index.ts +20 -0
- package/src/menu/index.native.tsx +28 -0
- package/src/menu/index.ts +7 -0
- package/src/menu/types.ts +32 -0
- package/src/split-view/index.ts +2 -0
- package/src/split-view/split-view.tsx +80 -0
- package/src/toolbar/index.native.tsx +39 -0
- package/src/toolbar/index.ts +11 -0
- package/src/toolbar/types.ts +39 -0
- package/src/zoom/index.native.tsx +56 -0
- package/src/zoom/index.ts +28 -0
- package/src/zoom/types.ts +30 -0
- package/tsconfig.json +7 -0
- package/types/color/android.dynamic.types.d.ts +384 -0
- package/types/color/android.dynamic.types.d.ts.map +1 -0
- package/types/color/android.material.types.d.ts +291 -0
- package/types/color/android.material.types.d.ts.map +1 -0
- package/types/color/index.d.ts +25 -0
- package/types/color/index.d.ts.map +1 -0
- package/types/color/index.native.d.ts +25 -0
- package/types/color/index.native.d.ts.map +1 -0
- package/types/color/ios.types.d.ts +156 -0
- package/types/color/ios.types.d.ts.map +1 -0
- package/types/color/materialColor.d.ts +3 -0
- package/types/color/materialColor.d.ts.map +1 -0
- package/types/color/materialColor.native.d.ts +3 -0
- package/types/color/materialColor.native.d.ts.map +1 -0
- package/types/index.d.ts +10 -0
- package/types/index.d.ts.map +1 -0
- package/types/menu/index.d.ts +4 -0
- package/types/menu/index.d.ts.map +1 -0
- package/types/menu/index.native.d.ts +4 -0
- package/types/menu/index.native.d.ts.map +1 -0
- package/types/menu/types.d.ts +32 -0
- package/types/menu/types.d.ts.map +1 -0
- package/types/split-view/index.d.ts +3 -0
- package/types/split-view/index.d.ts.map +1 -0
- package/types/split-view/split-view.d.ts +19 -0
- package/types/split-view/split-view.d.ts.map +1 -0
- package/types/toolbar/index.d.ts +5 -0
- package/types/toolbar/index.d.ts.map +1 -0
- package/types/toolbar/index.native.d.ts +5 -0
- package/types/toolbar/index.native.d.ts.map +1 -0
- package/types/toolbar/types.d.ts +37 -0
- package/types/toolbar/types.d.ts.map +1 -0
- package/types/zoom/index.d.ts +6 -0
- package/types/zoom/index.d.ts.map +1 -0
- package/types/zoom/index.native.d.ts +6 -0
- package/types/zoom/index.native.d.ts.map +1 -0
- package/types/zoom/types.d.ts +26 -0
- package/types/zoom/types.d.ts.map +1 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
// adapted from expo-router (MIT license) - https://github.com/expo/expo
|
|
2
|
+
import React
|
|
3
|
+
|
|
4
|
+
// RNSScreen accessed via NSClassFromString without direct import
|
|
5
|
+
import UIKit
|
|
6
|
+
|
|
7
|
+
// MARK: - singleton manager for zoom transition repositories
|
|
8
|
+
|
|
9
|
+
class VxrnNativeZoomManager {
|
|
10
|
+
static let shared = VxrnNativeZoomManager()
|
|
11
|
+
let sourceRepository = ZoomTransitionsSourceRepository()
|
|
12
|
+
let alignmentViewRepository = ZoomTransitionsAlignmentViewRepository()
|
|
13
|
+
private init() {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// MARK: - source info
|
|
17
|
+
|
|
18
|
+
class SourceInfo {
|
|
19
|
+
var alignment: CGRect?
|
|
20
|
+
var animateAspectRatioChange: Bool
|
|
21
|
+
weak var view: UIView?
|
|
22
|
+
|
|
23
|
+
init(view: UIView, alignment: CGRect?, animateAspectRatioChange: Bool) {
|
|
24
|
+
self.view = view
|
|
25
|
+
self.alignment = alignment
|
|
26
|
+
self.animateAspectRatioChange = animateAspectRatioChange
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MARK: - repositories
|
|
31
|
+
|
|
32
|
+
class ZoomTransitionsSourceRepository {
|
|
33
|
+
private var sources: [String: SourceInfo] = [:]
|
|
34
|
+
private let lock = NSLock()
|
|
35
|
+
|
|
36
|
+
func registerSource(identifier: String, source: SourceInfo) {
|
|
37
|
+
lock.lock()
|
|
38
|
+
defer { lock.unlock() }
|
|
39
|
+
if sources[identifier] != nil {
|
|
40
|
+
NSLog("[@vxrn/native] ZoomSource with identifier %@ is already registered.", identifier)
|
|
41
|
+
}
|
|
42
|
+
if !identifier.isEmpty {
|
|
43
|
+
sources[identifier] = source
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func unregisterSource(identifier: String) {
|
|
48
|
+
lock.lock()
|
|
49
|
+
defer { lock.unlock() }
|
|
50
|
+
sources.removeValue(forKey: identifier)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func getSource(identifier: String) -> SourceInfo? {
|
|
54
|
+
lock.lock()
|
|
55
|
+
defer { lock.unlock() }
|
|
56
|
+
return sources[identifier]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func updateIdentifier(oldIdentifier: String, newIdentifier: String) {
|
|
60
|
+
lock.lock()
|
|
61
|
+
defer { lock.unlock() }
|
|
62
|
+
if let source = sources[oldIdentifier] {
|
|
63
|
+
if !newIdentifier.isEmpty {
|
|
64
|
+
sources[newIdentifier] = source
|
|
65
|
+
}
|
|
66
|
+
sources.removeValue(forKey: oldIdentifier)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func updateAlignment(identifier: String, alignment: CGRect?) {
|
|
71
|
+
lock.lock()
|
|
72
|
+
defer { lock.unlock() }
|
|
73
|
+
if let source = sources[identifier], !identifier.isEmpty {
|
|
74
|
+
source.alignment = alignment
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
func updateAnimateAspectRatioChange(identifier: String, animateAspectRatioChange: Bool) {
|
|
79
|
+
lock.lock()
|
|
80
|
+
defer { lock.unlock() }
|
|
81
|
+
if let source: SourceInfo = sources[identifier], !identifier.isEmpty {
|
|
82
|
+
source.animateAspectRatioChange = animateAspectRatioChange
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class ZoomTransitionsAlignmentViewRepository {
|
|
88
|
+
private var alignmentViews: [String: WeakUIView] = [:]
|
|
89
|
+
private let lock = NSLock()
|
|
90
|
+
|
|
91
|
+
func addIfNotExists(identifier: String, alignmentView: UIView) {
|
|
92
|
+
lock.lock()
|
|
93
|
+
defer { lock.unlock() }
|
|
94
|
+
if alignmentViews[identifier] == nil && !identifier.isEmpty {
|
|
95
|
+
alignmentViews[identifier] = WeakUIView(view: alignmentView)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func removeIfSame(identifier: String, alignmentView: UIView) {
|
|
100
|
+
lock.lock()
|
|
101
|
+
defer { lock.unlock() }
|
|
102
|
+
if let existing = alignmentViews[identifier], existing.view === alignmentView {
|
|
103
|
+
alignmentViews.removeValue(forKey: identifier)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func get(identifier: String) -> UIView? {
|
|
108
|
+
lock.lock()
|
|
109
|
+
defer { lock.unlock() }
|
|
110
|
+
return alignmentViews[identifier]?.view
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private class WeakUIView {
|
|
114
|
+
weak var view: UIView?
|
|
115
|
+
init(view: UIView) {
|
|
116
|
+
self.view = view
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// MARK: - dismissal bounds rect (plain struct, no Expo Record)
|
|
122
|
+
|
|
123
|
+
struct DismissalBoundsRect {
|
|
124
|
+
var minX: Double?
|
|
125
|
+
var maxX: Double?
|
|
126
|
+
var minY: Double?
|
|
127
|
+
var maxY: Double?
|
|
128
|
+
|
|
129
|
+
init(dict: NSDictionary?) {
|
|
130
|
+
guard let dict = dict else { return }
|
|
131
|
+
minX = dict["minX"] as? Double
|
|
132
|
+
maxX = dict["maxX"] as? Double
|
|
133
|
+
minY = dict["minY"] as? Double
|
|
134
|
+
maxY = dict["maxY"] as? Double
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// MARK: - zoom transition source view
|
|
139
|
+
|
|
140
|
+
@objc(ZoomTransitionSourceView) public
|
|
141
|
+
class ZoomTransitionSourceView: RCTView {
|
|
142
|
+
private var child: UIView?
|
|
143
|
+
private var sourceRepository: ZoomTransitionsSourceRepository { VxrnNativeZoomManager.shared.sourceRepository }
|
|
144
|
+
|
|
145
|
+
private var _identifier: String = ""
|
|
146
|
+
@objc var identifier: NSString = "" {
|
|
147
|
+
didSet {
|
|
148
|
+
let newId = identifier as String
|
|
149
|
+
let oldId = _identifier
|
|
150
|
+
_identifier = newId
|
|
151
|
+
guard newId != oldId else { return }
|
|
152
|
+
if let child = child {
|
|
153
|
+
if oldId.isEmpty {
|
|
154
|
+
sourceRepository.registerSource(
|
|
155
|
+
identifier: newId,
|
|
156
|
+
source: SourceInfo(
|
|
157
|
+
view: child, alignment: _alignment,
|
|
158
|
+
animateAspectRatioChange: _animateAspectRatioChange))
|
|
159
|
+
} else {
|
|
160
|
+
sourceRepository.updateIdentifier(oldIdentifier: oldId, newIdentifier: newId)
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
sourceRepository.unregisterSource(identifier: oldId)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private var _alignment: CGRect?
|
|
169
|
+
@objc var alignment: NSDictionary? {
|
|
170
|
+
didSet {
|
|
171
|
+
if let dict = alignment {
|
|
172
|
+
let x = dict["x"] as? Double ?? 0
|
|
173
|
+
let y = dict["y"] as? Double ?? 0
|
|
174
|
+
let width = dict["width"] as? Double ?? 0
|
|
175
|
+
let height = dict["height"] as? Double ?? 0
|
|
176
|
+
_alignment = CGRect(x: x, y: y, width: width, height: height)
|
|
177
|
+
} else {
|
|
178
|
+
_alignment = nil
|
|
179
|
+
}
|
|
180
|
+
if child != nil {
|
|
181
|
+
sourceRepository.updateAlignment(identifier: _identifier, alignment: _alignment)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private var _animateAspectRatioChange: Bool = false
|
|
187
|
+
@objc var animateAspectRatioChange: Bool = false {
|
|
188
|
+
didSet {
|
|
189
|
+
_animateAspectRatioChange = animateAspectRatioChange
|
|
190
|
+
if child != nil {
|
|
191
|
+
sourceRepository.updateAnimateAspectRatioChange(
|
|
192
|
+
identifier: _identifier, animateAspectRatioChange: animateAspectRatioChange)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
public override func insertReactSubview(_ subview: UIView!, at atIndex: Int) {
|
|
198
|
+
guard child == nil else {
|
|
199
|
+
NSLog("[@vxrn/native] ZoomSource can only have a single native child.")
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
child = subview
|
|
203
|
+
sourceRepository.registerSource(
|
|
204
|
+
identifier: _identifier,
|
|
205
|
+
source: SourceInfo(
|
|
206
|
+
view: subview, alignment: _alignment,
|
|
207
|
+
animateAspectRatioChange: _animateAspectRatioChange))
|
|
208
|
+
super.insertReactSubview(subview, at: atIndex)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public override func removeReactSubview(_ subview: UIView!) {
|
|
212
|
+
guard subview === self.child else { return }
|
|
213
|
+
self.child = nil
|
|
214
|
+
sourceRepository.unregisterSource(identifier: _identifier)
|
|
215
|
+
super.removeReactSubview(subview)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// MARK: - zoom transition alignment rect detector view
|
|
220
|
+
|
|
221
|
+
@objc(ZoomTransitionAlignmentRectDetectorView) public
|
|
222
|
+
class ZoomTransitionAlignmentRectDetectorView: RCTView {
|
|
223
|
+
private var child: UIView?
|
|
224
|
+
private var alignmentViewRepository: ZoomTransitionsAlignmentViewRepository { VxrnNativeZoomManager.shared.alignmentViewRepository }
|
|
225
|
+
|
|
226
|
+
private var _identifier: String = ""
|
|
227
|
+
@objc var identifier: NSString = "" {
|
|
228
|
+
didSet {
|
|
229
|
+
let newId = identifier as String
|
|
230
|
+
let oldId = _identifier
|
|
231
|
+
if oldId != newId && !oldId.isEmpty {
|
|
232
|
+
NSLog("[@vxrn/native] AlignmentRectDetector does not support changing identifier.")
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
_identifier = newId
|
|
236
|
+
if let child = child {
|
|
237
|
+
alignmentViewRepository.addIfNotExists(identifier: newId, alignmentView: child)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public override func insertReactSubview(_ subview: UIView!, at atIndex: Int) {
|
|
243
|
+
guard child == nil else {
|
|
244
|
+
NSLog("[@vxrn/native] ZoomTarget can only have a single native child.")
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
if !_identifier.isEmpty {
|
|
248
|
+
alignmentViewRepository.addIfNotExists(identifier: _identifier, alignmentView: subview)
|
|
249
|
+
}
|
|
250
|
+
self.child = subview
|
|
251
|
+
super.insertReactSubview(subview, at: atIndex)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
public override func removeReactSubview(_ subview: UIView!) {
|
|
255
|
+
guard subview === self.child else { return }
|
|
256
|
+
self.child = nil
|
|
257
|
+
alignmentViewRepository.removeIfSame(identifier: _identifier, alignmentView: subview)
|
|
258
|
+
super.removeReactSubview(subview)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// MARK: - zoom transition enabler view
|
|
263
|
+
|
|
264
|
+
@objc(ZoomTransitionEnablerView) public
|
|
265
|
+
class ZoomTransitionEnablerView: RCTView {
|
|
266
|
+
private var sourceRepository: ZoomTransitionsSourceRepository { VxrnNativeZoomManager.shared.sourceRepository }
|
|
267
|
+
private var alignmentViewRepository: ZoomTransitionsAlignmentViewRepository { VxrnNativeZoomManager.shared.alignmentViewRepository }
|
|
268
|
+
|
|
269
|
+
private var _zoomTransitionSourceIdentifier: String = ""
|
|
270
|
+
@objc var zoomTransitionSourceIdentifier: NSString = "" {
|
|
271
|
+
didSet {
|
|
272
|
+
_zoomTransitionSourceIdentifier = zoomTransitionSourceIdentifier as String
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private var _dismissalBoundsRect: DismissalBoundsRect?
|
|
277
|
+
@objc var dismissalBoundsRect: NSDictionary? {
|
|
278
|
+
didSet {
|
|
279
|
+
_dismissalBoundsRect = DismissalBoundsRect(dict: dismissalBoundsRect)
|
|
280
|
+
if superview != nil {
|
|
281
|
+
DispatchQueue.main.async { self.setupZoomTransition() }
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
public override func didMoveToSuperview() {
|
|
287
|
+
super.didMoveToSuperview()
|
|
288
|
+
if superview != nil {
|
|
289
|
+
DispatchQueue.main.async { self.setupZoomTransition() }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private func setupZoomTransition() {
|
|
294
|
+
if _zoomTransitionSourceIdentifier.isEmpty {
|
|
295
|
+
NSLog("[@vxrn/native] No zoomTransitionSourceIdentifier passed to ZoomTransitionEnabler.")
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
if let controller = self.findViewController() {
|
|
299
|
+
NSLog("[@vxrn/native] Found VC: %@ (class: %@) for zoom id: %@", controller.description, String(describing: type(of: controller)), _zoomTransitionSourceIdentifier)
|
|
300
|
+
if #available(iOS 18.0, *) {
|
|
301
|
+
let options = UIViewController.Transition.ZoomOptions()
|
|
302
|
+
|
|
303
|
+
options.alignmentRectProvider = { context in
|
|
304
|
+
guard let sourceInfo = self.sourceRepository.getSource(
|
|
305
|
+
identifier: self._zoomTransitionSourceIdentifier)
|
|
306
|
+
else { return nil }
|
|
307
|
+
guard let alignmentView = self.alignmentViewRepository.get(
|
|
308
|
+
identifier: self._zoomTransitionSourceIdentifier)
|
|
309
|
+
else { return sourceInfo.alignment }
|
|
310
|
+
|
|
311
|
+
let rect = alignmentView.convert(
|
|
312
|
+
alignmentView.bounds, to: context.zoomedViewController.view)
|
|
313
|
+
if sourceInfo.animateAspectRatioChange, let sourceView = sourceInfo.view {
|
|
314
|
+
return self.calculateAdjustedRect(rect, toMatch: sourceView.bounds.size)
|
|
315
|
+
}
|
|
316
|
+
return rect
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if let rect = _dismissalBoundsRect {
|
|
320
|
+
options.interactiveDismissShouldBegin = { context in
|
|
321
|
+
let location = context.location
|
|
322
|
+
if let minX = rect.minX, location.x < minX { return false }
|
|
323
|
+
if let maxX = rect.maxX, location.x > maxX { return false }
|
|
324
|
+
if let minY = rect.minY, location.y < minY { return false }
|
|
325
|
+
if let maxY = rect.maxY, location.y > maxY { return false }
|
|
326
|
+
return true
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
controller.preferredTransition = .zoom(options: options) { _ in
|
|
331
|
+
let sourceInfo = self.sourceRepository.getSource(
|
|
332
|
+
identifier: self._zoomTransitionSourceIdentifier)
|
|
333
|
+
guard let view = sourceInfo?.view else {
|
|
334
|
+
NSLog("[@vxrn/native] No source view found for identifier %@.", self._zoomTransitionSourceIdentifier)
|
|
335
|
+
return nil
|
|
336
|
+
}
|
|
337
|
+
NSLog("[@vxrn/native] Zoom source view found: %@ for id: %@", view.description, self._zoomTransitionSourceIdentifier)
|
|
338
|
+
return view
|
|
339
|
+
}
|
|
340
|
+
NSLog("[@vxrn/native] preferredTransition set to .zoom for VC: %@", controller.description)
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
NSLog("[@vxrn/native] No navigation controller found to enable zoom transition.")
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private func calculateAdjustedRect(_ rect: CGRect, toMatch sourceSize: CGSize) -> CGRect {
|
|
349
|
+
guard sourceSize.width > 0, sourceSize.height > 0, rect.width > 0, rect.height > 0 else {
|
|
350
|
+
return rect
|
|
351
|
+
}
|
|
352
|
+
let sourceAspectRatio = sourceSize.width / sourceSize.height
|
|
353
|
+
let rectAspectRatio = rect.width / rect.height
|
|
354
|
+
if abs(sourceAspectRatio - rectAspectRatio) < 0.001 { return rect }
|
|
355
|
+
if rectAspectRatio > sourceAspectRatio {
|
|
356
|
+
let adjustedWidth = rect.height * sourceAspectRatio
|
|
357
|
+
return CGRect(x: rect.midX - (adjustedWidth / 2), y: rect.origin.y, width: adjustedWidth, height: rect.height)
|
|
358
|
+
}
|
|
359
|
+
let adjustedHeight = rect.width / sourceAspectRatio
|
|
360
|
+
return CGRect(x: rect.origin.x, y: rect.midY - (adjustedHeight / 2), width: rect.width, height: adjustedHeight)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private func findViewController() -> UIViewController? {
|
|
364
|
+
// find RNSScreen (react-native-screens VC) via class name to avoid import
|
|
365
|
+
let rnsScreenClass: AnyClass? = NSClassFromString("RNSScreen")
|
|
366
|
+
var responder: UIResponder? = self
|
|
367
|
+
while let r = responder {
|
|
368
|
+
if let vc = r as? UIViewController {
|
|
369
|
+
// prefer RNSScreen if available, otherwise fall back to any VC
|
|
370
|
+
if let rnsClass = rnsScreenClass, vc.isKind(of: rnsClass) {
|
|
371
|
+
return vc
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
responder = r.next
|
|
375
|
+
}
|
|
376
|
+
// fallback: return first VC found
|
|
377
|
+
responder = self
|
|
378
|
+
while let r = responder {
|
|
379
|
+
if let vc = r as? UIViewController { return vc }
|
|
380
|
+
responder = r.next
|
|
381
|
+
}
|
|
382
|
+
return nil
|
|
383
|
+
}
|
|
384
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vxrn/native",
|
|
3
|
+
"version": "1.12.8-1774390675831",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Native navigation features for One - zoom transitions, toolbar, color API, split view",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/cjs/index.cjs",
|
|
8
|
+
"module": "dist/esm/index.mjs",
|
|
9
|
+
"types": "types/index.d.ts",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tamagui-build",
|
|
13
|
+
"clean": "tamagui-build clean"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"react-native": {
|
|
18
|
+
"import": "./dist/esm/index.native.js",
|
|
19
|
+
"require": "./dist/cjs/index.native.js"
|
|
20
|
+
},
|
|
21
|
+
"import": "./dist/esm/index.mjs",
|
|
22
|
+
"require": "./dist/cjs/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./color": {
|
|
25
|
+
"react-native": {
|
|
26
|
+
"import": "./dist/esm/color/index.native.js",
|
|
27
|
+
"require": "./dist/cjs/color/index.native.js"
|
|
28
|
+
},
|
|
29
|
+
"import": "./dist/esm/color/index.mjs",
|
|
30
|
+
"require": "./dist/cjs/color/index.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./zoom": {
|
|
33
|
+
"react-native": {
|
|
34
|
+
"import": "./dist/esm/zoom/index.native.js",
|
|
35
|
+
"require": "./dist/cjs/zoom/index.native.js"
|
|
36
|
+
},
|
|
37
|
+
"import": "./dist/esm/zoom/index.mjs",
|
|
38
|
+
"require": "./dist/cjs/zoom/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./toolbar": {
|
|
41
|
+
"react-native": {
|
|
42
|
+
"import": "./dist/esm/toolbar/index.native.js",
|
|
43
|
+
"require": "./dist/cjs/toolbar/index.native.js"
|
|
44
|
+
},
|
|
45
|
+
"import": "./dist/esm/toolbar/index.mjs",
|
|
46
|
+
"require": "./dist/cjs/toolbar/index.cjs"
|
|
47
|
+
},
|
|
48
|
+
"./menu": {
|
|
49
|
+
"react-native": {
|
|
50
|
+
"import": "./dist/esm/menu/index.native.js",
|
|
51
|
+
"require": "./dist/cjs/menu/index.native.js"
|
|
52
|
+
},
|
|
53
|
+
"import": "./dist/esm/menu/index.mjs",
|
|
54
|
+
"require": "./dist/cjs/menu/index.cjs"
|
|
55
|
+
},
|
|
56
|
+
"./split-view": {
|
|
57
|
+
"react-native": {
|
|
58
|
+
"import": "./dist/esm/split-view/index.native.js",
|
|
59
|
+
"require": "./dist/cjs/split-view/index.native.js"
|
|
60
|
+
},
|
|
61
|
+
"import": "./dist/esm/split-view/index.mjs",
|
|
62
|
+
"require": "./dist/cjs/split-view/index.cjs"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"react": "*",
|
|
70
|
+
"react-native": "*",
|
|
71
|
+
"react-native-screens": "~4.24.0",
|
|
72
|
+
"react-native-safe-area-context": ">=5.4.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependenciesMeta": {
|
|
75
|
+
"react-native-safe-area-context": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@tamagui/build": "2.0.0-rc.31"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
dependency: {
|
|
3
|
+
platforms: {
|
|
4
|
+
ios: {
|
|
5
|
+
podspecPath: './ios/VxrnNative.podspec',
|
|
6
|
+
},
|
|
7
|
+
android: {
|
|
8
|
+
sourceDir: './android',
|
|
9
|
+
packageImportPath: 'import dev.vxrn.native.VxrnNativePackage;',
|
|
10
|
+
packageInstance: 'new VxrnNativePackage()',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}
|