@@ -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