
This will provide a common interface for Lorentz product regulators, curvature regulators, and hopefully all the other regulators too.
245 lines
No EOL
8.4 KiB
Rust
245 lines
No EOL
8.4 KiB
Rust
use itertools::Itertools;
|
|
use std::rc::Rc;
|
|
use sycamore::prelude::*;
|
|
use web_sys::{
|
|
KeyboardEvent,
|
|
MouseEvent,
|
|
wasm_bindgen::JsCast
|
|
};
|
|
|
|
use crate::{
|
|
AppState,
|
|
assembly,
|
|
assembly::{ElementKey, Regulator, RegulatorKey},
|
|
specified::SpecifiedValue
|
|
};
|
|
|
|
// an editable view of a regulator
|
|
#[component(inline_props)]
|
|
fn RegulatorInput(regulator: Rc<dyn Regulator>) -> View {
|
|
// get the regulator's measurement and set point signals
|
|
let measurement = regulator.measurement();
|
|
let set_point = regulator.set_point();
|
|
|
|
// the `valid` signal tracks whether the last entered value is a valid set
|
|
// point specification
|
|
let valid = create_signal(true);
|
|
|
|
// the `value` signal holds the current set point specification
|
|
let value = create_signal(
|
|
set_point.with_untracked(|set_pt| set_pt.spec.clone())
|
|
);
|
|
|
|
// this `reset_value` closure resets the input value to the regulator's set
|
|
// point specification
|
|
let reset_value = move || {
|
|
batch(|| {
|
|
valid.set(true);
|
|
value.set(set_point.with(|set_pt| set_pt.spec.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() {
|
|
set_point.with(|set_pt| {
|
|
if set_pt.is_present() {
|
|
"regulator-input constraint"
|
|
} else {
|
|
"regulator-input"
|
|
}
|
|
})
|
|
} else {
|
|
"regulator-input invalid"
|
|
}
|
|
},
|
|
placeholder=measurement.with(|result| result.to_string()),
|
|
bind:value=value,
|
|
on:change=move |_| {
|
|
valid.set(
|
|
match SpecifiedValue::try_from(value.get_clone_untracked()) {
|
|
Ok(set_pt) => {
|
|
set_point.set(set_pt);
|
|
true
|
|
}
|
|
Err(_) => false
|
|
}
|
|
)
|
|
},
|
|
on:keydown={
|
|
move |event: KeyboardEvent| {
|
|
match event.key().as_str() {
|
|
"Escape" => reset_value(),
|
|
_ => ()
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
// a list item that shows a regulator in an outline view of an element
|
|
#[component(inline_props)]
|
|
fn RegulatorOutlineItem(regulator_key: RegulatorKey, element_key: ElementKey) -> View {
|
|
let state = use_context::<AppState>();
|
|
let assembly = &state.assembly;
|
|
let regulator = assembly.regulators.with(|regs| regs[regulator_key].clone());
|
|
let subjects = regulator.subjects();
|
|
let other_subject = if subjects[0] == element_key {
|
|
subjects[1]
|
|
} else {
|
|
subjects[0]
|
|
};
|
|
let other_subject_label = assembly.elements.with(|elts| elts[other_subject].label.clone());
|
|
view! {
|
|
li(class="regulator") {
|
|
div(class="regulator-label") { (other_subject_label) }
|
|
div(class="regulator-type") { "Inversive distance" }
|
|
RegulatorInput(regulator=regulator)
|
|
div(class="status")
|
|
}
|
|
}
|
|
}
|
|
|
|
// a list item that shows an element in an outline view of an assembly
|
|
#[component(inline_props)]
|
|
fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View {
|
|
let state = use_context::<AppState>();
|
|
let class = state.selection.map(
|
|
move |sel| if sel.contains(&key) { "selected" } else { "" }
|
|
);
|
|
let label = element.label.clone();
|
|
let rep_components = move || {
|
|
element.representation.with(
|
|
|rep| rep.iter().map(
|
|
|u| {
|
|
let u_str = format!("{:.3}", u).replace("-", "\u{2212}");
|
|
view! { div { (u_str) } }
|
|
}
|
|
).collect::<Vec<_>>()
|
|
)
|
|
};
|
|
let regulated = element.regulators.map(|regs| regs.len() > 0);
|
|
let regulator_list = element.regulators.map(
|
|
|regs| regs.clone().into_iter().collect()
|
|
);
|
|
let details_node = create_node_ref();
|
|
view! {
|
|
li {
|
|
details(ref=details_node) {
|
|
summary(
|
|
class=class.get(),
|
|
on:keydown={
|
|
move |event: KeyboardEvent| {
|
|
match event.key().as_str() {
|
|
"Enter" => {
|
|
state.select(key, event.shift_key());
|
|
event.prevent_default();
|
|
},
|
|
"ArrowRight" if regulated.get() => {
|
|
let _ = details_node
|
|
.get()
|
|
.unchecked_into::<web_sys::Element>()
|
|
.set_attribute("open", "");
|
|
},
|
|
"ArrowLeft" => {
|
|
let _ = details_node
|
|
.get()
|
|
.unchecked_into::<web_sys::Element>()
|
|
.remove_attribute("open");
|
|
},
|
|
_ => ()
|
|
}
|
|
}
|
|
}
|
|
) {
|
|
div(
|
|
class="element-switch",
|
|
on:click=|event: MouseEvent| event.stop_propagation()
|
|
)
|
|
div(
|
|
class="element",
|
|
on:click={
|
|
move |event: MouseEvent| {
|
|
if event.shift_key() {
|
|
state.selection.update(|sel| {
|
|
if !sel.remove(&key) {
|
|
sel.insert(key);
|
|
}
|
|
});
|
|
} else {
|
|
state.selection.update(|sel| {
|
|
sel.clear();
|
|
sel.insert(key);
|
|
});
|
|
}
|
|
event.stop_propagation();
|
|
event.prevent_default();
|
|
}
|
|
}
|
|
) {
|
|
div(class="element-label") { (label) }
|
|
div(class="element-representation") { (rep_components) }
|
|
div(class="status")
|
|
}
|
|
}
|
|
ul(class="regulators") {
|
|
Keyed(
|
|
list=regulator_list,
|
|
view=move |reg_key| view! {
|
|
RegulatorOutlineItem(
|
|
regulator_key=reg_key,
|
|
element_key=key
|
|
)
|
|
},
|
|
key=|reg_key| reg_key.clone()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// a component that lists the elements of the current assembly, showing each
|
|
// element's regulators in a collapsible sub-list. its implementation is based
|
|
// on Kate Morley's HTML + CSS tree views:
|
|
//
|
|
// https://iamkate.com/code/tree-views/
|
|
//
|
|
#[component]
|
|
pub fn Outline() -> View {
|
|
let state = use_context::<AppState>();
|
|
|
|
// list the elements alphabetically by ID
|
|
let element_list = state.assembly.elements.map(
|
|
|elts| elts
|
|
.clone()
|
|
.into_iter()
|
|
.sorted_by_key(|(_, elt)| elt.id.clone())
|
|
.collect()
|
|
);
|
|
|
|
view! {
|
|
ul(
|
|
id="outline",
|
|
on:click={
|
|
let state = use_context::<AppState>();
|
|
move |_| state.selection.update(|sel| sel.clear())
|
|
}
|
|
) {
|
|
Keyed(
|
|
list=element_list,
|
|
view=|(key, elt)| view! {
|
|
ElementOutlineItem(key=key, element=elt)
|
|
},
|
|
key=|(_, elt)| elt.serial
|
|
)
|
|
}
|
|
}
|
|
} |