@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,376 @@
|
|
|
1
|
+
// `ChartContent`, its result builder, the structural content types and the
|
|
2
|
+
// mark modifiers. Content resolves to `_RawMark`s (one per data point) that
|
|
3
|
+
// Chart.swift scales into the `chart` element's unit-coordinate marks.
|
|
4
|
+
|
|
5
|
+
/// The content of a `Chart`: marks, groups of marks, or a type with a `body`.
|
|
6
|
+
/// Unlike `View` it is not main-actor isolated: the runtime resolves it from
|
|
7
|
+
/// the (nonisolated) `_HostView` requirements.
|
|
8
|
+
public protocol ChartContent {
|
|
9
|
+
associatedtype Body: ChartContent
|
|
10
|
+
@ChartContentBuilder var body: Self.Body { get }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
extension Never: @preconcurrency ChartContent {}
|
|
14
|
+
|
|
15
|
+
/// Content the resolver reads directly instead of evaluating `body`.
|
|
16
|
+
public protocol _PrimitiveChartContent: ChartContent where Body == Never {}
|
|
17
|
+
|
|
18
|
+
extension _PrimitiveChartContent {
|
|
19
|
+
public var body: Never { fatalError("\(Self.self) is primitive chart content and has no body") }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Internal: contributes raw marks.
|
|
23
|
+
protocol _ChartContentNode {
|
|
24
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
extension ChartContent {
|
|
28
|
+
func _collectAny(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
29
|
+
if let node = self as? any _ChartContentNode {
|
|
30
|
+
node._collect(into: &collector, modifiers: modifiers)
|
|
31
|
+
} else {
|
|
32
|
+
body._collectAny(into: &collector, modifiers: modifiers)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// MARK: - Raw marks
|
|
38
|
+
|
|
39
|
+
/// An annotation view attached to a mark.
|
|
40
|
+
struct _MarkAnnotation {
|
|
41
|
+
var position: AnnotationPosition
|
|
42
|
+
var alignment: Alignment
|
|
43
|
+
var spacing: Double
|
|
44
|
+
var content: any View
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// Everything the modifiers set; an inner modifier overrides an outer one.
|
|
48
|
+
struct _MarkModifiers {
|
|
49
|
+
var style: (any ShapeStyle)?
|
|
50
|
+
var styleBy: _LabelledValue?
|
|
51
|
+
var symbolBy: _LabelledValue?
|
|
52
|
+
var symbol: String?
|
|
53
|
+
var symbolSize: Double?
|
|
54
|
+
var interpolation = "linear"
|
|
55
|
+
var opacity = 1.0
|
|
56
|
+
var lineWidth: Double?
|
|
57
|
+
var cornerRadius = 0.0
|
|
58
|
+
var mask: (any ChartContent)?
|
|
59
|
+
var annotations: [_MarkAnnotation] = []
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
enum _RawMarkKind: Equatable {
|
|
63
|
+
case bar, rectangle, line, area, point, rule
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// One mark before scaling.
|
|
67
|
+
struct _RawMark {
|
|
68
|
+
var kind: _RawMarkKind
|
|
69
|
+
var x: _PlottableValue?
|
|
70
|
+
var xStart: _PlottableValue?
|
|
71
|
+
var xEnd: _PlottableValue?
|
|
72
|
+
var y: _PlottableValue?
|
|
73
|
+
var yStart: _PlottableValue?
|
|
74
|
+
var yEnd: _PlottableValue?
|
|
75
|
+
var series: _PlottableValue?
|
|
76
|
+
var width: MarkDimension?
|
|
77
|
+
var height: MarkDimension?
|
|
78
|
+
var modifiers = _MarkModifiers()
|
|
79
|
+
|
|
80
|
+
var xValues: [_PlottableValue] { [x, xStart, xEnd].compactMap { $0 } }
|
|
81
|
+
var yValues: [_PlottableValue] { [y, yStart, yEnd].compactMap { $0 } }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
struct _ChartCollector {
|
|
85
|
+
var marks: [_RawMark] = []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// MARK: - Structural content
|
|
89
|
+
|
|
90
|
+
public struct _EmptyChartContent {
|
|
91
|
+
public init() {}
|
|
92
|
+
}
|
|
93
|
+
extension _EmptyChartContent: _PrimitiveChartContent {}
|
|
94
|
+
extension _EmptyChartContent: _ChartContentNode {
|
|
95
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public struct _TupleChartContent<T> {
|
|
99
|
+
let children: [any ChartContent]
|
|
100
|
+
}
|
|
101
|
+
extension _TupleChartContent: _PrimitiveChartContent {}
|
|
102
|
+
extension _TupleChartContent: _ChartContentNode {
|
|
103
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
104
|
+
for child in children { child._collectAny(into: &collector, modifiers: modifiers) }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public struct _ConditionalChartContent<TrueContent: ChartContent, FalseContent: ChartContent> {
|
|
109
|
+
enum Storage {
|
|
110
|
+
case trueContent(TrueContent)
|
|
111
|
+
case falseContent(FalseContent)
|
|
112
|
+
}
|
|
113
|
+
let storage: Storage
|
|
114
|
+
}
|
|
115
|
+
extension _ConditionalChartContent: _PrimitiveChartContent {}
|
|
116
|
+
extension _ConditionalChartContent: _ChartContentNode {
|
|
117
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
118
|
+
switch storage {
|
|
119
|
+
case .trueContent(let content): content._collectAny(into: &collector, modifiers: modifiers)
|
|
120
|
+
case .falseContent(let content): content._collectAny(into: &collector, modifiers: modifiers)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
extension Optional: ChartContent where Wrapped: ChartContent {
|
|
126
|
+
public typealias Body = Never
|
|
127
|
+
public var body: Never { fatalError("Optional is primitive chart content and has no body") }
|
|
128
|
+
}
|
|
129
|
+
extension Optional: _ChartContentNode where Wrapped: ChartContent {
|
|
130
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
131
|
+
if case .some(let content) = self { content._collectAny(into: &collector, modifiers: modifiers) }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public struct _ArrayChartContent<Element: ChartContent> {
|
|
136
|
+
let elements: [Element]
|
|
137
|
+
}
|
|
138
|
+
extension _ArrayChartContent: _PrimitiveChartContent {}
|
|
139
|
+
extension _ArrayChartContent: _ChartContentNode {
|
|
140
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
141
|
+
for element in elements { element._collectAny(into: &collector, modifiers: modifiers) }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/// Type-erased chart content.
|
|
146
|
+
public struct AnyChartContent {
|
|
147
|
+
let storage: any ChartContent
|
|
148
|
+
public init<C: ChartContent>(_ content: C) { storage = content }
|
|
149
|
+
}
|
|
150
|
+
extension AnyChartContent: _PrimitiveChartContent {}
|
|
151
|
+
extension AnyChartContent: _ChartContentNode {
|
|
152
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
153
|
+
storage._collectAny(into: &collector, modifiers: modifiers)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// `ForEach` over chart content: one mark group per element, with modifiers
|
|
158
|
+
/// applied to the `ForEach` distributed to each element's marks.
|
|
159
|
+
extension ForEach: ChartContent where Content: ChartContent {
|
|
160
|
+
public typealias Body = Never
|
|
161
|
+
public var body: Never { fatalError("ForEach is primitive chart content and has no body") }
|
|
162
|
+
}
|
|
163
|
+
extension ForEach: _ChartContentNode where Content: ChartContent {
|
|
164
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
165
|
+
for element in data { content(element)._collectAny(into: &collector, modifiers: modifiers) }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
extension ForEach where Content: ChartContent {
|
|
170
|
+
public init(_ data: Data, id: KeyPath<Data.Element, ID>,
|
|
171
|
+
@ChartContentBuilder content: @escaping (Data.Element) -> Content) {
|
|
172
|
+
self.init(_data: data, id: { $0[keyPath: id] }, content: content)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
extension ForEach where Content: ChartContent, Data.Element: Identifiable, ID == Data.Element.ID {
|
|
177
|
+
public init(_ data: Data, @ChartContentBuilder content: @escaping (Data.Element) -> Content) {
|
|
178
|
+
self.init(_data: data, id: { $0.id }, content: content)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
extension ForEach where Content: ChartContent, Data == Range<Int>, ID == Int {
|
|
183
|
+
public init(_ data: Range<Int>, @ChartContentBuilder content: @escaping (Int) -> Content) {
|
|
184
|
+
self.init(_data: data, id: { $0 }, content: content)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MARK: - ChartContentBuilder
|
|
189
|
+
|
|
190
|
+
@resultBuilder
|
|
191
|
+
public enum ChartContentBuilder {
|
|
192
|
+
public static func buildBlock() -> _EmptyChartContent { _EmptyChartContent() }
|
|
193
|
+
|
|
194
|
+
public static func buildBlock<Content: ChartContent>(_ content: Content) -> Content { content }
|
|
195
|
+
|
|
196
|
+
public static func buildBlock<each Content: ChartContent>(
|
|
197
|
+
_ content: repeat each Content
|
|
198
|
+
) -> _TupleChartContent<(repeat each Content)> {
|
|
199
|
+
var children: [any ChartContent] = []
|
|
200
|
+
for child in repeat each content { children.append(child) }
|
|
201
|
+
return _TupleChartContent(children: children)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
public static func buildExpression<Content: ChartContent>(_ content: Content) -> Content { content }
|
|
205
|
+
|
|
206
|
+
public static func buildOptional<Content: ChartContent>(_ content: Content?) -> Content? { content }
|
|
207
|
+
|
|
208
|
+
public static func buildEither<TrueContent: ChartContent, FalseContent: ChartContent>(
|
|
209
|
+
first: TrueContent
|
|
210
|
+
) -> _ConditionalChartContent<TrueContent, FalseContent> {
|
|
211
|
+
_ConditionalChartContent(storage: .trueContent(first))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public static func buildEither<TrueContent: ChartContent, FalseContent: ChartContent>(
|
|
215
|
+
second: FalseContent
|
|
216
|
+
) -> _ConditionalChartContent<TrueContent, FalseContent> {
|
|
217
|
+
_ConditionalChartContent(storage: .falseContent(second))
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
public static func buildArray<Content: ChartContent>(_ components: [Content]) -> _ArrayChartContent<Content> {
|
|
221
|
+
_ArrayChartContent(elements: components)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public static func buildLimitedAvailability<Content: ChartContent>(_ content: Content) -> AnyChartContent {
|
|
225
|
+
AnyChartContent(content)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// MARK: - Modifiers
|
|
230
|
+
|
|
231
|
+
/// Content with a modifier applied to every mark beneath it.
|
|
232
|
+
public struct _ModifiedChartContent<Content: ChartContent> {
|
|
233
|
+
let content: Content
|
|
234
|
+
let transform: (inout _MarkModifiers) -> Void
|
|
235
|
+
}
|
|
236
|
+
extension _ModifiedChartContent: _PrimitiveChartContent {}
|
|
237
|
+
extension _ModifiedChartContent: _ChartContentNode {
|
|
238
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
239
|
+
// Outer modifiers run first so that the mark's own (inner) ones win.
|
|
240
|
+
var modified = modifiers
|
|
241
|
+
transform(&modified)
|
|
242
|
+
content._collectAny(into: &collector, modifiers: modified)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/// Where an annotation sits relative to its mark (also the legend position).
|
|
247
|
+
public enum AnnotationPosition: Hashable, Sendable {
|
|
248
|
+
case automatic, top, bottom, leading, trailing, overlay
|
|
249
|
+
case topLeading, topTrailing, bottomLeading, bottomTrailing
|
|
250
|
+
|
|
251
|
+
/// The protocol's position and the alignment it implies.
|
|
252
|
+
var _resolved: (position: String, alignment: Alignment?) {
|
|
253
|
+
switch self {
|
|
254
|
+
case .automatic, .top: ("top", nil)
|
|
255
|
+
case .bottom: ("bottom", nil)
|
|
256
|
+
case .leading: ("leading", nil)
|
|
257
|
+
case .trailing: ("trailing", nil)
|
|
258
|
+
case .overlay: ("overlay", nil)
|
|
259
|
+
case .topLeading: ("top", .leading)
|
|
260
|
+
case .topTrailing: ("top", .trailing)
|
|
261
|
+
case .bottomLeading: ("bottom", .leading)
|
|
262
|
+
case .bottomTrailing: ("bottom", .trailing)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// How a line or area is interpolated between its points.
|
|
268
|
+
public enum InterpolationMethod: Hashable, Sendable {
|
|
269
|
+
case linear, cardinal, catmullRom, monotone, stepStart, stepCenter, stepEnd
|
|
270
|
+
|
|
271
|
+
var _name: String {
|
|
272
|
+
switch self {
|
|
273
|
+
case .linear: "linear"
|
|
274
|
+
case .cardinal: "cardinal"
|
|
275
|
+
case .catmullRom: "catmullRom"
|
|
276
|
+
case .monotone: "monotone"
|
|
277
|
+
case .stepStart: "stepStart"
|
|
278
|
+
case .stepCenter: "stepCenter"
|
|
279
|
+
case .stepEnd: "stepEnd"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
public static func cardinal(tension: CGFloat) -> InterpolationMethod { .cardinal }
|
|
284
|
+
public static func catmullRom(alpha: CGFloat) -> InterpolationMethod { .catmullRom }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/// The shape of a point or legend symbol.
|
|
288
|
+
public struct BasicChartSymbolShape: Hashable, Sendable {
|
|
289
|
+
let name: String
|
|
290
|
+
|
|
291
|
+
public static let circle = BasicChartSymbolShape(name: "circle")
|
|
292
|
+
public static let square = BasicChartSymbolShape(name: "square")
|
|
293
|
+
public static let triangle = BasicChartSymbolShape(name: "triangle")
|
|
294
|
+
public static let diamond = BasicChartSymbolShape(name: "diamond")
|
|
295
|
+
public static let pentagon = BasicChartSymbolShape(name: "diamond")
|
|
296
|
+
public static let cross = BasicChartSymbolShape(name: "cross")
|
|
297
|
+
public static let plus = BasicChartSymbolShape(name: "plus")
|
|
298
|
+
public static let asterisk = BasicChartSymbolShape(name: "asterisk")
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// The symbol palette for `symbol(by:)`, in the protocol's order.
|
|
302
|
+
let _chartSymbolPalette = ["circle", "square", "triangle", "diamond", "cross", "plus", "asterisk"]
|
|
303
|
+
|
|
304
|
+
/// The color palette for `foregroundStyle(by:)`.
|
|
305
|
+
let _chartColorPalette: [Color] = [.blue, .green, .orange, .purple, .red, .teal,
|
|
306
|
+
.yellow, .pink, .indigo, .mint, .cyan, .brown]
|
|
307
|
+
|
|
308
|
+
extension ChartContent {
|
|
309
|
+
func _modified(_ transform: @escaping (inout _MarkModifiers) -> Void) -> _ModifiedChartContent<Self> {
|
|
310
|
+
_ModifiedChartContent(content: self, transform: transform)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
public func foregroundStyle<S: ShapeStyle>(_ style: S) -> some ChartContent {
|
|
314
|
+
_modified { $0.style = style }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
public func foregroundStyle<Value: Plottable>(by value: PlottableValue<Value>) -> some ChartContent {
|
|
318
|
+
let erased = value._erased
|
|
319
|
+
return _modified { $0.styleBy = erased }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
public func symbol<Value: Plottable>(by value: PlottableValue<Value>) -> some ChartContent {
|
|
323
|
+
let erased = value._erased
|
|
324
|
+
return _modified { $0.symbolBy = erased }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public func symbol(_ shape: BasicChartSymbolShape) -> some ChartContent {
|
|
328
|
+
_modified { $0.symbol = shape.name }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
public func symbolSize(_ area: CGFloat) -> some ChartContent {
|
|
332
|
+
// SwiftUI takes an area in points²; the protocol wants a diameter.
|
|
333
|
+
let diameter = area.squareRoot()
|
|
334
|
+
return _modified { $0.symbolSize = diameter }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
public func symbolSize(_ size: CGSize) -> some ChartContent {
|
|
338
|
+
_modified { $0.symbolSize = min(size.width, size.height) }
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
public func interpolationMethod(_ method: InterpolationMethod) -> some ChartContent {
|
|
342
|
+
_modified { $0.interpolation = method._name }
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
public func opacity(_ opacity: Double) -> some ChartContent {
|
|
346
|
+
_modified { $0.opacity = max(0, min(1, opacity)) }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
public func lineStyle(_ style: StrokeStyle) -> some ChartContent {
|
|
350
|
+
_modified { $0.lineWidth = style.lineWidth }
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
public func cornerRadius(_ radius: CGFloat, style: RoundedCornerStyle = .continuous) -> some ChartContent {
|
|
354
|
+
_modified { $0.cornerRadius = radius }
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// Clips the marks with the geometry of the first area or rectangle mark
|
|
358
|
+
/// in `content`, which is scaled like the chart's own marks.
|
|
359
|
+
public func mask<C: ChartContent>(@ChartContentBuilder _ content: () -> C) -> some ChartContent {
|
|
360
|
+
let mask = content()
|
|
361
|
+
return _modified { $0.mask = mask }
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
public func annotation<C: View>(position: AnnotationPosition = .automatic, alignment: Alignment = .center,
|
|
365
|
+
spacing: CGFloat? = nil,
|
|
366
|
+
@ViewBuilder content: () -> C) -> some ChartContent {
|
|
367
|
+
let view = content()
|
|
368
|
+
let annotation = _MarkAnnotation(position: position, alignment: alignment, spacing: spacing ?? 4, content: view)
|
|
369
|
+
return _modified { $0.annotations.append(annotation) }
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/// Accepted for source compatibility; the shim has no accessibility tree.
|
|
373
|
+
public func accessibilityLabel(_ label: String) -> some ChartContent { self }
|
|
374
|
+
public func accessibilityValue(_ value: String) -> some ChartContent { self }
|
|
375
|
+
public func accessibilityHidden(_ hidden: Bool) -> some ChartContent { self }
|
|
376
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// The marks: BarMark, LineMark, AreaMark, PointMark, RuleMark, RectangleMark.
|
|
2
|
+
// Each is one `_RawMark`; Chart.swift groups lines and areas by series.
|
|
3
|
+
|
|
4
|
+
/// The width (or height) of a bar or rectangle.
|
|
5
|
+
public enum MarkDimension: Equatable, Sendable {
|
|
6
|
+
/// 80 % of the band.
|
|
7
|
+
case automatic
|
|
8
|
+
/// A fraction of the band.
|
|
9
|
+
case ratio(CGFloat)
|
|
10
|
+
/// Points, centered on the value.
|
|
11
|
+
case fixed(CGFloat)
|
|
12
|
+
/// The band inset by points on each side (treated as `automatic`).
|
|
13
|
+
case inset(CGFloat)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
extension MarkDimension: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral {
|
|
17
|
+
public init(floatLiteral value: Double) { self = .fixed(value) }
|
|
18
|
+
public init(integerLiteral value: Int) { self = .fixed(CGFloat(value)) }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// A mark that resolves to one `_RawMark`.
|
|
22
|
+
protocol _SingleMark: _PrimitiveChartContent, _ChartContentNode {
|
|
23
|
+
var _raw: _RawMark { get }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
extension _SingleMark {
|
|
27
|
+
func _collect(into collector: inout _ChartCollector, modifiers: _MarkModifiers) {
|
|
28
|
+
var mark = _raw
|
|
29
|
+
mark.modifiers = modifiers
|
|
30
|
+
collector.marks.append(mark)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - BarMark
|
|
35
|
+
|
|
36
|
+
public struct BarMark {
|
|
37
|
+
let _raw: _RawMark
|
|
38
|
+
|
|
39
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>,
|
|
40
|
+
width: MarkDimension = .automatic, stacking: MarkStackingMethod = .standard) {
|
|
41
|
+
_raw = _RawMark(kind: .bar, x: x.value._plottableValue, y: y.value._plottableValue, width: width)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>,
|
|
45
|
+
width: MarkDimension = .automatic) {
|
|
46
|
+
_raw = _RawMark(kind: .bar, x: x.value._plottableValue,
|
|
47
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue, width: width)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public init<X: Plottable, Y: Plottable>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>, y: PlottableValue<Y>,
|
|
51
|
+
height: MarkDimension = .automatic) {
|
|
52
|
+
_raw = _RawMark(kind: .bar, xStart: xStart.value._plottableValue, xEnd: xEnd.value._plottableValue,
|
|
53
|
+
y: y.value._plottableValue, height: height)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public init<X: Plottable>(x: PlottableValue<X>, width: MarkDimension = .automatic,
|
|
57
|
+
stacking: MarkStackingMethod = .standard) {
|
|
58
|
+
_raw = _RawMark(kind: .bar, x: x.value._plottableValue, width: width)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public init<Y: Plottable>(y: PlottableValue<Y>, height: MarkDimension = .automatic,
|
|
62
|
+
stacking: MarkStackingMethod = .standard) {
|
|
63
|
+
_raw = _RawMark(kind: .bar, y: y.value._plottableValue, height: height)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Accepted for source compatibility; the shim does not stack bars.
|
|
68
|
+
public enum MarkStackingMethod: Hashable, Sendable {
|
|
69
|
+
case standard, normalized, center, unstacked
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// MARK: - LineMark
|
|
73
|
+
|
|
74
|
+
public struct LineMark {
|
|
75
|
+
let _raw: _RawMark
|
|
76
|
+
|
|
77
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>) {
|
|
78
|
+
_raw = _RawMark(kind: .line, x: x.value._plottableValue, y: y.value._plottableValue)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public init<X: Plottable, Y: Plottable, S: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>,
|
|
82
|
+
series: PlottableValue<S>) {
|
|
83
|
+
_raw = _RawMark(kind: .line, x: x.value._plottableValue, y: y.value._plottableValue,
|
|
84
|
+
series: series.value._plottableValue)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// MARK: - AreaMark
|
|
89
|
+
|
|
90
|
+
public struct AreaMark {
|
|
91
|
+
let _raw: _RawMark
|
|
92
|
+
|
|
93
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>,
|
|
94
|
+
stacking: MarkStackingMethod = .standard) {
|
|
95
|
+
_raw = _RawMark(kind: .area, x: x.value._plottableValue, y: y.value._plottableValue)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public init<X: Plottable, Y: Plottable, S: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>,
|
|
99
|
+
series: PlottableValue<S>,
|
|
100
|
+
stacking: MarkStackingMethod = .standard) {
|
|
101
|
+
_raw = _RawMark(kind: .area, x: x.value._plottableValue, y: y.value._plottableValue,
|
|
102
|
+
series: series.value._plottableValue)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) {
|
|
106
|
+
_raw = _RawMark(kind: .area, x: x.value._plottableValue,
|
|
107
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public init<X: Plottable, Y: Plottable, S: Plottable>(x: PlottableValue<X>, yStart: PlottableValue<Y>,
|
|
111
|
+
yEnd: PlottableValue<Y>, series: PlottableValue<S>) {
|
|
112
|
+
_raw = _RawMark(kind: .area, x: x.value._plottableValue,
|
|
113
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue,
|
|
114
|
+
series: series.value._plottableValue)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// MARK: - PointMark
|
|
119
|
+
|
|
120
|
+
public struct PointMark {
|
|
121
|
+
let _raw: _RawMark
|
|
122
|
+
|
|
123
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>) {
|
|
124
|
+
_raw = _RawMark(kind: .point, x: x.value._plottableValue, y: y.value._plottableValue)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// MARK: - RuleMark
|
|
129
|
+
|
|
130
|
+
public struct RuleMark {
|
|
131
|
+
let _raw: _RawMark
|
|
132
|
+
|
|
133
|
+
/// A horizontal rule across the plot.
|
|
134
|
+
public init<Y: Plottable>(y: PlottableValue<Y>) {
|
|
135
|
+
_raw = _RawMark(kind: .rule, y: y.value._plottableValue)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/// A vertical rule across the plot.
|
|
139
|
+
public init<X: Plottable>(x: PlottableValue<X>) {
|
|
140
|
+
_raw = _RawMark(kind: .rule, x: x.value._plottableValue)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) {
|
|
144
|
+
_raw = _RawMark(kind: .rule, x: x.value._plottableValue,
|
|
145
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public init<X: Plottable, Y: Plottable>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>, y: PlottableValue<Y>) {
|
|
149
|
+
_raw = _RawMark(kind: .rule, xStart: xStart.value._plottableValue, xEnd: xEnd.value._plottableValue,
|
|
150
|
+
y: y.value._plottableValue)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// MARK: - RectangleMark
|
|
155
|
+
|
|
156
|
+
public struct RectangleMark {
|
|
157
|
+
let _raw: _RawMark
|
|
158
|
+
|
|
159
|
+
/// A band between two x values across the plot's height.
|
|
160
|
+
public init<X: Plottable>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>) {
|
|
161
|
+
_raw = _RawMark(kind: .rectangle, xStart: xStart.value._plottableValue, xEnd: xEnd.value._plottableValue)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// A band between two y values across the plot's width.
|
|
165
|
+
public init<Y: Plottable>(yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) {
|
|
166
|
+
_raw = _RawMark(kind: .rectangle, yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public init<X: Plottable, Y: Plottable>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>,
|
|
170
|
+
yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) {
|
|
171
|
+
_raw = _RawMark(kind: .rectangle, xStart: xStart.value._plottableValue, xEnd: xEnd.value._plottableValue,
|
|
172
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>,
|
|
176
|
+
width: MarkDimension = .automatic) {
|
|
177
|
+
_raw = _RawMark(kind: .rectangle, x: x.value._plottableValue,
|
|
178
|
+
yStart: yStart.value._plottableValue, yEnd: yEnd.value._plottableValue, width: width)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public init<X: Plottable, Y: Plottable>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>, y: PlottableValue<Y>,
|
|
182
|
+
height: MarkDimension = .automatic) {
|
|
183
|
+
_raw = _RawMark(kind: .rectangle, xStart: xStart.value._plottableValue, xEnd: xEnd.value._plottableValue,
|
|
184
|
+
y: y.value._plottableValue, height: height)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public init<X: Plottable, Y: Plottable>(x: PlottableValue<X>, y: PlottableValue<Y>,
|
|
188
|
+
width: MarkDimension = .automatic, height: MarkDimension = .automatic) {
|
|
189
|
+
_raw = _RawMark(kind: .rectangle, x: x.value._plottableValue, y: y.value._plottableValue,
|
|
190
|
+
width: width, height: height)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
extension BarMark: _SingleMark {}
|
|
195
|
+
|
|
196
|
+
extension LineMark: _SingleMark {}
|
|
197
|
+
|
|
198
|
+
extension AreaMark: _SingleMark {}
|
|
199
|
+
|
|
200
|
+
extension PointMark: _SingleMark {}
|
|
201
|
+
|
|
202
|
+
extension RuleMark: _SingleMark {}
|
|
203
|
+
|
|
204
|
+
extension RectangleMark: _SingleMark {}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Swift Charts' `Plottable` and `PlottableValue`. The shim reduces every
|
|
2
|
+
// plottable value to `_PlottableValue`, a number, a string or a date (seconds
|
|
3
|
+
// since 1970, UTC), so the chart can scale it without knowing the type.
|
|
4
|
+
// `Date` conforms in the `Charts` target, which links Foundation.
|
|
5
|
+
|
|
6
|
+
/// The primitive a chart scales: numbers and dates are continuous, strings
|
|
7
|
+
/// are categories.
|
|
8
|
+
public enum _PlottableValue: Hashable, Sendable {
|
|
9
|
+
case number(Double)
|
|
10
|
+
case string(String)
|
|
11
|
+
/// Seconds since 1970 (UTC).
|
|
12
|
+
case date(Double)
|
|
13
|
+
|
|
14
|
+
/// The position on a continuous scale, if any.
|
|
15
|
+
var continuous: Double? {
|
|
16
|
+
switch self {
|
|
17
|
+
case .number(let v): v
|
|
18
|
+
case .date(let v): v
|
|
19
|
+
case .string: nil
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var isDate: Bool { if case .date = self { true } else { false } }
|
|
24
|
+
var isString: Bool { if case .string = self { true } else { false } }
|
|
25
|
+
|
|
26
|
+
/// A plain text form (categories as themselves, numbers formatted).
|
|
27
|
+
var text: String {
|
|
28
|
+
switch self {
|
|
29
|
+
case .number(let v): _formatChartNumber(v)
|
|
30
|
+
case .string(let s): s
|
|
31
|
+
case .date(let s): _formatChartDate(s, as: .dayMonth)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// A type a chart can place on an axis.
|
|
37
|
+
public protocol Plottable {
|
|
38
|
+
var _plottableValue: _PlottableValue { get }
|
|
39
|
+
init?(_plottableValue: _PlottableValue)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
extension Double: Plottable {
|
|
43
|
+
public var _plottableValue: _PlottableValue { .number(self) }
|
|
44
|
+
public init?(_plottableValue value: _PlottableValue) {
|
|
45
|
+
guard case .number(let v) = value else { return nil }
|
|
46
|
+
self = v
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
extension Float: Plottable {
|
|
51
|
+
public var _plottableValue: _PlottableValue { .number(Double(self)) }
|
|
52
|
+
public init?(_plottableValue value: _PlottableValue) {
|
|
53
|
+
guard case .number(let v) = value else { return nil }
|
|
54
|
+
self = Float(v)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
extension Int: Plottable {
|
|
59
|
+
public var _plottableValue: _PlottableValue { .number(Double(self)) }
|
|
60
|
+
public init?(_plottableValue value: _PlottableValue) {
|
|
61
|
+
guard case .number(let v) = value, v.isFinite, abs(v) < 9e15 else { return nil }
|
|
62
|
+
self = Int(v.rounded())
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
extension String: Plottable {
|
|
67
|
+
public var _plottableValue: _PlottableValue { .string(self) }
|
|
68
|
+
public init?(_plottableValue value: _PlottableValue) {
|
|
69
|
+
guard case .string(let s) = value else { return nil }
|
|
70
|
+
self = s
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// A labelled value for a mark's dimension: `.value("Sales", sale.sales)`.
|
|
75
|
+
public struct PlottableValue<Value: Plottable> {
|
|
76
|
+
public let label: String
|
|
77
|
+
public let value: Value
|
|
78
|
+
|
|
79
|
+
init(label: String, value: Value) {
|
|
80
|
+
self.label = label
|
|
81
|
+
self.value = value
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public static func value(_ labelKey: LocalizedStringKey, _ value: Value) -> PlottableValue<Value> {
|
|
85
|
+
PlottableValue(label: labelKey.key, value: value)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public static func value<S: StringProtocol>(_ label: S, _ value: Value) -> PlottableValue<Value> {
|
|
89
|
+
PlottableValue(label: String(label), value: value)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
var _erased: _LabelledValue { _LabelledValue(label: label, value: value._plottableValue) }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// A `PlottableValue` without its type.
|
|
96
|
+
struct _LabelledValue: Hashable {
|
|
97
|
+
var label: String
|
|
98
|
+
var value: _PlottableValue
|
|
99
|
+
}
|