@swiftbrowser/swift 0.0.0-stage → 0.1.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/Package.swift +23 -0
- package/README.md +12 -2
- package/Sources/Charts/Charts.swift +65 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
- package/Sources/FoundationBridge/Locale.swift +86 -0
- package/Sources/FoundationBridge/Timer.swift +48 -0
- package/Sources/FoundationBridge/UITextChecker.swift +30 -0
- package/Sources/FoundationLite/Common.swift +3 -0
- package/Sources/FoundationLite/FoundationLite.swift +171 -0
- package/Sources/FoundationLite/Interaction.swift +146 -0
- package/Sources/FoundationLite/Timer.swift +48 -0
- package/Sources/SwiftUI/API/Accessibility.swift +132 -0
- package/Sources/SwiftUI/API/Alert.swift +221 -0
- package/Sources/SwiftUI/API/Animation.swift +253 -0
- package/Sources/SwiftUI/API/App.swift +57 -0
- package/Sources/SwiftUI/API/Color.swift +161 -0
- package/Sources/SwiftUI/API/Controls.swift +268 -0
- package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
- package/Sources/SwiftUI/API/Decorations.swift +92 -0
- package/Sources/SwiftUI/API/EditMode.swift +269 -0
- package/Sources/SwiftUI/API/Effects.swift +290 -0
- package/Sources/SwiftUI/API/Environment.swift +355 -0
- package/Sources/SwiftUI/API/Focus.swift +129 -0
- package/Sources/SwiftUI/API/Font.swift +131 -0
- package/Sources/SwiftUI/API/Gauge.swift +289 -0
- package/Sources/SwiftUI/API/Geometry.swift +89 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
- package/Sources/SwiftUI/API/Gestures.swift +342 -0
- package/Sources/SwiftUI/API/Grid.swift +188 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
- package/Sources/SwiftUI/API/Layout.swift +165 -0
- package/Sources/SwiftUI/API/Lists.swift +276 -0
- package/Sources/SwiftUI/API/Menu.swift +51 -0
- package/Sources/SwiftUI/API/Modifiers.swift +243 -0
- package/Sources/SwiftUI/API/Navigation.swift +371 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
- package/Sources/SwiftUI/API/OnChange.swift +48 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
- package/Sources/SwiftUI/API/Picker.swift +176 -0
- package/Sources/SwiftUI/API/Presentation.swift +109 -0
- package/Sources/SwiftUI/API/Primitives.swift +390 -0
- package/Sources/SwiftUI/API/Publishers.swift +107 -0
- package/Sources/SwiftUI/API/RowActions.swift +83 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
- package/Sources/SwiftUI/API/State.swift +127 -0
- package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
- package/Sources/SwiftUI/API/Tabs.swift +119 -0
- package/Sources/SwiftUI/API/Tasks.swift +35 -0
- package/Sources/SwiftUI/API/Timeline.swift +212 -0
- package/Sources/SwiftUI/API/Toolbar.swift +215 -0
- package/Sources/SwiftUI/API/View.swift +207 -0
- package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
- package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
- package/Sources/SwiftUI/Charts/Axis.swift +319 -0
- package/Sources/SwiftUI/Charts/Chart.swift +646 -0
- package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
- package/Sources/SwiftUI/Charts/Marks.swift +204 -0
- package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
- package/Sources/SwiftUI/Charts/Scales.swift +326 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
- package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
- package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
- package/Sources/_FoundationCommon/Bundle.swift +82 -0
- package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
- package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
- package/index.d.ts +2 -0
- package/index.mjs +16 -0
- package/package.json +28 -4
- package/sync-sources.mjs +19 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/// A node of the element tree produced from the mounted view tree each pass.
|
|
2
|
+
struct _Element {
|
|
3
|
+
var kind: String
|
|
4
|
+
var props: _JSON
|
|
5
|
+
var children: [_Element]
|
|
6
|
+
var handler: ((_JSON) -> Void)?
|
|
7
|
+
/// Identity for keyed children (`ForEach`); unkeyed elements match by position.
|
|
8
|
+
var key: AnyHashable?
|
|
9
|
+
/// What makes this element a selectable or deletable `List` row (Phase 8).
|
|
10
|
+
var row: _RowMark?
|
|
11
|
+
/// Identity of this element value: a fresh token per produced element,
|
|
12
|
+
/// kept by copies. Two elements with one token are the same subtree (a
|
|
13
|
+
/// clean node's cached output), which the reconciler skips whole.
|
|
14
|
+
var token: Int = _Element.nextToken()
|
|
15
|
+
|
|
16
|
+
nonisolated(unsafe) private static var tokenCounter = 0
|
|
17
|
+
static func nextToken() -> Int {
|
|
18
|
+
tokenCounter += 1
|
|
19
|
+
return tokenCounter
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// Row metadata the runtime attaches while producing elements and consumes
|
|
24
|
+
/// when it emits a `list`: a `.tag` (or a `ForEach` id, the implicit tag), a
|
|
25
|
+
/// `NavigationLink(value:)`'s value and handler, and the `onDelete` of the
|
|
26
|
+
/// row's `ForEach`.
|
|
27
|
+
struct _RowMark {
|
|
28
|
+
var tag: AnyHashable?
|
|
29
|
+
var linkValue: AnyHashable?
|
|
30
|
+
var linkHandler: ((_JSON) -> Void)?
|
|
31
|
+
var delete: (() -> Void)?
|
|
32
|
+
var deleteDisabled = false
|
|
33
|
+
|
|
34
|
+
var isEmpty: Bool { tag == nil && linkValue == nil && delete == nil && !deleteDisabled }
|
|
35
|
+
|
|
36
|
+
/// Fills what `self` lacks from an inner element's mark.
|
|
37
|
+
func merged(with inner: _RowMark) -> _RowMark {
|
|
38
|
+
var result = self
|
|
39
|
+
if result.tag == nil { result.tag = inner.tag }
|
|
40
|
+
if result.linkValue == nil {
|
|
41
|
+
result.linkValue = inner.linkValue
|
|
42
|
+
result.linkHandler = inner.linkHandler
|
|
43
|
+
}
|
|
44
|
+
if result.delete == nil { result.delete = inner.delete }
|
|
45
|
+
result.deleteDisabled = result.deleteDisabled || inner.deleteDisabled
|
|
46
|
+
return result
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Diffs successive element trees and emits ops. Ids are assigned by position
|
|
51
|
+
/// (or key) and kind: an element that keeps both keeps its id across passes.
|
|
52
|
+
@MainActor
|
|
53
|
+
final class _Reconciler {
|
|
54
|
+
struct Rendered {
|
|
55
|
+
var id: Int
|
|
56
|
+
var kind: String
|
|
57
|
+
var props: _JSON
|
|
58
|
+
var children: [Rendered]
|
|
59
|
+
var key: AnyHashable?
|
|
60
|
+
/// The token of the element this was rendered from.
|
|
61
|
+
var token: Int
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private(set) var roots: [Rendered] = []
|
|
65
|
+
private var nextId = 1
|
|
66
|
+
private(set) var handlers: [Int: (_JSON) -> Void] = [:]
|
|
67
|
+
|
|
68
|
+
func update(with elements: [_Element], animation: _JSON? = nil) -> [_Op] {
|
|
69
|
+
var ops: [_Op] = []
|
|
70
|
+
roots = diff(parent: 0, old: roots, new: elements, ops: &ops)
|
|
71
|
+
ops.append(.commit(animation: animation))
|
|
72
|
+
return ops
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private func diff(parent: Int, old: [Rendered], new: [_Element], ops: inout [_Op]) -> [Rendered] {
|
|
76
|
+
let keyed = new.contains { $0.key != nil } || old.contains { $0.key != nil }
|
|
77
|
+
return keyed
|
|
78
|
+
? diffKeyed(parent: parent, old: old, new: new, ops: &ops)
|
|
79
|
+
: diffPositional(parent: parent, old: old, new: new, ops: &ops)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private func diffPositional(parent: Int, old: [Rendered], new: [_Element], ops: inout [_Op]) -> [Rendered] {
|
|
83
|
+
var result: [Rendered] = []
|
|
84
|
+
result.reserveCapacity(new.count)
|
|
85
|
+
|
|
86
|
+
for (index, element) in new.enumerated() {
|
|
87
|
+
if index < old.count, old[index].kind == element.kind {
|
|
88
|
+
result.append(reuse(old[index], for: element, ops: &ops))
|
|
89
|
+
} else {
|
|
90
|
+
if index < old.count { destroy(old[index], ops: &ops) }
|
|
91
|
+
result.append(create(element, parent: parent, index: index, ops: &ops))
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for stale in old.dropFirst(new.count) {
|
|
96
|
+
destroy(stale, ops: &ops)
|
|
97
|
+
}
|
|
98
|
+
return result
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Keyed children: match by key, move what survives, create the rest.
|
|
102
|
+
/// Unkeyed children in a keyed list are matched by their position.
|
|
103
|
+
private func diffKeyed(parent: Int, old: [Rendered], new: [_Element], ops: inout [_Op]) -> [Rendered] {
|
|
104
|
+
func identity(_ key: AnyHashable?, _ index: Int) -> AnyHashable {
|
|
105
|
+
key ?? AnyHashable(_PositionKey(index: index))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var oldByIdentity: [AnyHashable: Rendered] = [:]
|
|
109
|
+
for (index, rendered) in old.enumerated() {
|
|
110
|
+
oldByIdentity[identity(rendered.key, index)] = rendered
|
|
111
|
+
}
|
|
112
|
+
let newIdentities = new.enumerated().map { identity($1.key, $0) }
|
|
113
|
+
let kept = Set(newIdentities.enumerated().compactMap { index, id in
|
|
114
|
+
oldByIdentity[id].map { $0.kind == new[index].kind ? id : nil } ?? nil
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
// 1. Remove everything that does not survive, so the parent's children
|
|
118
|
+
// are exactly the kept old elements in their old order.
|
|
119
|
+
var pending: [AnyHashable] = []
|
|
120
|
+
for (index, rendered) in old.enumerated() {
|
|
121
|
+
let id = identity(rendered.key, index)
|
|
122
|
+
if kept.contains(id) { pending.append(id) } else { destroy(rendered, ops: &ops) }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2. Walk the new order; anything not already at the front is moved there.
|
|
126
|
+
var result: [Rendered] = []
|
|
127
|
+
result.reserveCapacity(new.count)
|
|
128
|
+
for (index, element) in new.enumerated() {
|
|
129
|
+
let id = newIdentities[index]
|
|
130
|
+
if kept.contains(id), let previous = oldByIdentity[id] {
|
|
131
|
+
if pending.first == id {
|
|
132
|
+
pending.removeFirst()
|
|
133
|
+
} else {
|
|
134
|
+
pending.removeAll { $0 == id }
|
|
135
|
+
ops.append(.insert(parent: parent, id: previous.id, index: index))
|
|
136
|
+
}
|
|
137
|
+
var rendered = reuse(previous, for: element, ops: &ops)
|
|
138
|
+
rendered.key = element.key
|
|
139
|
+
result.append(rendered)
|
|
140
|
+
} else {
|
|
141
|
+
var rendered = create(element, parent: parent, index: index, ops: &ops)
|
|
142
|
+
rendered.key = element.key
|
|
143
|
+
result.append(rendered)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private func reuse(_ previous: Rendered, for element: _Element, ops: inout [_Op]) -> Rendered {
|
|
150
|
+
// The same element value as last pass (a clean subtree's cached
|
|
151
|
+
// output): nothing below it changed, and its handlers are registered.
|
|
152
|
+
if previous.token == element.token { return previous }
|
|
153
|
+
var rendered = previous
|
|
154
|
+
rendered.token = element.token
|
|
155
|
+
if rendered.props != element.props {
|
|
156
|
+
ops.append(.update(id: rendered.id, props: element.props))
|
|
157
|
+
rendered.props = element.props
|
|
158
|
+
}
|
|
159
|
+
setHandler(element.handler, for: rendered.id)
|
|
160
|
+
rendered.children = diff(parent: rendered.id, old: rendered.children, new: element.children, ops: &ops)
|
|
161
|
+
return rendered
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private func create(_ element: _Element, parent: Int, index: Int, ops: inout [_Op]) -> Rendered {
|
|
165
|
+
let id = nextId
|
|
166
|
+
nextId += 1
|
|
167
|
+
ops.append(.create(id: id, kind: element.kind, props: element.props))
|
|
168
|
+
ops.append(.insert(parent: parent, id: id, index: index))
|
|
169
|
+
setHandler(element.handler, for: id)
|
|
170
|
+
let children = element.children.enumerated().map { childIndex, child in
|
|
171
|
+
create(child, parent: id, index: childIndex, ops: &ops)
|
|
172
|
+
}
|
|
173
|
+
return Rendered(id: id, kind: element.kind, props: element.props, children: children, key: element.key, token: element.token)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private func destroy(_ rendered: Rendered, ops: inout [_Op]) {
|
|
177
|
+
ops.append(.remove(id: rendered.id))
|
|
178
|
+
forget(rendered)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private func forget(_ rendered: Rendered) {
|
|
182
|
+
handlers[rendered.id] = nil
|
|
183
|
+
for child in rendered.children { forget(child) }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private func setHandler(_ handler: ((_JSON) -> Void)?, for id: Int) {
|
|
187
|
+
if let handler {
|
|
188
|
+
handlers[id] = handler
|
|
189
|
+
} else {
|
|
190
|
+
handlers[id] = nil
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private struct _PositionKey: Hashable {
|
|
196
|
+
let index: Int
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/// Key given to the elements a keyed mounted node produces.
|
|
200
|
+
struct _ElementKey: Hashable {
|
|
201
|
+
let base: AnyHashable
|
|
202
|
+
let index: Int
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/// `_ElementKey` qualified by the keyed group's position among its siblings,
|
|
206
|
+
/// so two `ForEach`es in one container never share a key.
|
|
207
|
+
struct _GroupKey: Hashable {
|
|
208
|
+
let position: Int
|
|
209
|
+
let key: AnyHashable
|
|
210
|
+
}
|