Skip to content

v0.4.0 #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
targets: ["LiveViewNativeCharts"]),
],
dependencies: [
.package(url: "https://github.com/liveview-native/liveview-client-swiftui", from: "0.3.0")
.package(url: "https://github.com/liveview-native/liveview-client-swiftui", from: "0.4.0-rc.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
7 changes: 4 additions & 3 deletions Sources/LiveViewNativeCharts/AxisContent/AxisMarks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ import SwiftUI
#if swift(>=5.8)
@_documentation(visibility: public)
#endif
struct AxisMarks<R: RootRegistry>: ComposedAxisContent {
struct AxisMarks<R: RootRegistry>: @preconcurrency ComposedAxisContent {
let element: ElementNode
let context: AxisContentBuilder.Context<R>

@MainActor
var body: some AxisContent {
let preset = (try? element.attributeValue(AxisMarkPreset.self, for: "preset")) ?? .automatic
let position = (try? element.attributeValue(AxisMarkPosition.self, for: "position")) ?? .automatic
Expand Down Expand Up @@ -138,15 +139,15 @@ struct AxisMarks<R: RootRegistry>: ComposedAxisContent {
let children = element.children()
let values = children
.compactMap {
try? $0.attributes
try? $0.attributes()
.first(where: { $0.name == "value" })
.map({ try Double.init(from: $0, on: element) })
}
Charts.AxisMarks(preset: preset, position: position, values: values) { (value: Charts.AxisValue) in
AnyAxisMark(
try! AxisContentBuilder.build(
children.filter({
(try? $0.attributes
(try? $0.attributes()
.first(where: { $0.name == "value" })
.map({ try Double.init(from: $0, on: element) })
)
Expand Down
17 changes: 11 additions & 6 deletions Sources/LiveViewNativeCharts/AxisMark/AxisMarkBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ struct AxisMarkBuilder: ContentBuilder {
case axisValueLabel = "AxisValueLabel"
}

enum ModifierType: ContentModifier {
enum ModifierType: ContentModifier, @preconcurrency Decodable {
typealias Builder = AxisMarkBuilder

case font(FontModifier)
case foregroundStyle(AxisMarkForegroundStyleModifier)
case offset(AxisMarkOffsetModifier)

static func parser(in context: ParseableModifierContext) -> some Parser<Substring.UTF8View, Self> {
OneOf {
FontModifier.parser(in: context).map(Self.font)
AxisMarkForegroundStyleModifier.parser(in: context).map(Self.foregroundStyle)
AxisMarkOffsetModifier.parser(in: context).map(Self.offset)
init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()

if let modifier = try? container.decode(FontModifier.self) {
self = .font(modifier)
} else if let modifier = try? container.decode(AxisMarkForegroundStyleModifier.self) {
self = .foregroundStyle(modifier)
} else {
self = .offset(try container.decode(AxisMarkOffsetModifier.self))
}
}

Expand Down Expand Up @@ -79,6 +83,7 @@ struct AxisMarkBuilder: ContentBuilder {
}
}

@MainActor
protocol ComposedAxisMark {
associatedtype Body: AxisMark
@Charts.AxisMarkBuilder
Expand Down
2 changes: 1 addition & 1 deletion Sources/LiveViewNativeCharts/AxisMark/AxisTick.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct AxisTick: ComposedAxisMark {
#if swift(>=5.8)
@_documentation(visibility: public)
#endif
extension Charts.AxisTick.Length: AttributeDecodable {
extension Charts.AxisTick.Length: @retroactive AttributeDecodable {
public init(from attribute: LiveViewNativeCore.Attribute?) throws {
guard let value = attribute?.value
else { throw AttributeDecodingError.missingAttribute(Self.self) }
Expand Down
1 change: 1 addition & 0 deletions Sources/LiveViewNativeCharts/AxisMark/AxisValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import LiveViewNative
#if swift(>=5.8)
@_documentation(visibility: public)
#endif
@MainActor
struct AxisValue<R: RootRegistry>: ComposedAxisMark {
let element: ElementNode
let context: AxisMarkBuilder.Context<R>
Expand Down
14 changes: 8 additions & 6 deletions Sources/LiveViewNativeCharts/AxisMark/AxisValueLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import LiveViewNativeCore
#if swift(>=5.8)
@_documentation(visibility: public)
#endif
@MainActor
struct AxisValueLabel<R: RootRegistry>: ComposedAxisMark {
let element: ElementNode
let context: AxisMarkBuilder.Context<R>
Expand Down Expand Up @@ -136,6 +137,10 @@ struct AxisValueLabel<R: RootRegistry>: ComposedAxisMark {
fatalError("Unknown format '\(format)'")
}
} else {
let childViews = AxisMarkBuilder.buildChildViews(
of: element,
in: context
)
Charts.AxisValueLabel(
centered: centered,
anchor: anchor,
Expand All @@ -146,16 +151,13 @@ struct AxisValueLabel<R: RootRegistry>: ComposedAxisMark {
horizontalSpacing: horizontalSpacing,
verticalSpacing: verticalSpacing
) {
AxisMarkBuilder.buildChildViews(
of: element,
in: context
)
childViews
}
}
}
}

extension AxisValueLabelCollisionResolution: AttributeDecodable {
extension AxisValueLabelCollisionResolution: @retroactive AttributeDecodable {
public init(from attribute: LiveViewNativeCore.Attribute?) throws {
guard let value = attribute?.value
else { throw AttributeDecodingError.missingAttribute(Self.self) }
Expand All @@ -170,7 +172,7 @@ extension AxisValueLabelCollisionResolution: AttributeDecodable {
}
}

extension AxisValueLabelOrientation: AttributeDecodable {
extension AxisValueLabelOrientation: @retroactive AttributeDecodable {
public init(from attribute: LiveViewNativeCore.Attribute?) throws {
guard let value = attribute?.value
else { throw AttributeDecodingError.missingAttribute(Self.self) }
Expand Down
31 changes: 20 additions & 11 deletions Sources/LiveViewNativeCharts/ChartContentBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct ChartContentBuilder: ContentBuilder {
case ruleMark = "RuleMark"
}

enum ModifierType: ContentModifier {
enum ModifierType: ContentModifier, @preconcurrency Decodable {
typealias Builder = ChartContentBuilder

case alignsMarkStylesWithPlotArea(AlignsMarkStylesWithPlotAreaModifier)
Expand All @@ -37,16 +37,25 @@ struct ChartContentBuilder: ContentBuilder {
case symbolSize(SymbolSizeModifier)
case zIndex(ZIndexModifier)

static func parser(in context: ParseableModifierContext) -> some Parser<Substring.UTF8View, Self> {
OneOf {
AlignsMarkStylesWithPlotAreaModifier.parser(in: context).map(Self.alignsMarkStylesWithPlotArea)
CornerRadiusModifier.parser(in: context).map(Self.cornerRadius)
ForegroundStyleModifier.parser(in: context).map(Self.foregroundStyle)
InterpolationMethodModifier.parser(in: context).map(Self.interpolationMethod)
OffsetModifier.parser(in: context).map(Self.offset)
SymbolModifier.parser(in: context).map(Self.symbol)
SymbolSizeModifier.parser(in: context).map(Self.symbolSize)
ZIndexModifier.parser(in: context).map(Self.zIndex)
init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()

if let modifier = try? container.decode(AlignsMarkStylesWithPlotAreaModifier.self) {
self = .alignsMarkStylesWithPlotArea(modifier)
} else if let modifier = try? container.decode(CornerRadiusModifier.self) {
self = .cornerRadius(modifier)
} else if let modifier = try? container.decode(ForegroundStyleModifier.self) {
self = .foregroundStyle(modifier)
} else if let modifier = try? container.decode(InterpolationMethodModifier.self) {
self = .interpolationMethod(modifier)
} else if let modifier = try? container.decode(OffsetModifier.self) {
self = .offset(modifier)
} else if let modifier = try? container.decode(SymbolModifier.self) {
self = .symbol(modifier)
} else if let modifier = try? container.decode(SymbolSizeModifier.self) {
self = .symbolSize(modifier)
} else {
self = .zIndex(try container.decode(ZIndexModifier.self))
}
}

Expand Down
26 changes: 15 additions & 11 deletions Sources/LiveViewNativeCharts/ChartsRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ public extension Addons {
case chart = "Chart"
}

@MainActor
public static func lookup(_ name: TagName, element: ElementNode) -> some View {
switch name {
case .chart:
Chart<Root>()
}
}

public struct CustomModifier: ViewModifier, ParseableModifierValue {
public struct CustomModifier: ViewModifier, @preconcurrency Decodable {
enum Storage {
case chartBackground(ChartBackgroundModifier<Root>)
case chartLegend(ChartLegendModifier<Root>)
Expand All @@ -38,16 +39,19 @@ public extension Addons {
}
let storage: Storage

public static func parser(in context: ParseableModifierContext) -> some Parser<Substring.UTF8View, Self> {
CustomModifierGroupParser(output: Self.self) {
ChartBackgroundModifier<Root>.parser(in: context).map({ Self(storage: .chartBackground($0)) })
ChartLegendModifier<Root>.parser(in: context).map({ Self(storage: .chartLegend($0)) })
ChartOverlayModifier<Root>.parser(in: context).map({ Self(storage: .chartOverlay($0)) })
ChartXAxisModifier<Root>.parser(in: context).map({ Self(storage: .chartXAxis($0)) })
ChartYAxisModifier<Root>.parser(in: context).map({ Self(storage: .chartYAxis($0)) })
ChartContentBuilder.ModifierType.parser(in: context).map({ _ in Self(storage: .noop) })
AxisContentBuilder.ModifierType.parser(in: context).map({ _ in Self(storage: .noop) })
AxisMarkBuilder.ModifierType.parser(in: context).map({ _ in Self(storage: .noop) })
public init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()

if let modifier = try? container.decode(ChartBackgroundModifier<Root>.self) {
self.storage = .chartBackground(modifier)
} else if let modifier = try? container.decode(ChartLegendModifier<Root>.self) {
self.storage = .chartLegend(modifier)
} else if let modifier = try? container.decode(ChartOverlayModifier<Root>.self) {
self.storage = .chartOverlay(modifier)
} else if let modifier = try? container.decode(ChartXAxisModifier<Root>.self) {
self.storage = .chartXAxis(modifier)
} else {
self.storage = .chartYAxis(try container.decode(ChartYAxisModifier<Root>.self))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import Charts
import LiveViewNative
import LiveViewNativeStylesheet

@ParseableExpression
struct AlignsMarkStylesWithPlotAreaModifier: ContentModifier {
@ASTDecodable("alignsMarkStylesWithPlotArea")
@MainActor
struct AlignsMarkStylesWithPlotAreaModifier: ContentModifier, @preconcurrency Decodable {
typealias Builder = ChartContentBuilder

static let name = "alignsMarkStylesWithPlotArea"
let aligns: AttributeReference<Bool>

let aligns: Bool

init(aligns: Bool) {
init(aligns: AttributeReference<Bool>) {
self.aligns = aligns
}

Expand All @@ -26,6 +25,6 @@ struct AlignsMarkStylesWithPlotAreaModifier: ContentModifier {
on element: ElementNode,
in context: Builder.Context<R>
) -> Builder.Content {
content.alignsMarkStylesWithPlotArea(aligns)
content.alignsMarkStylesWithPlotArea(aligns.resolve(on: element, in: context))
}
}
47 changes: 24 additions & 23 deletions Sources/LiveViewNativeCharts/Modifiers/ChartLegendModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,55 @@ import SwiftUI
import LiveViewNative
import LiveViewNativeStylesheet

@ParseableExpression
struct ChartLegendModifier<R: RootRegistry>: ViewModifier {
static var name: String { "chartLegend" }

@ASTDecodable("chartLegend")
@MainActor
struct ChartLegendModifier<R: RootRegistry>: ViewModifier, @preconcurrency Decodable {
enum Storage {
case visibility(Visibility)
case visibility(Visibility.Resolvable)
case content(
position: AnnotationPosition,
alignment: Alignment?,
spacing: CGFloat?,
position: AttributeReference<AnnotationPosition.Resolvable>,
alignment: Alignment.Resolvable?,
spacing: CGFloat.Resolvable?,
content: ViewReference
)
case position(
position: AnnotationPosition,
alignment: Alignment?,
spacing: CGFloat?
position: AttributeReference<AnnotationPosition.Resolvable>,
alignment: Alignment.Resolvable?,
spacing: CGFloat.Resolvable?
)
}
let storage: Storage

@ObservedElement private var element
@LiveContext<R> private var context

init(_ visibility: Visibility) {
init(_ visibility: Visibility.Resolvable) {
self.storage = .visibility(visibility)
}

@MainActor
init(
position: AnnotationPosition = .automatic,
alignment: Alignment? = nil,
spacing: CGFloat? = nil,
position: AttributeReference<AnnotationPosition.Resolvable>? = nil,
alignment: Alignment.Resolvable? = nil,
spacing: CGFloat.Resolvable? = nil,
content: ViewReference
) {
self.storage = .content(
position: position,
position: position ?? .constant(.automatic),
alignment: alignment,
spacing: spacing,
content: content
)
}

@MainActor
init(
position: AnnotationPosition = .automatic,
alignment: Alignment? = nil,
spacing: CGFloat? = nil
position: AttributeReference<AnnotationPosition.Resolvable>? = nil,
alignment: Alignment.Resolvable? = nil,
spacing: CGFloat.Resolvable? = nil
) {
self.storage = .position(
position: position,
position: position ?? .constant(.automatic),
alignment: alignment,
spacing: spacing
)
Expand All @@ -66,13 +67,13 @@ struct ChartLegendModifier<R: RootRegistry>: ViewModifier {
func body(content: Content) -> some View {
switch self.storage {
case let .visibility(visibility):
content.chartLegend(visibility)
content.chartLegend(visibility.resolve(on: element, in: context))
case let .content(position, alignment, spacing, _content):
content.chartLegend(position: position, alignment: alignment, spacing: spacing) {
content.chartLegend(position: position.resolve(on: element, in: context).resolve(on: element, in: context), alignment: alignment?.resolve(on: element, in: context), spacing: spacing?.resolve(on: element, in: context)) {
_content.resolve(on: element, in: context)
}
case let .position(position, alignment, spacing):
content.chartLegend(position: position, alignment: alignment, spacing: spacing)
content.chartLegend(position: position.resolve(on: element, in: context).resolve(on: element, in: context), alignment: alignment?.resolve(on: element, in: context), spacing: spacing?.resolve(on: element, in: context))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,21 @@ import SwiftUI
import LiveViewNative
import LiveViewNativeStylesheet

@ParseableExpression
struct ChartOverlayModifier<R: RootRegistry>: ViewModifier {
static var name: String { "chartOverlay" }

private let alignment: Alignment
@ASTDecodable("chartOverlay")
struct ChartOverlayModifier<R: RootRegistry>: ViewModifier, @preconcurrency Decodable {
private let alignment: Alignment.Resolvable
private let content: ViewReference

@ObservedElement private var element
@LiveContext<R> private var context

init(alignment: Alignment, content: ViewReference) {
init(alignment: Alignment.Resolvable, content: ViewReference) {
self.alignment = alignment
self.content = content
}

func body(content: Content) -> some View {
content.chartOverlay(alignment: alignment) { _ in
content.chartOverlay(alignment: alignment.resolve(on: element, in: context)) { _ in
self.content.resolve(on: element, in: context)
}
}
Expand Down
Loading