diff --git a/app-proto/main.css b/app-proto/main.css index d71ce4a..7ba1206 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -133,28 +133,25 @@ details[open]:has(li) .element-switch::after { font-size: 10pt; } -.regulator.invalid-constraint { - color: var(--text-invalid); -} - -.regulator > input { +.regulator-input { color: inherit; background-color: inherit; border: 1px solid var(--border); border-radius: 2px; } -.regulator > input::placeholder { +.regulator-input::placeholder { color: inherit; opacity: 54%; font-style: italic; } -.regulator.valid-constraint > input { +.regulator-input.constraint { background-color: var(--display-background); } -.regulator.invalid-constraint > input { +.regulator-input.invalid { + color: var(--text-invalid); border-color: var(--border-invalid); } @@ -166,7 +163,7 @@ details[open]:has(li) .element-switch::after { font-style: normal; } -.invalid-constraint > .status::after, details:has(.invalid-constraint):not([open]) .status::after { +.regulator:has(.invalid) > .status::after, details:has(.invalid):not([open]) .status::after { content: '⚠'; color: var(--text-invalid); } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index c1eba1e..e8a8dc0 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -111,6 +111,7 @@ impl Element { } } +// `set_point_spec` must always be a valid specification of `set_point` #[derive(Clone, Copy)] pub struct Regulator { pub subjects: (ElementKey, ElementKey), @@ -119,12 +120,6 @@ pub struct Regulator { pub set_point_spec: Signal } -impl Regulator { - pub fn has_no_set_point_spec(&self) -> bool { - self.set_point_spec.with(|spec| spec.is_empty()) - } -} - // the velocity is expressed in uniform coordinates pub struct ElementMotion<'a> { pub key: ElementKey, diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index d892f0f..b0347f6 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -19,17 +19,55 @@ use crate::{ // an editable view of a regulator #[component(inline_props)] fn RegulatorInput(regulator: Regulator) -> View { + let valid = create_signal(true); let value = create_signal(regulator.set_point_spec.get_clone_untracked()); - create_effect(move || value.set(regulator.set_point_spec.get_clone())); + + // this closure resets the input value to the regulator's set point + // specification, which is always a valid specification + let reset_value = move || { + batch(|| { + valid.set(true); + value.set(regulator.set_point_spec.get_clone()); + }) + }; + + // reset the input value whenever the regulator's set point specification + // is updated + create_effect(reset_value); + view! { input( r#type="text", + class=move || { + if valid.get() { + match regulator.set_point.get() { + Some(_) => "regulator-input constraint", + None => "regulator-input" + } + } else { + "regulator-input invalid" + } + }, placeholder=regulator.measurement.with(|result| result.to_string()), bind:value=value, on:change=move |_| { let value_val = value.get_clone_untracked(); - regulator.set_point.set(value_val.parse::().ok()); - regulator.set_point_spec.set(value_val); + match value_val.parse::() { + Err(_) if !value_val.is_empty() => valid.set(false), + set_pt => batch(|| { + regulator.set_point.set(set_pt.ok()); + regulator.set_point_spec.set(value_val); + valid.set(true); + }) + }; + }, + on:keydown={ + move |event: KeyboardEvent| { + match event.key().as_str() { + "Escape" => reset_value(), + _ => () + } + } } ) } @@ -47,15 +85,8 @@ fn RegulatorOutlineItem(regulator_key: RegulatorKey, element_key: ElementKey) -> regulator.subjects.0 }; let other_subject_label = assembly.elements.with(|elts| elts[other_subject].label.clone()); - let class = create_memo(move || { - match regulator.set_point.get() { - None if regulator.has_no_set_point_spec() => "regulator", - None => "regulator invalid-constraint", - Some(_) => "regulator valid-constraint" - } - }); view! { - li(class=class.get()) { + li(class="regulator") { div(class="regulator-label") { (other_subject_label) } div(class="regulator-type") { "Inversive distance" } RegulatorInput(regulator=regulator)