Skip to content

Commit e679cd3

Browse files
authored
new: Add Select component. (#117)
* Add select. * Use unicode chars. * Use index instead. * Change prop names. * Add multiselect. * Test reporter. * Add estimator. * Update int types.
1 parent ccf6453 commit e679cd3

File tree

13 files changed

+694
-147
lines changed

13 files changed

+694
-147
lines changed

Cargo.lock

+18-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@ members = ["crates/*", "examples/*"]
66
async-trait = "0.1.83"
77
crossterm = "0.28.1"
88
dirs = "5.0.1"
9-
iocraft = "0.5.0"
9+
iocraft = "0.5.1"
10+
# iocraft = { git = "https://github.com/ccbrown/iocraft", branch = "main" }
1011
miette = "7.4.0"
1112
regex = { version = "1.11.1", default-features = false }
1213
relative-path = "1.9.3"
1314
reqwest = { version = "0.12.9", default-features = false }
1415
rustc-hash = "2.1.0"
1516
serial_test = "3.2.0"
16-
serde = { version = "1.0.215", features = ["derive"] }
17+
serde = { version = "1.0.216", features = ["derive"] }
1718
serde_json = "1.0.133"
1819
serde_yaml = "0.9.34"
19-
thiserror = "2.0.4"
20+
thiserror = "2.0.8"
2021
tokio = { version = "1.42.0", default-features = false, features = [
2122
"io-util",
2223
"rt",

crates/console/src/components/confirm.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use super::input_field::*;
2-
use super::styled_text::*;
32
use crate::ui::ConsoleTheme;
43
use iocraft::prelude::*;
54

@@ -12,7 +11,7 @@ pub struct ConfirmProps<'a> {
1211
pub no_char: char,
1312
pub yes_label: String,
1413
pub yes_char: char,
15-
pub value: Option<&'a mut bool>,
14+
pub on_confirm: Option<&'a mut bool>,
1615
}
1716

1817
impl Default for ConfirmProps<'_> {
@@ -25,7 +24,7 @@ impl Default for ConfirmProps<'_> {
2524
no_char: 'n',
2625
yes_label: "Yes".into(),
2726
yes_char: 'y',
28-
value: None,
27+
on_confirm: None,
2928
}
3029
}
3130
}
@@ -74,9 +73,6 @@ pub fn Confirm<'a>(props: &mut ConfirmProps<'a>, mut hooks: Hooks) -> impl Into<
7473
error.set(Some(format!("Please press [{yes}] or [{no}] to confirm")));
7574
}
7675
}
77-
KeyCode::Esc => {
78-
handle_confirm(false);
79-
}
8076
KeyCode::Left | KeyCode::Up | KeyCode::BackTab => {
8177
set_focused(focused.get() - 1);
8278
}
@@ -91,7 +87,7 @@ pub fn Confirm<'a>(props: &mut ConfirmProps<'a>, mut hooks: Hooks) -> impl Into<
9187
});
9288

9389
if should_exit.get() {
94-
if let Some(outer_value) = &mut props.value {
90+
if let Some(outer_value) = &mut props.on_confirm {
9591
**outer_value = confirmed.get();
9692
}
9793

@@ -113,10 +109,11 @@ pub fn Confirm<'a>(props: &mut ConfirmProps<'a>, mut hooks: Hooks) -> impl Into<
113109
error: Some(error),
114110
footer: props.legend.then(|| {
115111
element! {
116-
StyledText(
117-
content: format!("<mutedlight>{yes}/{no}</mutedlight> confirm ⁃ <mutedlight>←/→</mutedlight> toggle ⁃ <mutedlight>ent/spc</mutedlight> select ⁃ <mutedlight>esc</mutedlight> cancel"),
118-
style: Style::Muted
119-
)
112+
InputLegend(legend: vec![
113+
(format!("{yes}/{no}"), "confirm".into()),
114+
("↔".into(), "toggle".into()),
115+
("↵".into(), "submit".into()),
116+
])
120117
}.into_any()
121118
})
122119
) {
@@ -162,5 +159,6 @@ pub fn Confirm<'a>(props: &mut ConfirmProps<'a>, mut hooks: Hooks) -> impl Into<
162159
}
163160
}
164161
}
165-
}.into_any()
162+
}
163+
.into_any()
166164
}

crates/console/src/components/input.rs

+16-28
Original file line numberDiff line numberDiff line change
@@ -10,62 +10,50 @@ pub struct InputProps<'a> {
1010
pub label: String,
1111
pub prefix_symbol: Option<String>,
1212
pub validate: Validator<'static, String>,
13-
pub value: Option<&'a mut String>,
13+
pub on_value: Option<&'a mut String>,
1414
}
1515

1616
#[component]
1717
pub fn Input<'a>(props: &mut InputProps<'a>, mut hooks: Hooks) -> impl Into<AnyElement<'a>> {
1818
let theme = hooks.use_context::<ConsoleTheme>();
1919
let mut system = hooks.use_context_mut::<SystemContext>();
2020
let mut value = hooks.use_state(|| props.default_value.clone());
21-
let mut submitted = hooks.use_state(|| false);
2221
let mut should_exit = hooks.use_state(|| false);
2322
let mut error = hooks.use_state(|| None);
2423

2524
let validate = props.validate.take();
2625

2726
hooks.use_local_terminal_events({
2827
move |event| match event {
29-
TerminalEvent::Key(KeyEvent { code, kind, .. }) if kind != KeyEventKind::Release => {
30-
match code {
31-
KeyCode::Enter => {
32-
if let Some(msg) = validate(value.to_string()) {
33-
error.set(Some(msg));
34-
return;
35-
} else {
36-
error.set(None);
37-
}
38-
39-
submitted.set(true);
40-
should_exit.set(true);
41-
}
42-
KeyCode::Esc => {
43-
should_exit.set(true);
44-
}
45-
_ => {}
28+
TerminalEvent::Key(KeyEvent {
29+
code: KeyCode::Enter,
30+
kind,
31+
..
32+
}) if kind != KeyEventKind::Release => {
33+
if let Some(msg) = validate(value.to_string()) {
34+
error.set(Some(msg));
35+
return;
36+
} else {
37+
error.set(None);
4638
}
39+
40+
should_exit.set(true);
4741
}
4842
_ => {}
4943
}
5044
});
5145

5246
if should_exit.get() {
53-
if submitted.get() {
54-
if let Some(outer_value) = &mut props.value {
55-
**outer_value = value.to_string();
56-
}
47+
if let Some(outer_value) = &mut props.on_value {
48+
**outer_value = value.to_string();
5749
}
5850

5951
system.exit();
6052

6153
return element! {
6254
InputFieldValue(
6355
label: &props.label,
64-
value: if submitted.get() {
65-
value.to_string()
66-
} else {
67-
String::new()
68-
}
56+
value: value.read().as_str(),
6957
)
7058
}
7159
.into_any();

crates/console/src/components/input_field.rs

+19
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,22 @@ pub fn InputFieldValue<'a>(
116116
}
117117
}
118118
}
119+
120+
#[derive(Default, Props)]
121+
pub struct InputLegendProps {
122+
pub legend: Vec<(String, String)>,
123+
}
124+
125+
#[component]
126+
pub fn InputLegend<'a>(props: &InputLegendProps) -> impl Into<AnyElement<'a>> {
127+
element! {
128+
StyledText(
129+
content: props.legend
130+
.iter()
131+
.map(|(key, label)| format!("<mutedlight>{key}</mutedlight> {label}"))
132+
.collect::<Vec<_>>()
133+
.join(" ⁃ "),
134+
style: Style::Muted
135+
)
136+
}
137+
}

crates/console/src/components/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod map;
88
mod notice;
99
mod progress;
1010
mod section;
11+
mod select;
1112
mod styled_text;
1213
mod table;
1314

@@ -20,6 +21,7 @@ pub use map::*;
2021
pub use notice::*;
2122
pub use progress::*;
2223
pub use section::*;
24+
pub use select::*;
2325
pub use styled_text::*;
2426
pub use table::*;
2527

0 commit comments

Comments
 (0)