Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions Sources/BuilderIO/Components/BuilderBlock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
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<Content: View>: 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<Content>.horizontalAlignment(
marginsLeft: marginLeft, marginsRight: marginRight, justify: justify,
alignItems: alignItems),
spacing: spacing,
content: content
)
} else if direction == "row" {
HStack(
alignment: BuilderBlockLayout<Content>.verticalAlignment(
justify: justify, alignItems: alignItems), spacing: spacing
) {
content()
}
} else {
VStack(
alignment: BuilderBlockLayout<Content>.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
}

}
2 changes: 1 addition & 1 deletion Sources/BuilderIO/Components/BuilderColumns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
6 changes: 0 additions & 6 deletions Sources/BuilderIO/Components/BuilderComponentProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,6 @@ 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)
}

}

Expand Down
1 change: 0 additions & 1 deletion Sources/BuilderIO/Components/BuilderImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ struct BuilderImage: BuilderViewProtocol {

var body: some View {
AsyncImage(url: imageURL).frame(width: 5, height: 5)
EmptyView()
}

}
61 changes: 0 additions & 61 deletions Sources/BuilderIO/Components/BuilderModel.swift

This file was deleted.

2 changes: 1 addition & 1 deletion Sources/BuilderIO/Components/BuilderSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

}
Expand Down
3 changes: 0 additions & 3 deletions Sources/BuilderIO/Components/BuilderText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

}
36 changes: 20 additions & 16 deletions Sources/BuilderIO/Helpers/CSSStyleUtil.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
import SwiftUI

enum HorizontalAlignment {
enum BuilderHorizontalAlignment {
case FullWidth
case Center
case LeftAlign
Expand Down Expand Up @@ -186,7 +186,9 @@ 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"]

Expand All @@ -198,29 +200,31 @@ 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 {
Expand All @@ -232,21 +236,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
Expand Down
6 changes: 2 additions & 4 deletions Sources/BuilderIO/Helpers/RenderContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ public struct RenderContent: View {
}

public var body: some View {
VStack(alignment: .leading, spacing: 0) {
BuilderModel(blocks: content.data.blocks)
ScrollView {
BuilderBlock(blocks: content.data.blocks)
.onAppear {
if !BuilderContentAPI.isPreviewing() {
sendTrackingPixel()
}
}
}
.frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity)
.background(Color.white)
}

func sendTrackingPixel() {
Expand Down