Skip to content

Commit cde301a

Browse files
committed
SwiftOCAUI: add OcaWritableTextPropertyView for deferred text commits
Simplify OcaWritablePropertyView back to immediate-commit behavior. Add OcaWritableTextPropertyView that embeds a TextField and defers commits to focus loss or Return, preventing per-character updates.
1 parent 32d63f7 commit cde301a

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

Sources/SwiftOCAUI/Views/PropertyView.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,96 @@ public struct OcaWritablePropertyView<Value: Sendable, Resolved: View>: View {
158158
}
159159
}
160160
}
161+
162+
public struct OcaWritableTextPropertyView: View {
163+
let object: OcaRoot
164+
nonisolated(unsafe) let property: any OcaPropertySubjectRepresentable
165+
let prompt: String
166+
167+
@State
168+
private var currentValue: Result<OcaString, Error>?
169+
@State
170+
private var editingValue: OcaString?
171+
@FocusState
172+
private var isFocused: Bool
173+
174+
public init(
175+
_ object: OcaRoot,
176+
_ property: any OcaPropertyRepresentable,
177+
prompt: String = "Label"
178+
) {
179+
self.object = object
180+
self.property = property as! (any OcaPropertySubjectRepresentable)
181+
self.prompt = prompt
182+
}
183+
184+
private var binding: Binding<String> {
185+
Binding<String>(
186+
get: {
187+
if let editingValue {
188+
return editingValue
189+
}
190+
if case let .success(value) = currentValue {
191+
return value
192+
}
193+
return ""
194+
},
195+
set: { newValue in
196+
editingValue = newValue
197+
}
198+
)
199+
}
200+
201+
private func commitEdit() {
202+
guard let editingValue else { return }
203+
currentValue = .success(editingValue)
204+
let value = editingValue
205+
self.editingValue = nil
206+
Task {
207+
try? await property._setValue(object, value)
208+
}
209+
}
210+
211+
public var body: some View {
212+
Group {
213+
if let currentValue {
214+
switch currentValue {
215+
case .success:
216+
TextField(prompt, text: binding)
217+
.textFieldStyle(.roundedBorder)
218+
.focused($isFocused)
219+
.onSubmit {
220+
commitEdit()
221+
}
222+
.onChange(of: isFocused) { _, focused in
223+
if !focused {
224+
commitEdit()
225+
}
226+
}
227+
case let .failure(error):
228+
OcaPropertyErrorView(error: error)
229+
}
230+
} else {
231+
ProgressView()
232+
}
233+
}
234+
.task {
235+
await property.subscribe(object)
236+
do {
237+
for try await result in property.async {
238+
guard editingValue == nil else { continue }
239+
switch result {
240+
case let .success(value):
241+
if let typed = value as? OcaString {
242+
currentValue = .success(typed)
243+
} else {
244+
currentValue = .failure(Ocp1Error.status(.badFormat))
245+
}
246+
case let .failure(error):
247+
currentValue = .failure(error)
248+
}
249+
}
250+
} catch {}
251+
}
252+
}
253+
}

0 commit comments

Comments
 (0)