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

175 lines
5.5 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
// MARK: - Input Control
struct InputControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
@State private var text: String = ""
@State private var isFocused: Bool = false
@State private var errorMessage: String = ""
var body: some View {
VStack(alignment: .leading, spacing: 4) {
if showLabel {
Text(effectiveLabel)
.font(.caption)
.foregroundColor(.secondary)
}
TextField(placeholder, text: $text)
.textFieldStyle(.roundedBorder)
.onChange(of: text) { _, newValue in
let formId = findParentFormId()
if let formId, let name = schema.options.name {
engine.store.setFormField(formId: formId, field: name, value: newValue)
}
engine.store.setValue(id: schema.effectiveId, value: newValue)
if isFocused { clearError() }
}
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(errorMessage.isEmpty ? Color.clear : Color.red, lineWidth: 1)
)
if !errorMessage.isEmpty {
Text(errorMessage)
.font(.caption2)
.foregroundColor(.red)
}
}
.onAppear {
text = schema.options.value ?? schema.options.defaultvalue ?? ""
let formId = findParentFormId()
if let formId, let name = schema.options.name {
text = engine.store.getFormField(formId: formId, field: name)
if text.isEmpty {
text = schema.options.value ?? schema.options.defaultvalue ?? ""
}
}
}
}
private var effectiveLabel: String {
schema.options.label ?? schema.options.title ?? schema.options.name ?? ""
}
private var placeholder: String {
let raw = schema.options.placeholder ?? ""
return raw.isEmpty ? effectiveLabel : engine.i18n.t(raw)
}
private var showLabel: Bool { true }
private func findParentFormId() -> String? {
// 使schema idform
schema.options.name
}
private func clearError() {
errorMessage = ""
}
}
// MARK: - Textarea Control
struct TextareaControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
@State private var text: String = ""
var body: some View {
VStack(alignment: .leading, spacing: 4) {
if let label = schema.options.label ?? schema.options.title {
Text(engine.i18n.t(label))
.font(.caption)
.foregroundColor(.secondary)
}
#if os(iOS)
TextEditor(text: $text)
.frame(minHeight: 100)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color.secondary.opacity(0.3), lineWidth: 1)
)
#else
TextEditor(text: $text)
.frame(minHeight: 100)
.border(Color.secondary.opacity(0.3))
#endif
}
.onAppear {
text = schema.options.value ?? ""
}
.onChange(of: text) { _, newValue in
engine.store.setValue(id: schema.effectiveId, value: newValue)
}
}
}
// MARK: - Number Control
struct NumberControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
@State private var value: Double = 0
var body: some View {
VStack(alignment: .leading, spacing: 4) {
if let label = schema.options.label ?? schema.options.title {
Text(engine.i18n.t(label))
.font(.caption)
.foregroundColor(.secondary)
}
HStack {
Button("") { value -= step }
.buttonStyle(.bordered)
Text(String(format: format, value))
.frame(minWidth: 60)
.multilineTextAlignment(.center)
Button("+") { value += step }
.buttonStyle(.bordered)
}
}
.onAppear {
value = Double(schema.options.value ?? "0") ?? 0
}
.onChange(of: value) { _, newValue in
engine.store.setValue(id: schema.effectiveId, value: "\(newValue)")
}
}
private var step: Double { 1 }
private var format: String { "%g" }
}
// MARK: - Date Control
struct DateControl: View {
let schema: ControlSchema
@ObservedObject var engine: BricksEngine
@State private var date: Date = Date()
var body: some View {
VStack(alignment: .leading, spacing: 4) {
if let label = schema.options.label ?? schema.options.title {
Text(engine.i18n.t(label))
.font(.caption)
.foregroundColor(.secondary)
}
DatePicker("", selection: $date, displayedComponents: [.date])
.labelsHidden()
}
.onChange(of: date) { _, newValue in
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
engine.store.setValue(id: schema.effectiveId, value: formatter.string(from: newValue))
}
}
}