From 9c4454f3bff270e7ae733fa19d55c1866e501367 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 7 Jun 2025 09:36:35 +0530 Subject: [PATCH 1/5] Create swift-format.yml --- .github/workflows/swift-format.yml | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/swift-format.yml diff --git a/.github/workflows/swift-format.yml b/.github/workflows/swift-format.yml new file mode 100644 index 0000000..6c46827 --- /dev/null +++ b/.github/workflows/swift-format.yml @@ -0,0 +1,35 @@ +# This workflow will build a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Swift Format Lint + +on: + pull_request: + paths: + - '**/*.swift' + - '.swift-format.json' + - '.github/workflows/swift-format.yml' + +jobs: + lint: + runs-on: macos-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Swift + uses: swift-actions/setup-swift@v1 + with: + swift-version: "5.9" + + - name: Build swift-format + run: | + git clone https://github.com/apple/swift-format.git + cd swift-format + swift build -c release + echo "$(pwd)/.build/release" >> $GITHUB_PATH + + - name: Run swift-format lint (will fail on issues) + run: | + swift-format lint --recursive Sources Tests From 97538c9ba7ba90efc08c34e85ff679ff2bf8aede Mon Sep 17 00:00:00 2001 From: aaron-demelo Date: Sat, 7 Jun 2025 09:46:00 +0530 Subject: [PATCH 2/5] formatted swift code using apple siwft-format tool --- .swiftformat | 8 - .../BuilderIO/Components/BuilderButton.swift | 129 ++--- .../BuilderIO/Components/BuilderColumns.swift | 77 +-- .../Components/BuilderComponentProtocol.swift | 111 ++-- .../Components/BuilderComponentRegistry.swift | 61 ++- .../Components/BuilderComponentType.swift | 26 +- .../BuilderIO/Components/BuilderImage.swift | 30 +- .../BuilderIO/Components/BuilderModel.swift | 101 ++-- .../BuilderIO/Components/BuilderSection.swift | 71 +-- .../BuilderIO/Components/BuilderText.swift | 38 +- .../Extensions/View+Conditional.swift | 14 +- .../BuilderIO/Helpers/BuilderContentAPI.swift | 129 ++--- .../Helpers/BuilderContentWrapper.swift | 19 +- Sources/BuilderIO/Helpers/CSSConstants.swift | 88 ++-- Sources/BuilderIO/Helpers/CSSStyleUtil.swift | 489 +++++++++--------- Sources/BuilderIO/Helpers/RenderContent.swift | 59 +-- .../BuilderIO/Helpers/ShakeNotification.swift | 9 +- .../BuilderIO/Schemas/BuilderBlockModel.swift | 22 +- .../BuilderIO/Schemas/BuilderContent.swift | 10 +- .../Schemas/BuilderCustomComponent.swift | 371 ++++++------- format.sh | 1 + 21 files changed, 948 insertions(+), 915 deletions(-) delete mode 100644 .swiftformat create mode 100644 format.sh diff --git a/.swiftformat b/.swiftformat deleted file mode 100644 index a29cb13..0000000 --- a/.swiftformat +++ /dev/null @@ -1,8 +0,0 @@ -# .swiftformat - -# Basic rules ---header strip ---wraparguments preserve ---wrapelements preserve ---self remove ---semicolons inline diff --git a/Sources/BuilderIO/Components/BuilderButton.swift b/Sources/BuilderIO/Components/BuilderButton.swift index 6b96ffd..374016f 100644 --- a/Sources/BuilderIO/Components/BuilderButton.swift +++ b/Sources/BuilderIO/Components/BuilderButton.swift @@ -1,79 +1,84 @@ import SwiftUI import WebKit + private typealias CSS = CSSStyleUtil @available(iOS 15.0, macOS 10.15, *) struct BuilderButton: View { - var text: String; - var urlStr: String?; - var openInNewTab: Bool = false; - - var responsiveStyles: [String: String]?; - var buttonAction: ((String, String?) -> Void)?; + var text: String + var urlStr: String? + var openInNewTab: Bool = false + + var responsiveStyles: [String: String]? + var buttonAction: ((String, String?) -> Void)? + + @State private var showWebView = false + + func defaultHandleButtonClick() { + if let str = urlStr, let url = URL(string: str) { + self.showWebView = !openInNewTab + if openInNewTab == true { + UIApplication.shared.open(url) + } + } + } + + var body: some View { + let foregroundColor = CSS.getColor(value: responsiveStyles?["color"]) + let bgColor = CSS.getColor(value: responsiveStyles?["backgroundColor"]) + // let _ = print("BG COLOR BUTTON ----", bgColor); + let cornerRadius = CSS.getFloatValue(cssString: responsiveStyles?["borderRadius"] ?? "0px") + let fontSize = CSS.getFloatValue(cssString: responsiveStyles?["fontSize"] ?? "16px") + let fontWeight = CSS.getFontWeightFromNumber( + value: CSS.getFloatValue(cssString: responsiveStyles?["fontWeight"] ?? "400")) + let horizontalAlignmentFrame = CSS.getFrameFromHorizontalAlignment( + styles: responsiveStyles ?? [:], isText: false) + Button(action: { + if let action = self.buttonAction { + action(text, urlStr ?? "") + } else { + self.defaultHandleButtonClick() + } + + }) { + Text(CSS.getTextWithoutHtml(text)) + .padding( + CSS.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:]) + ) // padding for the button + .font(.system(size: fontSize).weight(fontWeight)) + .frame(alignment: horizontalAlignmentFrame.alignment) - @State private var showWebView = false - - func defaultHandleButtonClick() { - if let str = urlStr, let url = URL(string: str) { - self.showWebView = !openInNewTab - if openInNewTab == true { - UIApplication.shared.open(url) - } - } } - - - var body: some View { - let foregroundColor = CSS.getColor(value: responsiveStyles?["color"]); - let bgColor = CSS.getColor(value: responsiveStyles?["backgroundColor"]); - // let _ = print("BG COLOR BUTTON ----", bgColor); - let cornerRadius = CSS.getFloatValue(cssString:responsiveStyles?["borderRadius"] ?? "0px") - let fontSize = CSS.getFloatValue(cssString: responsiveStyles?["fontSize"] ?? "16px") - let fontWeight = CSS.getFontWeightFromNumber(value: CSS.getFloatValue(cssString: responsiveStyles?["fontWeight"] ?? "400")) - let horizontalAlignmentFrame = CSS.getFrameFromHorizontalAlignment(styles: responsiveStyles ?? [:], isText: false); - Button(action: { - if let action = self.buttonAction { - action(text, urlStr ?? ""); - } else { - self.defaultHandleButtonClick(); - } - - }) { - Text(CSS.getTextWithoutHtml(text)) - .padding(CSS.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:])) // padding for the button - .font(.system(size: fontSize).weight(fontWeight)) - .frame(alignment: horizontalAlignmentFrame.alignment) - - } - .frame(idealWidth: horizontalAlignmentFrame.idealWidth, maxWidth: horizontalAlignmentFrame.maxWidth, alignment: horizontalAlignmentFrame.alignment) - .foregroundColor(foregroundColor) - .background(RoundedRectangle(cornerRadius: cornerRadius).fill(bgColor)) - - - .sheet(isPresented: $showWebView) { - if let str = urlStr, let url = URL(string: str) { - WebView(url: url) - } - } -// .frame(maxWidth: .infinity) - -// .frame(maxWidth: .infinity) + .frame( + idealWidth: horizontalAlignmentFrame.idealWidth, maxWidth: horizontalAlignmentFrame.maxWidth, + alignment: horizontalAlignmentFrame.alignment + ) + .foregroundColor(foregroundColor) + .background(RoundedRectangle(cornerRadius: cornerRadius).fill(bgColor)) + + .sheet(isPresented: $showWebView) { + if let str = urlStr, let url = URL(string: str) { + WebView(url: url) + } } -} + // .frame(maxWidth: .infinity) + // .frame(maxWidth: .infinity) + } +} @available(iOS 15.0, *) struct WebView: UIViewRepresentable { - typealias UIViewType = WKWebView + typealias UIViewType = WKWebView - var url: URL + var url: URL - func makeUIView(context: Context) -> WKWebView { - return WKWebView() - } + func makeUIView(context: Context) -> WKWebView { + return WKWebView() + } - func updateUIView(_ webView: WKWebView, context: Context) { - let request = URLRequest(url: url) - webView.load(request) - } + func updateUIView(_ webView: WKWebView, context: Context) { + let request = URLRequest(url: url) + webView.load(request) + } } diff --git a/Sources/BuilderIO/Components/BuilderColumns.swift b/Sources/BuilderIO/Components/BuilderColumns.swift index 1b3da00..2feb58d 100644 --- a/Sources/BuilderIO/Components/BuilderColumns.swift +++ b/Sources/BuilderIO/Components/BuilderColumns.swift @@ -2,47 +2,48 @@ import Foundation import SwiftUI import SwiftyJSON - struct BuilderColumns: BuilderViewProtocol { - var componentType: BuilderComponentType = .columns - var block: BuilderBlockModel - - var columns: [BuilderContentData] - var space: CGFloat = 0 - var responsiveStyles: [String: String]?; - - init(block: BuilderBlockModel) { - self.block = block - - if - let jsonString = block.component?.options?["columns"].rawString(), - let jsonData = jsonString.data(using: .utf8) - { - let decoder = JSONDecoder() - do { - self.columns = try decoder.decode([BuilderContentData].self, from: jsonData) - } catch { - self.columns = [] - } - } else { - self.columns = [] - } - - self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) - self.space = block.component?.options?["space"].doubleValue ?? 0 + var componentType: BuilderComponentType = .columns + var block: BuilderBlockModel + + var columns: [BuilderContentData] + var space: CGFloat = 0 + var responsiveStyles: [String: String]? + + init(block: BuilderBlockModel) { + self.block = block + + if let jsonString = block.component?.options?["columns"].rawString(), + let jsonData = jsonString.data(using: .utf8) + { + let decoder = JSONDecoder() + do { + self.columns = try decoder.decode([BuilderContentData].self, from: jsonData) + } catch { + self.columns = [] + } + } else { + self.columns = [] } - var body: some View { - let hasBackground = responsiveStyles?["backgroundColor"] != nil - let backgroundColor = CSSStyleUtil.getColor(value: responsiveStyles?["backgroundColor"]) + self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) + self.space = block.component?.options?["space"].doubleValue ?? 0 + } + + var body: some View { + let hasBackground = responsiveStyles?["backgroundColor"] != nil + let backgroundColor = CSSStyleUtil.getColor(value: responsiveStyles?["backgroundColor"]) - VStack(spacing: space) { - ForEach(columns.indices, id: \.self) { index in - BuilderModel(blocks: columns[index].blocks) - } - } - .padding(CSSStyleUtil.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:])) - .background(hasBackground ? backgroundColor : nil) - .padding(CSSStyleUtil.getBoxStyle(boxStyleProperty: "margin", finalStyles: responsiveStyles ?? [:])) + VStack(spacing: space) { + ForEach(columns.indices, id: \.self) { index in + BuilderModel(blocks: columns[index].blocks) + } } + .padding( + CSSStyleUtil.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:]) + ) + .background(hasBackground ? backgroundColor : nil) + .padding( + CSSStyleUtil.getBoxStyle(boxStyleProperty: "margin", finalStyles: responsiveStyles ?? [:])) + } } diff --git a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift index fcbb18e..0a6a7f7 100644 --- a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift +++ b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift @@ -1,78 +1,81 @@ import SwiftUI import SwiftyJSON - protocol BuilderViewProtocol: View { - var componentType: BuilderComponentType { get } - var block : BuilderBlockModel { get } - init(block: BuilderBlockModel); + var componentType: BuilderComponentType { get } + var block: BuilderBlockModel { get } + init(block: BuilderBlockModel) } extension BuilderViewProtocol { - func getFinalStyle(responsiveStyles: BuilderBlockResponsiveStyles?) -> [String: String] { - return CSSStyleUtil.getFinalStyle(responsiveStyles: responsiveStyles) - } + func getFinalStyle(responsiveStyles: BuilderBlockResponsiveStyles?) -> [String: String] { + return CSSStyleUtil.getFinalStyle(responsiveStyles: responsiveStyles) + } } struct BuilderEmptyView: BuilderViewProtocol { - var block: BuilderBlockModel - - var componentType: BuilderComponentType = .empty + var block: BuilderBlockModel - init(block: BuilderBlockModel) { - self.block = block - } + var componentType: BuilderComponentType = .empty - var body: some View { - EmptyView() - } + init(block: BuilderBlockModel) { + self.block = block + } + + var body: some View { + EmptyView() + } } //--------------view modifier to apply responsive styles------------------- - struct ResponsiveStylesBuilderView: ViewModifier { - - var responsiveStyles: [String: String] - let horizontalAlignmentFrame: FrameDimensions; - let foregroundColor: Color? - let cornerRadius: CGFloat? - let fontSize: CGFloat? - let fontWeight: Font.Weight? - - - init(responsiveStyles: [String: String], isText: Bool) { - self.responsiveStyles = responsiveStyles - - foregroundColor = responsiveStyles["color"].map { CSSStyleUtil.getColor(value: $0) } - cornerRadius = responsiveStyles["borderRadius"].map { CSSStyleUtil.getFloatValue(cssString: $0) } - fontSize = responsiveStyles["fontSize"].map { CSSStyleUtil.getFloatValue(cssString: $0) } - fontWeight = responsiveStyles["fontWeight"].map { fontWeight in CSSStyleUtil.getFontWeightFromNumber(value: CSSStyleUtil.getFloatValue(cssString: fontWeight)) } - - horizontalAlignmentFrame = CSSStyleUtil.getFrameFromHorizontalAlignment(styles: responsiveStyles ?? [:], isText: true); - } - - func body(content: Content) -> some View { - content - .frame(idealWidth: horizontalAlignmentFrame.idealWidth, maxWidth: horizontalAlignmentFrame.maxWidth, alignment: horizontalAlignmentFrame.alignment) - .if(fontSize != nil){ view in - view.font(.system(size: fontSize!).weight(fontWeight!)) - } - .if(foregroundColor != nil){ view in - view.foregroundColor(foregroundColor) - } - + var responsiveStyles: [String: String] + let horizontalAlignmentFrame: FrameDimensions + let foregroundColor: Color? + let cornerRadius: CGFloat? + let fontSize: CGFloat? + let fontWeight: Font.Weight? + + init(responsiveStyles: [String: String], isText: Bool) { + self.responsiveStyles = responsiveStyles + + foregroundColor = responsiveStyles["color"].map { CSSStyleUtil.getColor(value: $0) } + cornerRadius = responsiveStyles["borderRadius"].map { + CSSStyleUtil.getFloatValue(cssString: $0) } - + fontSize = responsiveStyles["fontSize"].map { CSSStyleUtil.getFloatValue(cssString: $0) } + fontWeight = responsiveStyles["fontWeight"].map { fontWeight in + CSSStyleUtil.getFontWeightFromNumber(value: CSSStyleUtil.getFloatValue(cssString: fontWeight)) + } + + horizontalAlignmentFrame = CSSStyleUtil.getFrameFromHorizontalAlignment( + styles: responsiveStyles ?? [:], isText: true) + } + + func body(content: Content) -> some View { + content + .frame( + idealWidth: horizontalAlignmentFrame.idealWidth, + maxWidth: horizontalAlignmentFrame.maxWidth, alignment: horizontalAlignmentFrame.alignment + ) + .if(fontSize != nil) { view in + view.font(.system(size: fontSize!).weight(fontWeight!)) + } + .if(foregroundColor != nil) { view in + view.foregroundColor(foregroundColor) + } + + } + } //Make the view modifier available to all Views extension View { - func responsiveStylesBuilderView(responsiveStyles: [String: String], isText: Bool = false) -> some View { - self.modifier(ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles, isText: isText)) - } + func responsiveStylesBuilderView(responsiveStyles: [String: String], isText: Bool = false) + -> some View + { + self.modifier(ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles, isText: isText)) + } } - - - diff --git a/Sources/BuilderIO/Components/BuilderComponentRegistry.swift b/Sources/BuilderIO/Components/BuilderComponentRegistry.swift index 14970b0..57e1794 100644 --- a/Sources/BuilderIO/Components/BuilderComponentRegistry.swift +++ b/Sources/BuilderIO/Components/BuilderComponentRegistry.swift @@ -4,36 +4,35 @@ import SwiftyJSON //BuilderComponentRegistry single instance factory for building preregistered components class BuilderComponentRegistry { - static let shared = BuilderComponentRegistry() - - //Component Registry - private var registry: [BuilderComponentType: any BuilderViewProtocol.Type] = [:] - - // Returns the view for a given block by looking up the component type in the registry. - // Wrapped in anyview as Swift UI does not support dynamic type instantiation directly - func view(for block: BuilderBlockModel) -> AnyView { - let type = BuilderComponentType(rawValue: block.component?.name ?? "") - - guard let viewType = registry[type] else { - // If the type is not registered, return an empty view - return AnyView(BuilderEmptyView(block: block)) - } - - let view = viewType.init(block: block) - return AnyView(view) - } - - //Register default components - func initialize() { - register(type: .text, viewClass: BuilderText.self) - register(type: .image, viewClass: BuilderImage.self) - register(type: .columns, viewClass: BuilderColumns.self) - } - - //Register Custom component - func register(type: BuilderComponentType, viewClass: any BuilderViewProtocol.Type) { - registry[type] = viewClass + static let shared = BuilderComponentRegistry() + + //Component Registry + private var registry: [BuilderComponentType: any BuilderViewProtocol.Type] = [:] + + // Returns the view for a given block by looking up the component type in the registry. + // Wrapped in anyview as Swift UI does not support dynamic type instantiation directly + func view(for block: BuilderBlockModel) -> AnyView { + let type = BuilderComponentType(rawValue: block.component?.name ?? "") + + guard let viewType = registry[type] else { + // If the type is not registered, return an empty view + return AnyView(BuilderEmptyView(block: block)) } - - + + let view = viewType.init(block: block) + return AnyView(view) + } + + //Register default components + func initialize() { + register(type: .text, viewClass: BuilderText.self) + register(type: .image, viewClass: BuilderImage.self) + register(type: .columns, viewClass: BuilderColumns.self) + } + + //Register Custom component + func register(type: BuilderComponentType, viewClass: any BuilderViewProtocol.Type) { + registry[type] = viewClass + } + } diff --git a/Sources/BuilderIO/Components/BuilderComponentType.swift b/Sources/BuilderIO/Components/BuilderComponentType.swift index 9387ad8..b890e70 100644 --- a/Sources/BuilderIO/Components/BuilderComponentType.swift +++ b/Sources/BuilderIO/Components/BuilderComponentType.swift @@ -1,17 +1,17 @@ struct BuilderComponentType: Equatable, Hashable { - let rawValue: String + let rawValue: String - static let text = BuilderComponentType(rawValue: "Text") - static let image = BuilderComponentType(rawValue: "Image") - static let coreButton = BuilderComponentType(rawValue: "Core:Button") - static let columns = BuilderComponentType(rawValue: "Columns") - static let section = BuilderComponentType(rawValue: "Core:Section") - static let box = BuilderComponentType(rawValue: "Box") - static let empty = BuilderComponentType(rawValue: "Empty") + static let text = BuilderComponentType(rawValue: "Text") + static let image = BuilderComponentType(rawValue: "Image") + static let coreButton = BuilderComponentType(rawValue: "Core:Button") + static let columns = BuilderComponentType(rawValue: "Columns") + static let section = BuilderComponentType(rawValue: "Core:Section") + static let box = BuilderComponentType(rawValue: "Box") + static let empty = BuilderComponentType(rawValue: "Empty") + + // Add new types dynamically + static func custom(_ name: String) -> BuilderComponentType { + return BuilderComponentType(rawValue: name) + } - // Add new types dynamically - static func custom(_ name: String) -> BuilderComponentType { - return BuilderComponentType(rawValue: name) - } - } diff --git a/Sources/BuilderIO/Components/BuilderImage.swift b/Sources/BuilderIO/Components/BuilderImage.swift index 458579a..b80131b 100644 --- a/Sources/BuilderIO/Components/BuilderImage.swift +++ b/Sources/BuilderIO/Components/BuilderImage.swift @@ -2,23 +2,21 @@ import SwiftUI import SwiftyJSON struct BuilderImage: BuilderViewProtocol { - var componentType: BuilderComponentType = .image + var componentType: BuilderComponentType = .image - var block: BuilderBlockModel - var responsiveStyles: [String: String]? - var imageURL: URL? - + var block: BuilderBlockModel + var responsiveStyles: [String: String]? + var imageURL: URL? - init(block: BuilderBlockModel) { - self.block = block - self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) - self.imageURL = URL(string: block.component?.options?["image"].string ?? "") - } - - var body: some View { - AsyncImage(url:imageURL).frame(width: 5,height: 5) - EmptyView() - } - + init(block: BuilderBlockModel) { + self.block = block + self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) + self.imageURL = URL(string: block.component?.options?["image"].string ?? "") + } + + var body: some View { + AsyncImage(url: imageURL).frame(width: 5, height: 5) + EmptyView() + } } diff --git a/Sources/BuilderIO/Components/BuilderModel.swift b/Sources/BuilderIO/Components/BuilderModel.swift index 4352f61..a9b0a6a 100644 --- a/Sources/BuilderIO/Components/BuilderModel.swift +++ b/Sources/BuilderIO/Components/BuilderModel.swift @@ -3,58 +3,59 @@ import SwiftyJSON //BuilderBox forms the out layout container for all components mimicking Blocks from response. As blocks can have layout direction of either horizontal or vertical a check is made and layout selected. -struct BuilderModel: View { - - var blocks: [BuilderBlockModel] - var componentType: BuilderComponentType = .box - - init(blocks: [BuilderBlockModel]) { - self.blocks = blocks - } - - - var body: some View { - - ScrollView { - ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in - let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) - - //Calculate the layout direction based on the responsive styles - let isHorizontal = (responsiveStyles["flexDirection"] == CSSConstants.FlexDirection.row.rawValue) - let layout = isHorizontal ? AnyLayout(HStackLayout(alignment: .center)) - : AnyLayout(VStackLayout(alignment: .center)) - - Group { - if isHorizontal { - ScrollView(.horizontal, showsIndicators: false) { - layout { - layoutContent(for: child) - }.modifier(ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles ?? [:], isText: false)) - } - } else { - layout { - layoutContent(for: child) - }.modifier(ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles ?? [:], isText: false)) - } - } - - +struct BuilderModel: View { + + var blocks: [BuilderBlockModel] + var componentType: BuilderComponentType = .box + + init(blocks: [BuilderBlockModel]) { + self.blocks = blocks + } + + var body: some View { + + ScrollView { + ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in + let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) + + //Calculate the layout direction based on the responsive styles + let isHorizontal = + (responsiveStyles["flexDirection"] == CSSConstants.FlexDirection.row.rawValue) + let layout = + isHorizontal + ? AnyLayout(HStackLayout(alignment: .center)) + : AnyLayout(VStackLayout(alignment: .center)) + + Group { + if isHorizontal { + ScrollView(.horizontal, showsIndicators: false) { + layout { + layoutContent(for: child) + }.modifier( + ResponsiveStylesBuilderView( + responsiveStyles: responsiveStyles ?? [:], isText: false)) } + } else { + layout { + layoutContent(for: child) + }.modifier( + ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles ?? [:], isText: false)) + } } + + } } - - @ViewBuilder - private func layoutContent(for child: BuilderBlockModel) -> some View { - if let component = child.component { - BuilderComponentRegistry.shared.view(for: child) - } else if let children = child.children, !children.isEmpty { - BuilderModel(blocks: children) - } else { - Spacer() - } - } - -} - + } + @ViewBuilder + private func layoutContent(for child: BuilderBlockModel) -> some View { + if let component = child.component { + BuilderComponentRegistry.shared.view(for: child) + } else if let children = child.children, !children.isEmpty { + BuilderModel(blocks: children) + } else { + Spacer() + } + } +} diff --git a/Sources/BuilderIO/Components/BuilderSection.swift b/Sources/BuilderIO/Components/BuilderSection.swift index 1cd4933..108b3c3 100644 --- a/Sources/BuilderIO/Components/BuilderSection.swift +++ b/Sources/BuilderIO/Components/BuilderSection.swift @@ -3,39 +3,42 @@ import SwiftUI import SwiftyJSON struct BuilderSection: BuilderViewProtocol { - - var componentType: BuilderComponentType = .section - - var block: BuilderBlockModel - var columns: [BuilderContentData] - var space: CGFloat = 0 - var responsiveStyles: [String: String]?; - - init(block: BuilderBlockModel) { - self.block = block - let decoder = JSONDecoder() - let jsonString = block.component!.options!["columns"].rawString()! - let columns = try! decoder.decode([BuilderContentData].self, from: Data(jsonString.utf8)) - self.columns = columns - self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) - self.space = block.component!.options?["space"].doubleValue ?? 0; - } - - var body: some View { - let hasBgColor = responsiveStyles?["backgroundColor"] != nil; - let bgColor = CSSStyleUtil.getColor(value: responsiveStyles?["backgroundColor"]); - - VStack(spacing: space) { - - ForEach(0...columns.count - 1, id: \.self) { index in - BuilderModel(blocks: columns[index].blocks) - } - - } - .padding(CSSStyleUtil.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:])) - .if(hasBgColor) { view in - view.background(bgColor) - }.padding(CSSStyleUtil.getBoxStyle(boxStyleProperty: "margin", finalStyles: responsiveStyles ?? [:])) - + + var componentType: BuilderComponentType = .section + + var block: BuilderBlockModel + var columns: [BuilderContentData] + var space: CGFloat = 0 + var responsiveStyles: [String: String]? + + init(block: BuilderBlockModel) { + self.block = block + let decoder = JSONDecoder() + let jsonString = block.component!.options!["columns"].rawString()! + let columns = try! decoder.decode([BuilderContentData].self, from: Data(jsonString.utf8)) + self.columns = columns + self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) + self.space = block.component!.options?["space"].doubleValue ?? 0 + } + + var body: some View { + let hasBgColor = responsiveStyles?["backgroundColor"] != nil + let bgColor = CSSStyleUtil.getColor(value: responsiveStyles?["backgroundColor"]) + + VStack(spacing: space) { + + ForEach(0...columns.count - 1, id: \.self) { index in + BuilderModel(blocks: columns[index].blocks) + } + } + .padding( + CSSStyleUtil.getBoxStyle(boxStyleProperty: "padding", finalStyles: responsiveStyles ?? [:]) + ) + .if(hasBgColor) { view in + view.background(bgColor) + }.padding( + CSSStyleUtil.getBoxStyle(boxStyleProperty: "margin", finalStyles: responsiveStyles ?? [:])) + + } } diff --git a/Sources/BuilderIO/Components/BuilderText.swift b/Sources/BuilderIO/Components/BuilderText.swift index 9b343ab..1563b77 100644 --- a/Sources/BuilderIO/Components/BuilderText.swift +++ b/Sources/BuilderIO/Components/BuilderText.swift @@ -2,24 +2,24 @@ import SwiftUI import SwiftyJSON struct BuilderText: BuilderViewProtocol { - var block: BuilderBlockModel - - var componentType: BuilderComponentType = .text - - var responsiveStyles: [String: String]? - var text: String? - - init(block: BuilderBlockModel) { - self.block = block - self.text = block.component?.options?["text"].string ?? "" - self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) - } - - var body: some View { - Text(CSSStyleUtil.getTextWithoutHtml(text ?? "")) - .if(!(self.responsiveStyles?.isEmpty ?? true)){ view in - view.responsiveStylesBuilderView(responsiveStyles: self.responsiveStyles!, isText: true) } - } - + var block: BuilderBlockModel + + var componentType: BuilderComponentType = .text + + var responsiveStyles: [String: String]? + var text: String? + + init(block: BuilderBlockModel) { + self.block = block + self.text = block.component?.options?["text"].string ?? "" + self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) + } + + var body: some View { + Text(CSSStyleUtil.getTextWithoutHtml(text ?? "")) + .if(!(self.responsiveStyles?.isEmpty ?? true)) { view in + view.responsiveStylesBuilderView(responsiveStyles: self.responsiveStyles!, isText: true) + } + } } diff --git a/Sources/BuilderIO/Extensions/View+Conditional.swift b/Sources/BuilderIO/Extensions/View+Conditional.swift index 5d6c042..b8f1f56 100644 --- a/Sources/BuilderIO/Extensions/View+Conditional.swift +++ b/Sources/BuilderIO/Extensions/View+Conditional.swift @@ -2,11 +2,13 @@ import SwiftUI @available(iOS 13.0, *) extension View { - @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { - if condition { - transform(self) - } else { - self - } + @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) + -> some View + { + if condition { + transform(self) + } else { + self } + } } diff --git a/Sources/BuilderIO/Helpers/BuilderContentAPI.swift b/Sources/BuilderIO/Helpers/BuilderContentAPI.swift index 2587dcc..42cdc7f 100644 --- a/Sources/BuilderIO/Helpers/BuilderContentAPI.swift +++ b/Sources/BuilderIO/Helpers/BuilderContentAPI.swift @@ -2,70 +2,75 @@ import Foundation import os public struct BuilderContentAPI { - - public static func isPreviewing() -> Bool { - // Assuming we are only using appetize for previewing in - // the content editor for now - let isAppetize = UserDefaults.standard.bool(forKey: "isAppetize"); - return isAppetize; + + public static func isPreviewing() -> Bool { + // Assuming we are only using appetize for previewing in + // the content editor for now + let isAppetize = UserDefaults.standard.bool(forKey: "isAppetize") + return isAppetize + } + + public static func getContent( + model: String, apiKey: String, url: String, locale: String? = nil, preview: String? = nil, + callback: @escaping ((BuilderContent?) -> Void) + ) { + let encodedUrl = String( + describing: url.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!) + var str = "https://cdn.builder.io/api/v3/content/\(model)" + + let overrideLocale = UserDefaults.standard.string(forKey: "builderLocale") + let overridePreviewContent = UserDefaults.standard.string(forKey: "builderContentId") + print("override preview content", overridePreviewContent ?? "no override") + + let useLocale = overrideLocale ?? locale + let usePreview = overridePreviewContent ?? preview + + if let localPreview = usePreview, !localPreview.isEmpty { + str += "/\(localPreview)" + print("LOCAL PREVIEW YES!") } - - public static func getContent(model: String, apiKey: String, url: String, locale: String? = nil, preview: String? = nil, callback: @escaping ((BuilderContent?)->())) { - let encodedUrl = String(describing: url.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!) - var str = "https://cdn.builder.io/api/v3/content/\(model)" - - let overrideLocale = UserDefaults.standard.string(forKey: "builderLocale"); - let overridePreviewContent = UserDefaults.standard.string(forKey: "builderContentId"); - print("override preview content", overridePreviewContent ?? "no override"); - - let useLocale = overrideLocale ?? locale - let usePreview = overridePreviewContent ?? preview - - if let localPreview = usePreview, !localPreview.isEmpty { - str += "/\(localPreview)" - print("LOCAL PREVIEW YES!"); - } - str += "?apiKey=\(apiKey)&url=\(encodedUrl)" - - if let locale = useLocale, !locale.isEmpty { - str += "&locale=\(locale)" - } - + str += "?apiKey=\(apiKey)&url=\(encodedUrl)" + + if let locale = useLocale, !locale.isEmpty { + str += "&locale=\(locale)" + } + + if let localPreview = usePreview, !localPreview.isEmpty { + str += "&preview=true" + str += "&cachebust=true" + str += "&cachebuster=\(Float.random(in: 1..<10))" + } + + let url = URL(string: str)! + + let session = + !(usePreview ?? "").isEmpty ? URLSession(configuration: .ephemeral) : URLSession.shared + print("Fetching URL = \(url)") + let task = session.dataTask(with: url) { (data, response, error) in + guard let data = data else { + callback(nil) + return + } + let decoder = JSONDecoder() + let jsonString = String(data: data, encoding: .utf8)! + do { if let localPreview = usePreview, !localPreview.isEmpty { - str += "&preview=true" - str += "&cachebust=true" - str += "&cachebuster=\(Float.random(in: 1..<10))" - } - - let url = URL(string: str)! - - let session = !(usePreview ?? "").isEmpty ? URLSession(configuration: .ephemeral) : URLSession.shared - print("Fetching URL = \(url)") - let task = session.dataTask(with: url) {(data, response, error) in - guard let data = data else { - callback(nil) - return - } - let decoder = JSONDecoder() - let jsonString = String(data: data, encoding: .utf8)! - do { - if let localPreview = usePreview, !localPreview.isEmpty { - print("Fetched FROM LOCAL PREVIEW = \(url)") - - let content = try decoder.decode(BuilderContent.self, from: Data(jsonString.utf8)) - callback(content) - } else { - let content = try decoder.decode(BuilderContentList.self, from: Data(jsonString.utf8)) - if content.results.count>0 { - callback(content.results[0]) - } - } - } catch { - print(error) - callback(nil) - } + print("Fetched FROM LOCAL PREVIEW = \(url)") + + let content = try decoder.decode(BuilderContent.self, from: Data(jsonString.utf8)) + callback(content) + } else { + let content = try decoder.decode(BuilderContentList.self, from: Data(jsonString.utf8)) + if content.results.count > 0 { + callback(content.results[0]) + } } - - task.resume() + } catch { + print(error) + callback(nil) + } } + + task.resume() + } } diff --git a/Sources/BuilderIO/Helpers/BuilderContentWrapper.swift b/Sources/BuilderIO/Helpers/BuilderContentWrapper.swift index cdb3119..dadfcae 100644 --- a/Sources/BuilderIO/Helpers/BuilderContentWrapper.swift +++ b/Sources/BuilderIO/Helpers/BuilderContentWrapper.swift @@ -1,15 +1,14 @@ - import Foundation @available(iOS 13.0, *) public class BuilderContentWrapper: ObservableObject { - public var content: BuilderContent? = nil; - public init(content: BuilderContent? = nil) { - self.content = content - } - - public func changeContent(_ newValue: BuilderContent?) { - self.content = newValue; - self.objectWillChange.send(); - } + public var content: BuilderContent? = nil + public init(content: BuilderContent? = nil) { + self.content = content + } + + public func changeContent(_ newValue: BuilderContent?) { + self.content = newValue + self.objectWillChange.send() + } } diff --git a/Sources/BuilderIO/Helpers/CSSConstants.swift b/Sources/BuilderIO/Helpers/CSSConstants.swift index 1afb282..71261b3 100644 --- a/Sources/BuilderIO/Helpers/CSSConstants.swift +++ b/Sources/BuilderIO/Helpers/CSSConstants.swift @@ -1,52 +1,48 @@ - enum CSSConstants { - enum Display: String { - case block, inline, flex, grid, none, inlineBlock - } + enum Display: String { + case block, inline, flex, grid, none, inlineBlock + } + + enum TextAlign: String { + case left, right, center, justify, start, end + } - enum TextAlign: String { - case left, right, center, justify, start, end - } - - enum Align: String { - case auto = "auto" - case flexStart = "flex-start" - case flexEnd = "flex-end" - case center = "center" - case baseline = "baseline" - case stretch = "stretch" - case spaceBetween = "space-between" - case spaceAround = "space-around" - case spaceEvenly = "space-evenly" - } - - enum FlexDirection: String { - case row - case column - } - - enum ResponsiveLayoutProperty: String { - case position - case display - case lineHeight - case width - case height - case maxWidth - case maxHeight - case minWidth - case minHeight - case flexDirection - case marginTop - case marginRight - case marginBottom - case marginLeft - case paddingTop - case paddingRight - case paddingBottom - case paddingLeft - } + enum Align: String { + case auto = "auto" + case flexStart = "flex-start" + case flexEnd = "flex-end" + case center = "center" + case baseline = "baseline" + case stretch = "stretch" + case spaceBetween = "space-between" + case spaceAround = "space-around" + case spaceEvenly = "space-evenly" + } + enum FlexDirection: String { + case row + case column + } + enum ResponsiveLayoutProperty: String { + case position + case display + case lineHeight + case width + case height + case maxWidth + case maxHeight + case minWidth + case minHeight + case flexDirection + case marginTop + case marginRight + case marginBottom + case marginLeft + case paddingTop + case paddingRight + case paddingBottom + case paddingLeft + } - } diff --git a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift index 7d4d6b3..32f4dd0 100644 --- a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift +++ b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift @@ -1,266 +1,281 @@ - import Foundation import SwiftUI enum HorizontalAlignment { - case FullWidth - case Center - case LeftAlign - case RightAlign + case FullWidth + case Center + case LeftAlign + case RightAlign } @available(iOS 13.0, *) struct FrameDimensions { - var minWidth: CGFloat? = nil; - var idealWidth: CGFloat? = nil; - var maxWidth: CGFloat? = nil; - var alignment: Alignment; + var minWidth: CGFloat? = nil + var idealWidth: CGFloat? = nil + var maxWidth: CGFloat? = nil + var alignment: Alignment } class CSSStyleUtil { - /* - Takes the responsive styles from Builder blocks response, and - returns a styles dictionary that lets the small responsive style take - precedence - */ - static func getFinalStyle(responsiveStyles: BuilderBlockResponsiveStyles?) -> [String: String] { - var finalStyle: [String:String] = [:] - finalStyle = finalStyle.merging(responsiveStyles?.large ?? [:]) { (_, new) in new } - finalStyle = finalStyle.merging(responsiveStyles?.medium ?? [:]) { (_, new) in new } - finalStyle = finalStyle.merging(responsiveStyles?.small ?? [:]) { (_, new) in new } - - return finalStyle; - } - - @available(iOS 13.0, *) - static func getColor(value: String?) -> Color { - if value != nil { - if value == "red" { - return Color.red - } else if value == "blue" { - return Color.blue - } else if value == "white" { - return Color.white - } else if value == "gray" { - return Color.gray - } else if value == "black" { - return Color.black - } - - let allMatches = matchingStrings(string: value!, regex: "rgba\\((\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); - if allMatches.count>0 { - let matches = allMatches[0] - - if (matches.count > 3) { - return Color(red: Double(matches[1])! / 255, green: Double(matches[2])! / 255, blue: Double(matches[3])! / 255, opacity: Double(matches[4])!) - } - } else { - if value?.hasPrefix("var") == true { - return hexStringToUIColor(hex: extractHexValueFromVarString(from: value ?? "#fff") ?? "#fff") - } - if value?.hasPrefix("#") == true { - return hexStringToUIColor(hex: value ?? "#fff") - } - } - } else { - return Color.white - } + /* + Takes the responsive styles from Builder blocks response, and + returns a styles dictionary that lets the small responsive style take + precedence + */ + static func getFinalStyle(responsiveStyles: BuilderBlockResponsiveStyles?) -> [String: String] { + var finalStyle: [String: String] = [:] + finalStyle = finalStyle.merging(responsiveStyles?.large ?? [:]) { (_, new) in new } + finalStyle = finalStyle.merging(responsiveStyles?.medium ?? [:]) { (_, new) in new } + finalStyle = finalStyle.merging(responsiveStyles?.small ?? [:]) { (_, new) in new } + + return finalStyle + } + + @available(iOS 13.0, *) + static func getColor(value: String?) -> Color { + if value != nil { + if value == "red" { + return Color.red + } else if value == "blue" { + return Color.blue + } else if value == "white" { return Color.white - } - - static func extractHexValueFromVarString(from input: String) -> String? { - let pattern = "#([0-9A-Fa-f]{6})" // Regular expression pattern to match hex color code - let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive) - let range = NSRange(input.startIndex.. 0 { + let matches = allMatches[0] + + if matches.count > 3 { + return Color( + red: Double(matches[1])! / 255, green: Double(matches[2])! / 255, + blue: Double(matches[3])! / 255, opacity: Double(matches[4])!) + } + } else { + if value?.hasPrefix("var") == true { + return hexStringToUIColor( + hex: extractHexValueFromVarString(from: value ?? "#fff") ?? "#fff") } - - return nil + if value?.hasPrefix("#") == true { + return hexStringToUIColor(hex: value ?? "#fff") + } + } + } else { + return Color.white } - - @available(iOS 13.0, *) - static func hexStringToUIColor (hex:String) -> Color { - var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + return Color.white + } - if (cString.hasPrefix("#")) { - cString.remove(at: cString.startIndex) - } + static func extractHexValueFromVarString(from input: String) -> String? { + let pattern = "#([0-9A-Fa-f]{6})" // Regular expression pattern to match hex color code + let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive) + let range = NSRange(input.startIndex..> 16) / 255.0, - green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, - blue: CGFloat(rgbValue & 0x0000FF) / 255.0 - ) - + return nil + } + + @available(iOS 13.0, *) + static func hexStringToUIColor(hex: String) -> Color { + var cString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased() + + if cString.hasPrefix("#") { + cString.remove(at: cString.startIndex) } - - static func matchingStrings(string: String, regex: String) -> [[String]] { - guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] } - let nsString = string as NSString - let results = regex.matches(in: string, options: [], range: NSMakeRange(0, nsString.length)) - return results.map { result in - (0.. CGFloat { - if (cssString == nil) { - return defaultValue - } - if let regex = try? NSRegularExpression(pattern: "px$") { // TODO: handle decimals - let newString = regex.stringByReplacingMatches(in: cssString ?? "", options: .withTransparentBounds, range: NSMakeRange(0, (cssString ?? "").count ), withTemplate: "") - - if let number = NumberFormatter().number(from: newString) { - let float = CGFloat(truncating: number) - return float - } - - } - - return defaultValue + + if (cString.count) != 6 { + return Color.gray } - - @available(iOS 13.0, *) - static func getBoxStyle(boxStyleProperty: String, finalStyles: [String: String]) -> EdgeInsets { - if finalStyles[boxStyleProperty] != nil { - - // Check if single value, or 2 values or 4 values. - // For now, assume single value and move on - let value = getFloatValue(cssString: finalStyles[boxStyleProperty]) - return EdgeInsets(top: value, leading: value, bottom: value, trailing: value) - } else { - let directions = ["Top", "Left", "Bottom", "Right"]; - for direction in directions { - if finalStyles[boxStyleProperty + direction] != nil { - return EdgeInsets(top: getFloatValue(cssString: finalStyles[boxStyleProperty + "Top"]), - leading: getFloatValue(cssString: finalStyles[boxStyleProperty + "Left"]), - bottom: getFloatValue(cssString: finalStyles[boxStyleProperty + "Bottom"]), - trailing: getFloatValue(cssString: finalStyles[boxStyleProperty + "Right"])) - } - } - } - return EdgeInsets() - + + var rgbValue: UInt64 = 0 + Scanner(string: cString).scanHexInt64(&rgbValue) + + return Color( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0 + ) + + } + + static func matchingStrings(string: String, regex: String) -> [[String]] { + guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] } + let nsString = string as NSString + let results = regex.matches(in: string, options: [], range: NSMakeRange(0, nsString.length)) + return results.map { result in + (0.. String { - if let regex = try? NSRegularExpression(pattern: "<.*?>") { // TODO: handle decimals - let newString = regex.stringByReplacingMatches(in: text, options: .withTransparentBounds, range: NSMakeRange(0, text.count ), withTemplate: "") - - return newString - } - - return "" + } + + static func getFloatValue(cssString: String?, defaultValue: CGFloat = CGFloat(0)) -> CGFloat { + if cssString == nil { + return defaultValue } - - static func getHorizontalAlignmentFromMargin(styles: [String: String]) -> HorizontalAlignment { - let marginLeft = styles["marginLeft"] - let marginRight = styles["marginRight"] - - let isMarginLeftAbsentOrZero = marginLeft == nil || getFloatValue(cssString: marginLeft) == CGFloat(0); - let isMarginRightAbsentOrZero = marginRight == nil || getFloatValue(cssString: marginRight) == CGFloat(0); - let isMarginLeftAuto = marginLeft?.lowercased() == "auto"; - let isMarginRightAuto = marginRight?.lowercased() == "auto"; - - if (isMarginLeftAuto && isMarginRightAuto) { - return HorizontalAlignment.Center; - } else if (isMarginLeftAuto) { - return HorizontalAlignment.RightAlign; - } else if (isMarginRightAuto) { - return HorizontalAlignment.LeftAlign; - } else if (isMarginLeftAbsentOrZero && isMarginRightAbsentOrZero) { - return HorizontalAlignment.FullWidth; - } - // Default full width? - return HorizontalAlignment.FullWidth; + if let regex = try? NSRegularExpression(pattern: "px$") { // TODO: handle decimals + let newString = regex.stringByReplacingMatches( + in: cssString ?? "", options: .withTransparentBounds, + range: NSMakeRange(0, (cssString ?? "").count), withTemplate: "") + + if let number = NumberFormatter().number(from: newString) { + let float = CGFloat(truncating: number) + return float + } + } - - static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) -> HorizontalAlignment { - let alignSelf = styles["alignSelf"]; - if (alignSelf == "center") { - return HorizontalAlignment.FullWidth; - } else if (alignSelf == "auto" || alignSelf == "stretch") { - return HorizontalAlignment.FullWidth; + + return defaultValue + } + + @available(iOS 13.0, *) + static func getBoxStyle(boxStyleProperty: String, finalStyles: [String: String]) -> EdgeInsets { + if finalStyles[boxStyleProperty] != nil { + + // Check if single value, or 2 values or 4 values. + // For now, assume single value and move on + let value = getFloatValue(cssString: finalStyles[boxStyleProperty]) + return EdgeInsets(top: value, leading: value, bottom: value, trailing: value) + } else { + let directions = ["Top", "Left", "Bottom", "Right"] + for direction in directions { + if finalStyles[boxStyleProperty + direction] != nil { + return EdgeInsets( + top: getFloatValue(cssString: finalStyles[boxStyleProperty + "Top"]), + leading: getFloatValue(cssString: finalStyles[boxStyleProperty + "Left"]), + bottom: getFloatValue(cssString: finalStyles[boxStyleProperty + "Bottom"]), + trailing: getFloatValue(cssString: finalStyles[boxStyleProperty + "Right"])) } - return HorizontalAlignment.FullWidth; + } } - - static func getHorizontalAlignment(styles: [String: String]) -> HorizontalAlignment { - if styles["alignSelf"] != nil { - return getHorizontalAlignmentFromAlignSelf(styles: styles) - } else { - return getHorizontalAlignmentFromMargin(styles: styles) - } + return EdgeInsets() + + } + + static func getTextWithoutHtml(_ text: String) -> String { + if let regex = try? NSRegularExpression(pattern: "<.*?>") { // TODO: handle decimals + let newString = regex.stringByReplacingMatches( + in: text, options: .withTransparentBounds, range: NSMakeRange(0, text.count), + withTemplate: "") + + return newString } - - @available(iOS 13.0, *) - static func getFrameFromHorizontalAlignment(styles: [String: String], isText: Bool) -> FrameDimensions { - var horizontalAlignment : HorizontalAlignment; - if (styles["alignSelf"] != nil) { - horizontalAlignment = getHorizontalAlignmentFromAlignSelf(styles: styles) - } else { - horizontalAlignment = getHorizontalAlignmentFromMargin(styles: styles) - } - - if (horizontalAlignment == HorizontalAlignment.FullWidth) { - return FrameDimensions(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, alignment: isText ? .leading : .center) - - } else if (horizontalAlignment == HorizontalAlignment.Center) { - return FrameDimensions(alignment: .center) - } else if (horizontalAlignment == HorizontalAlignment.LeftAlign) { - return FrameDimensions(alignment: .leading) - } else { - // Right align - return FrameDimensions(alignment: .trailing) - } + + return "" + } + + static func getHorizontalAlignmentFromMargin(styles: [String: String]) -> HorizontalAlignment { + let marginLeft = styles["marginLeft"] + let marginRight = styles["marginRight"] + + let isMarginLeftAbsentOrZero = + marginLeft == nil || getFloatValue(cssString: marginLeft) == CGFloat(0) + let isMarginRightAbsentOrZero = + marginRight == nil || getFloatValue(cssString: marginRight) == CGFloat(0) + let isMarginLeftAuto = marginLeft?.lowercased() == "auto" + let isMarginRightAuto = marginRight?.lowercased() == "auto" + + if isMarginLeftAuto && isMarginRightAuto { + return HorizontalAlignment.Center + } else if isMarginLeftAuto { + return HorizontalAlignment.RightAlign + } else if isMarginRightAuto { + return HorizontalAlignment.LeftAlign + } else if isMarginLeftAbsentOrZero && isMarginRightAbsentOrZero { + return HorizontalAlignment.FullWidth } - - @available(iOS 13.0, *) - static func getFontWeightFromNumber(value: CGFloat) -> Font.Weight { - switch (value) { - case _ where value <= 100: - return Font.Weight.thin; - case _ where value <= 200: - return Font.Weight.ultraLight; - case _ where value <= 300: - return Font.Weight.light; - case _ where value <= 400: - return Font.Weight.regular; - case _ where value <= 500: - return Font.Weight.medium; - case _ where value <= 600: - return Font.Weight.semibold; - case _ where value <= 700: - return Font.Weight.bold; - case _ where value <= 800: - return Font.Weight.heavy; - default: - return Font.Weight.black; - } + // Default full width? + return HorizontalAlignment.FullWidth + } + + static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) -> HorizontalAlignment { + let alignSelf = styles["alignSelf"] + if alignSelf == "center" { + return HorizontalAlignment.FullWidth + } else if alignSelf == "auto" || alignSelf == "stretch" { + return HorizontalAlignment.FullWidth + } + return HorizontalAlignment.FullWidth + } + + static func getHorizontalAlignment(styles: [String: String]) -> HorizontalAlignment { + if styles["alignSelf"] != nil { + return getHorizontalAlignmentFromAlignSelf(styles: styles) + } else { + return getHorizontalAlignmentFromMargin(styles: styles) + } + } + + @available(iOS 13.0, *) + static func getFrameFromHorizontalAlignment(styles: [String: String], isText: Bool) + -> FrameDimensions + { + var horizontalAlignment: HorizontalAlignment + if styles["alignSelf"] != nil { + horizontalAlignment = getHorizontalAlignmentFromAlignSelf(styles: styles) + } else { + horizontalAlignment = getHorizontalAlignmentFromMargin(styles: styles) } - + + if horizontalAlignment == HorizontalAlignment.FullWidth { + return FrameDimensions( + minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, + alignment: isText ? .leading : .center) + + } else if horizontalAlignment == HorizontalAlignment.Center { + return FrameDimensions(alignment: .center) + } else if horizontalAlignment == HorizontalAlignment.LeftAlign { + return FrameDimensions(alignment: .leading) + } else { + // Right align + return FrameDimensions(alignment: .trailing) + } + } + + @available(iOS 13.0, *) + static func getFontWeightFromNumber(value: CGFloat) -> Font.Weight { + switch value { + case _ where value <= 100: + return Font.Weight.thin + case _ where value <= 200: + return Font.Weight.ultraLight + case _ where value <= 300: + return Font.Weight.light + case _ where value <= 400: + return Font.Weight.regular + case _ where value <= 500: + return Font.Weight.medium + case _ where value <= 600: + return Font.Weight.semibold + case _ where value <= 700: + return Font.Weight.bold + case _ where value <= 800: + return Font.Weight.heavy + default: + return Font.Weight.black + } + } + } diff --git a/Sources/BuilderIO/Helpers/RenderContent.swift b/Sources/BuilderIO/Helpers/RenderContent.swift index 8f7041a..9808553 100644 --- a/Sources/BuilderIO/Helpers/RenderContent.swift +++ b/Sources/BuilderIO/Helpers/RenderContent.swift @@ -2,39 +2,40 @@ import SwiftUI @available(iOS 15.0, macOS 10.15, *) public struct RenderContent: View { - static var registered = false; - var content: BuilderContent; - var apiKey: String; + static var registered = false + var content: BuilderContent + var apiKey: String - public init(content: BuilderContent, apiKey: String, clickActionHandler: ((String, String?) -> Void)? = nil) { - self.content = content - self.apiKey = apiKey - - if (!RenderContent.registered) { - BuilderComponentRegistry.shared.initialize() - RenderContent.registered = true - } - + public init( + content: BuilderContent, apiKey: String, clickActionHandler: ((String, String?) -> Void)? = nil + ) { + self.content = content + self.apiKey = apiKey + + if !RenderContent.registered { + BuilderComponentRegistry.shared.initialize() + RenderContent.registered = true } - - - public var body: some View { - VStack(alignment: .leading, spacing: 0) { - BuilderModel(blocks: content.data.blocks) - .onAppear{ - if (!BuilderContentAPI.isPreviewing()) { - sendTrackingPixel() - } - } + + } + + public var body: some View { + VStack(alignment: .leading, spacing: 0) { + BuilderModel(blocks: content.data.blocks) + .onAppear { + if !BuilderContentAPI.isPreviewing() { + sendTrackingPixel() + } } - .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity) - .background(Color.white) } + .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity) + .background(Color.white) + } - func sendTrackingPixel() { - if let url = URL(string: "https://cdn.builder.io/api/v1/pixel?apiKey=\(self.apiKey)") { - let task = URLSession.shared.dataTask(with: url) - task.resume() - } + func sendTrackingPixel() { + if let url = URL(string: "https://cdn.builder.io/api/v1/pixel?apiKey=\(self.apiKey)") { + let task = URLSession.shared.dataTask(with: url) + task.resume() } + } } diff --git a/Sources/BuilderIO/Helpers/ShakeNotification.swift b/Sources/BuilderIO/Helpers/ShakeNotification.swift index b589210..9b6ae54 100644 --- a/Sources/BuilderIO/Helpers/ShakeNotification.swift +++ b/Sources/BuilderIO/Helpers/ShakeNotification.swift @@ -1,12 +1,11 @@ - import Foundation import UIKit public let deviceDidShakeNotification = NSNotification.Name("BuilderIOShakeNotification") extension UIWindow { - open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { - super.motionEnded(motion, with: event) - NotificationCenter.default.post(name: deviceDidShakeNotification, object: event) - } + open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { + super.motionEnded(motion, with: event) + NotificationCenter.default.post(name: deviceDidShakeNotification, object: event) + } } diff --git a/Sources/BuilderIO/Schemas/BuilderBlockModel.swift b/Sources/BuilderIO/Schemas/BuilderBlockModel.swift index e30bdd4..3bb950d 100644 --- a/Sources/BuilderIO/Schemas/BuilderBlockModel.swift +++ b/Sources/BuilderIO/Schemas/BuilderBlockModel.swift @@ -2,21 +2,21 @@ import SwiftyJSON // Schema for Builder blocks public struct BuilderBlockModel: Codable { - var id: String - var properties: [String: String]? = [:] - var bindings: [String: String]? = [:] - var children: [BuilderBlockModel]? = [] - var component: BuilderBlockComponent? = nil - var responsiveStyles: BuilderBlockResponsiveStyles? = BuilderBlockResponsiveStyles() // for inner style of the component + var id: String + var properties: [String: String]? = [:] + var bindings: [String: String]? = [:] + var children: [BuilderBlockModel]? = [] + var component: BuilderBlockComponent? = nil + var responsiveStyles: BuilderBlockResponsiveStyles? = BuilderBlockResponsiveStyles() // for inner style of the component } public struct BuilderBlockComponent: Codable { - var name: String - var options: JSON? = [:] + var name: String + var options: JSON? = [:] } public struct BuilderBlockResponsiveStyles: Codable { - var large: [String: String]? = [:] - var medium: [String: String]? = [:] - var small: [String: String]? = [:] + var large: [String: String]? = [:] + var medium: [String: String]? = [:] + var small: [String: String]? = [:] } diff --git a/Sources/BuilderIO/Schemas/BuilderContent.swift b/Sources/BuilderIO/Schemas/BuilderContent.swift index d593514..21a6a18 100644 --- a/Sources/BuilderIO/Schemas/BuilderContent.swift +++ b/Sources/BuilderIO/Schemas/BuilderContent.swift @@ -1,14 +1,14 @@ // Schema for Builder content public struct BuilderContent: Codable { - var data = BuilderContentData() - var screenshot: String? = nil - var ownerId: String? = nil + var data = BuilderContentData() + var screenshot: String? = nil + var ownerId: String? = nil } struct BuilderContentData: Codable { - var blocks: [BuilderBlockModel] = [] + var blocks: [BuilderBlockModel] = [] } struct BuilderContentList: Codable { - var results: [BuilderContent] = [] + var results: [BuilderContent] = [] } diff --git a/Sources/BuilderIO/Schemas/BuilderCustomComponent.swift b/Sources/BuilderIO/Schemas/BuilderCustomComponent.swift index e3b661a..8a841ab 100644 --- a/Sources/BuilderIO/Schemas/BuilderCustomComponent.swift +++ b/Sources/BuilderIO/Schemas/BuilderCustomComponent.swift @@ -1,192 +1,205 @@ import Foundation public struct BuilderInput: Encodable { - public init(name: String, friendlyName: String? = nil, description: String? = nil, defaultValue: BuilderInput.DefaultValue? = nil, type: String, required: Bool? = nil, subFields: [BuilderInput]? = nil, helperText: String? = nil, allowedFileTypes: [String]? = nil, imageHeight: Int? = nil, imageWidth: Int? = nil, mediaHeight: Int? = nil, mediaWidth: Int? = nil, hideFromUI: Bool? = nil, modelId: String? = nil, max: Int? = nil, min: Int? = nil, step: Int? = nil, broadcast: Bool? = nil, bubble: Bool? = nil, localized: Bool? = nil, `enum`: [BuilderInput.EnumValue]? = nil, advanced: Bool? = nil, onChange: String? = nil, code: Bool? = nil, richText: Bool? = nil, showIf: String? = nil, copyOnAdd: Bool? = nil, model: String? = nil) { - self.name = name - self.friendlyName = friendlyName - self.description = description - self.defaultValue = defaultValue - self.type = type - self.required = required - self.subFields = subFields - self.helperText = helperText - self.allowedFileTypes = allowedFileTypes - self.imageHeight = imageHeight - self.imageWidth = imageWidth - self.mediaHeight = mediaHeight - self.mediaWidth = mediaWidth - self.hideFromUI = hideFromUI - self.modelId = modelId - self.max = max - self.min = min - self.step = step - self.broadcast = broadcast - self.bubble = bubble - self.localized = localized - self.`enum` = `enum` - self.advanced = advanced - self.onChange = onChange - self.code = code - self.richText = richText - self.showIf = showIf - self.copyOnAdd = copyOnAdd - self.model = model - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(name, forKey: .name) - try container.encodeIfPresent(friendlyName, forKey: .friendlyName) - try container.encodeIfPresent(description, forKey: .description) - try container.encodeIfPresent(defaultValue, forKey: .defaultValue) - try container.encode(type, forKey: .type) - try container.encodeIfPresent(required, forKey: .required) - try container.encodeIfPresent(subFields, forKey: .subFields) - try container.encodeIfPresent(helperText, forKey: .helperText) - try container.encodeIfPresent(allowedFileTypes, forKey: .allowedFileTypes) - try container.encodeIfPresent(imageHeight, forKey: .imageHeight) - try container.encodeIfPresent(imageWidth, forKey: .imageWidth) - try container.encodeIfPresent(mediaHeight, forKey: .mediaHeight) - try container.encodeIfPresent(mediaWidth, forKey: .mediaWidth) - try container.encodeIfPresent(hideFromUI, forKey: .hideFromUI) - try container.encodeIfPresent(modelId, forKey: .modelId) - try container.encodeIfPresent(max, forKey: .max) - try container.encodeIfPresent(min, forKey: .min) - try container.encodeIfPresent(step, forKey: .step) - try container.encodeIfPresent(broadcast, forKey: .broadcast) - try container.encodeIfPresent(bubble, forKey: .bubble) - try container.encodeIfPresent(localized, forKey: .localized) - try container.encodeIfPresent(`enum`, forKey: .enum) - try container.encodeIfPresent(advanced, forKey: .advanced) - try container.encodeIfPresent(onChange, forKey: .onChange) - try container.encodeIfPresent(code, forKey: .code) - try container.encodeIfPresent(richText, forKey: .richText) - try container.encodeIfPresent(showIf, forKey: .showIf) - try container.encodeIfPresent(copyOnAdd, forKey: .copyOnAdd) - try container.encodeIfPresent(model, forKey: .model) - } - + public init( + name: String, friendlyName: String? = nil, description: String? = nil, + defaultValue: BuilderInput.DefaultValue? = nil, type: String, required: Bool? = nil, + subFields: [BuilderInput]? = nil, helperText: String? = nil, allowedFileTypes: [String]? = nil, + imageHeight: Int? = nil, imageWidth: Int? = nil, mediaHeight: Int? = nil, + mediaWidth: Int? = nil, hideFromUI: Bool? = nil, modelId: String? = nil, max: Int? = nil, + min: Int? = nil, step: Int? = nil, broadcast: Bool? = nil, bubble: Bool? = nil, + localized: Bool? = nil, `enum`: [BuilderInput.EnumValue]? = nil, advanced: Bool? = nil, + onChange: String? = nil, code: Bool? = nil, richText: Bool? = nil, showIf: String? = nil, + copyOnAdd: Bool? = nil, model: String? = nil + ) { + self.name = name + self.friendlyName = friendlyName + self.description = description + self.defaultValue = defaultValue + self.type = type + self.required = required + self.subFields = subFields + self.helperText = helperText + self.allowedFileTypes = allowedFileTypes + self.imageHeight = imageHeight + self.imageWidth = imageWidth + self.mediaHeight = mediaHeight + self.mediaWidth = mediaWidth + self.hideFromUI = hideFromUI + self.modelId = modelId + self.max = max + self.min = min + self.step = step + self.broadcast = broadcast + self.bubble = bubble + self.localized = localized + self.`enum` = `enum` + self.advanced = advanced + self.onChange = onChange + self.code = code + self.richText = richText + self.showIf = showIf + self.copyOnAdd = copyOnAdd + self.model = model + } - public enum CodingKeys: String, CodingKey { - case name - case friendlyName - case description - case defaultValue - case type - case required - case subFields - case helperText - case allowedFileTypes - case imageHeight - case imageWidth - case mediaHeight - case mediaWidth - case hideFromUI - case modelId - case max - case min - case step - case broadcast - case bubble - case localized - case `enum` - case advanced - case onChange - case code - case richText - case showIf - case copyOnAdd - case model - } + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(name, forKey: .name) + try container.encodeIfPresent(friendlyName, forKey: .friendlyName) + try container.encodeIfPresent(description, forKey: .description) + try container.encodeIfPresent(defaultValue, forKey: .defaultValue) + try container.encode(type, forKey: .type) + try container.encodeIfPresent(required, forKey: .required) + try container.encodeIfPresent(subFields, forKey: .subFields) + try container.encodeIfPresent(helperText, forKey: .helperText) + try container.encodeIfPresent(allowedFileTypes, forKey: .allowedFileTypes) + try container.encodeIfPresent(imageHeight, forKey: .imageHeight) + try container.encodeIfPresent(imageWidth, forKey: .imageWidth) + try container.encodeIfPresent(mediaHeight, forKey: .mediaHeight) + try container.encodeIfPresent(mediaWidth, forKey: .mediaWidth) + try container.encodeIfPresent(hideFromUI, forKey: .hideFromUI) + try container.encodeIfPresent(modelId, forKey: .modelId) + try container.encodeIfPresent(max, forKey: .max) + try container.encodeIfPresent(min, forKey: .min) + try container.encodeIfPresent(step, forKey: .step) + try container.encodeIfPresent(broadcast, forKey: .broadcast) + try container.encodeIfPresent(bubble, forKey: .bubble) + try container.encodeIfPresent(localized, forKey: .localized) + try container.encodeIfPresent(`enum`, forKey: .enum) + try container.encodeIfPresent(advanced, forKey: .advanced) + try container.encodeIfPresent(onChange, forKey: .onChange) + try container.encodeIfPresent(code, forKey: .code) + try container.encodeIfPresent(richText, forKey: .richText) + try container.encodeIfPresent(showIf, forKey: .showIf) + try container.encodeIfPresent(copyOnAdd, forKey: .copyOnAdd) + try container.encodeIfPresent(model, forKey: .model) + } + + public enum CodingKeys: String, CodingKey { + case name + case friendlyName + case description + case defaultValue + case type + case required + case subFields + case helperText + case allowedFileTypes + case imageHeight + case imageWidth + case mediaHeight + case mediaWidth + case hideFromUI + case modelId + case max + case min + case step + case broadcast + case bubble + case localized + case `enum` + case advanced + case onChange + case code + case richText + case showIf + case copyOnAdd + case model + } - var name: String - var friendlyName: String? - var description: String? - var defaultValue: DefaultValue? - var type: String - var required: Bool? - var subFields: [BuilderInput]? + var name: String + var friendlyName: String? + var description: String? + var defaultValue: DefaultValue? + var type: String + var required: Bool? + var subFields: [BuilderInput]? + var helperText: String? + var allowedFileTypes: [String]? + var imageHeight: Int? + var imageWidth: Int? + var mediaHeight: Int? + var mediaWidth: Int? + var hideFromUI: Bool? + var modelId: String? + var max: Int? + var min: Int? + var step: Int? + var broadcast: Bool? + var bubble: Bool? + var localized: Bool? + var `enum`: [EnumValue]? + var advanced: Bool? + var onChange: String? + var code: Bool? + var richText: Bool? + var showIf: String? + var copyOnAdd: Bool? + var model: String? + + public struct EnumValue: Encodable { + var label: String + var value: String var helperText: String? - var allowedFileTypes: [String]? - var imageHeight: Int? - var imageWidth: Int? - var mediaHeight: Int? - var mediaWidth: Int? - var hideFromUI: Bool? - var modelId: String? - var max: Int? - var min: Int? - var step: Int? - var broadcast: Bool? - var bubble: Bool? - var localized: Bool? - var `enum`: [EnumValue]? - var advanced: Bool? - var onChange: String? - var code: Bool? - var richText: Bool? - var showIf: String? - var copyOnAdd: Bool? - var model: String? - - public struct EnumValue: Encodable { - var label: String - var value: String - var helperText: String? - } - - public enum DefaultValue: Encodable { - case string(String) - case int(Int) - case hash([String: Any]) - case stringArray([String]) - case dictionaryArray([[String: Any]]) + } + + public enum DefaultValue: Encodable { + case string(String) + case int(Int) + case hash([String: Any]) + case stringArray([String]) + case dictionaryArray([[String: Any]]) - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - switch self { - case .string(let value): - try container.encode(value) - case .int(let value): - try container.encode(value) - case .hash(let value): - // Encode the dictionary manually as JSON data - let jsonData = try JSONSerialization.data(withJSONObject: value, options: []) - try container.encode(jsonData) - case .stringArray(let value): - try container.encode(value) - case .dictionaryArray(let value): - let jsonData = try JSONSerialization.data(withJSONObject: value, options: []) - try container.encode(jsonData) - } - } + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .string(let value): + try container.encode(value) + case .int(let value): + try container.encode(value) + case .hash(let value): + // Encode the dictionary manually as JSON data + let jsonData = try JSONSerialization.data(withJSONObject: value, options: []) + try container.encode(jsonData) + case .stringArray(let value): + try container.encode(value) + case .dictionaryArray(let value): + let jsonData = try JSONSerialization.data(withJSONObject: value, options: []) + try container.encode(jsonData) + } } + } } public struct BuilderCustomComponent: Encodable { - public init(name: String, docsLink: String? = nil, image: String? = nil, screenshot: String? = nil, `override`: Bool? = nil, inputs: [BuilderInput]? = nil, defaultStyles: [String : String]? = nil, noWrap: Bool? = nil, hideFromInsertMenu: Bool? = nil, models: [String]? = nil) { - self.name = name - self.docsLink = docsLink - self.image = image - self.screenshot = screenshot - self.`override` = `override` - self.inputs = inputs - self.defaultStyles = defaultStyles - self.noWrap = noWrap - self.hideFromInsertMenu = hideFromInsertMenu - self.models = models - } - - var name: String - var docsLink: String? - var image: String? - var screenshot: String? - var `override`: Bool? - var inputs: [BuilderInput]? - var defaultStyles: [String: String]? - var noWrap: Bool? - var hideFromInsertMenu: Bool? - var models: [String]? + public init( + name: String, docsLink: String? = nil, image: String? = nil, screenshot: String? = nil, + `override`: Bool? = nil, inputs: [BuilderInput]? = nil, defaultStyles: [String: String]? = nil, + noWrap: Bool? = nil, hideFromInsertMenu: Bool? = nil, models: [String]? = nil + ) { + self.name = name + self.docsLink = docsLink + self.image = image + self.screenshot = screenshot + self.`override` = `override` + self.inputs = inputs + self.defaultStyles = defaultStyles + self.noWrap = noWrap + self.hideFromInsertMenu = hideFromInsertMenu + self.models = models + } + + var name: String + var docsLink: String? + var image: String? + var screenshot: String? + var `override`: Bool? + var inputs: [BuilderInput]? + var defaultStyles: [String: String]? + var noWrap: Bool? + var hideFromInsertMenu: Bool? + var models: [String]? } diff --git a/format.sh b/format.sh new file mode 100644 index 0000000..bb1a795 --- /dev/null +++ b/format.sh @@ -0,0 +1 @@ +swift-format format --in-place --recursive Sources \ No newline at end of file From b7415c2188477dd3487bddbe5837ffb5b5cb023e Mon Sep 17 00:00:00 2001 From: aaron-demelo Date: Mon, 9 Jun 2025 15:36:14 +0530 Subject: [PATCH 3/5] Implemented Alignment Margin and Padding for block layout --- .../BuilderIO/Components/BuilderBlock.swift | 149 ++++++++++++++++++ .../BuilderIO/Components/BuilderColumns.swift | 2 +- .../Components/BuilderComponentProtocol.swift | 7 +- .../BuilderIO/Components/BuilderModel.swift | 61 ------- .../BuilderIO/Components/BuilderSection.swift | 2 +- Sources/BuilderIO/Helpers/CSSStyleUtil.swift | 32 ++-- Sources/BuilderIO/Helpers/RenderContent.swift | 18 +-- 7 files changed, 176 insertions(+), 95 deletions(-) create mode 100644 Sources/BuilderIO/Components/BuilderBlock.swift delete mode 100644 Sources/BuilderIO/Components/BuilderModel.swift diff --git a/Sources/BuilderIO/Components/BuilderBlock.swift b/Sources/BuilderIO/Components/BuilderBlock.swift new file mode 100644 index 0000000..410eaa0 --- /dev/null +++ b/Sources/BuilderIO/Components/BuilderBlock.swift @@ -0,0 +1,149 @@ +import SwiftUI +import SwiftyJSON + +//BuilderBlock forms the out layout container for all components mimicking Blocks from response. As blocks can have layout direction of either horizontal or vertical a check is made and layout selected. + +struct BuilderBlock: View { + + var blocks: [BuilderBlockModel] + var componentType: BuilderComponentType = .box + + init(blocks: [BuilderBlockModel]) { + self.blocks = blocks + } + + var body: some View { + + ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in + let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) + + BuilderBlockLayout(responsiveStyles: responsiveStyles ?? [:]) { + if let component = child.component { + BuilderComponentRegistry.shared.view(for: child) + } else if let children = child.children, !children.isEmpty { + BuilderBlock(blocks: children) + } else { + Spacer() + } + } + + } + + } + +} + + + +struct BuilderBlockLayout: View +{ + let responsiveStyles: [String: String] + @ViewBuilder let content: () -> Content + + var body: some View { + + // 1. Extract basic layout parameters + let direction = responsiveStyles["flexDirection"] ?? "column" + let wrap = responsiveStyles["flexWrap"] == "wrap" + let scroll = responsiveStyles["overflow"] == "auto" && direction == "row" + + let justify = responsiveStyles["justifyContent"] + let alignItems = responsiveStyles["alignItems"] + + let marginLeft = responsiveStyles["marginLeft"]?.lowercased() + let marginRight = responsiveStyles["marginRight"]?.lowercased() + + let spacing = extractPixels(responsiveStyles["gap"]) ?? 0 + let padding = extractEdgeInsets(for:"padding", from: responsiveStyles) + let margin = extractEdgeInsets(for:"margin", from: responsiveStyles) + + let minHeight = extractPixels(responsiveStyles["minHeight"]) + let maxHeight = extractPixels(responsiveStyles["maxHeight"]) + let minWidth = extractPixels(responsiveStyles["minWidth"]) + let maxWidth = extractPixels(responsiveStyles["maxWidth"]) + + let borderRadius = extractPixels(responsiveStyles["borderRadius"]) ?? 0 + + // 2. Build base layout (wrapped or not) + let layoutView: some View = Group { + if wrap { + LazyVGrid( + columns: [GridItem(.adaptive(minimum: 100), spacing: spacing)], + alignment: BuilderBlockLayout.horizontalAlignment(marginsLeft:marginLeft, marginsRight:marginRight, justify: justify, alignItems: alignItems), + spacing: spacing, + content: content + ) + } else if direction == "row" { + HStack(alignment:BuilderBlockLayout.verticalAlignment( justify: justify, alignItems: alignItems),spacing: spacing) { + content() + } + } else { + VStack(alignment: BuilderBlockLayout.horizontalAlignment(marginsLeft:marginLeft, marginsRight:marginRight, justify: justify, alignItems: alignItems), spacing: spacing) { + content() + } + } + } + + // 3. Wrap in scroll if overflow: auto + let scrollableView: some View = Group { + if scroll { + ScrollView(.horizontal, showsIndicators: false) { + layoutView + } + } else { + layoutView + } + } + + // 4. Apply visual and layout modifiers + return scrollableView + .padding(padding) + .frame(minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight) + .padding(margin) //margin + .cornerRadius(borderRadius) + } + + + func extractPixels(_ value: String?) -> CGFloat? { + guard let value = value?.replacingOccurrences(of: "px", with: ""), + let number = Double(value) else { return nil } + return CGFloat(number) + } + + func extractEdgeInsets(for insetType:String, from styles: [String: String]) -> EdgeInsets { + + return EdgeInsets( + top: extractPixels(styles["\(insetType)Top"]) ?? 0, + leading: extractPixels(styles["\(insetType)Left"]) ?? 0, + bottom: extractPixels(styles["\(insetType)Bottom"]) ?? 0, + trailing: extractPixels(styles["\(insetType)Right"]) ?? 0 + ) + } + + + static func horizontalAlignment(marginsLeft:String?, marginsRight:String?, justify: String?, alignItems:String?) -> HorizontalAlignment { + + if((marginsLeft == "auto" && marginsRight == "auto") || justify == "center" || alignItems == "center") { + return .center + } else if(marginsLeft == "auto" || justify == "flex-start" || alignItems == "flex-start") { + return .leading + } else if(marginsRight == "auto" || justify == "flex-end" || alignItems == "flex-end") { + return .trailing + } + return .center + } + + + static func verticalAlignment(justify: String?, alignItems:String?) -> VerticalAlignment { + + if(justify == "center" || alignItems == "center") { + return .center + } else if(justify == "flex-start" || alignItems == "flex-start") { + return .top + } else if( justify == "flex-end" || alignItems == "flex-end") { + return .bottom + } + return .center + } + +} diff --git a/Sources/BuilderIO/Components/BuilderColumns.swift b/Sources/BuilderIO/Components/BuilderColumns.swift index 2feb58d..3d33c65 100644 --- a/Sources/BuilderIO/Components/BuilderColumns.swift +++ b/Sources/BuilderIO/Components/BuilderColumns.swift @@ -36,7 +36,7 @@ struct BuilderColumns: BuilderViewProtocol { VStack(spacing: space) { ForEach(columns.indices, id: \.self) { index in - BuilderModel(blocks: columns[index].blocks) + BuilderBlock(blocks: columns[index].blocks) } } .padding( diff --git a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift index 0a6a7f7..32b68c3 100644 --- a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift +++ b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift @@ -60,12 +60,7 @@ struct ResponsiveStylesBuilderView: ViewModifier { idealWidth: horizontalAlignmentFrame.idealWidth, maxWidth: horizontalAlignmentFrame.maxWidth, alignment: horizontalAlignmentFrame.alignment ) - .if(fontSize != nil) { view in - view.font(.system(size: fontSize!).weight(fontWeight!)) - } - .if(foregroundColor != nil) { view in - view.foregroundColor(foregroundColor) - } + } diff --git a/Sources/BuilderIO/Components/BuilderModel.swift b/Sources/BuilderIO/Components/BuilderModel.swift deleted file mode 100644 index a9b0a6a..0000000 --- a/Sources/BuilderIO/Components/BuilderModel.swift +++ /dev/null @@ -1,61 +0,0 @@ -import SwiftUI -import SwiftyJSON - -//BuilderBox forms the out layout container for all components mimicking Blocks from response. As blocks can have layout direction of either horizontal or vertical a check is made and layout selected. - -struct BuilderModel: View { - - var blocks: [BuilderBlockModel] - var componentType: BuilderComponentType = .box - - init(blocks: [BuilderBlockModel]) { - self.blocks = blocks - } - - var body: some View { - - ScrollView { - ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in - let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) - - //Calculate the layout direction based on the responsive styles - let isHorizontal = - (responsiveStyles["flexDirection"] == CSSConstants.FlexDirection.row.rawValue) - let layout = - isHorizontal - ? AnyLayout(HStackLayout(alignment: .center)) - : AnyLayout(VStackLayout(alignment: .center)) - - Group { - if isHorizontal { - ScrollView(.horizontal, showsIndicators: false) { - layout { - layoutContent(for: child) - }.modifier( - ResponsiveStylesBuilderView( - responsiveStyles: responsiveStyles ?? [:], isText: false)) - } - } else { - layout { - layoutContent(for: child) - }.modifier( - ResponsiveStylesBuilderView(responsiveStyles: responsiveStyles ?? [:], isText: false)) - } - } - - } - } - } - - @ViewBuilder - private func layoutContent(for child: BuilderBlockModel) -> some View { - if let component = child.component { - BuilderComponentRegistry.shared.view(for: child) - } else if let children = child.children, !children.isEmpty { - BuilderModel(blocks: children) - } else { - Spacer() - } - } - -} diff --git a/Sources/BuilderIO/Components/BuilderSection.swift b/Sources/BuilderIO/Components/BuilderSection.swift index 108b3c3..4a79295 100644 --- a/Sources/BuilderIO/Components/BuilderSection.swift +++ b/Sources/BuilderIO/Components/BuilderSection.swift @@ -28,7 +28,7 @@ struct BuilderSection: BuilderViewProtocol { VStack(spacing: space) { ForEach(0...columns.count - 1, id: \.self) { index in - BuilderModel(blocks: columns[index].blocks) + BuilderBlock(blocks: columns[index].blocks) } } diff --git a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift index 32f4dd0..ae45901 100644 --- a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift +++ b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift @@ -1,7 +1,7 @@ import Foundation import SwiftUI -enum HorizontalAlignment { +enum BuilderHorizontalAlignment { case FullWidth case Center case LeftAlign @@ -186,7 +186,7 @@ class CSSStyleUtil { return "" } - static func getHorizontalAlignmentFromMargin(styles: [String: String]) -> HorizontalAlignment { + static func getHorizontalAlignmentFromMargin(styles: [String: String]) -> BuilderHorizontalAlignment { let marginLeft = styles["marginLeft"] let marginRight = styles["marginRight"] @@ -198,29 +198,29 @@ class CSSStyleUtil { let isMarginRightAuto = marginRight?.lowercased() == "auto" if isMarginLeftAuto && isMarginRightAuto { - return HorizontalAlignment.Center + return BuilderHorizontalAlignment.Center } else if isMarginLeftAuto { - return HorizontalAlignment.RightAlign + return BuilderHorizontalAlignment.RightAlign } else if isMarginRightAuto { - return HorizontalAlignment.LeftAlign + return BuilderHorizontalAlignment.LeftAlign } else if isMarginLeftAbsentOrZero && isMarginRightAbsentOrZero { - return HorizontalAlignment.FullWidth + return BuilderHorizontalAlignment.FullWidth } // Default full width? - return HorizontalAlignment.FullWidth + return BuilderHorizontalAlignment.FullWidth } - static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) -> HorizontalAlignment { + static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) -> BuilderHorizontalAlignment { let alignSelf = styles["alignSelf"] if alignSelf == "center" { - return HorizontalAlignment.FullWidth + return BuilderHorizontalAlignment.FullWidth } else if alignSelf == "auto" || alignSelf == "stretch" { - return HorizontalAlignment.FullWidth + return BuilderHorizontalAlignment.FullWidth } - return HorizontalAlignment.FullWidth + return BuilderHorizontalAlignment.FullWidth } - static func getHorizontalAlignment(styles: [String: String]) -> HorizontalAlignment { + static func getHorizontalAlignment(styles: [String: String]) -> BuilderHorizontalAlignment { if styles["alignSelf"] != nil { return getHorizontalAlignmentFromAlignSelf(styles: styles) } else { @@ -232,21 +232,21 @@ class CSSStyleUtil { static func getFrameFromHorizontalAlignment(styles: [String: String], isText: Bool) -> FrameDimensions { - var horizontalAlignment: HorizontalAlignment + var horizontalAlignment: BuilderHorizontalAlignment if styles["alignSelf"] != nil { horizontalAlignment = getHorizontalAlignmentFromAlignSelf(styles: styles) } else { horizontalAlignment = getHorizontalAlignmentFromMargin(styles: styles) } - if horizontalAlignment == HorizontalAlignment.FullWidth { + if horizontalAlignment == BuilderHorizontalAlignment.FullWidth { return FrameDimensions( minWidth: 0, idealWidth: .infinity, maxWidth: .infinity, alignment: isText ? .leading : .center) - } else if horizontalAlignment == HorizontalAlignment.Center { + } else if horizontalAlignment == BuilderHorizontalAlignment.Center { return FrameDimensions(alignment: .center) - } else if horizontalAlignment == HorizontalAlignment.LeftAlign { + } else if horizontalAlignment == BuilderHorizontalAlignment.LeftAlign { return FrameDimensions(alignment: .leading) } else { // Right align diff --git a/Sources/BuilderIO/Helpers/RenderContent.swift b/Sources/BuilderIO/Helpers/RenderContent.swift index 9808553..e2aba64 100644 --- a/Sources/BuilderIO/Helpers/RenderContent.swift +++ b/Sources/BuilderIO/Helpers/RenderContent.swift @@ -20,16 +20,14 @@ public struct RenderContent: View { } public var body: some View { - VStack(alignment: .leading, spacing: 0) { - BuilderModel(blocks: content.data.blocks) - .onAppear { - if !BuilderContentAPI.isPreviewing() { - sendTrackingPixel() - } - } - } - .frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity) - .background(Color.white) + ScrollView { + BuilderBlock(blocks: content.data.blocks) + .onAppear { + if !BuilderContentAPI.isPreviewing() { + sendTrackingPixel() + } + } + } } func sendTrackingPixel() { From 113b43a6f29f9593d6e863f3c23e95582b07657e Mon Sep 17 00:00:00 2001 From: aaron-demelo Date: Mon, 9 Jun 2025 15:38:50 +0530 Subject: [PATCH 4/5] fixed code formats --- .../BuilderIO/Components/BuilderBlock.swift | 239 +++++++++--------- .../Components/BuilderComponentProtocol.swift | 1 - Sources/BuilderIO/Helpers/CSSStyleUtil.swift | 8 +- Sources/BuilderIO/Helpers/RenderContent.swift | 16 +- 4 files changed, 138 insertions(+), 126 deletions(-) diff --git a/Sources/BuilderIO/Components/BuilderBlock.swift b/Sources/BuilderIO/Components/BuilderBlock.swift index 410eaa0..915903f 100644 --- a/Sources/BuilderIO/Components/BuilderBlock.swift +++ b/Sources/BuilderIO/Components/BuilderBlock.swift @@ -14,136 +14,145 @@ struct BuilderBlock: View { var body: some View { - ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in - let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) - - BuilderBlockLayout(responsiveStyles: responsiveStyles ?? [:]) { - if let component = child.component { - BuilderComponentRegistry.shared.view(for: child) - } else if let children = child.children, !children.isEmpty { - BuilderBlock(blocks: children) - } else { - Spacer() - } - } - + ForEach(Array(blocks.enumerated()), id: \.offset) { index, child in + let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles) + + BuilderBlockLayout(responsiveStyles: responsiveStyles ?? [:]) { + if let component = child.component { + BuilderComponentRegistry.shared.view(for: child) + } else if let children = child.children, !children.isEmpty { + BuilderBlock(blocks: children) + } else { + Spacer() + } } - + + } + } } +struct BuilderBlockLayout: View { + let responsiveStyles: [String: String] + @ViewBuilder let content: () -> Content + var body: some View { -struct BuilderBlockLayout: View -{ - let responsiveStyles: [String: String] - @ViewBuilder let content: () -> Content - - var body: some View { - - // 1. Extract basic layout parameters - let direction = responsiveStyles["flexDirection"] ?? "column" - let wrap = responsiveStyles["flexWrap"] == "wrap" - let scroll = responsiveStyles["overflow"] == "auto" && direction == "row" - - let justify = responsiveStyles["justifyContent"] - let alignItems = responsiveStyles["alignItems"] - - let marginLeft = responsiveStyles["marginLeft"]?.lowercased() - let marginRight = responsiveStyles["marginRight"]?.lowercased() - - let spacing = extractPixels(responsiveStyles["gap"]) ?? 0 - let padding = extractEdgeInsets(for:"padding", from: responsiveStyles) - let margin = extractEdgeInsets(for:"margin", from: responsiveStyles) - - let minHeight = extractPixels(responsiveStyles["minHeight"]) - let maxHeight = extractPixels(responsiveStyles["maxHeight"]) - let minWidth = extractPixels(responsiveStyles["minWidth"]) - let maxWidth = extractPixels(responsiveStyles["maxWidth"]) - - let borderRadius = extractPixels(responsiveStyles["borderRadius"]) ?? 0 - - // 2. Build base layout (wrapped or not) - let layoutView: some View = Group { - if wrap { - LazyVGrid( - columns: [GridItem(.adaptive(minimum: 100), spacing: spacing)], - alignment: BuilderBlockLayout.horizontalAlignment(marginsLeft:marginLeft, marginsRight:marginRight, justify: justify, alignItems: alignItems), - spacing: spacing, - content: content - ) - } else if direction == "row" { - HStack(alignment:BuilderBlockLayout.verticalAlignment( justify: justify, alignItems: alignItems),spacing: spacing) { - content() - } - } else { - VStack(alignment: BuilderBlockLayout.horizontalAlignment(marginsLeft:marginLeft, marginsRight:marginRight, justify: justify, alignItems: alignItems), spacing: spacing) { - content() - } - } + // 1. Extract basic layout parameters + let direction = responsiveStyles["flexDirection"] ?? "column" + let wrap = responsiveStyles["flexWrap"] == "wrap" + let scroll = responsiveStyles["overflow"] == "auto" && direction == "row" + + let justify = responsiveStyles["justifyContent"] + let alignItems = responsiveStyles["alignItems"] + + let marginLeft = responsiveStyles["marginLeft"]?.lowercased() + let marginRight = responsiveStyles["marginRight"]?.lowercased() + + let spacing = extractPixels(responsiveStyles["gap"]) ?? 0 + let padding = extractEdgeInsets(for: "padding", from: responsiveStyles) + let margin = extractEdgeInsets(for: "margin", from: responsiveStyles) + + let minHeight = extractPixels(responsiveStyles["minHeight"]) + let maxHeight = extractPixels(responsiveStyles["maxHeight"]) + let minWidth = extractPixels(responsiveStyles["minWidth"]) + let maxWidth = extractPixels(responsiveStyles["maxWidth"]) + + let borderRadius = extractPixels(responsiveStyles["borderRadius"]) ?? 0 + + // 2. Build base layout (wrapped or not) + let layoutView: some View = Group { + if wrap { + LazyVGrid( + columns: [GridItem(.adaptive(minimum: 100), spacing: spacing)], + alignment: BuilderBlockLayout.horizontalAlignment( + marginsLeft: marginLeft, marginsRight: marginRight, justify: justify, + alignItems: alignItems), + spacing: spacing, + content: content + ) + } else if direction == "row" { + HStack( + alignment: BuilderBlockLayout.verticalAlignment( + justify: justify, alignItems: alignItems), spacing: spacing + ) { + content() } - - // 3. Wrap in scroll if overflow: auto - let scrollableView: some View = Group { - if scroll { - ScrollView(.horizontal, showsIndicators: false) { - layoutView - } - } else { - layoutView - } + } else { + VStack( + alignment: BuilderBlockLayout.horizontalAlignment( + marginsLeft: marginLeft, marginsRight: marginRight, justify: justify, + alignItems: alignItems), spacing: spacing + ) { + content() } - - // 4. Apply visual and layout modifiers - return scrollableView - .padding(padding) - .frame(minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight) - .padding(margin) //margin - .cornerRadius(borderRadius) + } } - - func extractPixels(_ value: String?) -> CGFloat? { - guard let value = value?.replacingOccurrences(of: "px", with: ""), - let number = Double(value) else { return nil } - return CGFloat(number) + // 3. Wrap in scroll if overflow: auto + let scrollableView: some View = Group { + if scroll { + ScrollView(.horizontal, showsIndicators: false) { + layoutView + } + } else { + layoutView + } } - func extractEdgeInsets(for insetType:String, from styles: [String: String]) -> EdgeInsets { - - return EdgeInsets( - top: extractPixels(styles["\(insetType)Top"]) ?? 0, - leading: extractPixels(styles["\(insetType)Left"]) ?? 0, - bottom: extractPixels(styles["\(insetType)Bottom"]) ?? 0, - trailing: extractPixels(styles["\(insetType)Right"]) ?? 0 - ) - } + // 4. Apply visual and layout modifiers + return + scrollableView + .padding(padding) + .frame(minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight) + .padding(margin) //margin + .cornerRadius(borderRadius) + } - - static func horizontalAlignment(marginsLeft:String?, marginsRight:String?, justify: String?, alignItems:String?) -> HorizontalAlignment { - - if((marginsLeft == "auto" && marginsRight == "auto") || justify == "center" || alignItems == "center") { - return .center - } else if(marginsLeft == "auto" || justify == "flex-start" || alignItems == "flex-start") { - return .leading - } else if(marginsRight == "auto" || justify == "flex-end" || alignItems == "flex-end") { - return .trailing - } - return .center + func extractPixels(_ value: String?) -> CGFloat? { + guard let value = value?.replacingOccurrences(of: "px", with: ""), + let number = Double(value) + else { return nil } + return CGFloat(number) + } + + func extractEdgeInsets(for insetType: String, from styles: [String: String]) -> EdgeInsets { + + return EdgeInsets( + top: extractPixels(styles["\(insetType)Top"]) ?? 0, + leading: extractPixels(styles["\(insetType)Left"]) ?? 0, + bottom: extractPixels(styles["\(insetType)Bottom"]) ?? 0, + trailing: extractPixels(styles["\(insetType)Right"]) ?? 0 + ) + } + + static func horizontalAlignment( + marginsLeft: String?, marginsRight: String?, justify: String?, alignItems: String? + ) -> HorizontalAlignment { + + if (marginsLeft == "auto" && marginsRight == "auto") || justify == "center" + || alignItems == "center" + { + return .center + } else if marginsLeft == "auto" || justify == "flex-start" || alignItems == "flex-start" { + return .leading + } else if marginsRight == "auto" || justify == "flex-end" || alignItems == "flex-end" { + return .trailing } + return .center + } - - static func verticalAlignment(justify: String?, alignItems:String?) -> VerticalAlignment { - - if(justify == "center" || alignItems == "center") { - return .center - } else if(justify == "flex-start" || alignItems == "flex-start") { - return .top - } else if( justify == "flex-end" || alignItems == "flex-end") { - return .bottom - } - return .center + static func verticalAlignment(justify: String?, alignItems: String?) -> VerticalAlignment { + + if justify == "center" || alignItems == "center" { + return .center + } else if justify == "flex-start" || alignItems == "flex-start" { + return .top + } else if justify == "flex-end" || alignItems == "flex-end" { + return .bottom } - + return .center + } + } diff --git a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift index 32b68c3..5269233 100644 --- a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift +++ b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift @@ -60,7 +60,6 @@ struct ResponsiveStylesBuilderView: ViewModifier { idealWidth: horizontalAlignmentFrame.idealWidth, maxWidth: horizontalAlignmentFrame.maxWidth, alignment: horizontalAlignmentFrame.alignment ) - } diff --git a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift index ae45901..7af5930 100644 --- a/Sources/BuilderIO/Helpers/CSSStyleUtil.swift +++ b/Sources/BuilderIO/Helpers/CSSStyleUtil.swift @@ -186,7 +186,9 @@ class CSSStyleUtil { return "" } - static func getHorizontalAlignmentFromMargin(styles: [String: String]) -> BuilderHorizontalAlignment { + static func getHorizontalAlignmentFromMargin(styles: [String: String]) + -> BuilderHorizontalAlignment + { let marginLeft = styles["marginLeft"] let marginRight = styles["marginRight"] @@ -210,7 +212,9 @@ class CSSStyleUtil { return BuilderHorizontalAlignment.FullWidth } - static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) -> BuilderHorizontalAlignment { + static func getHorizontalAlignmentFromAlignSelf(styles: [String: String]) + -> BuilderHorizontalAlignment + { let alignSelf = styles["alignSelf"] if alignSelf == "center" { return BuilderHorizontalAlignment.FullWidth diff --git a/Sources/BuilderIO/Helpers/RenderContent.swift b/Sources/BuilderIO/Helpers/RenderContent.swift index e2aba64..2d84b5d 100644 --- a/Sources/BuilderIO/Helpers/RenderContent.swift +++ b/Sources/BuilderIO/Helpers/RenderContent.swift @@ -20,14 +20,14 @@ public struct RenderContent: View { } public var body: some View { - ScrollView { - BuilderBlock(blocks: content.data.blocks) - .onAppear { - if !BuilderContentAPI.isPreviewing() { - sendTrackingPixel() - } - } - } + ScrollView { + BuilderBlock(blocks: content.data.blocks) + .onAppear { + if !BuilderContentAPI.isPreviewing() { + sendTrackingPixel() + } + } + } } func sendTrackingPixel() { From 80259a9d3d384b8bdc78495b258ee30b254c5183 Mon Sep 17 00:00:00 2001 From: aaron-demelo Date: Mon, 9 Jun 2025 15:50:10 +0530 Subject: [PATCH 5/5] code clean up fo components --- Sources/BuilderIO/Components/BuilderImage.swift | 1 - Sources/BuilderIO/Components/BuilderText.swift | 3 --- 2 files changed, 4 deletions(-) diff --git a/Sources/BuilderIO/Components/BuilderImage.swift b/Sources/BuilderIO/Components/BuilderImage.swift index b80131b..a5291ee 100644 --- a/Sources/BuilderIO/Components/BuilderImage.swift +++ b/Sources/BuilderIO/Components/BuilderImage.swift @@ -16,7 +16,6 @@ struct BuilderImage: BuilderViewProtocol { var body: some View { AsyncImage(url: imageURL).frame(width: 5, height: 5) - EmptyView() } } diff --git a/Sources/BuilderIO/Components/BuilderText.swift b/Sources/BuilderIO/Components/BuilderText.swift index 1563b77..bc82fc5 100644 --- a/Sources/BuilderIO/Components/BuilderText.swift +++ b/Sources/BuilderIO/Components/BuilderText.swift @@ -17,9 +17,6 @@ struct BuilderText: BuilderViewProtocol { var body: some View { Text(CSSStyleUtil.getTextWithoutHtml(text ?? "")) - .if(!(self.responsiveStyles?.isEmpty ?? true)) { view in - view.responsiveStylesBuilderView(responsiveStyles: self.responsiveStyles!, isText: true) - } } }