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
89 changes: 58 additions & 31 deletions Sources/BuilderIO/Components/BuilderBlock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,56 @@ import SwiftUI

//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.

//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.

enum BuilderLayoutDirection {
case horizontal
case vertical
case parentLayout
}

@MainActor
struct BuilderBlock: View {

var blocks: [BuilderBlockModel]
static let componentType: BuilderComponentType = .box
let builderLayoutDirection: BuilderLayoutDirection // Default to vertical direction
let spacing: CGFloat // Default to vertical direction

init(blocks: [BuilderBlockModel]) {
init(
blocks: [BuilderBlockModel], builderLayoutDirection: BuilderLayoutDirection = .parentLayout,
spacing: CGFloat = 0
) {
self.blocks = blocks
self.builderLayoutDirection = builderLayoutDirection
self.spacing = spacing
}

var body: some View {

Group {
if builderLayoutDirection == .parentLayout {
blockContent()
} else if builderLayoutDirection == .horizontal {
HStack(spacing: spacing) { // Adjust alignment and spacing as needed
blockContent()
}
} else { // Default to column
VStack(spacing: spacing) { // Adjust alignment and spacing as needed
blockContent()
}
}
}
}

@ViewBuilder
private func blockContent() -> some View {
ForEach(blocks) { child in
let responsiveStyles = CSSStyleUtil.getFinalStyle(responsiveStyles: child.responsiveStyles)
let component = child.component
let spacing = CSSStyleUtil.extractPixels(responsiveStyles["gap"]) ?? 0

//Only checking links for now, can be expanded to cover events in the future
// Only checking links for now, can be expanded to cover events in the future
let isTappable =
component?.name == BuilderComponentType.coreButton.rawValue
|| !(component?.options?["Link"].isEmpty ?? true) || !(child.linkUrl?.isEmpty ?? true)
Expand All @@ -30,27 +63,28 @@ struct BuilderBlock: View {
options: child.component?.options,
eventActions: child.actions,
linkURL: child.linkUrl) : nil

if responsiveStyles["display"] == "none" {
EmptyView()
} else {

BuilderBlockLayout(
responsiveStyles: responsiveStyles ?? [:], builderAction: builderAction,
component: component
) {
if let component = component {
BuilderComponentRegistry.shared.view(for: child)
} else if let children = child.children, !children.isEmpty {
BuilderBlock(blocks: children)
BuilderBlock(
blocks: children,
builderLayoutDirection: responsiveStyles["flexDirection"] == "row"
? .horizontal : .vertical, spacing: spacing)
} else {
Rectangle().fill(Color.clear)
}
}
}

}
}

}

struct BuilderBlockLayout<Content: View>: View {
Expand Down Expand Up @@ -79,23 +113,23 @@ struct BuilderBlockLayout<Content: View>: View {
let marginTop = responsiveStyles["marginTop"]?.lowercased()
let marginBottom = responsiveStyles["marginBottom"]?.lowercased()

let spacing = extractPixels(responsiveStyles["gap"]) ?? 0
let spacing = CSSStyleUtil.extractPixels(responsiveStyles["gap"]) ?? 0
let padding = extractEdgeInsets(
for: "padding", from: responsiveStyles, with: getBorderWidth(from: responsiveStyles))
let margin = extractEdgeInsets(for: "margin", from: responsiveStyles)

let minHeight = extractPixels(responsiveStyles["minHeight"])
let maxHeight = extractPixels(responsiveStyles["maxHeight"])
let width = extractPixels(responsiveStyles["width"])
let height = extractPixels(responsiveStyles["height"])
let minHeight = CSSStyleUtil.extractPixels(responsiveStyles["minHeight"])
let maxHeight = CSSStyleUtil.extractPixels(responsiveStyles["maxHeight"])
let width = CSSStyleUtil.extractPixels(responsiveStyles["width"])
let height = CSSStyleUtil.extractPixels(responsiveStyles["height"])

let minWidth = extractPixels(responsiveStyles["minWidth"])
let minWidth = CSSStyleUtil.extractPixels(responsiveStyles["minWidth"])
let maxWidth =
extractPixels(responsiveStyles["maxWidth"])
CSSStyleUtil.extractPixels(responsiveStyles["maxWidth"])
?? ((marginLeft == "auto" || marginRight == "auto" || alignSelf == "center")
? nil : .infinity)

let borderRadius = extractPixels(responsiveStyles["borderRadius"]) ?? 0
let borderRadius = CSSStyleUtil.extractPixels(responsiveStyles["borderRadius"]) ?? 0

// 2. Build base layout (wrapped or not)
let layoutView: some View = Group {
Expand Down Expand Up @@ -188,16 +222,19 @@ struct BuilderBlockLayout<Content: View>: View {
buttonActionManager.handleButtonPress(builderAction: builderAction)
} label: {
componentView
.if(marginTop == "auto" || marginBottom == "auto") { view in
view.fixedSize(horizontal: false, vertical: true)
}
}
} else {
componentView
.if(marginTop == "auto" || marginBottom == "auto") { view in
view.fixedSize(horizontal: false, vertical: true)
}
}

if marginBottom == "auto" { Spacer() }
}
.if(frameAlignment == .center && component == nil) { view in
view.fixedSize(horizontal: true, vertical: false)
}
.frame(maxWidth: frameAlignment == .center ? nil : .infinity, alignment: frameAlignment)
}
}
Expand All @@ -216,20 +253,10 @@ struct BuilderBlockLayout<Content: View>: View {
// 4. Apply visual and layout modifiers
return
scrollableView
.if(component == nil) { view in
view.builderBackground(responsiveStyles: responsiveStyles)
}
.padding(margin) //margin

}

func extractPixels(_ value: String?) -> CGFloat? {
guard let value = value?.replacingOccurrences(of: "px", with: ""),
let number = Int(value)
else { return nil }
return CGFloat(number)
}

func getBorderWidth(from styles: [String: String]) -> CGFloat {
var borderWidth: CGFloat = 0
if let widthString = responsiveStyles["borderWidth"],
Expand All @@ -246,10 +273,10 @@ struct BuilderBlockLayout<Content: View>: View {
) -> EdgeInsets {

let edgeInsets = EdgeInsets(
top: (extractPixels(styles["\(insetType)Top"]) ?? 0) + bufferWidth,
leading: (extractPixels(styles["\(insetType)Left"]) ?? 0) + bufferWidth,
bottom: (extractPixels(styles["\(insetType)Bottom"]) ?? 0) + bufferWidth,
trailing: (extractPixels(styles["\(insetType)Right"]) ?? 0) + bufferWidth
top: (CSSStyleUtil.extractPixels(styles["\(insetType)Top"]) ?? 0) + bufferWidth,
leading: (CSSStyleUtil.extractPixels(styles["\(insetType)Left"]) ?? 0) + bufferWidth,
bottom: (CSSStyleUtil.extractPixels(styles["\(insetType)Bottom"]) ?? 0) + bufferWidth,
trailing: (CSSStyleUtil.extractPixels(styles["\(insetType)Right"]) ?? 0) + bufferWidth
)
return edgeInsets
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/BuilderIO/Components/BuilderColumns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct BuilderColumns: BuilderViewProtocol {
let columnsForLayout = reverseColumnsWhenStacked ? columns.reversed() : columns
VStack(spacing: space) {
ForEach(columnsForLayout) { column in
BuilderBlock(blocks: column.blocks)
BuilderBlock(blocks: column.blocks, builderLayoutDirection: .vertical)
}
}
} else {
Expand Down
6 changes: 3 additions & 3 deletions Sources/BuilderIO/Components/BuilderImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ struct BuilderImage: BuilderViewProtocol {
Group {
if let children = children, !children.isEmpty {
VStack(spacing: 0) {
BuilderBlock(blocks: children).fixedSize(horizontal: true, vertical: true)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
BuilderBlock(blocks: children).fixedSize(horizontal: true, vertical: false)
}
} else {
EmptyView()
}
Expand All @@ -78,7 +78,7 @@ struct BuilderImage: BuilderViewProtocol {
}

case .failure:
Color.gray
EmptyView()
@unknown default:
EmptyView()
}
Expand Down
7 changes: 5 additions & 2 deletions Sources/BuilderIO/Components/BuilderText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ struct HTMLTextView: View {
var body: some View {
Group {
if let attributedString = attributedString {
Text(attributedString)
.padding(.bottom, -24)

Text(attributedString).multilineTextAlignment(
CSSAlignments.textAlignment(responsiveStyles: responsiveStyles ?? [:])
)
.padding(.bottom, -24)
} else if let errorInProcessing = errorInProcessing {
Text(htmlPlainText)
} else {
Expand Down
7 changes: 7 additions & 0 deletions Sources/BuilderIO/Helpers/CSSStyleUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ class CSSStyleUtil {

return finalStyle
}

static func extractPixels(_ value: String?) -> CGFloat? {
guard let value = value?.replacingOccurrences(of: "px", with: ""),
let number = Int(value)
else { return nil }
return CGFloat(number)
}
}
Loading