Require regulators to have valid specifications

When an invalid specification is entered into a regulator input, keep it
confined to that input. Reset a regulator input by pressing *escape*.
This commit is contained in:
Aaron Fenyes 2025-02-18 01:27:11 -08:00
parent fef4127f69
commit 302d93638d
3 changed files with 49 additions and 26 deletions

View file

@ -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);
}

View file

@ -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<String>
}
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,

View file

@ -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::<f64>().ok());
regulator.set_point_spec.set(value_val);
match value_val.parse::<f64>() {
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)