Hermes Agent 36967370fd SwiftBricks: Bricks JSON UI framework native Swift/SwiftUI implementation
- Core: Schema (Codable), Store, EventBus, RPC, Engine, I18n
- Controls: Text, Title1-6, Label, Input, Textarea, Number, Date, Button, Select
  VBox, HBox, ScrollPanel, DynamicColumn, Tabular, Form, InlineForm,
  TabView, Menu, PopupWindow, Html, Image, UrlWidget
- Renderer: BricksView (main entry), ControlRenderer (recursive dispatch)
- 30+ widget types, 9 bind actiontypes
- Form validation (required/minlength/maxlength/min/max/email/number/pattern)
- i18n with multi-language JSON files
- 18 tests covering schema parsing, store, i18n, events
2026-06-21 12:12:03 +08:00

143 lines
4.0 KiB
Swift

import SwiftUI
// MARK: - Text Control
struct TextControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
var body: some View {
let text = displayText
Text(text)
.font(resolveFont())
.foregroundColor(resolveColor())
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
}
private var displayText: String {
let raw = schema.options.otext ?? schema.options.text ?? ""
if schema.options.i18n != false {
return engine.i18n.t(raw)
}
return raw
}
private func resolveFont() -> Font {
if let fs = schema.options.cfontsize {
return .system(size: CGFloat(fs * 14))
}
if let fs = schema.options.fontsize, let size = Double(fs.replacingOccurrences(of: "px", with: "")) {
return .system(size: CGFloat(size))
}
return .body
}
private func resolveColor() -> Color {
if let color = schema.options.color {
return Color.fromCSS(color)
}
return .primary
}
}
// MARK: - Title Control
struct TitleControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
let level: Int
var body: some View {
let text = schema.options.text ?? schema.options.otext ?? ""
let translated = (schema.options.i18n != false) ? engine.i18n.t(text) : text
Group {
switch level {
case 1: Text(translated).font(.largeTitle).bold()
case 2: Text(translated).font(.title).bold()
case 3: Text(translated).font(.title2).bold()
case 4: Text(translated).font(.title3).bold()
case 5: Text(translated).font(.headline)
case 6: Text(translated).font(.subheadline).bold()
default: Text(translated).font(.body)
}
}
.foregroundColor(resolveColor())
}
private func resolveColor() -> Color {
if let color = schema.options.color {
return Color.fromCSS(color)
}
return .primary
}
}
// MARK: - Label Control
struct LabelControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
var body: some View {
let text = schema.options.text ?? schema.options.otext ?? ""
let translated = (schema.options.i18n != false) ? engine.i18n.t(text) : text
HStack(spacing: 4) {
if let icon = schema.options.icon {
Text(icon)
}
Text(translated)
.font(.body)
.foregroundColor(.secondary)
}
}
}
// MARK: - Color Extension
extension Color {
static func fromCSS(_ css: String) -> Color {
let cleaned = css.trimmingCharacters(in: .whitespaces)
if cleaned.hasPrefix("#") {
return Color(hex: cleaned)
}
if cleaned.hasPrefix("var(") {
// CSS 使
return .primary
}
switch cleaned.lowercased() {
case "red": return .red
case "blue": return .blue
case "green": return .green
case "yellow": return .yellow
case "orange": return .orange
case "purple": return .purple
case "pink": return .pink
case "white": return .white
case "black": return .black
case "gray", "grey": return .gray
case "transparent": return .clear
default: return .primary
}
}
init(hex: String) {
var hexStr = hex.trimmingCharacters(in: .whitespacesAndNewlines)
if hexStr.hasPrefix("#") { hexStr.removeFirst() }
var rgb: UInt64 = 0
Scanner(string: hexStr).scanHexInt64(&rgb)
let r = Double((rgb >> 16) & 0xFF) / 255.0
let g = Double((rgb >> 8) & 0xFF) / 255.0
let b = Double(rgb & 0xFF) / 255.0
self.init(red: r, green: g, blue: b)
}
}