worldwideportal/src/editor_view/editor_nav.rs

126 lines
5.4 KiB
Rust

use std::{ops::Deref, rc::Rc};
use itertools::Itertools;
use web_sys::{KeyboardEvent, MouseEvent};
use yew::{function_component, html, AttrValue, Html, UseStateHandle};
use super::{close_editor, run_script, select_file, EditorViewDetailProps, EditorViewState};
fn show_create_dialog(state: &UseStateHandle<Rc<EditorViewState>>) {
let mut new_state: EditorViewState = (*state.deref().deref()).clone();
new_state.show_create_dialog = true;
state.set(new_state.into());
}
fn item_keyboard_handler(
item: AttrValue,
editor_state: UseStateHandle<Rc<EditorViewState>>,
) -> impl Fn(KeyboardEvent) {
move |ev: KeyboardEvent| {
if ev.code() == "Enter" && !ev.get_modifier_state("Control") {
let mut state_mut = (*editor_state.as_ref()).clone();
state_mut.open_file = item.clone();
editor_state.set(state_mut.into());
} else if ev.code() == "ArrowUp" {
if let Some((pos, _)) = editor_state
.available_files
.iter()
.find_position(|v| **v == editor_state.open_file)
{
if pos > 0 {
let mut state_mut = (*editor_state.as_ref()).clone();
state_mut.open_file = editor_state.available_files[pos - 1].clone();
editor_state.set(state_mut.into());
}
}
} else if ev.code() == "ArrowDown" {
if let Some((pos, _)) = editor_state
.available_files
.iter()
.find_position(|v| **v == editor_state.open_file)
{
if pos < editor_state.available_files.len() - 1 {
let mut state_mut = (*editor_state.as_ref()).clone();
state_mut.open_file = editor_state.available_files[pos + 1].clone();
editor_state.set(state_mut.into());
}
}
}
}
}
fn show_delete_dialog(state: &UseStateHandle<Rc<EditorViewState>>, script: &AttrValue) {
let mut new_state = (*state.as_ref()).clone();
new_state.show_delete_dialog = Some(script.clone());
state.set(new_state.into());
}
#[function_component(EditorNav)]
pub(super) fn editor_nav(props: &EditorViewDetailProps) -> Html {
let global_memo = props.global_memo.clone();
let global_layout = props.global_layout.clone();
let frame = props.frame.clone();
let current_script = props.editor_state.open_file.clone();
let set_err_state = props.editor_state.clone();
let set_err = Rc::new(move |msg: Option<&str>| {
let mut new_state = (*set_err_state.as_ref()).clone();
new_state.error_msg = msg.map(|v| AttrValue::from(String::from(v)));
set_err_state.set(new_state.into());
});
let editor_state = props.editor_state.clone();
let frame_for_runclick = frame.clone();
html! {
<div class="editornav">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<button class="btn" aria-label="Run" title="Run"
onclick={move |_ev| run_script(&current_script, &global_memo, set_err.clone(), &frame_for_runclick)}>
<i class="bi bi-file-earmark-play"></i></button>
<button class="btn" aria-label="New file" title="New file"
onclick={move |_ev| show_create_dialog(&editor_state) }
><i class="bi bi-file-earmark-plus"></i></button>
<div class="flex-fill"/>
<button class="btn" onclick={move |_ev| close_editor(&frame, &global_layout)} aria-label="Close Editor" title="Close editor"><i class="bi bi-arrow-return-left"></i></button>
</nav>
<ul class="p-2 list-group">
{props
.editor_state
.available_files
.iter()
.map(|f| {
let mut classes = vec!["list-group-item", "d-flex"];
let mut aria_current = None;
if *f == props.editor_state.open_file {
aria_current = Some("true");
classes.push("active");
}
let filename_for_click: AttrValue = f.clone();
let state_for_click = props.editor_state.clone();
let filename_for_kb: AttrValue = f.clone();
let state_for_kb = props.editor_state.clone();
let state_for_delscript = state_for_kb.clone();
let filename_for_delscript: AttrValue = f.clone();
html! { <li aria-current={aria_current}
class={classes.iter().join(" ")}
>
<div tabindex={0}
class="pe-auto"
onkeydown={item_keyboard_handler(filename_for_kb, state_for_kb)}
style="cursor: pointer"
onclick={move |_ev| select_file(&filename_for_click, &state_for_click)}>{f}</div>
<div class="flex-fill"/>
{if f != "init.lua" {
html! { <button class="btn" onclick={move |ev: MouseEvent| {
show_delete_dialog(&state_for_delscript, &filename_for_delscript);
ev.prevent_default();
}} aria-label="Delete" title="Delete"><i class="bi bi-trash"></i></button> }
} else { html!{<></>} } }
</li>}
})
.collect::<Vec<Html>>()
}
</ul>
</div>
}
}