diff --git a/Sources/BuilderIO/Components/BuilderBlock.swift b/Sources/BuilderIO/Components/BuilderBlock.swift index 915903f..6b9dd5f 100644 --- a/Sources/BuilderIO/Components/BuilderBlock.swift +++ b/Sources/BuilderIO/Components/BuilderBlock.swift @@ -57,7 +57,7 @@ struct BuilderBlockLayout: View { let minHeight = extractPixels(responsiveStyles["minHeight"]) let maxHeight = extractPixels(responsiveStyles["maxHeight"]) let minWidth = extractPixels(responsiveStyles["minWidth"]) - let maxWidth = extractPixels(responsiveStyles["maxWidth"]) + let maxWidth = extractPixels(responsiveStyles["maxWidth"]) ?? .infinity let borderRadius = extractPixels(responsiveStyles["borderRadius"]) ?? 0 @@ -65,7 +65,9 @@ struct BuilderBlockLayout: View { let layoutView: some View = Group { if wrap { LazyVGrid( - columns: [GridItem(.adaptive(minimum: 100), spacing: spacing)], + columns: [ + GridItem(.flexible(minimum: minWidth ?? 0, maximum: maxWidth), spacing: spacing) + ], alignment: BuilderBlockLayout.horizontalAlignment( marginsLeft: marginLeft, marginsRight: marginRight, justify: justify, alignItems: alignItems), @@ -73,19 +75,47 @@ struct BuilderBlockLayout: View { content: content ) } else if direction == "row" { + let hStackAlignment = BuilderBlockLayout.verticalAlignment( + justify: justify, alignItems: alignItems) + + let frameAlignment: Alignment = + switch hStackAlignment { + case .top: .top + case .center: .center + case .bottom: .bottom + default: .center + } + HStack( - alignment: BuilderBlockLayout.verticalAlignment( - justify: justify, alignItems: alignItems), spacing: spacing + alignment: hStackAlignment, spacing: spacing ) { - content() + content().padding(padding) + .frame( + minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, + alignment: frameAlignment) } } else { + + let vStackAlignment = BuilderBlockLayout.horizontalAlignment( + marginsLeft: marginLeft, marginsRight: marginRight, justify: justify, + alignItems: alignItems) + + let frameAlignment: Alignment = + switch vStackAlignment { + case .leading: .leading + case .center: .center + case .trailing: .trailing + default: .center + } + VStack( - alignment: BuilderBlockLayout.horizontalAlignment( - marginsLeft: marginLeft, marginsRight: marginRight, justify: justify, - alignItems: alignItems), spacing: spacing + alignment: vStackAlignment, spacing: spacing ) { - content() + + content().padding(padding) + .frame( + minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, + alignment: frameAlignment) } } } @@ -104,8 +134,6 @@ struct BuilderBlockLayout: View { // 4. Apply visual and layout modifiers return scrollableView - .padding(padding) - .frame(minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight) .padding(margin) //margin .cornerRadius(borderRadius) } @@ -135,9 +163,9 @@ struct BuilderBlockLayout: View { || alignItems == "center" { return .center - } else if marginsLeft == "auto" || justify == "flex-start" || alignItems == "flex-start" { + } else if marginsRight == "auto" || justify == "flex-start" || alignItems == "flex-start" { return .leading - } else if marginsRight == "auto" || justify == "flex-end" || alignItems == "flex-end" { + } else if marginsLeft == "auto" || justify == "flex-end" || alignItems == "flex-end" { return .trailing } return .center diff --git a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift index 5269233..738571f 100644 --- a/Sources/BuilderIO/Components/BuilderComponentProtocol.swift +++ b/Sources/BuilderIO/Components/BuilderComponentProtocol.swift @@ -32,7 +32,6 @@ struct BuilderEmptyView: BuilderViewProtocol { struct ResponsiveStylesBuilderView: ViewModifier { var responsiveStyles: [String: String] - let horizontalAlignmentFrame: FrameDimensions let foregroundColor: Color? let cornerRadius: CGFloat? let fontSize: CGFloat? @@ -50,17 +49,10 @@ struct ResponsiveStylesBuilderView: ViewModifier { 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 - ) - } } diff --git a/Sources/BuilderIO/Components/BuilderImage.swift b/Sources/BuilderIO/Components/BuilderImage.swift index a5291ee..751e8c0 100644 --- a/Sources/BuilderIO/Components/BuilderImage.swift +++ b/Sources/BuilderIO/Components/BuilderImage.swift @@ -6,16 +6,40 @@ struct BuilderImage: BuilderViewProtocol { var block: BuilderBlockModel var responsiveStyles: [String: String]? + var aspectRatio: CGSize? + var imageURL: URL? init(block: BuilderBlockModel) { self.block = block self.responsiveStyles = getFinalStyle(responsiveStyles: block.responsiveStyles) self.imageURL = URL(string: block.component?.options?["image"].string ?? "") + + if let ratio = block.component?.options?["aspectRatio"].doubleValue { + self.aspectRatio = CGSize(width: ratio, height: 1) + } else { + self.aspectRatio = nil + } + } var body: some View { - AsyncImage(url: imageURL).frame(width: 5, height: 5) - } + AsyncImage(url: imageURL) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + image + .resizable() + .if(aspectRatio != nil) { view in + view.aspectRatio(self.aspectRatio!, contentMode: .fit) + } + case .failure: + Color.gray + @unknown default: + EmptyView() + } + }.responsiveStylesBuilderView(responsiveStyles: self.responsiveStyles ?? [:], isText: false) + } } diff --git a/Sources/BuilderIO/Components/BuilderText.swift b/Sources/BuilderIO/Components/BuilderText.swift index bc82fc5..22d5e91 100644 --- a/Sources/BuilderIO/Components/BuilderText.swift +++ b/Sources/BuilderIO/Components/BuilderText.swift @@ -16,7 +16,63 @@ struct BuilderText: BuilderViewProtocol { } var body: some View { - Text(CSSStyleUtil.getTextWithoutHtml(text ?? "")) + + Text(CSSStyleUtil.getTextWithoutHtml(text ?? "")).responsiveStylesBuilderView( + responsiveStyles: self.responsiveStyles ?? [:], isText: true) + + } + +} + +struct BuilderText_Previews: PreviewProvider { + static let builderJSONString = """ + { + "@type": "@builder.io/sdk:Element", + "@version": 2, + "id": "builder-ad756879bc6c4ee3ac7977d5af0b6811", + "meta": { + "previousId": "builder-e8da4929479a4abf9cd8e3959a1e6699" + }, + "component": { + "name": "Text", + "options": { + "text": "

Left Align Text

" + } + }, + "responsiveStyles": { + "large": { + "display": "flex", + "flexDirection": "column", + "position": "relative", + "flexShrink": "0", + "boxSizing": "border-box", + "marginTop": "20px", + "lineHeight": "normal", + "height": "auto", + "marginRight": "auto", + "paddingLeft": "20px", + "paddingRight": "20px" + } + } + } + """ + + static func decodeBuilderBlockModel(from jsonString: String) -> BuilderBlockModel? { + let data = Data(jsonString.utf8) + do { + let decoder = JSONDecoder() + return try decoder.decode(BuilderBlockModel.self, from: data) + } catch { + print("Decoding failed:", error) + return nil + } } + static var previews: some View { + if let block = decodeBuilderBlockModel(from: builderJSONString) { + BuilderText(block: block) + } else { + Text("Failed to decode block") + } + } }