@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.
Files changed (74) hide show
  1. package/Package.swift +23 -0
  2. package/README.md +12 -2
  3. package/Sources/Charts/Charts.swift +65 -0
  4. package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
  5. package/Sources/FoundationBridge/Locale.swift +86 -0
  6. package/Sources/FoundationBridge/Timer.swift +48 -0
  7. package/Sources/FoundationBridge/UITextChecker.swift +30 -0
  8. package/Sources/FoundationLite/Common.swift +3 -0
  9. package/Sources/FoundationLite/FoundationLite.swift +171 -0
  10. package/Sources/FoundationLite/Interaction.swift +146 -0
  11. package/Sources/FoundationLite/Timer.swift +48 -0
  12. package/Sources/SwiftUI/API/Accessibility.swift +132 -0
  13. package/Sources/SwiftUI/API/Alert.swift +221 -0
  14. package/Sources/SwiftUI/API/Animation.swift +253 -0
  15. package/Sources/SwiftUI/API/App.swift +57 -0
  16. package/Sources/SwiftUI/API/Color.swift +161 -0
  17. package/Sources/SwiftUI/API/Controls.swift +268 -0
  18. package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
  19. package/Sources/SwiftUI/API/Decorations.swift +92 -0
  20. package/Sources/SwiftUI/API/EditMode.swift +269 -0
  21. package/Sources/SwiftUI/API/Effects.swift +290 -0
  22. package/Sources/SwiftUI/API/Environment.swift +355 -0
  23. package/Sources/SwiftUI/API/Focus.swift +129 -0
  24. package/Sources/SwiftUI/API/Font.swift +131 -0
  25. package/Sources/SwiftUI/API/Gauge.swift +289 -0
  26. package/Sources/SwiftUI/API/Geometry.swift +89 -0
  27. package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
  28. package/Sources/SwiftUI/API/Gestures.swift +342 -0
  29. package/Sources/SwiftUI/API/Grid.swift +188 -0
  30. package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
  31. package/Sources/SwiftUI/API/Layout.swift +165 -0
  32. package/Sources/SwiftUI/API/Lists.swift +276 -0
  33. package/Sources/SwiftUI/API/Menu.swift +51 -0
  34. package/Sources/SwiftUI/API/Modifiers.swift +243 -0
  35. package/Sources/SwiftUI/API/Navigation.swift +371 -0
  36. package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
  37. package/Sources/SwiftUI/API/OnChange.swift +48 -0
  38. package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
  39. package/Sources/SwiftUI/API/Picker.swift +176 -0
  40. package/Sources/SwiftUI/API/Presentation.swift +109 -0
  41. package/Sources/SwiftUI/API/Primitives.swift +390 -0
  42. package/Sources/SwiftUI/API/Publishers.swift +107 -0
  43. package/Sources/SwiftUI/API/RowActions.swift +83 -0
  44. package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
  45. package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
  46. package/Sources/SwiftUI/API/State.swift +127 -0
  47. package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
  48. package/Sources/SwiftUI/API/Tabs.swift +119 -0
  49. package/Sources/SwiftUI/API/Tasks.swift +35 -0
  50. package/Sources/SwiftUI/API/Timeline.swift +212 -0
  51. package/Sources/SwiftUI/API/Toolbar.swift +215 -0
  52. package/Sources/SwiftUI/API/View.swift +207 -0
  53. package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
  54. package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
  55. package/Sources/SwiftUI/Charts/Axis.swift +319 -0
  56. package/Sources/SwiftUI/Charts/Chart.swift +646 -0
  57. package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
  58. package/Sources/SwiftUI/Charts/Marks.swift +204 -0
  59. package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
  60. package/Sources/SwiftUI/Charts/Scales.swift +326 -0
  61. package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
  62. package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
  63. package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
  64. package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
  65. package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
  66. package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
  67. package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
  68. package/Sources/_FoundationCommon/Bundle.swift +82 -0
  69. package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
  70. package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
  71. package/index.d.ts +2 -0
  72. package/index.mjs +16 -0
  73. package/package.json +28 -4
  74. package/sync-sources.mjs +19 -0
@@ -0,0 +1,311 @@
1
+ /// Paints: colors, hierarchical label/fill levels, gradients and materials.
2
+ /// A style serializes to the JSON the renderer resolves (see
3
+ /// docs/ops-protocol.md, "Phase 6 additions"): a `Color`'s own forms, or
4
+ /// `{"hierarchical": 1...4}`, `{"linearGradient": {...}}`,
5
+ /// `{"radialGradient": {...}}`, `{"colorGradient": color}`, `{"material": name}`.
6
+ public protocol ShapeStyle: Sendable {
7
+ var _json: _JSON { get }
8
+ }
9
+
10
+ extension Color: ShapeStyle {}
11
+
12
+ // MARK: - Hierarchical (.primary ... .quaternary)
13
+
14
+ public struct HierarchicalShapeStyle: ShapeStyle, Hashable {
15
+ let level: Int
16
+
17
+ public static let primary = HierarchicalShapeStyle(level: 1)
18
+ public static let secondary = HierarchicalShapeStyle(level: 2)
19
+ public static let tertiary = HierarchicalShapeStyle(level: 3)
20
+ public static let quaternary = HierarchicalShapeStyle(level: 4)
21
+
22
+ public var _json: _JSON { .object(["hierarchical": .number(Double(level))]) }
23
+
24
+ public func opacity(_ opacity: Double) -> AnyShapeStyle {
25
+ AnyShapeStyle(_json: .object(["hierarchical": .number(Double(level)), "opacity": .number(opacity)]))
26
+ }
27
+ }
28
+
29
+ extension ShapeStyle where Self == HierarchicalShapeStyle {
30
+ public static var primary: HierarchicalShapeStyle { .primary }
31
+ public static var secondary: HierarchicalShapeStyle { .secondary }
32
+ public static var tertiary: HierarchicalShapeStyle { .tertiary }
33
+ public static var quaternary: HierarchicalShapeStyle { .quaternary }
34
+ }
35
+
36
+ /// The view's background color (`.background` as a style).
37
+ public struct BackgroundStyle: ShapeStyle, Hashable {
38
+ public init() {}
39
+ public var _json: _JSON { .object(["name": .string("systemBackground")]) }
40
+ }
41
+
42
+ extension ShapeStyle where Self == BackgroundStyle {
43
+ public static var background: BackgroundStyle { BackgroundStyle() }
44
+ }
45
+
46
+ /// The current foreground color (`.foreground` as a style; `currentColor`).
47
+ public struct ForegroundStyle: ShapeStyle, Hashable {
48
+ public init() {}
49
+ public var _json: _JSON { .object(["name": .string("primary")]) }
50
+ }
51
+
52
+ extension ShapeStyle where Self == ForegroundStyle {
53
+ public static var foreground: ForegroundStyle { ForegroundStyle() }
54
+ }
55
+
56
+ // The named colors, reachable as styles (`.fill(.indigo)`, `.background(.red)`).
57
+ extension ShapeStyle where Self == Color {
58
+ public static var red: Color { .red }
59
+ public static var orange: Color { .orange }
60
+ public static var yellow: Color { .yellow }
61
+ public static var green: Color { .green }
62
+ public static var mint: Color { .mint }
63
+ public static var teal: Color { .teal }
64
+ public static var cyan: Color { .cyan }
65
+ public static var blue: Color { .blue }
66
+ public static var indigo: Color { .indigo }
67
+ public static var purple: Color { .purple }
68
+ public static var pink: Color { .pink }
69
+ public static var brown: Color { .brown }
70
+ public static var gray: Color { .gray }
71
+ public static var black: Color { .black }
72
+ public static var white: Color { .white }
73
+ public static var clear: Color { .clear }
74
+ public static var accentColor: Color { .accentColor }
75
+ }
76
+
77
+ // MARK: - Gradients
78
+
79
+ public struct Gradient: Equatable, Sendable {
80
+ public struct Stop: Equatable, Sendable {
81
+ public var color: Color
82
+ public var location: CGFloat
83
+
84
+ public init(color: Color, location: CGFloat) {
85
+ self.color = color
86
+ self.location = location
87
+ }
88
+ }
89
+
90
+ public var stops: [Stop]
91
+
92
+ public init(stops: [Stop]) { self.stops = stops }
93
+
94
+ /// Evenly spaced stops.
95
+ public init(colors: [Color]) {
96
+ let count = max(colors.count - 1, 1)
97
+ stops = colors.enumerated().map { index, color in
98
+ Stop(color: color, location: colors.count == 1 ? 0 : CGFloat(index) / CGFloat(count))
99
+ }
100
+ }
101
+
102
+ var _json: _JSON {
103
+ .array(stops.map { .object(["color": $0.color._json, "location": .number($0.location)]) })
104
+ }
105
+ }
106
+
107
+ /// `Color.gradient`: a subtle vertical gradient derived from one color.
108
+ public struct AnyGradient: ShapeStyle, Equatable {
109
+ let color: Color
110
+
111
+ public var _json: _JSON { .object(["colorGradient": color._json]) }
112
+ }
113
+
114
+ extension Color {
115
+ public var gradient: AnyGradient { AnyGradient(color: self) }
116
+ }
117
+
118
+ public struct LinearGradient: ShapeStyle, Equatable {
119
+ public var gradient: Gradient
120
+ public var startPoint: UnitPoint
121
+ public var endPoint: UnitPoint
122
+
123
+ public init(gradient: Gradient, startPoint: UnitPoint, endPoint: UnitPoint) {
124
+ self.gradient = gradient
125
+ self.startPoint = startPoint
126
+ self.endPoint = endPoint
127
+ }
128
+
129
+ public init(colors: [Color], startPoint: UnitPoint, endPoint: UnitPoint) {
130
+ self.init(gradient: Gradient(colors: colors), startPoint: startPoint, endPoint: endPoint)
131
+ }
132
+
133
+ public init(stops: [Gradient.Stop], startPoint: UnitPoint, endPoint: UnitPoint) {
134
+ self.init(gradient: Gradient(stops: stops), startPoint: startPoint, endPoint: endPoint)
135
+ }
136
+
137
+ public var _json: _JSON {
138
+ .object(["linearGradient": .object([
139
+ "stops": gradient._json,
140
+ "start": startPoint._json,
141
+ "end": endPoint._json,
142
+ ])])
143
+ }
144
+ }
145
+
146
+ public struct RadialGradient: ShapeStyle, Equatable {
147
+ public var gradient: Gradient
148
+ public var center: UnitPoint
149
+ public var startRadius: CGFloat
150
+ public var endRadius: CGFloat
151
+
152
+ public init(gradient: Gradient, center: UnitPoint, startRadius: CGFloat, endRadius: CGFloat) {
153
+ self.gradient = gradient
154
+ self.center = center
155
+ self.startRadius = startRadius
156
+ self.endRadius = endRadius
157
+ }
158
+
159
+ public init(colors: [Color], center: UnitPoint, startRadius: CGFloat, endRadius: CGFloat) {
160
+ self.init(gradient: Gradient(colors: colors), center: center, startRadius: startRadius, endRadius: endRadius)
161
+ }
162
+
163
+ public init(stops: [Gradient.Stop], center: UnitPoint, startRadius: CGFloat, endRadius: CGFloat) {
164
+ self.init(gradient: Gradient(stops: stops), center: center, startRadius: startRadius, endRadius: endRadius)
165
+ }
166
+
167
+ public var _json: _JSON {
168
+ .object(["radialGradient": .object([
169
+ "stops": gradient._json,
170
+ "center": center._json,
171
+ "startRadius": .number(startRadius),
172
+ "endRadius": .number(endRadius),
173
+ ])])
174
+ }
175
+ }
176
+
177
+ extension ShapeStyle where Self == LinearGradient {
178
+ public static func linearGradient(_ gradient: Gradient, startPoint: UnitPoint, endPoint: UnitPoint) -> LinearGradient {
179
+ LinearGradient(gradient: gradient, startPoint: startPoint, endPoint: endPoint)
180
+ }
181
+
182
+ public static func linearGradient(colors: [Color], startPoint: UnitPoint, endPoint: UnitPoint) -> LinearGradient {
183
+ LinearGradient(colors: colors, startPoint: startPoint, endPoint: endPoint)
184
+ }
185
+
186
+ public static func linearGradient(stops: [Gradient.Stop], startPoint: UnitPoint, endPoint: UnitPoint) -> LinearGradient {
187
+ LinearGradient(stops: stops, startPoint: startPoint, endPoint: endPoint)
188
+ }
189
+ }
190
+
191
+ extension ShapeStyle where Self == RadialGradient {
192
+ public static func radialGradient(_ gradient: Gradient, center: UnitPoint, startRadius: CGFloat,
193
+ endRadius: CGFloat) -> RadialGradient {
194
+ RadialGradient(gradient: gradient, center: center, startRadius: startRadius, endRadius: endRadius)
195
+ }
196
+
197
+ public static func radialGradient(colors: [Color], center: UnitPoint, startRadius: CGFloat,
198
+ endRadius: CGFloat) -> RadialGradient {
199
+ RadialGradient(colors: colors, center: center, startRadius: startRadius, endRadius: endRadius)
200
+ }
201
+ }
202
+
203
+ extension UnitPoint {
204
+ var _json: _JSON { .object(["x": .number(x), "y": .number(y)]) }
205
+ }
206
+
207
+ // Gradients as views: a rectangle filled with the gradient (a `shape` element).
208
+ private func _gradientViewProps(_ style: some ShapeStyle) -> _JSON {
209
+ .object([
210
+ "shape": .string("rectangle"),
211
+ "cornerRadius": .number(0),
212
+ "fill": style._json,
213
+ "stroke": .null,
214
+ ])
215
+ }
216
+
217
+ extension LinearGradient: _PrimitiveView {}
218
+ extension LinearGradient: _HostView {
219
+ var _kind: String { "shape" }
220
+ func _props(in environment: EnvironmentValues) -> _JSON { _gradientViewProps(self) }
221
+ }
222
+
223
+ extension RadialGradient: _PrimitiveView {}
224
+ extension RadialGradient: _HostView {
225
+ var _kind: String { "shape" }
226
+ func _props(in environment: EnvironmentValues) -> _JSON { _gradientViewProps(self) }
227
+ }
228
+
229
+ extension AnyGradient: _PrimitiveView {}
230
+ extension AnyGradient: _HostView {
231
+ var _kind: String { "shape" }
232
+ func _props(in environment: EnvironmentValues) -> _JSON { _gradientViewProps(self) }
233
+ }
234
+
235
+ // MARK: - Shadows
236
+
237
+ /// A shadow a style can carry (`.shadow(.drop(...))`). The renderer has no
238
+ /// per-style shadows, so a shadowed style paints as its base style.
239
+ public struct ShadowStyle: Equatable, Sendable {
240
+ public enum Kind: Equatable, Sendable { case drop, inner }
241
+
242
+ public let kind: Kind
243
+ public let color: Color
244
+ public let radius: CGFloat
245
+ public let x: CGFloat
246
+ public let y: CGFloat
247
+
248
+ public static func drop(color: Color = Color(white: 0, opacity: 0.33), radius: CGFloat,
249
+ x: CGFloat = 0, y: CGFloat = 0) -> ShadowStyle {
250
+ ShadowStyle(kind: .drop, color: color, radius: radius, x: x, y: y)
251
+ }
252
+
253
+ public static func inner(color: Color = Color(white: 0, opacity: 0.55), radius: CGFloat,
254
+ x: CGFloat = 0, y: CGFloat = 0) -> ShadowStyle {
255
+ ShadowStyle(kind: .inner, color: color, radius: radius, x: x, y: y)
256
+ }
257
+ }
258
+
259
+ extension ShapeStyle {
260
+ /// Approximation: the shadow is dropped and the style paints as before.
261
+ public func shadow(_ style: ShadowStyle) -> AnyShapeStyle {
262
+ AnyShapeStyle(_json: _json)
263
+ }
264
+ }
265
+
266
+ // MARK: - Materials
267
+
268
+ public struct Material: ShapeStyle, Hashable {
269
+ let name: String
270
+
271
+ public static let ultraThinMaterial = Material(name: "ultraThin")
272
+ public static let thinMaterial = Material(name: "thin")
273
+ public static let regularMaterial = Material(name: "regular")
274
+ public static let thickMaterial = Material(name: "thick")
275
+ public static let ultraThickMaterial = Material(name: "ultraThick")
276
+ public static let bar = Material(name: "regular")
277
+
278
+ public var _json: _JSON { .object(["material": .string(name)]) }
279
+ }
280
+
281
+ extension ShapeStyle where Self == Material {
282
+ public static var ultraThinMaterial: Material { .ultraThinMaterial }
283
+ public static var thinMaterial: Material { .thinMaterial }
284
+ public static var regularMaterial: Material { .regularMaterial }
285
+ public static var thickMaterial: Material { .thickMaterial }
286
+ public static var ultraThickMaterial: Material { .ultraThickMaterial }
287
+ public static var bar: Material { .bar }
288
+ }
289
+
290
+ // MARK: - Type erasure
291
+
292
+ public struct AnyShapeStyle: ShapeStyle {
293
+ public let _json: _JSON
294
+
295
+ public init<S: ShapeStyle>(_ style: S) { _json = style._json }
296
+
297
+ init(_json: _JSON) { self._json = _json }
298
+ }
299
+
300
+ /// Opacity on any style: wraps the style's JSON with an `opacity` multiplier.
301
+ extension ShapeStyle {
302
+ public func opacity(_ opacity: Double) -> AnyShapeStyle {
303
+ if let color = self as? Color { return AnyShapeStyle(color.opacity(opacity)) }
304
+ if case .object(var fields) = _json {
305
+ let existing: Double = if case .number(let n)? = fields["opacity"] { n } else { 1 }
306
+ fields["opacity"] = .number(existing * opacity)
307
+ return AnyShapeStyle(_json: .object(fields))
308
+ }
309
+ return AnyShapeStyle(_json: _json)
310
+ }
311
+ }
@@ -0,0 +1,127 @@
1
+ /// Marker protocol matching SwiftUI's, so user code that constrains on it compiles.
2
+ public protocol DynamicProperty {
3
+ mutating func update()
4
+ }
5
+
6
+ extension DynamicProperty {
7
+ public mutating func update() {}
8
+ }
9
+
10
+ /// Type-erased access to a `State` box, used by the runtime to carry storage
11
+ /// across re-renders and to observe mutations.
12
+ protocol _AnyStateBox: AnyObject {
13
+ var _anyValue: Any { get set }
14
+ var _onChange: (() -> Void)? { get set }
15
+ /// Shares the persistent storage of a box from a previous render, so that
16
+ /// closures captured earlier keep writing to the live value.
17
+ func _adopt(_ previous: any _AnyStateBox)
18
+ }
19
+
20
+ /// Views expose their `State` properties to the runtime through this protocol.
21
+ protocol _StateProperty {
22
+ var _box: any _AnyStateBox { get }
23
+ }
24
+
25
+ /// The persistent value behind a `State`; survives the view struct being
26
+ /// recreated because every box for the same mounted view shares it.
27
+ final class _StateStorage<Value> {
28
+ var value: Value {
29
+ didSet { onChange?() }
30
+ }
31
+ var onChange: (() -> Void)?
32
+
33
+ init(_ value: Value) { self.value = value }
34
+ }
35
+
36
+ final class _StateBox<Value>: _AnyStateBox {
37
+ var storage: _StateStorage<Value>
38
+
39
+ var value: Value {
40
+ get { storage.value }
41
+ set { storage.value = newValue }
42
+ }
43
+
44
+ var _onChange: (() -> Void)? {
45
+ get { storage.onChange }
46
+ set { storage.onChange = newValue }
47
+ }
48
+
49
+ init(_ value: Value) { storage = _StateStorage(value) }
50
+
51
+ var _anyValue: Any {
52
+ get { storage.value }
53
+ set { storage.value = newValue as! Value }
54
+ }
55
+
56
+ func _adopt(_ previous: any _AnyStateBox) {
57
+ guard let previous = previous as? _StateBox<Value> else { return }
58
+ storage = previous.storage
59
+ }
60
+ }
61
+
62
+ /// A property wrapper that reads and writes a value managed by the runtime.
63
+ ///
64
+ /// A view struct is recreated every time its parent's body runs; the runtime
65
+ /// finds `State` properties by reflection and moves the previous values into
66
+ /// the new struct, so the value persists for as long as the view stays mounted.
67
+ @propertyWrapper
68
+ public struct State<Value>: DynamicProperty {
69
+ let box: _StateBox<Value>
70
+
71
+ public init(wrappedValue: Value) {
72
+ box = _StateBox(wrappedValue)
73
+ }
74
+
75
+ public init(initialValue: Value) {
76
+ self.init(wrappedValue: initialValue)
77
+ }
78
+
79
+ public var wrappedValue: Value {
80
+ get { box.value }
81
+ nonmutating set { box.value = newValue }
82
+ }
83
+
84
+ public var projectedValue: Binding<Value> {
85
+ let box = self.box
86
+ return Binding(get: { box.value }, set: { box.value = $0 })
87
+ }
88
+ }
89
+
90
+ extension State where Value: ExpressibleByNilLiteral {
91
+ public init() { self.init(wrappedValue: nil) }
92
+ }
93
+
94
+ extension State: _StateProperty {
95
+ var _box: any _AnyStateBox { box }
96
+ }
97
+
98
+ /// A two-way connection to a value owned elsewhere.
99
+ @propertyWrapper
100
+ @dynamicMemberLookup
101
+ public struct Binding<Value>: DynamicProperty {
102
+ let getter: () -> Value
103
+ let setter: (Value) -> Void
104
+
105
+ public init(get: @escaping () -> Value, set: @escaping (Value) -> Void) {
106
+ getter = get
107
+ setter = set
108
+ }
109
+
110
+ public static func constant(_ value: Value) -> Binding<Value> {
111
+ Binding(get: { value }, set: { _ in })
112
+ }
113
+
114
+ public var wrappedValue: Value {
115
+ get { getter() }
116
+ nonmutating set { setter(newValue) }
117
+ }
118
+
119
+ public var projectedValue: Binding<Value> { self }
120
+
121
+ public subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> {
122
+ Binding<Subject>(
123
+ get: { self.wrappedValue[keyPath: keyPath] },
124
+ set: { self.wrappedValue[keyPath: keyPath] = $0 }
125
+ )
126
+ }
127
+ }
@@ -0,0 +1,30 @@
1
+ /// The line cap of a stroke (CoreGraphics' `CGLineCap`).
2
+ public enum CGLineCap: Int32, Hashable, Sendable {
3
+ case butt, round, square
4
+ }
5
+
6
+ /// The line join of a stroke (CoreGraphics' `CGLineJoin`).
7
+ public enum CGLineJoin: Int32, Hashable, Sendable {
8
+ case miter, round, bevel
9
+ }
10
+
11
+ /// How a line is stroked. Charts read `lineWidth`; the other fields are kept
12
+ /// for source compatibility.
13
+ public struct StrokeStyle: Equatable, Sendable {
14
+ public var lineWidth: CGFloat
15
+ public var lineCap: CGLineCap
16
+ public var lineJoin: CGLineJoin
17
+ public var miterLimit: CGFloat
18
+ public var dash: [CGFloat]
19
+ public var dashPhase: CGFloat
20
+
21
+ public init(lineWidth: CGFloat = 1, lineCap: CGLineCap = .butt, lineJoin: CGLineJoin = .miter,
22
+ miterLimit: CGFloat = 10, dash: [CGFloat] = [], dashPhase: CGFloat = 0) {
23
+ self.lineWidth = lineWidth
24
+ self.lineCap = lineCap
25
+ self.lineJoin = lineJoin
26
+ self.miterLimit = miterLimit
27
+ self.dash = dash
28
+ self.dashPhase = dashPhase
29
+ }
30
+ }
@@ -0,0 +1,119 @@
1
+ /// A view that switches between child views with a tab bar.
2
+ public struct TabView<SelectionValue: Hashable, Content: View> {
3
+ let content: Content
4
+ let selection: Binding<SelectionValue>?
5
+ @State var selectedIndex = 0
6
+
7
+ public init(selection: Binding<SelectionValue>?, @ViewBuilder content: () -> Content) {
8
+ self.selection = selection
9
+ self.content = content()
10
+ }
11
+ }
12
+
13
+ extension TabView where SelectionValue == Int {
14
+ public init(@ViewBuilder content: () -> Content) {
15
+ self.selection = nil
16
+ self.content = content()
17
+ }
18
+ }
19
+
20
+ protocol _TabViewHost {
21
+ var _tabContent: any View { get }
22
+ /// Index of the selected tab given the tabs' tags, and a setter.
23
+ func _selectedIndex(tags: [AnyHashable?]) -> Int
24
+ func _select(index: Int, tags: [AnyHashable?])
25
+ }
26
+
27
+ extension TabView: _PrimitiveView {}
28
+ extension TabView: _HostView {
29
+ var _kind: String { "tabview" }
30
+ func _props(in environment: EnvironmentValues) -> _JSON { .object([:]) }
31
+ // Children and props are produced by the runtime.
32
+ }
33
+ extension TabView: _TabViewHost {
34
+ var _tabContent: any View { content }
35
+
36
+ func _selectedIndex(tags: [AnyHashable?]) -> Int {
37
+ if let selection {
38
+ let current = AnyHashable(selection.wrappedValue)
39
+ return tags.firstIndex { $0 == current } ?? 0
40
+ }
41
+ return min(selectedIndex, max(0, tags.count - 1))
42
+ }
43
+
44
+ func _select(index: Int, tags: [AnyHashable?]) {
45
+ if let selection {
46
+ if index < tags.count, let tag = tags[index], let value = tag.base as? SelectionValue {
47
+ selection.wrappedValue = value
48
+ }
49
+ } else {
50
+ selectedIndex = index
51
+ }
52
+ }
53
+ }
54
+
55
+ /// One tab, produced by the runtime from the TabView's content.
56
+ struct _TabScreenView {
57
+ let content: any View
58
+ let title: String
59
+ let systemImage: String?
60
+ /// Only the selected tab's `navigationTitle` and `.toolbar` reach the
61
+ /// enclosing screen (a tab's own `NavigationStack` is flattened into it).
62
+ var isSelected = true
63
+ }
64
+ extension _TabScreenView: _PrimitiveView {}
65
+ extension _TabScreenView: _HostView {
66
+ var _kind: String { "tab" }
67
+ func _props(in environment: EnvironmentValues) -> _JSON {
68
+ .object(["title": .string(title), "systemImage": systemImage.map(_JSON.string) ?? .null])
69
+ }
70
+ var _content: [any View] { [content] }
71
+ }
72
+
73
+ // MARK: - .tabItem and .tag
74
+
75
+ protocol _TabItemProviding {
76
+ var _tabLabel: any View { get }
77
+ var _tabContent: any View { get }
78
+ }
79
+
80
+ struct _TabItemView<Content: View, Label: View> {
81
+ let content: Content
82
+ let label: Label
83
+ }
84
+ extension _TabItemView: _PrimitiveView {}
85
+ extension _TabItemView: _GroupView {
86
+ var _children: [any View] { [content] }
87
+ }
88
+ extension _TabItemView: _TabItemProviding {
89
+ var _tabLabel: any View { label }
90
+ var _tabContent: any View { content }
91
+ }
92
+
93
+ protocol _TagProviding {
94
+ var _tag: AnyHashable { get }
95
+ var _tagged: any View { get }
96
+ }
97
+
98
+ struct _TagView<Content: View, Tag: Hashable> {
99
+ let content: Content
100
+ let tag: Tag
101
+ }
102
+ extension _TagView: _PrimitiveView {}
103
+ extension _TagView: _GroupView {
104
+ var _children: [any View] { [content] }
105
+ }
106
+ extension _TagView: _TagProviding {
107
+ var _tag: AnyHashable { AnyHashable(tag) }
108
+ var _tagged: any View { content }
109
+ }
110
+
111
+ extension View {
112
+ public func tabItem<Label: View>(@ViewBuilder _ label: () -> Label) -> some View {
113
+ _TabItemView(content: self, label: label())
114
+ }
115
+
116
+ public func tag<Tag: Hashable>(_ tag: Tag) -> some View {
117
+ _TagView(content: self, tag: tag)
118
+ }
119
+ }
@@ -0,0 +1,35 @@
1
+ /// Views that start an asynchronous task when they appear.
2
+ protocol _TaskStarting {
3
+ /// Identity of the task; when it changes the task restarts.
4
+ var _taskID: AnyHashable? { get }
5
+ var _taskAction: @Sendable () async -> Void { get }
6
+ }
7
+
8
+ struct _TaskView<Content: View> {
9
+ let content: Content
10
+ let id: AnyHashable?
11
+ let action: @Sendable () async -> Void
12
+ }
13
+ extension _TaskView: _PrimitiveView {}
14
+ extension _TaskView: _GroupView {
15
+ var _children: [any View] { [content] }
16
+ }
17
+ extension _TaskView: _TaskStarting {
18
+ var _taskID: AnyHashable? { id }
19
+ var _taskAction: @Sendable () async -> Void { action }
20
+ }
21
+
22
+ extension View {
23
+ /// Runs `action` in a task when the view appears, cancelling it when the
24
+ /// view disappears.
25
+ public func task(priority: TaskPriority = .userInitiated,
26
+ @_inheritActorContext _ action: @escaping @Sendable () async -> Void) -> some View {
27
+ _TaskView(content: self, id: nil, action: action)
28
+ }
29
+
30
+ /// Like `task(_:)`, restarting the task whenever `id` changes.
31
+ public func task<T: Hashable>(id: T, priority: TaskPriority = .userInitiated,
32
+ @_inheritActorContext _ action: @escaping @Sendable () async -> Void) -> some View {
33
+ _TaskView(content: self, id: AnyHashable(id), action: action)
34
+ }
35
+ }