@expo/ui 58.0.5 → 58.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/CHANGELOG.md +10 -0
- package/android/build.gradle +2 -2
- package/build/swift-ui/List/DataListForEach.d.ts +59 -0
- package/build/swift-ui/List/DataListForEach.js +137 -0
- package/build/swift-ui/List/DataListForEach.js.map +1 -0
- package/build/swift-ui/List/ListForEach.d.ts +15 -4
- package/build/swift-ui/List/ListForEach.js +40 -3
- package/build/swift-ui/List/ListForEach.js.map +1 -1
- package/build/swift-ui/List/index.d.ts +1 -1
- package/build/swift-ui/List/index.js.map +1 -1
- package/build/swift-ui/List/window.d.ts +5 -0
- package/build/swift-ui/List/window.js +28 -0
- package/build/swift-ui/List/window.js.map +1 -0
- package/build/swift-ui/modifiers/index.d.ts +16 -7
- package/build/swift-ui/modifiers/index.js +10 -5
- package/build/swift-ui/modifiers/index.js.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/DataListForEachView.swift +255 -0
- package/ios/ExpoUIModule.swift +3 -0
- package/ios/ListForEachView.swift +12 -3
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.module → 58.0.6/expo.modules.ui-58.0.6.module} +7 -7
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.pom → 58.0.6/expo.modules.ui-58.0.6.pom} +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.md5 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha1 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha256 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha512 +1 -0
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
- package/package.json +2 -2
- package/prebuilds/output/debug/xcframeworks/ExpoUI.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoUI.tar.gz +0 -0
- package/src/swift-ui/List/DataListForEach.tsx +249 -0
- package/src/swift-ui/List/ListForEach.tsx +56 -6
- package/src/swift-ui/List/index.tsx +1 -1
- package/src/swift-ui/List/window.ts +28 -0
- package/src/swift-ui/modifiers/index.ts +50 -14
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.module.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.module.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.module.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.module.sha512 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.pom.md5 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.pom.sha1 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.pom.sha256 +0 -1
- package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.5/expo.modules.ui-58.0.5.pom.sha512 +0 -1
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5-sources.jar → 58.0.6/expo.modules.ui-58.0.6-sources.jar} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5-sources.jar.md5 → 58.0.6/expo.modules.ui-58.0.6-sources.jar.md5} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5-sources.jar.sha1 → 58.0.6/expo.modules.ui-58.0.6-sources.jar.sha1} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5-sources.jar.sha256 → 58.0.6/expo.modules.ui-58.0.6-sources.jar.sha256} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5-sources.jar.sha512 → 58.0.6/expo.modules.ui-58.0.6-sources.jar.sha512} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.aar → 58.0.6/expo.modules.ui-58.0.6.aar} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.aar.md5 → 58.0.6/expo.modules.ui-58.0.6.aar.md5} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.aar.sha1 → 58.0.6/expo.modules.ui-58.0.6.aar.sha1} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.aar.sha256 → 58.0.6/expo.modules.ui-58.0.6.aar.sha256} +0 -0
- /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{58.0.5/expo.modules.ui-58.0.5.aar.sha512 → 58.0.6/expo.modules.ui-58.0.6.aar.sha512} +0 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Copyright 2026-present 650 Industries. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import ExpoModulesCore
|
|
4
|
+
import SwiftUI
|
|
5
|
+
|
|
6
|
+
final class DataListForEachProps: UIBaseViewProps {
|
|
7
|
+
@Field var itemKeys: [String] = []
|
|
8
|
+
@Field var revision: Int = 0
|
|
9
|
+
@Field var estimatedItemSize: Double = 64
|
|
10
|
+
@Field var deleteEnabled: Bool = false
|
|
11
|
+
@Field var moveEnabled: Bool = false
|
|
12
|
+
var onDelete = EventDispatcher()
|
|
13
|
+
var onMove = EventDispatcher()
|
|
14
|
+
var onWindowChange = EventDispatcher()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
final class DataListForEachItemProps: ExpoSwiftUI.ViewProps {
|
|
18
|
+
@Field var itemKey: String = ""
|
|
19
|
+
@Field var index: Int = 0
|
|
20
|
+
@Field var revision: Int = 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
struct DataListForEachItemView: ExpoSwiftUI.View {
|
|
24
|
+
@ObservedObject var props: DataListForEachItemProps
|
|
25
|
+
|
|
26
|
+
init(props: DataListForEachItemProps) {
|
|
27
|
+
self.props = props
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var body: some View {
|
|
31
|
+
Children()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
final class DataListForEachPoolProps: ExpoSwiftUI.ViewProps {}
|
|
36
|
+
|
|
37
|
+
// Holds the recycled slots, so mounting a slot never republishes the list's own props.
|
|
38
|
+
struct DataListForEachPoolView: ExpoSwiftUI.View {
|
|
39
|
+
@ObservedObject var props: DataListForEachPoolProps
|
|
40
|
+
|
|
41
|
+
init(props: DataListForEachPoolProps) {
|
|
42
|
+
self.props = props
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var body: some View {
|
|
46
|
+
EmptyView()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
struct DataListForEachView: ExpoSwiftUI.View {
|
|
51
|
+
@ObservedObject var props: DataListForEachProps
|
|
52
|
+
@StateObject private var window = DataListForEachWindow()
|
|
53
|
+
|
|
54
|
+
init(props: DataListForEachProps) {
|
|
55
|
+
self.props = props
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var body: some View {
|
|
59
|
+
let revision = props.revision
|
|
60
|
+
let pool = (props.children ?? []).lazy.compactMap { ($0.childView as? DataListForEachPoolView)?.props }.first
|
|
61
|
+
if let pool {
|
|
62
|
+
ForEach(Array(props.itemKeys.enumerated()), id: \.element) { index, key in
|
|
63
|
+
DataListForEachRow(itemKey: key, index: index, listProps: props, poolProps: pool, window: window)
|
|
64
|
+
.tag(AnyHashable(key))
|
|
65
|
+
}
|
|
66
|
+
.onDelete(perform: props.deleteEnabled ? { offsets in
|
|
67
|
+
props.onDelete(["indices": Array(offsets), "revision": revision])
|
|
68
|
+
} : nil)
|
|
69
|
+
.onMove(perform: props.moveEnabled ? { sources, destination in
|
|
70
|
+
props.onMove(["sourceIndices": Array(sources), "destination": destination, "revision": revision])
|
|
71
|
+
} : nil)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private struct DataListForEachRow: View {
|
|
77
|
+
let itemKey: String
|
|
78
|
+
let index: Int
|
|
79
|
+
@ObservedObject var listProps: DataListForEachProps
|
|
80
|
+
@ObservedObject var poolProps: DataListForEachPoolProps
|
|
81
|
+
let window: DataListForEachWindow
|
|
82
|
+
@State private var appeared = false
|
|
83
|
+
@State private var visibilityID = UUID()
|
|
84
|
+
|
|
85
|
+
// Slots are the pool's children; JS assigns item `index` to slot `index % count`.
|
|
86
|
+
// Reading them here, not in the parent, keeps the row current when the slot count changes.
|
|
87
|
+
private var slot: DataListForEachItemView? {
|
|
88
|
+
guard let slots = poolProps.children, !slots.isEmpty else {
|
|
89
|
+
return nil
|
|
90
|
+
}
|
|
91
|
+
return slots[index % slots.count].childView as? DataListForEachItemView
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
var body: some View {
|
|
95
|
+
VStack(alignment: .leading, spacing: 0) {
|
|
96
|
+
if let slot {
|
|
97
|
+
DataListForEachSlotContent(
|
|
98
|
+
props: slot.props,
|
|
99
|
+
itemKey: itemKey,
|
|
100
|
+
index: index,
|
|
101
|
+
revision: listProps.revision,
|
|
102
|
+
estimatedHeight: CGFloat(listProps.estimatedItemSize),
|
|
103
|
+
window: window
|
|
104
|
+
)
|
|
105
|
+
} else {
|
|
106
|
+
Color.clear
|
|
107
|
+
.frame(height: window.height(for: itemKey, fallback: listProps.estimatedItemSize))
|
|
108
|
+
.accessibilityHidden(true)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
.frame(maxWidth: .infinity, alignment: .leading)
|
|
112
|
+
.onAppear {
|
|
113
|
+
appeared = true
|
|
114
|
+
window.appear(itemKey, index: index, token: visibilityID, props: listProps)
|
|
115
|
+
}
|
|
116
|
+
.onChange(of: listProps.revision) { _ in
|
|
117
|
+
window.reset(listProps)
|
|
118
|
+
if appeared { window.appear(itemKey, index: index, token: visibilityID, props: listProps) }
|
|
119
|
+
}
|
|
120
|
+
.onGeometryChange(for: CGFloat.self, of: { $0.size.width }, action: { width in
|
|
121
|
+
window.updateWidth(width)
|
|
122
|
+
})
|
|
123
|
+
.onDisappear {
|
|
124
|
+
appeared = false
|
|
125
|
+
window.disappear(itemKey, token: visibilityID)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private struct DataListForEachSlotContent: View {
|
|
131
|
+
@ObservedObject var props: DataListForEachItemProps
|
|
132
|
+
let itemKey: String
|
|
133
|
+
let index: Int
|
|
134
|
+
let revision: Int
|
|
135
|
+
let estimatedHeight: CGFloat
|
|
136
|
+
let window: DataListForEachWindow
|
|
137
|
+
// we use this state so swiftui re-layouts when the slot is reused for a different item
|
|
138
|
+
@State private var showsContent = false
|
|
139
|
+
|
|
140
|
+
private var matches: Bool {
|
|
141
|
+
props.itemKey == itemKey && props.index == index && props.revision == revision
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
var body: some View {
|
|
145
|
+
Group {
|
|
146
|
+
if showsContent {
|
|
147
|
+
DataListForEachItemView(props: props)
|
|
148
|
+
.id(itemKey)
|
|
149
|
+
.onGeometryChange(for: CGFloat.self, of: { $0.size.height }, action: { height in
|
|
150
|
+
window.measure(height, for: itemKey, revision: revision)
|
|
151
|
+
})
|
|
152
|
+
} else {
|
|
153
|
+
// Hide the previous item while JS updates this slot.
|
|
154
|
+
Color.clear
|
|
155
|
+
.frame(height: window.height(for: itemKey, fallback: Double(estimatedHeight)))
|
|
156
|
+
.accessibilityHidden(true)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
.onAppear { showsContent = matches }
|
|
160
|
+
.onChange(of: matches) { showsContent = $0 }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Tracks rows without triggering view updates and batches window requests to JS.
|
|
165
|
+
private final class DataListForEachWindow: ObservableObject {
|
|
166
|
+
private weak var props: DataListForEachProps?
|
|
167
|
+
private var appeared: [String: Int] = [:]
|
|
168
|
+
private var visibilityTokens: [String: UUID] = [:]
|
|
169
|
+
private var heights: [String: CGFloat] = [:]
|
|
170
|
+
private var width: CGFloat = 0
|
|
171
|
+
private var revision = -1
|
|
172
|
+
private var pending: DispatchWorkItem?
|
|
173
|
+
private var lastSent: [Int] = []
|
|
174
|
+
|
|
175
|
+
deinit {
|
|
176
|
+
pending?.cancel()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
func reset(_ props: DataListForEachProps) {
|
|
180
|
+
self.props = props
|
|
181
|
+
guard revision != props.revision else {
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
revision = props.revision
|
|
185
|
+
heights.removeAll()
|
|
186
|
+
let positions = Dictionary(uniqueKeysWithValues: props.itemKeys.enumerated().map { ($0.element, $0.offset) })
|
|
187
|
+
appeared = positions.filter { appeared[$0.key] != nil }
|
|
188
|
+
visibilityTokens = visibilityTokens.filter { positions[$0.key] != nil }
|
|
189
|
+
lastSent = []
|
|
190
|
+
schedule()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
func updateWidth(_ newWidth: CGFloat) {
|
|
194
|
+
if width != newWidth {
|
|
195
|
+
if width > 0 {
|
|
196
|
+
heights.removeAll()
|
|
197
|
+
}
|
|
198
|
+
width = newWidth
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func appear(_ key: String, index: Int, token: UUID, props: DataListForEachProps) {
|
|
203
|
+
reset(props)
|
|
204
|
+
guard props.itemKeys.indices.contains(index), props.itemKeys[index] == key else {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
appeared[key] = index
|
|
208
|
+
visibilityTokens[key] = token
|
|
209
|
+
schedule()
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func disappear(_ key: String, token: UUID) {
|
|
213
|
+
// Ignore callbacks from a row that has been replaced.
|
|
214
|
+
guard visibilityTokens[key] == token else {
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
appeared.removeValue(forKey: key)
|
|
218
|
+
visibilityTokens.removeValue(forKey: key)
|
|
219
|
+
if appeared.isEmpty { lastSent = [] }
|
|
220
|
+
schedule()
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
func height(for key: String, fallback: Double) -> CGFloat {
|
|
224
|
+
heights[key] ?? CGFloat(fallback)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
func measure(_ height: CGFloat, for key: String, revision: Int) {
|
|
228
|
+
guard revision == self.revision, height.isFinite, height > 0 else {
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
heights[key] = height
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private func schedule() {
|
|
235
|
+
guard pending == nil else {
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
let work = DispatchWorkItem { [weak self] in
|
|
239
|
+
guard let self else { return }
|
|
240
|
+
self.pending = nil
|
|
241
|
+
guard let props = self.props,
|
|
242
|
+
let first = self.appeared.values.min(), let last = self.appeared.values.max() else {
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
let event = [first, last, self.revision]
|
|
246
|
+
guard event != self.lastSent else {
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
self.lastSent = event
|
|
250
|
+
props.onWindowChange(["first": first, "last": last, "revision": self.revision])
|
|
251
|
+
}
|
|
252
|
+
pending = work
|
|
253
|
+
DispatchQueue.main.async(execute: work)
|
|
254
|
+
}
|
|
255
|
+
}
|
package/ios/ExpoUIModule.swift
CHANGED
|
@@ -170,6 +170,9 @@ public final class ExpoUIModule: Module {
|
|
|
170
170
|
ExpoUIView(ImageView.self)
|
|
171
171
|
ExpoUIView(LabelView.self)
|
|
172
172
|
ExpoUIView(ListView.self)
|
|
173
|
+
ExpoUIView(DataListForEachView.self)
|
|
174
|
+
View(DataListForEachItemView.self)
|
|
175
|
+
View(DataListForEachPoolView.self)
|
|
173
176
|
ExpoUIView(ListForEachView.self)
|
|
174
177
|
|
|
175
178
|
ExpoUIView(PickerView.self)
|
|
@@ -4,6 +4,8 @@ import ExpoModulesCore
|
|
|
4
4
|
import SwiftUI
|
|
5
5
|
|
|
6
6
|
final class ListForEachProps: UIBaseViewProps {
|
|
7
|
+
@Field var deleteEnabled: Bool = false
|
|
8
|
+
@Field var moveEnabled: Bool = false
|
|
7
9
|
var onDelete = EventDispatcher()
|
|
8
10
|
var onMove = EventDispatcher()
|
|
9
11
|
}
|
|
@@ -16,9 +18,16 @@ struct ListForEachView: ExpoSwiftUI.View {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
var body: some View {
|
|
19
|
-
|
|
20
|
-
.
|
|
21
|
-
|
|
21
|
+
ForEach(props.children ?? [], id: \.childIdentity) { child in
|
|
22
|
+
if let item = child.childView as? DataListForEachItemView {
|
|
23
|
+
item.tag(AnyHashable(item.props.itemKey))
|
|
24
|
+
} else {
|
|
25
|
+
let view: any View = child.childView
|
|
26
|
+
AnyView(view)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
.onDelete(perform: props.deleteEnabled ? handleDelete : nil)
|
|
30
|
+
.onMove(perform: props.moveEnabled ? handleMove : nil)
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
func handleDelete(at offsets: IndexSet) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"component": {
|
|
4
4
|
"group": "expo.modules.ui",
|
|
5
5
|
"module": "expo.modules.ui",
|
|
6
|
-
"version": "58.0.
|
|
6
|
+
"version": "58.0.6",
|
|
7
7
|
"attributes": {
|
|
8
8
|
"org.gradle.status": "release"
|
|
9
9
|
}
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
{
|
|
27
|
-
"name": "expo.modules.ui-58.0.
|
|
28
|
-
"url": "expo.modules.ui-58.0.
|
|
27
|
+
"name": "expo.modules.ui-58.0.6.aar",
|
|
28
|
+
"url": "expo.modules.ui-58.0.6.aar",
|
|
29
29
|
"size": 1792968,
|
|
30
30
|
"sha512": "ddcedbdcc0e95db83f0657a68090572e6d7c7fb9233e796ea875d86e9782c619560ef0e98316cdf6642abbed4b42eb43b5e8264be55130f47e473adbc0f77c11",
|
|
31
31
|
"sha256": "9b228fbd60d559e9dfebe894e1846a8c7ce3c72e5c91936b4382c97295e9b072",
|
|
@@ -141,8 +141,8 @@
|
|
|
141
141
|
],
|
|
142
142
|
"files": [
|
|
143
143
|
{
|
|
144
|
-
"name": "expo.modules.ui-58.0.
|
|
145
|
-
"url": "expo.modules.ui-58.0.
|
|
144
|
+
"name": "expo.modules.ui-58.0.6.aar",
|
|
145
|
+
"url": "expo.modules.ui-58.0.6.aar",
|
|
146
146
|
"size": 1792968,
|
|
147
147
|
"sha512": "ddcedbdcc0e95db83f0657a68090572e6d7c7fb9233e796ea875d86e9782c619560ef0e98316cdf6642abbed4b42eb43b5e8264be55130f47e473adbc0f77c11",
|
|
148
148
|
"sha256": "9b228fbd60d559e9dfebe894e1846a8c7ce3c72e5c91936b4382c97295e9b072",
|
|
@@ -161,8 +161,8 @@
|
|
|
161
161
|
},
|
|
162
162
|
"files": [
|
|
163
163
|
{
|
|
164
|
-
"name": "expo.modules.ui-58.0.
|
|
165
|
-
"url": "expo.modules.ui-58.0.
|
|
164
|
+
"name": "expo.modules.ui-58.0.6-sources.jar",
|
|
165
|
+
"url": "expo.modules.ui-58.0.6-sources.jar",
|
|
166
166
|
"size": 106293,
|
|
167
167
|
"sha512": "5a6dc466f00aa1d74a9e1df2c4a06f3bf9c00b9eebe03e54e1ae68db446806f3f47f98e4a1d04bd90db6683459dd11785d22499d9cef48dd979293f8a88104d3",
|
|
168
168
|
"sha256": "d4e08c526d892c05d1ab6e42bd451df874e4da5cc58c18080ad730fc0ae59dfa",
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.md5
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7e592a224a0fc4b1d3190d5e0453dca4
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha1
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
351d8fdc59045d3033183991bc4a41010bbdf928
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
67875636aba690f3ed01c1885a8f5dd5c72824b9028ed012d77016a5c5bfef85
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.module.sha512
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
b7469559d94d21ec59ac3c32700d5c1651d2384574457e83c5f11eea0d6e947dd0ae5b68209f09056c30a0493e561c3a1dd74ea51cd3ec0435e8887603646187
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<modelVersion>4.0.0</modelVersion>
|
|
10
10
|
<groupId>expo.modules.ui</groupId>
|
|
11
11
|
<artifactId>expo.modules.ui</artifactId>
|
|
12
|
-
<version>58.0.
|
|
12
|
+
<version>58.0.6</version>
|
|
13
13
|
<packaging>aar</packaging>
|
|
14
14
|
<name>expo.modules.ui</name>
|
|
15
15
|
<url>https://github.com/expo/expo</url>
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.md5
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5c827bb51c32599862e86feff95a8e6d
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha1
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
df98a93ccb93778ea5f6d701bf92e3caf1565000
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3b1a0a5386bc7a66e1e3097896cf2e63f8faccfa982ad72fc4d9f81dd6d7b9b7
|
package/local-maven-repo/expo/modules/ui/expo.modules.ui/58.0.6/expo.modules.ui-58.0.6.pom.sha512
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d66ff20ada5d5bcd01c7ca0506db62ecc07e8eae9113f3e174b776efa9588663ef4bb7ea2761fba9c7ae518b3732c4185a7aee70d4a64e6fb0fcd579abdb5d94
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
<groupId>expo.modules.ui</groupId>
|
|
4
4
|
<artifactId>expo.modules.ui</artifactId>
|
|
5
5
|
<versioning>
|
|
6
|
-
<latest>58.0.
|
|
7
|
-
<release>58.0.
|
|
6
|
+
<latest>58.0.6</latest>
|
|
7
|
+
<release>58.0.6</release>
|
|
8
8
|
<versions>
|
|
9
|
-
<version>58.0.
|
|
9
|
+
<version>58.0.6</version>
|
|
10
10
|
</versions>
|
|
11
|
-
<lastUpdated>
|
|
11
|
+
<lastUpdated>20260923154017</lastUpdated>
|
|
12
12
|
</versioning>
|
|
13
13
|
</metadata>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
96559b0eabb8170769333ad05cdcfafe
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
9b146187e50f805cfcdce4249b9d56e1e1fdf9d6
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
89c63e4e2a7fce899cd9101093d5c1105c62feffaea5ec288e6a39e5b8739446
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
50a66b6bfe1bbcedd9157651a817cac9309fb48c2a649c274b5a34e625370c8cd487af0235f5801320779f19049a619e06bcb71e69df646a129d06bfb0041c27
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/ui",
|
|
3
|
-
"version": "58.0.
|
|
3
|
+
"version": "58.0.6",
|
|
4
4
|
"description": "A collection of UI components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Jetpack Compose",
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"optional": true
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
|
-
"gitHead": "
|
|
174
|
+
"gitHead": "8aeb4368d396d2d71a2fc8c652c4d1d4d53ed858",
|
|
175
175
|
"scripts": {
|
|
176
176
|
"precompile-ios": "et prebuild-package-for-publish",
|
|
177
177
|
"build": "pnpm run build:lib && pnpm run build:plugin",
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { requireNativeView } from 'expo';
|
|
2
|
+
import { memo, useLayoutEffect, useMemo, useRef, useState, type ReactElement } from 'react';
|
|
3
|
+
|
|
4
|
+
import { type ViewEvent } from '../../types';
|
|
5
|
+
import { createViewModifierEventListener } from '../modifiers/utils';
|
|
6
|
+
import { type CommonViewModifierProps } from '../types';
|
|
7
|
+
import { getSlotIndices, getWindow } from './window';
|
|
8
|
+
|
|
9
|
+
export interface ListForEachProps<ItemT> extends CommonViewModifierProps {
|
|
10
|
+
/** Called with deleted indices from this group's `data` array. */
|
|
11
|
+
onDelete?: (indices: number[]) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Called with the source indices and the destination index. The destination index counts
|
|
14
|
+
* positions before the moved items are removed, so it can equal `data.length`.
|
|
15
|
+
*/
|
|
16
|
+
onMove?: (sourceIndices: number[], destination: number) => void;
|
|
17
|
+
/** Items to display. Replace the array when updating data. */
|
|
18
|
+
data: readonly ItemT[];
|
|
19
|
+
/** Returns a stable, unique string key, also used for `List` selection. */
|
|
20
|
+
keyExtractor: (item: ItemT, index: number) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Renders a row. When `recycling` is `true`, wrap it in `useCallback`, or every row
|
|
23
|
+
* re-renders on each parent render.
|
|
24
|
+
* Recycled rows are reused for other items, so their local state (`useState`) carries over.
|
|
25
|
+
* Reset it when the item changes, or keep the state outside the row.
|
|
26
|
+
*/
|
|
27
|
+
children: (info: { item: ItemT; index: number }) => ReactElement;
|
|
28
|
+
/**
|
|
29
|
+
* Renders only the rows near the visible range and reuses them while scrolling. Set to `false` to
|
|
30
|
+
* render every row at once. Set it once; changing it remounts the rows.
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
recycling?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Extra rows to prepare on each side of the visible rows. Must be a non-negative integer.
|
|
36
|
+
* Ignored when `recycling` is `false`.
|
|
37
|
+
* @default 10
|
|
38
|
+
*/
|
|
39
|
+
overscanCount?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Placeholder height in points, excluding `List` insets, until a row is measured.
|
|
42
|
+
* Must be positive. Measurements reset when `data` or width changes.
|
|
43
|
+
* Ignored when `recycling` is `false`.
|
|
44
|
+
* @default 64
|
|
45
|
+
*/
|
|
46
|
+
estimatedItemSize?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type WindowEvent = { first: number; last: number; revision: number };
|
|
50
|
+
type NativeProps = CommonViewModifierProps &
|
|
51
|
+
ViewEvent<'onWindowChange', WindowEvent> &
|
|
52
|
+
ViewEvent<'onDelete', { indices: number[]; revision: number }> &
|
|
53
|
+
ViewEvent<'onMove', { sourceIndices: number[]; destination: number; revision: number }> & {
|
|
54
|
+
deleteEnabled: boolean;
|
|
55
|
+
moveEnabled: boolean;
|
|
56
|
+
itemKeys: string[];
|
|
57
|
+
revision: number;
|
|
58
|
+
estimatedItemSize: number;
|
|
59
|
+
children: ReactElement;
|
|
60
|
+
};
|
|
61
|
+
type SlotProps = {
|
|
62
|
+
itemKey: string;
|
|
63
|
+
index: number;
|
|
64
|
+
revision: number;
|
|
65
|
+
children: ReactElement;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const NativeList = requireNativeView<NativeProps>('ExpoUI', 'DataListForEachView');
|
|
69
|
+
export const NativeSlot = requireNativeView<SlotProps>('ExpoUI', 'DataListForEachItemView');
|
|
70
|
+
const NativePool = requireNativeView<{ children: ReactElement[] }>(
|
|
71
|
+
'ExpoUI',
|
|
72
|
+
'DataListForEachPoolView'
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Skip unchanged rows when the window moves.
|
|
76
|
+
const RecycledRow = memo(function RecycledRow<ItemT>({
|
|
77
|
+
item,
|
|
78
|
+
index,
|
|
79
|
+
itemKey,
|
|
80
|
+
revision,
|
|
81
|
+
renderItem,
|
|
82
|
+
}: SlotPropsWithoutChildren & {
|
|
83
|
+
item: ItemT;
|
|
84
|
+
renderItem: ListForEachProps<ItemT>['children'];
|
|
85
|
+
}) {
|
|
86
|
+
return (
|
|
87
|
+
<NativeSlot itemKey={itemKey} index={index} revision={revision}>
|
|
88
|
+
{renderItem({ item, index })}
|
|
89
|
+
</NativeSlot>
|
|
90
|
+
);
|
|
91
|
+
}) as <ItemT>(
|
|
92
|
+
props: SlotPropsWithoutChildren & {
|
|
93
|
+
item: ItemT;
|
|
94
|
+
renderItem: ListForEachProps<ItemT>['children'];
|
|
95
|
+
}
|
|
96
|
+
) => ReactElement;
|
|
97
|
+
type SlotPropsWithoutChildren = Omit<SlotProps, 'children'>;
|
|
98
|
+
|
|
99
|
+
export function useItemKeys<ItemT>(
|
|
100
|
+
data: readonly ItemT[],
|
|
101
|
+
keyExtractor: ListForEachProps<ItemT>['keyExtractor']
|
|
102
|
+
): string[] {
|
|
103
|
+
const keyExtractorRef = useRef(keyExtractor);
|
|
104
|
+
keyExtractorRef.current = keyExtractor;
|
|
105
|
+
return useMemo(() => {
|
|
106
|
+
const keys = data.map((item, index) => keyExtractorRef.current(item, index));
|
|
107
|
+
if (keys.some((key) => typeof key !== 'string') || new Set(keys).size !== keys.length) {
|
|
108
|
+
throw new Error('List.ForEach keyExtractor must return a unique string for every item.');
|
|
109
|
+
}
|
|
110
|
+
return keys;
|
|
111
|
+
}, [data]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Renders a window of recycled rows inside List or Section.
|
|
116
|
+
* Rows show placeholders while JS prepares content. Native keys still cover the full data set.
|
|
117
|
+
* @platform ios
|
|
118
|
+
*/
|
|
119
|
+
export function DataListForEach<ItemT>({
|
|
120
|
+
data,
|
|
121
|
+
keyExtractor,
|
|
122
|
+
children: renderItem,
|
|
123
|
+
overscanCount = 10,
|
|
124
|
+
estimatedItemSize = 64,
|
|
125
|
+
modifiers,
|
|
126
|
+
onDelete,
|
|
127
|
+
onMove,
|
|
128
|
+
...props
|
|
129
|
+
}: ListForEachProps<ItemT>) {
|
|
130
|
+
if (!Number.isSafeInteger(overscanCount) || overscanCount < 0) {
|
|
131
|
+
throw new Error('List.ForEach overscanCount must be a non-negative integer.');
|
|
132
|
+
}
|
|
133
|
+
if (!Number.isFinite(estimatedItemSize) || estimatedItemSize <= 0) {
|
|
134
|
+
throw new Error('List.ForEach estimatedItemSize must be a positive finite number.');
|
|
135
|
+
}
|
|
136
|
+
const itemKeys = useItemKeys(data, keyExtractor);
|
|
137
|
+
const [state, setState] = useState({
|
|
138
|
+
keys: itemKeys,
|
|
139
|
+
first: 0,
|
|
140
|
+
last: 0,
|
|
141
|
+
revision: 0,
|
|
142
|
+
capacity: 0,
|
|
143
|
+
overscanCount,
|
|
144
|
+
});
|
|
145
|
+
let current = state;
|
|
146
|
+
if (state.keys !== itemKeys) {
|
|
147
|
+
const previousKey = state.keys[state.first];
|
|
148
|
+
const anchor = previousKey === undefined ? 0 : Math.max(0, itemKeys.indexOf(previousKey));
|
|
149
|
+
current = {
|
|
150
|
+
...state,
|
|
151
|
+
keys: itemKeys,
|
|
152
|
+
first: Math.min(anchor, Math.max(0, data.length - 1)),
|
|
153
|
+
last: Math.min(anchor + state.last - state.first, Math.max(0, data.length - 1)),
|
|
154
|
+
revision: state.revision + 1,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (current.overscanCount !== overscanCount) {
|
|
158
|
+
current = { ...current, overscanCount, capacity: 0 };
|
|
159
|
+
}
|
|
160
|
+
if (current !== state) setState(current);
|
|
161
|
+
const { start, capacity } = getWindow(
|
|
162
|
+
data.length,
|
|
163
|
+
current.first,
|
|
164
|
+
current.last,
|
|
165
|
+
overscanCount,
|
|
166
|
+
current.capacity
|
|
167
|
+
);
|
|
168
|
+
const committedRevision = useRef(current.revision);
|
|
169
|
+
useLayoutEffect(() => {
|
|
170
|
+
committedRevision.current = current.revision;
|
|
171
|
+
}, [current.revision]);
|
|
172
|
+
const indices = getSlotIndices(start, capacity);
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<NativeList
|
|
176
|
+
{...props}
|
|
177
|
+
modifiers={modifiers}
|
|
178
|
+
{...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
|
|
179
|
+
deleteEnabled={!!onDelete}
|
|
180
|
+
moveEnabled={!!onMove}
|
|
181
|
+
onDelete={
|
|
182
|
+
onDelete
|
|
183
|
+
? ({ nativeEvent }) => {
|
|
184
|
+
if (nativeEvent.revision === committedRevision.current) onDelete(nativeEvent.indices);
|
|
185
|
+
}
|
|
186
|
+
: undefined
|
|
187
|
+
}
|
|
188
|
+
onMove={
|
|
189
|
+
onMove
|
|
190
|
+
? ({ nativeEvent }) => {
|
|
191
|
+
if (nativeEvent.revision === committedRevision.current) {
|
|
192
|
+
onMove(nativeEvent.sourceIndices, nativeEvent.destination);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
: undefined
|
|
196
|
+
}
|
|
197
|
+
itemKeys={itemKeys}
|
|
198
|
+
revision={current.revision}
|
|
199
|
+
estimatedItemSize={estimatedItemSize}
|
|
200
|
+
onWindowChange={({ nativeEvent: event }) => {
|
|
201
|
+
if (
|
|
202
|
+
!Number.isSafeInteger(event.first) ||
|
|
203
|
+
!Number.isSafeInteger(event.last) ||
|
|
204
|
+
event.last < event.first
|
|
205
|
+
) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
setState((previous) => {
|
|
209
|
+
// Ignore requests from before a data change.
|
|
210
|
+
if (event.revision !== previous.revision) return previous;
|
|
211
|
+
const maximum = Math.max(0, previous.keys.length - 1);
|
|
212
|
+
const first = Math.max(0, Math.min(event.first, maximum));
|
|
213
|
+
const last = Math.max(first, Math.min(event.last, maximum));
|
|
214
|
+
const { capacity } = getWindow(
|
|
215
|
+
previous.keys.length,
|
|
216
|
+
first,
|
|
217
|
+
last,
|
|
218
|
+
previous.overscanCount,
|
|
219
|
+
previous.capacity
|
|
220
|
+
);
|
|
221
|
+
return first === previous.first &&
|
|
222
|
+
last === previous.last &&
|
|
223
|
+
capacity === previous.capacity
|
|
224
|
+
? previous
|
|
225
|
+
: { ...previous, first, last, capacity };
|
|
226
|
+
});
|
|
227
|
+
}}>
|
|
228
|
+
<NativePool>
|
|
229
|
+
{indices.map((index, slot) => {
|
|
230
|
+
const item = data[index];
|
|
231
|
+
const itemKey = itemKeys[index];
|
|
232
|
+
if (item === undefined || itemKey === undefined) {
|
|
233
|
+
throw new Error(`List.ForEach could not resolve the item at index ${index}.`);
|
|
234
|
+
}
|
|
235
|
+
return (
|
|
236
|
+
<RecycledRow
|
|
237
|
+
key={slot}
|
|
238
|
+
item={item}
|
|
239
|
+
index={index}
|
|
240
|
+
itemKey={itemKey}
|
|
241
|
+
revision={current.revision}
|
|
242
|
+
renderItem={renderItem}
|
|
243
|
+
/>
|
|
244
|
+
);
|
|
245
|
+
})}
|
|
246
|
+
</NativePool>
|
|
247
|
+
</NativeList>
|
|
248
|
+
);
|
|
249
|
+
}
|