import SwiftUI // MARK: - VBox Control struct VBoxControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine var body: some View { VStack(alignment: .leading, spacing: spacing) { if let subwidgets = schema.subwidgets { SubWidgetsView(subwidgets: subwidgets, engine: engine) } } .padding(padding) .frame(maxWidth: .infinity, alignment: .leading) .background(backgroundColor) .cornerRadius(cornerRadius) .overlay( RoundedRectangle(cornerRadius: cornerRadius) .stroke(borderColor, lineWidth: borderWidth) ) .contentShape(Rectangle()) .onTapGesture { Task { await engine.triggerEvent(widgetId: schema.effectiveId, event: "click") } } } private var spacing: CGFloat { CGFloat(schema.options.spacing ?? 8) } private var padding: CGFloat { parsePadding(schema.options.padding) } private var cornerRadius: CGFloat { (schema.options.css?.contains("card") ?? false) ? 8 : 0 } private var borderWidth: CGFloat { (schema.options.css?.contains("card") ?? false) ? 1 : 0 } private var borderColor: Color { (schema.options.css?.contains("card") ?? false) ? Color.secondary.opacity(0.2) : .clear } private var backgroundColor: Color { if let bg = schema.options.bgcolor { return Color.fromCSS(bg) } if schema.options.css?.contains("card") ?? false { return Color.secondary.opacity(0.05) } return .clear } private func parsePadding(_ padding: String?) -> CGFloat { guard let p = padding else { return 0 } if let val = Double(p.replacingOccurrences(of: "px", with: "")) { return CGFloat(val) } return 8 } } // MARK: - HBox Control struct HBoxControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine var body: some View { HStack(alignment: verticalAlignment, spacing: spacing) { if let subwidgets = schema.subwidgets { SubWidgetsView(subwidgets: subwidgets, engine: engine) } } .padding(padding) .frame(maxWidth: .infinity, alignment: .leading) .background(backgroundColor) .cornerRadius(8) } private var spacing: CGFloat { CGFloat(schema.options.spacing ?? 8) } private var padding: CGFloat { parsePadding(schema.options.padding) } private var verticalAlignment: VerticalAlignment { switch schema.options.alignItems { case "center": return .center case "flex-end", "end": return .bottom default: return .top } } private var backgroundColor: Color { if let bg = schema.options.bgcolor { return Color.fromCSS(bg) } return .clear } private func parsePadding(_ padding: String?) -> CGFloat { guard let p = padding else { return 0 } if let val = Double(p.replacingOccurrences(of: "px", with: "")) { return CGFloat(val) } return 8 } } // MARK: - ScrollPanel Control struct ScrollPanelControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine var body: some View { if schema.widgettype == "HScrollPanel" { ScrollView(.horizontal, showsIndicators: true) { HStack(alignment: .top, spacing: spacing) { if let subwidgets = schema.subwidgets { SubWidgetsView(subwidgets: subwidgets, engine: engine) } } .padding(padding) } .frame(maxWidth: .infinity, maxHeight: .infinity) } else { ScrollView(.vertical, showsIndicators: true) { VStack(alignment: .leading, spacing: spacing) { if let subwidgets = schema.subwidgets { SubWidgetsView(subwidgets: subwidgets, engine: engine) } } .padding(padding) .frame(maxWidth: .infinity, alignment: .leading) } .frame(maxWidth: .infinity, maxHeight: .infinity) } } private var spacing: CGFloat { CGFloat(schema.options.spacing ?? 8) } private var padding: CGFloat { if let p = schema.options.padding, let val = Double(p.replacingOccurrences(of: "px", with: "")) { return CGFloat(val) } return 8 } } // MARK: - DynamicColumn Control struct DynamicColumnControl: View { let schema: ControlSchema @ObservedObject var engine: BricksEngine private let columns = [GridItem(.adaptive(minimum: 200), spacing: 12)] var body: some View { ScrollView { LazyVGrid(columns: resolvedColumns, spacing: gap) { if let subwidgets = schema.subwidgets { ForEach(Array(subwidgets.enumerated()), id: \.offset) { _, sub in ControlRenderer(schema: sub, engine: engine) .frame(maxWidth: .infinity) } } } .padding(8) } } private var gap: CGFloat { CGFloat(schema.options.col_cgap ?? 1) * 12 } private var resolvedColumns: [GridItem] { let minWidth = CGFloat(schema.options.col_cwidth ?? 20) * 8 return [GridItem(.adaptive(minimum: minWidth), spacing: gap)] } }