@@ -4,21 +4,24 @@ use dioxus::prelude::*;
44use property_model:: { CellId , Sheet } ;
55
66use crate :: bridge:: Labels ;
7+ use crate :: spectrum:: { SpDivider , SpFieldLabel , SpHeading , SpTextfield } ;
78
89/// Sidebar panel showing all cells with labels, current values, and text inputs for writing.
910///
1011/// Editing an input field immediately writes the parsed value to the sheet and propagates
11- /// constraints. If propagation fails (for example, division by zero), the input field shows
12- /// a red border until the user blurs. The input is not reset while the field is focused;
13- /// it syncs back to the computed value on blur, keeping non-edited cells up to date.
12+ /// constraints. If parsing or propagation fails (for example, non-numeric input or division
13+ /// by zero), `SpTextfield` renders in its invalid state until the user blurs. The input is
14+ /// not reset while the field is focused; it syncs back to the computed value on blur,
15+ /// keeping non-edited cells up to date.
1416#[ component]
1517pub fn Inspector ( sheet : Signal < Sheet > , labels : Signal < Labels > ) -> Element {
1618 let ids: Vec < CellId > = labels. read ( ) . cells . keys ( ) . copied ( ) . collect ( ) ;
1719
1820 rsx ! {
1921 div {
20- style: "width: 260px; min-width: 260px; height: 100%; overflow-y: auto; border-left: 1px solid #ddd; padding: 12px; box-sizing: border-box; font-family: monospace; font-size: 13px;" ,
21- h3 { style: "margin: 0 0 12px 0; font-size: 14px;" , "Cells" }
22+ style: "width: 260px; min-width: 260px; height: 100%; overflow-y: auto; padding: 12px; box-sizing: border-box;" ,
23+ SpHeading { "Cells" }
24+ SpDivider { }
2225 for id in ids {
2326 CellRow { key: "{id:?}" , id, sheet, labels }
2427 }
@@ -59,41 +62,55 @@ fn CellRow(id: CellId, sheet: Signal<Sheet>, labels: Signal<Labels>) -> Element
5962 }
6063 } ) ;
6164
65+ let field_id = format ! ( "cell-{id:?}" ) ;
66+
6267 rsx ! {
6368 div {
64- style: "margin-bottom: 10px;" ,
65- div { style: "font-weight: bold; margin-bottom: 2px;" , "{label}" }
66- div { style: "color: #888; margin-bottom: 4px; font-size: 11px;" , "{value}" }
67- input {
68- r#type: "text" ,
69- value: "{input}" ,
70- style: if * has_error. read( ) {
71- "width: 100%; box-sizing: border-box; font-family: monospace; font-size: 12px; border-color: #c00;"
72- } else {
73- "width: 100%; box-sizing: border-box; font-family: monospace; font-size: 12px;"
69+ style: "margin-bottom: 8px;" ,
70+ SpFieldLabel { for_: field_id. clone( ) , "{label}" }
71+ SpTextfield {
72+ id: field_id,
73+ value: input. read( ) . clone( ) ,
74+ invalid: * has_error. read( ) ,
75+ // Dioxus's event serializer only reads event.target.value for
76+ // HTMLInputElement — custom elements (sp-textfield) always give "".
77+ // Use dioxus.send() in JS and eval.recv() to read the live value.
78+ oninput: move |_: FormEvent | {
79+ spawn( async move {
80+ let mut eval = document:: eval( & format!(
81+ r#"dioxus.send(document.getElementById("cell-{id:?}").value)"#
82+ ) ) ;
83+ let Ok ( val) = eval. recv:: <String >( ) . await else { return ; } ;
84+ // Discard the result if the user blurred while the round-trip was
85+ // in flight; blur already cleared the error and use_effect will
86+ // restore the last valid computed value.
87+ if !* is_focused. read( ) {
88+ return ;
89+ }
90+ input. set( val. clone( ) ) ;
91+ let mut sheet_w = sheet. write( ) ;
92+ let labels_r = labels. read( ) ;
93+ if let Some ( meta) = labels_r. cells. get( & id) {
94+ if ( meta. write_str) ( & mut sheet_w, & val) . is_ok( ) {
95+ let result = if sheet_w. is_source( id) {
96+ sheet_w. propagate_without_replan( )
97+ } else {
98+ sheet_w. propagate( )
99+ } ;
100+ has_error. set( result. is_err( ) ) ;
101+ } else {
102+ has_error. set( true ) ;
103+ }
104+ }
105+ } ) ;
74106 } ,
75107 onfocus: move |_| is_focused. set( true ) ,
76108 onblur: move |_| {
77109 is_focused. set( false ) ;
78110 has_error. set( false ) ;
79111 } ,
80- oninput: move |e| {
81- let s = e. value( ) ;
82- input. set( s. clone( ) ) ;
83- let mut sheet_w = sheet. write( ) ;
84- let labels_r = labels. read( ) ;
85- if let Some ( meta) = labels_r. cells. get( & id)
86- && ( meta. write_str) ( & mut sheet_w, & s) . is_ok( )
87- {
88- let result = if sheet_w. is_source( id) {
89- sheet_w. propagate_without_replan( )
90- } else {
91- sheet_w. propagate( )
92- } ;
93- has_error. set( result. is_err( ) ) ;
94- }
95- } ,
96112 }
97113 }
114+ SpDivider { }
98115 }
99116}
0 commit comments