Support run selected sql
This commit is contained in:
@@ -36,6 +36,9 @@ mod bindgen {
|
||||
|
||||
#[wasm_bindgen(method, js_name = getValue)]
|
||||
pub fn get_value(this: &Editor) -> String;
|
||||
|
||||
#[wasm_bindgen(method, js_name = getSelectedText)]
|
||||
pub fn get_selected_text(this: &Editor) -> String;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,4 +174,8 @@ impl Editor {
|
||||
pub fn get_value(&self) -> String {
|
||||
self.js.get_value()
|
||||
}
|
||||
|
||||
pub fn get_selected_value(&self) -> String {
|
||||
self.js.get_selected_text()
|
||||
}
|
||||
}
|
||||
|
||||
35
src/app/advanced_options_menu.rs
Normal file
35
src/app/advanced_options_menu.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use leptos::prelude::*;
|
||||
use reactive_stores::Store;
|
||||
|
||||
use crate::app::{
|
||||
GlobalState, GlobalStateStoreFields, config_element::Either, menu_group::MenuGroup,
|
||||
};
|
||||
|
||||
#[component]
|
||||
pub fn AdvancedOptionsMenu() -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
let value = move || *state.run_selected_code().read();
|
||||
let is_default = move || !*state.run_selected_code().read();
|
||||
let on_change = move |value: &bool| {
|
||||
state.run_selected_code().set(*value);
|
||||
};
|
||||
|
||||
view! {
|
||||
<>
|
||||
<MenuGroup title="Advanced options".into()>
|
||||
<Either
|
||||
id="run_selected_code".into()
|
||||
name="Run Selected Code".into()
|
||||
a=true
|
||||
b=false
|
||||
a_label=Some("On".to_string())
|
||||
b_label=Some("Off".to_string())
|
||||
value=value
|
||||
is_default=Box::new(is_default)
|
||||
on_change=on_change
|
||||
/>
|
||||
</MenuGroup>
|
||||
</>
|
||||
}
|
||||
}
|
||||
@@ -66,3 +66,22 @@ where
|
||||
pub fn Rule() -> impl IntoView {
|
||||
view! { <span class=styles::rule /> }
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn IconButton<C>(
|
||||
#[prop(default = false)] is_small: bool,
|
||||
#[prop(optional)] node_ref: NodeRef<html::element::Button>,
|
||||
on_click: C,
|
||||
children: Children,
|
||||
) -> impl IntoView
|
||||
where
|
||||
C: FnMut(MouseEvent) + Send + 'static,
|
||||
{
|
||||
let style = if is_small { styles::small } else { "" };
|
||||
let style = format!("{} {style}", styles::icon);
|
||||
view! {
|
||||
<button node_ref=node_ref class=style on:click=on_click>
|
||||
{children()}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use istyles::istyles;
|
||||
use leptos::prelude::*;
|
||||
use web_sys::Event;
|
||||
@@ -9,11 +11,83 @@ istyles!(
|
||||
"assets/module.postcss/config_element.module.css.map"
|
||||
);
|
||||
|
||||
#[component]
|
||||
pub fn Either<E, V, T>(
|
||||
on_change: E,
|
||||
id: String,
|
||||
a: T,
|
||||
b: T,
|
||||
a_label: Option<String>,
|
||||
b_label: Option<String>,
|
||||
value: V,
|
||||
name: String,
|
||||
#[prop(default = Box::new(|| true))] is_default: Box<dyn Fn() -> bool + Send>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
E: Fn(&T) + Send + Sync + 'static,
|
||||
V: Fn() -> T + Send + Sync + 'static,
|
||||
T: PartialEq + Eq + Send + Sync + ToString + 'static,
|
||||
{
|
||||
let a_label = if let Some(label) = a_label {
|
||||
label
|
||||
} else {
|
||||
a.to_string()
|
||||
};
|
||||
|
||||
let b_label = if let Some(label) = b_label {
|
||||
label
|
||||
} else {
|
||||
b.to_string()
|
||||
};
|
||||
|
||||
let a_id = format!("{id}-a");
|
||||
let b_id = format!("{id}-b");
|
||||
|
||||
let on_change1 = Arc::new(on_change);
|
||||
let on_change2 = Arc::clone(&on_change1);
|
||||
|
||||
let value1 = Arc::new(value);
|
||||
let value2 = Arc::clone(&value1);
|
||||
|
||||
let a_value = a.to_string();
|
||||
let b_value = b.to_string();
|
||||
|
||||
let a1 = Arc::new(a);
|
||||
let a2 = Arc::clone(&a1);
|
||||
let b1 = Arc::new(b);
|
||||
let b2 = Arc::clone(&b1);
|
||||
|
||||
view! {
|
||||
<ConfigElement name=name is_default=is_default>
|
||||
<div class=styles::toggle>
|
||||
<input
|
||||
id=a_id.clone()
|
||||
name=id.clone()
|
||||
value=a_value
|
||||
type="radio"
|
||||
checked=move || value1().eq(&a1)
|
||||
on:change=move |_ev| on_change1(&a2)
|
||||
/>
|
||||
<label for=a_id>{a_label}</label>
|
||||
<input
|
||||
id=b_id.clone()
|
||||
name=id.clone()
|
||||
value=b_value
|
||||
type="radio"
|
||||
checked=move || value2().eq(&b1)
|
||||
on:change=move |_ev| on_change2(&b2)
|
||||
/>
|
||||
<label for=b_id>{b_label}</label>
|
||||
</div>
|
||||
</ConfigElement>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Select<E>(
|
||||
on_change: E,
|
||||
name: String,
|
||||
#[prop(default = true)] is_default: bool,
|
||||
#[prop(default = Box::new(|| true))] is_default: Box<dyn Fn() -> bool + Send>,
|
||||
children: Children,
|
||||
) -> impl IntoView
|
||||
where
|
||||
@@ -31,13 +105,15 @@ where
|
||||
#[component]
|
||||
pub fn ConfigElement(
|
||||
name: String,
|
||||
#[prop(default = true)] is_default: bool,
|
||||
#[prop(default = Box::new(|| true))] is_default: Box<dyn Fn() -> bool + Send>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
let style = if is_default {
|
||||
styles::name
|
||||
} else {
|
||||
styles::notDefault
|
||||
let style = move || {
|
||||
if is_default() {
|
||||
styles::name
|
||||
} else {
|
||||
styles::notDefault
|
||||
}
|
||||
};
|
||||
view! {
|
||||
<MenuItem>
|
||||
|
||||
@@ -6,10 +6,11 @@ use web_sys::{Url, UrlSearchParams};
|
||||
use crate::{
|
||||
PrepareOptions, WorkerRequest,
|
||||
app::{
|
||||
button_set::{Button, ButtonSet, Rule},
|
||||
advanced_options_menu::AdvancedOptionsMenu,
|
||||
button_set::{Button, ButtonSet, IconButton, Rule},
|
||||
config_menu::ConfigMenu,
|
||||
context_menu::ContextMenu,
|
||||
icon::{build_icon, config_icon, expandable_icon},
|
||||
icon::{build_icon, config_icon, expandable_icon, more_options_icon},
|
||||
output::change_focus,
|
||||
pop_button::PopButton,
|
||||
state::{Focus, GlobalState, GlobalStateStoreFields},
|
||||
@@ -35,6 +36,8 @@ pub fn Header() -> impl IntoView {
|
||||
<VfsMenuButton menu_container=menu_container />
|
||||
<Rule />
|
||||
<ContextMenuButton menu_container=menu_container />
|
||||
<Rule />
|
||||
<AdvancedOptionsMenuButton menu_container=menu_container />
|
||||
</ButtonSet>
|
||||
</div>
|
||||
<div class=styles::right>
|
||||
@@ -53,21 +56,29 @@ pub fn Header() -> impl IntoView {
|
||||
|
||||
pub fn execute(state: Store<GlobalState>) -> Box<dyn Fn() + Send + 'static> {
|
||||
Box::new(move || {
|
||||
let Some(code) = state
|
||||
let Some((code, selected_code)) = state
|
||||
.editor()
|
||||
.read_untracked()
|
||||
.as_ref()
|
||||
.map(|editor| editor.get_value())
|
||||
.map(|editor| (editor.get_value(), editor.get_selected_value()))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let run_selected_code = state.run_selected_code().get();
|
||||
|
||||
state.code().set(code.clone());
|
||||
change_focus(state, Some(Focus::Execute));
|
||||
std::mem::take(&mut *state.output().write());
|
||||
|
||||
if let Some(worker) = &*state.worker().read_untracked() {
|
||||
worker.send_task(WorkerRequest::Prepare(PrepareOptions {
|
||||
id: String::new(),
|
||||
sql: code,
|
||||
sql: if !selected_code.is_empty() && run_selected_code {
|
||||
selected_code
|
||||
} else {
|
||||
code
|
||||
},
|
||||
clear_on_prepare: !*state.keep_ctx().read_untracked(),
|
||||
}));
|
||||
worker.send_task(WorkerRequest::Continue(String::new()));
|
||||
@@ -157,6 +168,26 @@ fn ConfigMenuButton(menu_container: NodeRef<html::element::Div>) -> impl IntoVie
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn AdvancedOptionsMenuButton(menu_container: NodeRef<html::element::Div>) -> impl IntoView {
|
||||
let button = |toggle, node_ref| {
|
||||
view! {
|
||||
<IconButton on_click=toggle node_ref=node_ref>
|
||||
{more_options_icon()}
|
||||
</IconButton>
|
||||
}
|
||||
.into_any()
|
||||
};
|
||||
|
||||
view! {
|
||||
<PopButton
|
||||
button=button
|
||||
menu=Box::new(|_close| { view! { <AdvancedOptionsMenu /> }.into_any() })
|
||||
menu_container=menu_container
|
||||
></PopButton>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ShareButton() -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
@@ -79,3 +79,17 @@ pub fn clipboard_icon() -> AnyView {
|
||||
</svg>
|
||||
}.into_any()
|
||||
}
|
||||
|
||||
pub fn more_options_icon() -> AnyView {
|
||||
view! {
|
||||
<svg
|
||||
class=styles::icon
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
|
||||
</svg>
|
||||
}.into_any()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod advanced_options_menu;
|
||||
mod button_set;
|
||||
mod config_element;
|
||||
mod config_menu;
|
||||
|
||||
@@ -58,6 +58,7 @@ fn hanlde_save_state(state: Store<GlobalState>) {
|
||||
state.theme().track();
|
||||
state.keep_ctx().track();
|
||||
state.code().track();
|
||||
state.run_selected_code().track();
|
||||
|
||||
state.read_untracked().save();
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ pub struct GlobalState {
|
||||
theme: Theme,
|
||||
keep_ctx: bool,
|
||||
code: String,
|
||||
run_selected_code: bool,
|
||||
// runtime state below
|
||||
#[serde(skip)]
|
||||
worker: Option<WorkerHandle>,
|
||||
@@ -57,6 +58,7 @@ impl Default for GlobalState {
|
||||
worker: None,
|
||||
editor: None,
|
||||
last_error: None,
|
||||
run_selected_code: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user