Base app with dioxus
commit
b9c02d98e0
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/dist
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "bottom-web"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus = "0.4.0"
|
||||||
|
dioxus-web = "0.4.0"
|
||||||
|
bottomify = "1.2.0"
|
@ -0,0 +1,17 @@
|
|||||||
|
[application]
|
||||||
|
name = "bottom-rs-web"
|
||||||
|
default_platform = "web"
|
||||||
|
|
||||||
|
[web.app]
|
||||||
|
title = "bottom-rs converter"
|
||||||
|
asset_dir = "public"
|
||||||
|
|
||||||
|
[web.watcher]
|
||||||
|
watch_path = ["src", "public"]
|
||||||
|
|
||||||
|
[web.resource]
|
||||||
|
style = [
|
||||||
|
"/style.css"
|
||||||
|
]
|
||||||
|
|
||||||
|
[web.resource.dev]
|
@ -0,0 +1,86 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&family=Shantell+Sans:wght@300&display=swap');
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: #2b5768;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
max-width: 600px;
|
||||||
|
width: calc(100vw - 24px);
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav, footer {
|
||||||
|
font-family: 'Quicksand', sans-serif;
|
||||||
|
background: #bae0ee;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 16px;
|
||||||
|
font-family: 'Shantell Sans', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
border: solid 2px #b0b1ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 8px -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form * {
|
||||||
|
border: solid 0px transparent;
|
||||||
|
border-radius: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
form textarea {
|
||||||
|
grid-column: 1/3;
|
||||||
|
height: 200px;
|
||||||
|
resize: none;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form button {
|
||||||
|
height: 50px;
|
||||||
|
background: #b0b1ff;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
form button:hover:not(:disabled) {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
form button:disabled {
|
||||||
|
opacity: .4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status {
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: .6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status.error {
|
||||||
|
color: red;
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use bottomify::bottom::{encode_string, decode_string};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus_web::launch(App);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn App(cx: Scope) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
Layout {
|
||||||
|
Encoder { }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
struct LayoutProps<'a> {
|
||||||
|
children: Element<'a>,
|
||||||
|
}
|
||||||
|
fn Layout<'a>(cx: Scope<'a, LayoutProps<'a>>) -> Element {
|
||||||
|
cx.render(rsx! {
|
||||||
|
nav {
|
||||||
|
h1 {
|
||||||
|
"bottom-rs-web"
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
href: "https://github.com/bottom-software-foundation/bottom-rs",
|
||||||
|
target: "_blank",
|
||||||
|
"original tool"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
&cx.props.children
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
span {
|
||||||
|
"Created by "
|
||||||
|
a {
|
||||||
|
href: "https://tempest.dev/about/rose",
|
||||||
|
"Ashelyn Rose"
|
||||||
|
},
|
||||||
|
" because they were far too bored"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Encoder<'a>(cx: Scope<'a, LayoutProps<'a>>) -> Element {
|
||||||
|
let input_text = use_state(cx, || "".to_string());
|
||||||
|
let status_text = use_state(cx, || "".to_string());
|
||||||
|
let error_status = use_state(cx, || false);
|
||||||
|
|
||||||
|
let input_stripped = strip_spaces(input_text.to_string());
|
||||||
|
let is_valid_bottom = decode_string(&input_stripped).is_ok();
|
||||||
|
|
||||||
|
let bottomify = move |event: Event<MouseData>| {
|
||||||
|
event.stop_propagation();
|
||||||
|
|
||||||
|
let input = input_text.to_string();
|
||||||
|
let space_stripped = strip_spaces(input_text.to_string());
|
||||||
|
|
||||||
|
if space_stripped.len() < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
input_text.set(encode_string(&input_text.to_string()));
|
||||||
|
status_text.set(format!("Bottomed from \"{}\"", input));
|
||||||
|
error_status.set(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
let debottomify = move |event: Event<MouseData>| {
|
||||||
|
event.stop_propagation();
|
||||||
|
|
||||||
|
let input = input_text.to_string();
|
||||||
|
let space_stripped = strip_spaces(input_text.to_string());
|
||||||
|
|
||||||
|
if space_stripped.len() < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = decode_string(&space_stripped);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(output) => {
|
||||||
|
input_text.set(output);
|
||||||
|
status_text.set(format!("De-bottomed from {}", input));
|
||||||
|
error_status.set(false);
|
||||||
|
}
|
||||||
|
Err(_error) => {
|
||||||
|
status_text.set("Could not decode bottom text".to_string());
|
||||||
|
error_status.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
label {
|
||||||
|
"for": "input",
|
||||||
|
"Enter a message to convert:"
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
action: "javascript:void(0);",
|
||||||
|
textarea {
|
||||||
|
id: "input",
|
||||||
|
value: "{input_text}",
|
||||||
|
oninput: move |evt| input_text.set(evt.value.clone()),
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
disabled: is_valid_bottom,
|
||||||
|
title: if is_valid_bottom {
|
||||||
|
"Not encoding, input is already valid bottom text"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
onclick: bottomify,
|
||||||
|
"Bottomify"
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
disabled: !is_valid_bottom,
|
||||||
|
title: if !is_valid_bottom {
|
||||||
|
"Cannot decode, input is not valid bottom text"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
onclick: debottomify,
|
||||||
|
"Debottomify"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
id: "status",
|
||||||
|
class: if **error_status {
|
||||||
|
"error"
|
||||||
|
} else {
|
||||||
|
"false"
|
||||||
|
},
|
||||||
|
"{status_text}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strip_spaces(input: String) -> String {
|
||||||
|
input.split(' ')
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("")
|
||||||
|
}
|
Loading…
Reference in New Issue