Add sqlformat support
This commit is contained in:
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -212,7 +212,7 @@ dependencies = [
|
|||||||
"pathdiff",
|
"pathdiff",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
"winnow",
|
"winnow 0.7.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1807,6 +1807,16 @@ dependencies = [
|
|||||||
"web-sys",
|
"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]]
|
[[package]]
|
||||||
name = "sqlight"
|
name = "sqlight"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1829,6 +1839,7 @@ dependencies = [
|
|||||||
"serde-wasm-bindgen",
|
"serde-wasm-bindgen",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"split-grid",
|
"split-grid",
|
||||||
|
"sqlformat",
|
||||||
"sqlite-wasm-rs",
|
"sqlite-wasm-rs",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2041,7 +2052,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow",
|
"winnow 0.7.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2088,6 +2099,12 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode_categories"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@@ -2349,6 +2366,15 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.6.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
|
|||||||
@@ -38,3 +38,4 @@ log = "0.4.27"
|
|||||||
fragile = "2.0.1"
|
fragile = "2.0.1"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
prettytable-rs = "0.10.0"
|
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)]
|
#[wasm_bindgen(method, js_name = getValue)]
|
||||||
pub fn get_value(this: &Editor) -> String;
|
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)]
|
#[wasm_bindgen(method, js_name = getSelectedText)]
|
||||||
pub fn get_selected_text(this: &Editor) -> String;
|
pub fn get_selected_text(this: &Editor) -> String;
|
||||||
}
|
}
|
||||||
@@ -201,6 +204,10 @@ impl Editor {
|
|||||||
self.js.get_value()
|
self.js.get_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_value(&self, value: String) {
|
||||||
|
self.js.set_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_selected_value(&self) -> String {
|
pub fn get_selected_value(&self) -> String {
|
||||||
self.js.get_selected_text()
|
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/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/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/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/execute.module.css" rel="css">
|
||||||
<link data-trunk href="./assets/module.postcss/output/header.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 prettytable::{Cell, Row, Table};
|
||||||
use reactive_stores::Store;
|
use reactive_stores::Store;
|
||||||
use wasm_bindgen::{JsCast, prelude::Closure};
|
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::{
|
use crate::{
|
||||||
FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, SQLiteStatementResult,
|
FragileComfirmed, LoadDbOptions, PrepareOptions, SQLightError, SQLiteStatementResult,
|
||||||
@@ -14,10 +14,12 @@ use crate::{
|
|||||||
button_set::{Button, ButtonSet, IconButton, LinkButton, Rule},
|
button_set::{Button, ButtonSet, IconButton, LinkButton, Rule},
|
||||||
config_menu::ConfigMenu,
|
config_menu::ConfigMenu,
|
||||||
context_menu::ContextMenu,
|
context_menu::ContextMenu,
|
||||||
|
database_menu::DatabaseMenu,
|
||||||
icon::{build_icon, config_icon, expandable_icon, github_icon, more_options_icon},
|
icon::{build_icon, config_icon, expandable_icon, github_icon, more_options_icon},
|
||||||
output::change_focus,
|
output::change_focus,
|
||||||
pop_button::PopButton,
|
pop_button::PopButton,
|
||||||
state::{Focus, GlobalState, GlobalStateStoreFields},
|
state::{Focus, GlobalState, GlobalStateStoreFields},
|
||||||
|
tools_menu::ToolsMenu,
|
||||||
vfs_menu::VfsMenu,
|
vfs_menu::VfsMenu,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -48,14 +50,16 @@ pub fn Header() -> impl IntoView {
|
|||||||
</div>
|
</div>
|
||||||
<div class=styles::right>
|
<div class=styles::right>
|
||||||
<input type="file" node_ref=input_ref style="display: none" />
|
<input type="file" node_ref=input_ref style="display: none" />
|
||||||
<ButtonSet>
|
|
||||||
<LoadButton input_ref=input_ref />
|
|
||||||
<Rule />
|
|
||||||
<DownloadButton />
|
|
||||||
</ButtonSet>
|
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
<ShareButton />
|
<ShareButton />
|
||||||
</ButtonSet>
|
</ButtonSet>
|
||||||
|
<ButtonSet>
|
||||||
|
<DatabaseButton input_ref=input_ref menu_container=menu_container />
|
||||||
|
</ButtonSet>
|
||||||
|
<ButtonSet>
|
||||||
|
<ToolsButton menu_container=menu_container />
|
||||||
|
</ButtonSet>
|
||||||
<ButtonSet>
|
<ButtonSet>
|
||||||
<ConfigMenuButton menu_container=menu_container />
|
<ConfigMenuButton menu_container=menu_container />
|
||||||
</ButtonSet>
|
</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]
|
#[component]
|
||||||
fn VfsMenuButton(menu_container: NodeRef<html::element::Div>) -> impl IntoView {
|
fn VfsMenuButton(menu_container: NodeRef<html::element::Div>) -> impl IntoView {
|
||||||
let state = expect_context::<Store<GlobalState>>();
|
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]
|
#[component]
|
||||||
fn ShareButton() -> impl IntoView {
|
fn ShareButton() -> impl IntoView {
|
||||||
let state = expect_context::<Store<GlobalState>>();
|
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 advanced_options_menu;
|
||||||
|
mod button_menu_item;
|
||||||
mod button_set;
|
mod button_set;
|
||||||
mod config_element;
|
mod config_element;
|
||||||
mod config_menu;
|
mod config_menu;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
|
mod database_menu;
|
||||||
mod editor;
|
mod editor;
|
||||||
mod header;
|
mod header;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod loader;
|
mod loader;
|
||||||
|
mod menu_aside;
|
||||||
mod menu_group;
|
mod menu_group;
|
||||||
mod menu_item;
|
mod menu_item;
|
||||||
mod output;
|
mod output;
|
||||||
@@ -15,6 +18,7 @@ mod pop_button;
|
|||||||
mod select_one;
|
mod select_one;
|
||||||
mod selectable_menu_item;
|
mod selectable_menu_item;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod tools_menu;
|
||||||
mod vfs_menu;
|
mod vfs_menu;
|
||||||
|
|
||||||
pub use playground::playground;
|
pub use playground::playground;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ INSERT INTO blobs(data) VALUES (randomblob(12));
|
|||||||
|
|
||||||
SELECT 'Hello World!',
|
SELECT 'Hello World!',
|
||||||
datetime('now','localtime') AS TM,
|
datetime('now','localtime') AS TM,
|
||||||
x'73716c69676874' AS BLOB_VAL,
|
unhex('73716c69676874') AS BLOB_VAL,
|
||||||
NULL as NULL_VAL;
|
NULL as NULL_VAL;
|
||||||
|
|
||||||
SELECT * FROM blobs;";
|
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