Skip to content

Commit

Permalink
Initial support for parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
0xWDG committed Jul 25, 2024
1 parent e1f9dfd commit ef49367
Show file tree
Hide file tree
Showing 34 changed files with 379 additions and 112 deletions.
9 changes: 6 additions & 3 deletions Playground/DynamicUI Playground/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ struct ContentView: View {
"children": [
{
"type": "Button",
"title": "Click me"
"title": "Click me",
"eventHandler": "customHandler"
},
{
"type": "Toggle",
"title": "Toggle me"
"title": "Toggle me",
"identifier": "my.toggle.1"
},
{
"type": "Text",
"title": "_Wait_, am i generating views from JSON?"
"title": "_Wait_, am i generating views from JSON?",
"modifiers": {"foregroundStyle":"red","opacity":0.5},
},
{
"type": "Label",
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,29 @@ struct ContentView: View {
[
{
"type": "Text",
"title": "Wait, am i generating views from JSON?"
"title": "Wait, am i generating views from JSON?",
"modifiers": {"foregroundStyle":"red","opacity":0.6}
},
{
"type": "Button",
"title": "Click me",
"eventHandler": "customHandler"
},
{
"type": "Toggle",
"title": "Toggle me",
"identifier": "my.toggle.1"
}
]
""".data(using: .utf8)

var body: some View {
DynamicUI(
json: json
json: json,
callback: { component in
// This contains everything passed as a component (type, title, identifier, ...)
print(component)
}
)
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/DynamicUI/DynamicUI.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//
//
// DynamicUI.swift
// DynamicUI
//
Expand Down
8 changes: 4 additions & 4 deletions Sources/DynamicUI/DynamicUIComponent.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// UIComponent.swift
// DynamicUIComponent.swift
// DynamicUI
//
// Created by Wesley de Groot on 16/04/2024.
Expand Down Expand Up @@ -38,11 +38,11 @@ public struct DynamicUIComponent: Codable, Hashable {
/// Default value of component
public let defaultValue: AnyCodable?

/// Styling of components (not yet used)
public let styling: [[String: AnyCodable]]?
/// Modifiers to components (not yet used)
public let modifiers: [String: AnyCodable]?

/// Parameters of component (not yet used)
public let parameters: [[String: AnyCodable]]?
public let parameters: [String: AnyCodable]?

/// Image URL
public let imageURL: String?
Expand Down
2 changes: 2 additions & 0 deletions Sources/DynamicUI/Extensions/Binding.onChange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import SwiftUI

extension Binding {
/// On Change of a Binding value
///
/// - Parameter handler: Callback handler
///
/// - Returns: Binding
func onChange(_ callback: @escaping (Value) -> Void) -> Binding<Value> {
Binding(
Expand Down
76 changes: 76 additions & 0 deletions Sources/DynamicUI/Extensions/View.modifiers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Modifiers.swift
// DynamicUI
//
// Created by Wesley de Groot on 25/07/2024.
// https://wesleydegroot.nl
//
// https://github.com/0xWDG/DynamicUI
// MIT LICENCE

import SwiftUI

extension View {
/// DynamicUIModifiers
///
/// This function adds modifiers to a DynamicUIView
///
/// - Parameter modifiers: The modifiers to apply
///
/// - Returns: The modified view
public func dynamicUIModifiers(_ modifiers: [String : AnyCodable]?) -> some View {
guard let modifiers = modifiers else {
return AnyView(self)
}

let helper = DynamicUIHelper()
var tempView = AnyView(self)

modifiers.forEach { key, value in
switch key {
case "foregroundStyle":
guard let string = value.toString(),
let color = helper.translateColor(string) else { return }
tempView = AnyView(tempView.foregroundStyle(color))

case "backgroundStyle":
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
guard let string = value.toString(),
let color = helper.translateColor(string) else { return }
tempView = AnyView(tempView.backgroundStyle(color))
}

case "fontWeight":
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
guard let string = value.toString(),
let weight = helper.translateFontWeight(string) else { return }
tempView = AnyView(tempView.fontWeight(.none))
}

case "font":
// guard let color:
tempView = AnyView(tempView.font(.none))

case "frame":
// guard let color:
// minWidth: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealWidth: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, minHeight: /*@START_MENU_TOKEN@*/0/*@END_MENU_TOKEN@*/, idealHeight: /*@START_MENU_TOKEN@*/100/*@END_MENU_TOKEN@*/, maxHeight: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
// width: <#0#> height: <#0#>
tempView = AnyView(tempView)

case "padding":
if let length = value.toInt() {
tempView = AnyView(tempView.padding(CGFloat(integerLiteral: length)))
}

case "opacity":
guard let opacity = value.toDouble() else { break }
tempView = AnyView(tempView.opacity(opacity))

default:
break
}
}

return tempView
}
}
File renamed without changes.
137 changes: 137 additions & 0 deletions Sources/DynamicUI/Helpers/DynamicUIHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//
// DynamicUIHelper.swift
// DynamicUI
//
// Created by Wesley de Groot on 25/07/2024.
// https://wesleydegroot.nl
//
// https://github.com/0xWDG/DynamicUI
// MIT LICENCE

import SwiftUI

/// DynamicUIHelper
///
/// DynamicUIHelper helps to translate Strings to native SwiftUI .context
public class DynamicUIHelper {

/// Translate string colors to native ``Color``.
///
/// - Parameter input: Color as string
///
/// - Returns: SwiftUI ``Color``
public func translateColor(_ input: String) -> Color? {
switch input.lowercased() {
case "red":
return .red

case "orange":
return .orange

case "yellow":
return .yellow

case "green":
return .green

case "mint":
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return .mint
}
return .primary

case "teal":
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return .teal
}
return .primary

case "cyan":
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return .cyan
}
return .primary

case "blue":
return .blue

case "indigo":
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return .indigo
}
return .primary

case "purple":
return .purple

case "pink":
return .pink

case "brown":
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return .brown
}
return .primary

case "white":
return .white

case "gray":
return .gray

case "black":
return .black

case "clear":
return .clear

case "primary":
return .primary

case "secondary":
return .secondary

default:
return .primary
}
}

/// Translate a string font weight to a native ``Font.Weight``
///
/// - Parameter input: Font weight as string
///
/// - Returns: Translated ``Font.Weight``
func translateFontWeight(_ input: String) -> Font.Weight? {
switch input {
case "ultraLight":
return .ultraLight

case "thin":
return .thin

case "light":
return .light

case "regular":
return .regular

case "medium":
return .medium

case "semibold":
return .semibold

case "bold":
return .bold

case "heavy":
return .heavy

case "black":
return .black

default:
return .regular
}
}
}
14 changes: 9 additions & 5 deletions Sources/DynamicUI/Views/DynamicButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import SwiftUI
/// ```json
/// {
/// "type": "Button",
/// "title": "Title"
/// "title": "Title",
/// "modifiers": {
/// "foregroundColor": "blue"
/// }
/// }
/// ```
///
Expand All @@ -28,26 +31,27 @@ public struct DynamicButton: View {
@Environment(\.internalDynamicUIEnvironment)
/// Internal: dynamicUIEnvironment
private var dynamicUIEnvironment

@State
/// The state of the TEMPLATE
private var state: Double

/// The component to display
private let component: DynamicUIComponent

/// Initialize the DynamicTEMPLATE
init(_ component: DynamicUIComponent) {
self.state = component.defaultValue?.toDouble() ?? 0
self.component = component
}

/// Generated body for SwiftUI
public var body: some View {
Button(action: {
dynamicUIEnvironment.callback(component)
}, label: {
Text(component.title ?? "Button")
})
.dynamicUIModifiers(component.modifiers)
}
}
5 changes: 3 additions & 2 deletions Sources/DynamicUI/Views/DynamicDisclosureGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import SwiftUI
/// DynamicUI: DisclosureGroup
///
/// DynamicDisclosureGroup is a SwiftUI View that can be used to display an DisclosureGroup.
///
///
/// JSON Example:
/// ```json
/// {
/// "type": "DisclosureGroup",
/// "children": [ ]
/// }
/// ```
///
///
/// - Note: This is a internal view, you should not use this directly. \
/// Use ``DynamicUI`` instead. this function is public to generate documentation.
public struct DynamicDisclosureGroup: View {
Expand All @@ -45,6 +45,7 @@ public struct DynamicDisclosureGroup: View {
AnyView(dynamicUIEnvironment.buildView(for: children))
}
}
.dynamicUIModifiers(component.modifiers)
#else
DynamicVStack(component)
#endif
Expand Down
7 changes: 4 additions & 3 deletions Sources/DynamicUI/Views/DynamicDivider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,18 @@ public struct DynamicDivider: View {
@Environment(\.internalDynamicUIEnvironment)
/// Internal: dynamicUIEnvironment
private var dynamicUIEnvironment

/// The component to display
private let component: DynamicUIComponent

/// Initialize the DynamicDivider
init(_ component: DynamicUIComponent) {
self.component = component
}

/// Generated body for SwiftUI
public var body: some View {
Divider()
.dynamicUIModifiers(component.modifiers)
}
}
Loading

0 comments on commit ef49367

Please sign in to comment.