Add sqlformat support
This commit is contained in:
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -212,7 +212,7 @@ dependencies = [
|
||||
"pathdiff",
|
||||
"serde",
|
||||
"toml",
|
||||
"winnow",
|
||||
"winnow 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1807,6 +1807,16 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d7b3e8a3b6f2ee93ac391a0f757c13790caa0147892e3545cd549dd5b54bc0"
|
||||
dependencies = [
|
||||
"unicode_categories",
|
||||
"winnow 0.6.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlight"
|
||||
version = "0.1.0"
|
||||
@@ -1829,6 +1839,7 @@ dependencies = [
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"split-grid",
|
||||
"sqlformat",
|
||||
"sqlite-wasm-rs",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -2041,7 +2052,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
"winnow 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2088,6 +2099,12 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
@@ -2349,6 +2366,15 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
|
||||
@@ -38,3 +38,4 @@ log = "0.4.27"
|
||||
fragile = "2.0.1"
|
||||
hex = "0.4.3"
|
||||
prettytable-rs = "0.10.0"
|
||||
sqlformat = "0.3.5"
|
||||
|
||||
38
assets/module.css/button_menu_item.module.css
Normal file
38
assets/module.css/button_menu_item.module.css
Normal file
@@ -0,0 +1,38 @@
|
||||
.-buttonReset {
|
||||
color: var(--font-color);
|
||||
border: none;
|
||||
background: inherit;
|
||||
background-color: transparent; /* IE 11 */
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
line-height: inherit;
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
.-menuItemFullButton {
|
||||
composes: -buttonReset;
|
||||
transition: color var(--header-transition);
|
||||
width: 100%;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.container {
|
||||
composes: -menuItemFullButton;
|
||||
|
||||
&:hover {
|
||||
color: var(--header-tint);
|
||||
}
|
||||
}
|
||||
|
||||
.-menuItemTitle {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.name {
|
||||
composes: -menuItemTitle;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
4
assets/module.css/menu_aside.module.css
Normal file
4
assets/module.css/menu_aside.module.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.aside {
|
||||
margin: 0.25em 0 0;
|
||||
color: #888;
|
||||
}
|
||||
@@ -28,6 +28,9 @@ mod bindgen {
|
||||
#[wasm_bindgen(method, js_name = getValue)]
|
||||
pub fn get_value(this: &Editor) -> String;
|
||||
|
||||
#[wasm_bindgen(method, js_name = setValue)]
|
||||
pub fn set_value(this: &Editor, value: String);
|
||||
|
||||
#[wasm_bindgen(method, js_name = getSelectedText)]
|
||||
pub fn get_selected_text(this: &Editor) -> String;
|
||||
}
|
||||
@@ -201,6 +204,10 @@ impl Editor {
|
||||
self.js.get_value()
|
||||
}
|
||||
|
||||
pub fn set_value(&self, value: String) {
|
||||
self.js.set_value(value);
|
||||
}
|
||||
|
||||
pub fn get_selected_value(&self) -> String {
|
||||
self.js.get_selected_text()
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
<link data-trunk href="./assets/module.postcss/selectable_menu_item.module.css" rel="css">
|
||||
<link data-trunk href="./assets/module.postcss/loader.module.css" rel="css">
|
||||
<link data-trunk href="./assets/module.postcss/config_element.module.css" rel="css">
|
||||
<link data-trunk href="./assets/module.postcss/menu_aside.module.css" rel="css">
|
||||
<link data-trunk href="./assets/module.postcss/button_menu_item.module.css" rel="css">
|
||||
|
||||
<link data-trunk href="./assets/module.postcss/output/execute.module.css" rel="css">
|
||||
<link data-trunk href="./assets/module.postcss/output/header.module.css" rel="css">
|
||||
|
||||
25
src/app/button_menu_item.rs
Normal file
25
src/app/button_menu_item.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use istyles::istyles;
|
||||
use leptos::prelude::*;
|
||||
use web_sys::MouseEvent;
|
||||
|
||||
use crate::app::menu_item::MenuItem;
|
||||
|
||||
istyles!(
|
||||
styles,
|
||||
"assets/module.postcss/button_menu_item.module.css.map"
|
||||
);
|
||||
|
||||
#[component]
|
||||
pub fn ButtonMenuItem<C>(name: String, on_click: C, children: Children) -> impl IntoView
|
||||
where
|
||||
C: Fn(MouseEvent) + Send + 'static,
|
||||
{
|
||||
view! {
|
||||
<MenuItem>
|
||||
<button class=styles::container on:click=on_click>
|
||||
<div class=styles::name>{name}</div>
|
||||
<div class=styles::description>{children()}</div>
|
||||
</button>
|
||||
</MenuItem>
|
||||
}
|
||||
}
|
||||
22
src/app/database_menu.rs
Normal file
22
src/app/database_menu.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use leptos::prelude::*;
|
||||
use web_sys::MouseEvent;
|
||||
|
||||
use crate::app::{button_menu_item::ButtonMenuItem, menu_aside::MenuAside, menu_group::MenuGroup};
|
||||
|
||||
#[component]
|
||||
pub fn DatabaseMenu<L, D>(load: L, download: D) -> impl IntoView
|
||||
where
|
||||
L: Fn(MouseEvent) + Send + 'static,
|
||||
D: Fn(MouseEvent) + Send + 'static,
|
||||
{
|
||||
view! {
|
||||
<MenuGroup title="Database".into()>
|
||||
<ButtonMenuItem name="Load".into() on_click=load>
|
||||
<MenuAside>"The KEEP CONTEXT will be automatically enabled."</MenuAside>
|
||||
</ButtonMenuItem>
|
||||
<ButtonMenuItem name="Download".into() on_click=download>
|
||||
<MenuAside>"Will be downloaded as test.db"</MenuAside>
|
||||
</ButtonMenuItem>
|
||||
</MenuGroup>
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use leptos::{html::Input, prelude::*, tachys::html};
|
||||
use prettytable::{Cell, Row, Table};
|
||||
use reactive_stores::Store;
|
||||
use wasm_bindgen::{JsCast, prelude::Closure};
|
||||
use web_sys::{Blob, Event, FileReader, HtmlInputElement, Url, UrlSearchParams};
|
||||
use web_sys::{Blob, Event, FileReader, HtmlInputElement, MouseEvent, Url, UrlSearchParams};
|
||||
|
||||
use crate::{
|
||||
FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, SQLiteStatementResult,
|
||||
@@ -14,10 +14,12 @@ use crate::{
|
||||
button_set::{Button, ButtonSet, IconButton, LinkButton, Rule},
|
||||
config_menu::ConfigMenu,
|
||||
context_menu::ContextMenu,
|
||||
database_menu::DatabaseMenu,
|
||||
icon::{build_icon, config_icon, expandable_icon, github_icon, more_options_icon},
|
||||
output::change_focus,
|
||||
pop_button::PopButton,
|
||||
state::{Focus, GlobalState, GlobalStateStoreFields},
|
||||
tools_menu::ToolsMenu,
|
||||
vfs_menu::VfsMenu,
|
||||
},
|
||||
};
|
||||
@@ -48,14 +50,16 @@ pub fn Header() -> impl IntoView {
|
||||
</div>
|
||||
<div class=styles::right>
|
||||
<input type="file" node_ref=input_ref style="display: none" />
|
||||
<ButtonSet>
|
||||
<LoadButton input_ref=input_ref />
|
||||
<Rule />
|
||||
<DownloadButton />
|
||||
</ButtonSet>
|
||||
|
||||
<ButtonSet>
|
||||
<ShareButton />
|
||||
</ButtonSet>
|
||||
<ButtonSet>
|
||||
<DatabaseButton input_ref=input_ref menu_container=menu_container />
|
||||
</ButtonSet>
|
||||
<ButtonSet>
|
||||
<ToolsButton menu_container=menu_container />
|
||||
</ButtonSet>
|
||||
<ButtonSet>
|
||||
<ConfigMenuButton menu_container=menu_container />
|
||||
</ButtonSet>
|
||||
@@ -114,140 +118,6 @@ fn ExecuteButton() -> impl IntoView {
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn DownloadButton() -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
Effect::new(move || {
|
||||
state.exported().track();
|
||||
|
||||
let Some(downloaded) = state.exported().write_untracked().take() else {
|
||||
return;
|
||||
};
|
||||
let filename = downloaded.filename;
|
||||
let buffer = downloaded.data;
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&buffer);
|
||||
|
||||
let blob = Blob::new_with_u8_array_sequence(&array).unwrap();
|
||||
let url = Url::create_object_url_with_blob(&blob).unwrap();
|
||||
|
||||
let document = document();
|
||||
let a = document
|
||||
.create_element("a")
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::HtmlAnchorElement>()
|
||||
.unwrap();
|
||||
|
||||
a.set_href(&url);
|
||||
a.set_download(&filename);
|
||||
a.click();
|
||||
|
||||
Url::revoke_object_url(&url).unwrap();
|
||||
});
|
||||
|
||||
let on_click = move |_| {
|
||||
if let Some(worker) = &*state.worker().read() {
|
||||
worker.send_task(WorkerRequest::DownloadDb);
|
||||
}
|
||||
};
|
||||
|
||||
view! { <Button on_click=on_click>"Download DB"</Button> }
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn LoadButton(input_ref: NodeRef<Input>) -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
let (file, set_file) = signal::<Option<FragileComfirmed<web_sys::File>>>(None);
|
||||
|
||||
Effect::new(move || {
|
||||
if let Some(file) = &*file.read() {
|
||||
let filename = file.name();
|
||||
|
||||
if let Ok(reader) = FileReader::new() {
|
||||
let on_progress = FragileComfirmed::new(Closure::wrap(Box::new(
|
||||
move |ev: web_sys::ProgressEvent| {
|
||||
if ev.length_computable() {
|
||||
state.import_progress().set(Some(ImportProgress {
|
||||
filename: filename.clone(),
|
||||
loaded: ev.loaded(),
|
||||
total: ev.total(),
|
||||
opened: None,
|
||||
}));
|
||||
}
|
||||
},
|
||||
)
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
let on_error = FragileComfirmed::new(Closure::wrap(Box::new(
|
||||
move |ev: web_sys::ProgressEvent| {
|
||||
let target = ev.target().unwrap();
|
||||
let reader = target.unchecked_into::<FileReader>();
|
||||
let dom_error = reader.error().unwrap();
|
||||
state.last_error().set(Some(FragileComfirmed::new(
|
||||
SQLightError::ImportDb(dom_error.message().to_string()),
|
||||
)));
|
||||
},
|
||||
)
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
let on_load =
|
||||
FragileComfirmed::new(Closure::wrap(Box::new(move |ev: web_sys::Event| {
|
||||
let target = ev.target().unwrap();
|
||||
let reader = target.unchecked_into::<FileReader>();
|
||||
let result = reader.result().unwrap();
|
||||
let array_buffer = result.unchecked_into::<js_sys::ArrayBuffer>();
|
||||
let data = js_sys::Uint8Array::new(&array_buffer);
|
||||
if let Some(worker) = &*state.worker().read() {
|
||||
worker.send_task(WorkerRequest::LoadDb(LoadDbOptions { data }));
|
||||
}
|
||||
})
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
reader.set_onprogress(Some(on_progress.as_ref().unchecked_ref()));
|
||||
reader.set_onerror(Some(on_error.as_ref().unchecked_ref()));
|
||||
reader.set_onload(Some(on_load.as_ref().unchecked_ref()));
|
||||
reader.read_as_array_buffer(file).unwrap();
|
||||
|
||||
on_cleanup(move || {
|
||||
drop(on_progress);
|
||||
drop(on_error);
|
||||
drop(on_load);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let on_change = move |ev: Event| {
|
||||
if let Some(target) = ev.target() {
|
||||
let input = target.unchecked_into::<HtmlInputElement>();
|
||||
if let Some(files) = input.files() {
|
||||
if files.length() > 0 {
|
||||
let file = FragileComfirmed::new(files.get(0).unwrap());
|
||||
set_file.set(Some(file));
|
||||
} else {
|
||||
set_file.set(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let on_click = move |_| {
|
||||
if let Some(input) = &*input_ref.read() {
|
||||
if input.onchange().is_none() {
|
||||
let callback = Closure::wrap(Box::new(on_change) as Box<dyn Fn(Event)>);
|
||||
input.set_onchange(Some(callback.as_ref().unchecked_ref::<js_sys::Function>()));
|
||||
callback.forget();
|
||||
}
|
||||
input.set_value("");
|
||||
input.click();
|
||||
}
|
||||
};
|
||||
|
||||
view! { <Button on_click=on_click>"Load DB"</Button> }
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn VfsMenuButton(menu_container: NodeRef<html::element::Div>) -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
@@ -337,6 +207,173 @@ fn AdvancedOptionsMenuButton(menu_container: NodeRef<html::element::Div>) -> imp
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ToolsButton(menu_container: NodeRef<html::element::Div>) -> impl IntoView {
|
||||
let button = |toggle, node_ref| {
|
||||
view! {
|
||||
<Button icon_right=expandable_icon() on_click=toggle node_ref=node_ref>
|
||||
"Tools"
|
||||
</Button>
|
||||
}
|
||||
.into_any()
|
||||
};
|
||||
|
||||
view! {
|
||||
<PopButton
|
||||
button=button
|
||||
menu=Box::new(|_close| { view! { <ToolsMenu /> }.into_any() })
|
||||
menu_container=menu_container
|
||||
></PopButton>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn DatabaseButton(
|
||||
input_ref: NodeRef<Input>,
|
||||
menu_container: NodeRef<html::element::Div>,
|
||||
) -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
let button = |toggle, node_ref| {
|
||||
view! {
|
||||
<Button icon_right=expandable_icon() on_click=toggle node_ref=node_ref>
|
||||
"Database"
|
||||
</Button>
|
||||
}
|
||||
.into_any()
|
||||
};
|
||||
|
||||
let (file, set_file) = signal::<Option<FragileComfirmed<web_sys::File>>>(None);
|
||||
|
||||
Effect::new(move || {
|
||||
if let Some(file) = &*file.read() {
|
||||
let filename = file.name();
|
||||
|
||||
if let Ok(reader) = FileReader::new() {
|
||||
let on_progress = FragileComfirmed::new(Closure::wrap(Box::new(
|
||||
move |ev: web_sys::ProgressEvent| {
|
||||
if ev.length_computable() {
|
||||
state.import_progress().set(Some(ImportProgress {
|
||||
filename: filename.clone(),
|
||||
loaded: ev.loaded(),
|
||||
total: ev.total(),
|
||||
opened: None,
|
||||
}));
|
||||
}
|
||||
},
|
||||
)
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
let on_error = FragileComfirmed::new(Closure::wrap(Box::new(
|
||||
move |ev: web_sys::ProgressEvent| {
|
||||
let target = ev.target().unwrap();
|
||||
let reader = target.unchecked_into::<FileReader>();
|
||||
let dom_error = reader.error().unwrap();
|
||||
state.last_error().set(Some(FragileComfirmed::new(
|
||||
SQLightError::ImportDb(dom_error.message().to_string()),
|
||||
)));
|
||||
},
|
||||
)
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
let on_load =
|
||||
FragileComfirmed::new(Closure::wrap(Box::new(move |ev: web_sys::Event| {
|
||||
let target = ev.target().unwrap();
|
||||
let reader = target.unchecked_into::<FileReader>();
|
||||
let result = reader.result().unwrap();
|
||||
let array_buffer = result.unchecked_into::<js_sys::ArrayBuffer>();
|
||||
let data = js_sys::Uint8Array::new(&array_buffer);
|
||||
if let Some(worker) = &*state.worker().read_untracked() {
|
||||
worker.send_task(WorkerRequest::LoadDb(LoadDbOptions { data }));
|
||||
}
|
||||
})
|
||||
as Box<dyn FnMut(_)>));
|
||||
|
||||
reader.set_onprogress(Some(on_progress.as_ref().unchecked_ref()));
|
||||
reader.set_onerror(Some(on_error.as_ref().unchecked_ref()));
|
||||
reader.set_onload(Some(on_load.as_ref().unchecked_ref()));
|
||||
reader.read_as_array_buffer(file).unwrap();
|
||||
|
||||
on_cleanup(move || {
|
||||
drop(on_progress);
|
||||
drop(on_error);
|
||||
drop(on_load);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let on_change = move |ev: Event| {
|
||||
if let Some(target) = ev.target() {
|
||||
let input = target.unchecked_into::<HtmlInputElement>();
|
||||
if let Some(files) = input.files() {
|
||||
if files.length() > 0 {
|
||||
let file = FragileComfirmed::new(files.get(0).unwrap());
|
||||
set_file.set(Some(file));
|
||||
} else {
|
||||
set_file.set(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let on_load = move |_: MouseEvent| {
|
||||
if let Some(input) = &*input_ref.read() {
|
||||
if input.onchange().is_none() {
|
||||
let callback = Closure::wrap(Box::new(on_change) as Box<dyn Fn(Event)>);
|
||||
input.set_onchange(Some(callback.as_ref().unchecked_ref::<js_sys::Function>()));
|
||||
callback.forget();
|
||||
}
|
||||
input.set_value("");
|
||||
input.click();
|
||||
}
|
||||
};
|
||||
|
||||
Effect::new(move || {
|
||||
state.exported().track();
|
||||
|
||||
let Some(downloaded) = state.exported().write_untracked().take() else {
|
||||
return;
|
||||
};
|
||||
let filename = downloaded.filename;
|
||||
let buffer = downloaded.data;
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&buffer);
|
||||
|
||||
let blob = Blob::new_with_u8_array_sequence(&array).unwrap();
|
||||
let url = Url::create_object_url_with_blob(&blob).unwrap();
|
||||
|
||||
let document = document();
|
||||
let a = document
|
||||
.create_element("a")
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::HtmlAnchorElement>()
|
||||
.unwrap();
|
||||
|
||||
a.set_href(&url);
|
||||
a.set_download(&filename);
|
||||
a.click();
|
||||
|
||||
Url::revoke_object_url(&url).unwrap();
|
||||
});
|
||||
|
||||
let on_download = move |_| {
|
||||
if let Some(worker) = &*state.worker().read() {
|
||||
worker.send_task(WorkerRequest::DownloadDb);
|
||||
}
|
||||
};
|
||||
|
||||
view! {
|
||||
<PopButton
|
||||
button=button
|
||||
menu=Box::new(move |_close| {
|
||||
view! { <DatabaseMenu load=on_load download=on_download /> }.into_any()
|
||||
})
|
||||
menu_container=menu_container
|
||||
></PopButton>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ShareButton() -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
|
||||
9
src/app/menu_aside.rs
Normal file
9
src/app/menu_aside.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use istyles::istyles;
|
||||
use leptos::prelude::*;
|
||||
|
||||
istyles!(styles, "assets/module.postcss/menu_aside.module.css.map");
|
||||
|
||||
#[component]
|
||||
pub fn MenuAside(children: Children) -> impl IntoView {
|
||||
view! { <p class=styles::aside>{children()}</p> }
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
mod advanced_options_menu;
|
||||
mod button_menu_item;
|
||||
mod button_set;
|
||||
mod config_element;
|
||||
mod config_menu;
|
||||
mod context_menu;
|
||||
mod database_menu;
|
||||
mod editor;
|
||||
mod header;
|
||||
mod icon;
|
||||
mod loader;
|
||||
mod menu_aside;
|
||||
mod menu_group;
|
||||
mod menu_item;
|
||||
mod output;
|
||||
@@ -15,6 +18,7 @@ mod pop_button;
|
||||
mod select_one;
|
||||
mod selectable_menu_item;
|
||||
mod state;
|
||||
mod tools_menu;
|
||||
mod vfs_menu;
|
||||
|
||||
pub use playground::playground;
|
||||
|
||||
@@ -20,7 +20,7 @@ INSERT INTO blobs(data) VALUES (randomblob(12));
|
||||
|
||||
SELECT 'Hello World!',
|
||||
datetime('now','localtime') AS TM,
|
||||
x'73716c69676874' AS BLOB_VAL,
|
||||
unhex('73716c69676874') AS BLOB_VAL,
|
||||
NULL as NULL_VAL;
|
||||
|
||||
SELECT * FROM blobs;";
|
||||
|
||||
41
src/app/tools_menu.rs
Normal file
41
src/app/tools_menu.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use leptos::prelude::*;
|
||||
use reactive_stores::Store;
|
||||
use sqlformat::{FormatOptions, QueryParams};
|
||||
|
||||
use crate::app::{
|
||||
GlobalState, GlobalStateStoreFields, button_menu_item::ButtonMenuItem, menu_aside::MenuAside,
|
||||
menu_group::MenuGroup,
|
||||
};
|
||||
|
||||
#[component]
|
||||
pub fn ToolsMenu() -> impl IntoView {
|
||||
let state = expect_context::<Store<GlobalState>>();
|
||||
let format = move |_event| {
|
||||
let Some(editor) = &*state.editor().read() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let format_options = FormatOptions {
|
||||
uppercase: Some(true),
|
||||
lines_between_queries: 2,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let sql = sqlformat::format(
|
||||
&editor.get_value(),
|
||||
&QueryParams::default(),
|
||||
&format_options,
|
||||
);
|
||||
|
||||
editor.set_value(sql);
|
||||
};
|
||||
|
||||
view! {
|
||||
<MenuGroup title="Tools".into()>
|
||||
<ButtonMenuItem name="SQL Format".into() on_click=format>
|
||||
<div>Format this code with sqlformat.</div>
|
||||
<MenuAside>"https://crates.io/crates/sqlformat"</MenuAside>
|
||||
</ButtonMenuItem>
|
||||
</MenuGroup>
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user