80 lines
2.2 KiB
Rust
80 lines
2.2 KiB
Rust
use web_sys::HtmlInputElement;
|
|
use yew::prelude::*;
|
|
|
|
#[derive(Properties, PartialEq)]
|
|
pub struct InputStringProps {
|
|
pub value: String,
|
|
}
|
|
|
|
pub enum Msg {
|
|
CharInput(usize, String),
|
|
}
|
|
|
|
pub struct InputString {
|
|
value: String,
|
|
nodes: Vec<NodeRef>,
|
|
focused_index: usize,
|
|
}
|
|
|
|
impl Component for InputString {
|
|
type Message = Msg;
|
|
type Properties = InputStringProps;
|
|
|
|
fn create(ctx: &Context<Self>) -> Self {
|
|
let value = ctx.props().value.clone();
|
|
let nodes = vec![NodeRef::default(); value.len()];
|
|
Self {
|
|
value,
|
|
nodes,
|
|
focused_index: 0,
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
match msg {
|
|
Msg::CharInput(index, new_char) => {
|
|
let mut new_value = self.value.clone();
|
|
new_value.replace_range(index..index + 1, &new_char);
|
|
self.value = new_value;
|
|
|
|
if index < self.value.len() - 1 {
|
|
self.focused_index = index + 1;
|
|
if let Some(next_node) = self.nodes.get(self.focused_index) {
|
|
if let Some(input) = next_node.cast::<HtmlInputElement>() {
|
|
input.focus().unwrap();
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
}
|
|
|
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
let chars = self.value.chars().enumerate().map(|(index, char)| {
|
|
let on_input = ctx.link().callback(move |input: InputEvent| {
|
|
let new_char = input.data();
|
|
Msg::CharInput(index, new_char.unwrap())
|
|
});
|
|
|
|
html! {
|
|
<input
|
|
type="text"
|
|
maxlength=1
|
|
value={char.to_string()}
|
|
oninput={on_input}
|
|
class="w-12 h-16 text-center"
|
|
ref={self.nodes.get(index).unwrap().clone()}
|
|
style={if index == self.focused_index { "background-color: yellow;" } else { "" }}
|
|
/>
|
|
}
|
|
});
|
|
|
|
html! {
|
|
<div style="display: flex; gap: 0.5rem;">
|
|
{ for chars }
|
|
</div>
|
|
}
|
|
}
|
|
}
|