@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,82 @@
1
+ /// `.visualEffect { content, proxy in … }` (docs/ops-protocol.md, "Phase 12").
2
+ /// The closure's effects (offset, scale, rotation, opacity, blur, …) apply as
3
+ /// the matching view modifiers. The proxy is empty: the browser does not
4
+ /// report a view's frame on every scroll step, so an effect that reads
5
+ /// `proxy.frame(in: .global)` sees a zero rectangle and the view renders
6
+ /// without that part of the effect. Compile-level support, honestly limited.
7
+
8
+ public protocol VisualEffect {}
9
+
10
+ /// The chained effects, as style changes on the content.
11
+ protocol _VisualEffectChaining {
12
+ var _effects: [(inout _Style) -> Void] { get }
13
+ }
14
+
15
+ public struct EmptyVisualEffect: VisualEffect, _VisualEffectChaining {
16
+ var _effects: [(inout _Style) -> Void] { [] }
17
+ public init() {}
18
+ }
19
+
20
+ public struct _VisualEffectChain: VisualEffect, _VisualEffectChaining {
21
+ var _effects: [(inout _Style) -> Void]
22
+ }
23
+
24
+ extension VisualEffect {
25
+ func _adding(_ effect: @escaping (inout _Style) -> Void) -> _VisualEffectChain {
26
+ _VisualEffectChain(_effects: ((self as? _VisualEffectChaining)?._effects ?? []) + [effect])
27
+ }
28
+
29
+ public func offset(x: CGFloat = 0, y: CGFloat = 0) -> _VisualEffectChain {
30
+ _adding { $0.extras["offset"] = .object(["x": .number(x), "y": .number(y)]) }
31
+ }
32
+ public func offset(_ offset: CGSize) -> _VisualEffectChain { self.offset(x: offset.width, y: offset.height) }
33
+ public func scaleEffect(_ scale: CGFloat, anchor: UnitPoint = .center) -> _VisualEffectChain {
34
+ scaleEffect(x: scale, y: scale, anchor: anchor)
35
+ }
36
+ public func scaleEffect(_ scale: CGSize, anchor: UnitPoint = .center) -> _VisualEffectChain {
37
+ scaleEffect(x: scale.width, y: scale.height, anchor: anchor)
38
+ }
39
+ public func scaleEffect(x: CGFloat = 1, y: CGFloat = 1, anchor: UnitPoint = .center) -> _VisualEffectChain {
40
+ _adding {
41
+ $0.extras["scale"] = .object(["x": .number(x), "y": .number(y)])
42
+ $0.extras["scaleAnchor"] = anchor._json
43
+ }
44
+ }
45
+ public func rotationEffect(_ angle: Angle, anchor: UnitPoint = .center) -> _VisualEffectChain {
46
+ _adding {
47
+ $0.extras["rotation"] = .number(angle.degrees)
48
+ $0.extras["rotationAnchor"] = anchor._json
49
+ }
50
+ }
51
+ public func rotation3DEffect(_ angle: Angle, axis: (x: CGFloat, y: CGFloat, z: CGFloat),
52
+ anchor: UnitPoint = .center, anchorZ: CGFloat = 0,
53
+ perspective: CGFloat = 1) -> _VisualEffectChain {
54
+ _adding {
55
+ $0.extras["rotation3D"] = .object([
56
+ "angle": .number(angle.degrees),
57
+ "axis": .object(["x": .number(axis.x), "y": .number(axis.y), "z": .number(axis.z)]),
58
+ "anchor": anchor._json,
59
+ "perspective": .number(perspective),
60
+ ])
61
+ }
62
+ }
63
+ public func opacity(_ opacity: Double) -> _VisualEffectChain { _adding { $0.opacity = opacity } }
64
+ public func blur(radius: CGFloat, opaque: Bool = false) -> _VisualEffectChain { _adding { $0.extras["blur"] = .number(radius) } }
65
+ /// Accepted; the renderer has no filter for these yet.
66
+ public func brightness(_ amount: Double) -> _VisualEffectChain { _adding { _ in } }
67
+ public func contrast(_ amount: Double) -> _VisualEffectChain { _adding { _ in } }
68
+ public func saturation(_ amount: Double) -> _VisualEffectChain { _adding { _ in } }
69
+ public func grayscale(_ amount: Double) -> _VisualEffectChain { _adding { _ in } }
70
+ public func hueRotation(_ angle: Angle) -> _VisualEffectChain { _adding { _ in } }
71
+ public func colorEffect<S: ShapeStyle>(_ shader: S) -> _VisualEffectChain { _adding { _ in } }
72
+ }
73
+
74
+ extension View {
75
+ public func visualEffect(_ effect: @escaping (EmptyVisualEffect, GeometryProxy) -> some VisualEffect) -> some View {
76
+ let result = effect(EmptyVisualEffect(), GeometryProxy(size: .zero))
77
+ let effects = (result as? _VisualEffectChaining)?._effects ?? []
78
+ return _styled { style in
79
+ for apply in effects { apply(&style) }
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,319 @@
1
+ // Axes: `chartXAxis { AxisMarks(values:) { value in AxisGridLine(); AxisTick();
2
+ // AxisValueLabel() } }`, `AxisValue`, and the axis builders. The resolved
3
+ // axis JSON is produced in Chart.swift.
4
+
5
+ // MARK: - AxisValue
6
+
7
+ /// A tick's value, handed to the `AxisMarks` content closure.
8
+ public struct AxisValue {
9
+ /// The tick's position among the axis's ticks.
10
+ public let index: Int
11
+ /// How many ticks the axis has.
12
+ public let count: Int
13
+ let value: _PlottableValue
14
+
15
+ /// The value as a plottable type (`value.as(Double.self)`, `.as(Date.self)`).
16
+ public func `as`<T: Plottable>(_ type: T.Type) -> T? {
17
+ T(_plottableValue: value)
18
+ }
19
+ }
20
+
21
+ // MARK: - Axis marks (grid line, tick, label)
22
+
23
+ /// What an `AxisMarks` closure produces for one tick.
24
+ public protocol AxisMark {
25
+ var _items: [_AxisMarkItem] { get }
26
+ }
27
+
28
+ public enum _AxisMarkLabel {
29
+ /// The default text for the value.
30
+ case automatic
31
+ case text(String)
32
+ case view(any View)
33
+ }
34
+
35
+ public enum _AxisMarkItem {
36
+ case gridLine
37
+ case tick
38
+ case label(_AxisMarkLabel)
39
+ }
40
+
41
+ public struct AxisGridLine: AxisMark {
42
+ public init(centered: Bool? = nil, stroke: StrokeStyle? = nil) {}
43
+ public var _items: [_AxisMarkItem] { [.gridLine] }
44
+ }
45
+
46
+ public struct AxisTick: AxisMark {
47
+ public init(centered: Bool? = nil, length: CGFloat? = nil, stroke: StrokeStyle? = nil) {}
48
+ public init(centered: Bool? = nil, length: AxisTickLength, stroke: StrokeStyle? = nil) {}
49
+ public var _items: [_AxisMarkItem] { [.tick] }
50
+ }
51
+
52
+ /// Accepted for source compatibility; the renderer draws 4 pt ticks.
53
+ public enum AxisTickLength: Equatable, Sendable {
54
+ case automatic
55
+ case label
56
+ case length(CGFloat)
57
+ }
58
+
59
+ public struct AxisValueLabel: AxisMark {
60
+ let label: _AxisMarkLabel
61
+
62
+ /// The default text for the value.
63
+ public init(centered: Bool? = nil, anchor: UnitPoint? = nil, multiLabelAlignment: Alignment? = nil,
64
+ offsetsMarks: Bool? = nil, horizontalSpacing: CGFloat? = nil, verticalSpacing: CGFloat? = nil) {
65
+ label = .automatic
66
+ }
67
+
68
+ public init(_ titleKey: LocalizedStringKey, centered: Bool? = nil, anchor: UnitPoint? = nil,
69
+ multiLabelAlignment: Alignment? = nil, offsetsMarks: Bool? = nil,
70
+ horizontalSpacing: CGFloat? = nil, verticalSpacing: CGFloat? = nil) {
71
+ label = .text(titleKey.key)
72
+ }
73
+
74
+ @_disfavoredOverload
75
+ public init<S: StringProtocol>(_ title: S, centered: Bool? = nil, anchor: UnitPoint? = nil,
76
+ multiLabelAlignment: Alignment? = nil, offsetsMarks: Bool? = nil,
77
+ horizontalSpacing: CGFloat? = nil, verticalSpacing: CGFloat? = nil) {
78
+ label = .text(String(title))
79
+ }
80
+
81
+ /// A view as the label: becomes a `chartitem` child of the chart.
82
+ public init<C: View>(centered: Bool? = nil, anchor: UnitPoint? = nil, multiLabelAlignment: Alignment? = nil,
83
+ offsetsMarks: Bool? = nil, horizontalSpacing: CGFloat? = nil,
84
+ verticalSpacing: CGFloat? = nil, @ViewBuilder content: () -> C) {
85
+ label = .view(content())
86
+ }
87
+
88
+ public var _items: [_AxisMarkItem] { [.label(label)] }
89
+ }
90
+
91
+ /// The marks an `AxisMarks` closure built for one tick.
92
+ public struct _AxisMarkList: AxisMark {
93
+ public let _items: [_AxisMarkItem]
94
+ }
95
+
96
+ @resultBuilder
97
+ public enum AxisMarkBuilder {
98
+ public static func buildBlock(_ parts: any AxisMark...) -> _AxisMarkList {
99
+ _AxisMarkList(_items: parts.flatMap(\._items))
100
+ }
101
+
102
+ public static func buildExpression<M: AxisMark>(_ mark: M) -> _AxisMarkList { _AxisMarkList(_items: mark._items) }
103
+
104
+ public static func buildOptional(_ list: _AxisMarkList?) -> _AxisMarkList { list ?? _AxisMarkList(_items: []) }
105
+
106
+ public static func buildEither(first: _AxisMarkList) -> _AxisMarkList { first }
107
+ public static func buildEither(second: _AxisMarkList) -> _AxisMarkList { second }
108
+
109
+ public static func buildArray(_ lists: [_AxisMarkList]) -> _AxisMarkList {
110
+ _AxisMarkList(_items: lists.flatMap(\._items))
111
+ }
112
+ }
113
+
114
+ // MARK: - AxisMarks
115
+
116
+ /// Which side of the plot an axis sits on.
117
+ public enum AxisMarkPosition: Hashable, Sendable {
118
+ case automatic, bottom, top, leading, trailing
119
+
120
+ var _name: String? {
121
+ switch self {
122
+ case .automatic: nil
123
+ case .bottom: "bottom"
124
+ case .top: "top"
125
+ case .leading: "leading"
126
+ case .trailing: "trailing"
127
+ }
128
+ }
129
+ }
130
+
131
+ /// Accepted for source compatibility; every preset renders the same marks.
132
+ public enum AxisMarkPreset: Hashable, Sendable {
133
+ case automatic, extended, aligned, inset
134
+ }
135
+
136
+ /// How an axis picks its tick values.
137
+ public struct AxisMarkValues: Equatable {
138
+ enum Spec: Equatable {
139
+ case automatic(_TickOptions)
140
+ case stride(Double, roundLowerBound: Bool, roundUpperBound: Bool)
141
+ case explicit([_PlottableValue])
142
+ }
143
+ let spec: Spec
144
+
145
+ public static var automatic: AxisMarkValues { AxisMarkValues(spec: .automatic(_TickOptions())) }
146
+
147
+ public static func automatic(desiredCount: Int? = nil, roundLowerBound: Bool? = nil,
148
+ roundUpperBound: Bool? = nil) -> AxisMarkValues {
149
+ AxisMarkValues(spec: .automatic(_TickOptions(desiredCount: desiredCount, minimumStride: nil,
150
+ roundLowerBound: roundLowerBound ?? true,
151
+ roundUpperBound: roundUpperBound ?? true)))
152
+ }
153
+
154
+ public static func automatic(minimumStride: Double, desiredCount: Int? = nil, roundLowerBound: Bool? = nil,
155
+ roundUpperBound: Bool? = nil) -> AxisMarkValues {
156
+ AxisMarkValues(spec: .automatic(_TickOptions(desiredCount: desiredCount, minimumStride: minimumStride,
157
+ roundLowerBound: roundLowerBound ?? true,
158
+ roundUpperBound: roundUpperBound ?? true)))
159
+ }
160
+
161
+ /// Ticks every `stride` along a numeric axis.
162
+ public static func stride(by stride: Double, roundLowerBound: Bool? = nil,
163
+ roundUpperBound: Bool? = nil) -> AxisMarkValues {
164
+ AxisMarkValues(spec: .stride(stride, roundLowerBound: roundLowerBound ?? true,
165
+ roundUpperBound: roundUpperBound ?? true))
166
+ }
167
+ }
168
+
169
+ /// The marks of one axis: ticks at `values`, each drawn as `content` says
170
+ /// (grid line, tick and label by default).
171
+ public struct AxisMarks: AxisContent {
172
+ let position: AxisMarkPosition
173
+ let values: AxisMarkValues
174
+ let content: ((AxisValue) -> _AxisMarkList)?
175
+
176
+ public init(preset: AxisMarkPreset = .automatic, position: AxisMarkPosition = .automatic,
177
+ values: AxisMarkValues = .automatic, stroke: StrokeStyle? = nil) {
178
+ self.position = position
179
+ self.values = values
180
+ content = nil
181
+ }
182
+
183
+ public init(preset: AxisMarkPreset = .automatic, position: AxisMarkPosition = .automatic,
184
+ values: AxisMarkValues = .automatic,
185
+ @AxisMarkBuilder content: @escaping (AxisValue) -> _AxisMarkList) {
186
+ self.position = position
187
+ self.values = values
188
+ self.content = content
189
+ }
190
+
191
+ /// Ticks exactly at `values`.
192
+ public init<T: Plottable>(preset: AxisMarkPreset = .automatic, position: AxisMarkPosition = .automatic,
193
+ values: [T], stroke: StrokeStyle? = nil) {
194
+ self.position = position
195
+ self.values = AxisMarkValues(spec: .explicit(values.map(\._plottableValue)))
196
+ content = nil
197
+ }
198
+
199
+ public init<T: Plottable>(preset: AxisMarkPreset = .automatic, position: AxisMarkPosition = .automatic,
200
+ values: [T], @AxisMarkBuilder content: @escaping (AxisValue) -> _AxisMarkList) {
201
+ self.position = position
202
+ self.values = AxisMarkValues(spec: .explicit(values.map(\._plottableValue)))
203
+ self.content = content
204
+ }
205
+
206
+ public var _axisMarks: [AxisMarks] { [self] }
207
+ }
208
+
209
+ // MARK: - AxisContent
210
+
211
+ /// The content of `chartXAxis { }` / `chartYAxis { }`: one or more `AxisMarks`.
212
+ public protocol AxisContent {
213
+ var _axisMarks: [AxisMarks] { get }
214
+ }
215
+
216
+ public struct _AxisContentList: AxisContent {
217
+ public let _axisMarks: [AxisMarks]
218
+ }
219
+
220
+ @resultBuilder
221
+ public enum AxisContentBuilder {
222
+ public static func buildBlock(_ parts: any AxisContent...) -> _AxisContentList {
223
+ _AxisContentList(_axisMarks: parts.flatMap(\._axisMarks))
224
+ }
225
+
226
+ public static func buildExpression<C: AxisContent>(_ content: C) -> _AxisContentList {
227
+ _AxisContentList(_axisMarks: content._axisMarks)
228
+ }
229
+
230
+ public static func buildOptional(_ list: _AxisContentList?) -> _AxisContentList {
231
+ list ?? _AxisContentList(_axisMarks: [])
232
+ }
233
+
234
+ public static func buildEither(first: _AxisContentList) -> _AxisContentList { first }
235
+ public static func buildEither(second: _AxisContentList) -> _AxisContentList { second }
236
+
237
+ public static func buildArray(_ lists: [_AxisContentList]) -> _AxisContentList {
238
+ _AxisContentList(_axisMarks: lists.flatMap(\._axisMarks))
239
+ }
240
+ }
241
+
242
+ // MARK: - Chart modifiers (environment)
243
+
244
+ /// An axis as configured by `chartXAxis` / `chartYAxis`.
245
+ struct _AxisSpec {
246
+ var hidden = false
247
+ /// Empty: the default `AxisMarks()`.
248
+ var marks: [AxisMarks] = []
249
+ }
250
+
251
+ /// Everything the `chart...` view modifiers set, read by the nearest `Chart`.
252
+ struct _ChartConfig {
253
+ var xAxis = _AxisSpec()
254
+ var yAxis = _AxisSpec()
255
+ var legendHidden = false
256
+ var legendPosition = AnnotationPosition.automatic
257
+ var xDomain = _ScaleDomainSpec.automatic(includesZero: nil, reversed: false)
258
+ var yDomain = _ScaleDomainSpec.automatic(includesZero: nil, reversed: false)
259
+ }
260
+
261
+ enum _ChartConfigKey: EnvironmentKey {
262
+ static var defaultValue: _ChartConfig { _ChartConfig() }
263
+ }
264
+
265
+ extension EnvironmentValues {
266
+ var _chartConfig: _ChartConfig {
267
+ get { self[_ChartConfigKey.self] }
268
+ set { self[_ChartConfigKey.self] = newValue }
269
+ }
270
+ }
271
+
272
+ extension View {
273
+ func _chartConfig(_ configure: @escaping (inout _ChartConfig) -> Void) -> some View {
274
+ _EnvironmentView(content: self) { configure(&$0._chartConfig) }
275
+ }
276
+
277
+ public func chartXAxis<C: AxisContent>(@AxisContentBuilder content: () -> C) -> some View {
278
+ let marks = content()._axisMarks
279
+ return _chartConfig { $0.xAxis = _AxisSpec(hidden: false, marks: marks) }
280
+ }
281
+
282
+ public func chartYAxis<C: AxisContent>(@AxisContentBuilder content: () -> C) -> some View {
283
+ let marks = content()._axisMarks
284
+ return _chartConfig { $0.yAxis = _AxisSpec(hidden: false, marks: marks) }
285
+ }
286
+
287
+ public func chartXAxis(_ visibility: Visibility) -> some View {
288
+ _chartConfig { $0.xAxis.hidden = visibility == .hidden }
289
+ }
290
+
291
+ public func chartYAxis(_ visibility: Visibility) -> some View {
292
+ _chartConfig { $0.yAxis.hidden = visibility == .hidden }
293
+ }
294
+
295
+ public func chartXScale<D: ScaleDomain>(domain: D) -> some View {
296
+ let spec = domain._domainSpec
297
+ return _chartConfig { $0.xDomain = spec }
298
+ }
299
+
300
+ public func chartYScale<D: ScaleDomain>(domain: D) -> some View {
301
+ let spec = domain._domainSpec
302
+ return _chartConfig { $0.yDomain = spec }
303
+ }
304
+
305
+ public func chartLegend(_ visibility: Visibility) -> some View {
306
+ _chartConfig { $0.legendHidden = visibility == .hidden }
307
+ }
308
+
309
+ public func chartLegend(position: AnnotationPosition = .automatic, alignment: Alignment? = nil,
310
+ spacing: CGFloat? = nil) -> some View {
311
+ _chartConfig { $0.legendHidden = false; $0.legendPosition = position }
312
+ }
313
+
314
+ /// A custom legend view is not rendered; the position still applies.
315
+ public func chartLegend<C: View>(position: AnnotationPosition = .automatic, alignment: Alignment? = nil,
316
+ spacing: CGFloat? = nil, @ViewBuilder content: () -> C) -> some View {
317
+ _chartConfig { $0.legendHidden = false; $0.legendPosition = position }
318
+ }
319
+ }