Reduce memory usage, remove Rc
This commit is contained in:
parent
8811c39e18
commit
46fafb619c
938
Cargo.lock
generated
938
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,13 @@ repository = "https://gitlab.com/adrian.wozniak/jirs"
|
|||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
#license-file = "../LICENSE"
|
#license-file = "../LICENSE"
|
||||||
|
|
||||||
|
[package.metadata.wasm-pack.profile.dev]
|
||||||
|
wasm-opt = false
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "print"
|
||||||
|
path = "./src/print.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
name = "jirs_client"
|
name = "jirs_client"
|
||||||
@ -23,21 +30,29 @@ opt-level = 's'
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jirs-data = { path = "../jirs-data" }
|
jirs-data = { path = "../jirs-data" }
|
||||||
|
|
||||||
|
wee_alloc = "*"
|
||||||
|
|
||||||
seed = { version = "0.7.0" }
|
seed = { version = "0.7.0" }
|
||||||
serde = "*"
|
|
||||||
bincode = "1.2.1"
|
serde = { version = "*" }
|
||||||
chrono = { version = "0.4", features = ["serde", "wasmbind"] }
|
serde_json = { version = "*" }
|
||||||
|
bincode = { version = "*" }
|
||||||
|
ron = { version = "*" }
|
||||||
|
bson = "1.1.0"
|
||||||
|
serde_cbor = "0.11.1"
|
||||||
|
|
||||||
|
chrono = { version = "0.4", default-features = false, features = ["serde", "wasmbind"] }
|
||||||
uuid = { version = "0.8.1", features = ["serde"] }
|
uuid = { version = "0.8.1", features = ["serde"] }
|
||||||
futures = "^0.1.26"
|
futures = "^0.1.26"
|
||||||
comrak = "*"
|
comrak = "*"
|
||||||
wee_alloc = "*"
|
|
||||||
num-traits = { version = "*" }
|
num-traits = { version = "*" }
|
||||||
|
|
||||||
lazy_static = "*"
|
lazy_static = "*"
|
||||||
syntect = { version = "*", default-features = false, features = ["html", "regex-fancy", "dump-load-rs"] }
|
syntect = { version = "*", default-features = false, features = ["html", "regex-fancy", "dump-load-rs"] }
|
||||||
|
|
||||||
[dependencies.wasm-bindgen]
|
[dependencies.wasm-bindgen]
|
||||||
version = "0.2.66"
|
version = "*"
|
||||||
features = ["enable-interning"]
|
features = ["enable-interning"]
|
||||||
|
|
||||||
[dependencies.js-sys]
|
[dependencies.js-sys]
|
||||||
@ -45,7 +60,7 @@ version = "*"
|
|||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.22"
|
version = "*"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
# elements
|
# elements
|
||||||
|
@ -9,6 +9,32 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/styles.css">
|
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="app"></main>
|
<main id="app">
|
||||||
|
<style>
|
||||||
|
.lds-hourglass {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.lds-hourglass:after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
margin: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 32px solid #fff;
|
||||||
|
border-color: #fff transparent #fff transparent;
|
||||||
|
animation: lds-hourglass 1.2s infinite;
|
||||||
|
}
|
||||||
|
@keyframes lds-hourglass {
|
||||||
|
0% { transform: rotate(0); animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); }
|
||||||
|
50% { transform: rotate(900deg); animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
|
||||||
|
100% { transform: rotate(1800deg); } }
|
||||||
|
</style>
|
||||||
|
<div class="lds-hourglass"></div>
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -8,9 +8,65 @@
|
|||||||
<title>JIRS</title>
|
<title>JIRS</title>
|
||||||
<link href="/styles.css" rel="stylesheet" type="text/css">
|
<link href="/styles.css" rel="stylesheet" type="text/css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
main#app.loading {
|
||||||
|
background: rgb(0, 82, 204);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
color: #fff;
|
||||||
|
width: 180px;
|
||||||
|
height: 80px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lds-hourglass {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
margin: 4rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lds-hourglass:after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
margin: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 32px solid #fff;
|
||||||
|
border-color: #fff transparent #fff transparent;
|
||||||
|
animation: lds-hourglass 1.2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes lds-hourglass {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(900deg);
|
||||||
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(1800deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="app"></main>
|
<main class="loading" id="app">
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="lds-hourglass"></div>
|
||||||
|
<div>Loading....</div>
|
||||||
|
<div>Please wait</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
<script src="/index.js" type="module"></script>
|
<script src="/index.js" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
PROJECT_ROOT=$(git rev-parse --show-toplevel)
|
||||||
|
CLIENT_ROOT=${PROJECT_ROOT}/jirs-client
|
||||||
|
|
||||||
|
cd ${CLIENT_ROOT}
|
||||||
|
|
||||||
. .env
|
. .env
|
||||||
|
cargo watch -s ${CLIENT_ROOT}/scripts/run-wasm-pack.sh -w ${CLIENT_ROOT}/src -w ${CLIENT_ROOT}/Cargo.toml -w ./static -w js
|
||||||
rm -Rf tmp
|
|
||||||
mkdir -p tmp
|
|
||||||
mkdir -p target
|
|
||||||
|
|
||||||
wasm-pack build --mode force --dev --out-name jirs --out-dir ./tmp --target web -- --verbose
|
|
||||||
|
|
||||||
../target/debug/jirs-css -i ./js/styles.css -O ./tmp/styles.css
|
|
||||||
|
|
||||||
cp -r ./static/* ./tmp
|
|
||||||
cat ./static/index.js \
|
|
||||||
| sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" \
|
|
||||||
| sed -e "s/process.env.JIRS_SERVER_PORT/'$JIRS_SERVER_PORT'/g" &> ./tmp/index.js
|
|
||||||
|
|
||||||
cp ./js/template.html ./tmp/index.html
|
|
||||||
|
20
jirs-client/scripts/run-wasm-pack.sh
Executable file
20
jirs-client/scripts/run-wasm-pack.sh
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
. .env
|
||||||
|
|
||||||
|
rm -Rf tmp
|
||||||
|
mkdir -p tmp
|
||||||
|
mkdir -p target
|
||||||
|
|
||||||
|
wasm-pack build --mode force --dev --out-name jirs --out-dir ./tmp --target web -- --verbose
|
||||||
|
|
||||||
|
../target/debug/jirs-css -i ./js/styles.css -O ./tmp/styles.css
|
||||||
|
|
||||||
|
cp -r ./static/* ./tmp
|
||||||
|
cat ./static/index.js |
|
||||||
|
sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" |
|
||||||
|
sed -e "s/process.env.JIRS_SERVER_PORT/'$JIRS_SERVER_PORT'/g" &>./tmp/index.js
|
||||||
|
|
||||||
|
cp ./js/template.html ./tmp/index.html
|
@ -16,53 +16,49 @@ impl JirsCodeBuilder {
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn hi_code(&mut self, lang: &str, code: &str) -> String {
|
pub fn hi_code(&mut self, lang: &str, code: &str) -> String {
|
||||||
let syntax = match crate::hi::SYNTAX_SET.find_syntax_by_name(lang) {
|
let syntax = {
|
||||||
Some(s) => s,
|
match crate::hi::syntax_set::load().find_syntax_by_name(lang) {
|
||||||
_ => {
|
Some(s) => s.clone(),
|
||||||
return code.to_string();
|
_ => {
|
||||||
|
return code.to_string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut buffer = String::new();
|
let mut buffer: Vec<String> = Vec::with_capacity(code.lines().count() * 2);
|
||||||
for line in code.lines() {
|
for line in code.lines() {
|
||||||
buffer.push_str(self.hi(syntax, line).as_str());
|
self.hi(&syntax, line, &mut buffer);
|
||||||
buffer.push_str("<br />");
|
buffer.push("<br />".to_string());
|
||||||
}
|
}
|
||||||
buffer
|
buffer.join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hi(&mut self, syntax: &SyntaxReference, line: &str) -> String {
|
fn hi<'l>(&mut self, syntax: &SyntaxReference, line: &'l str, buffer: &mut Vec<String>) {
|
||||||
let mut h = HighlightLines::new(syntax, &crate::hi::THEME_SET.themes["base16-ocean-dark"]); // inspired-github
|
let mut h = HighlightLines::new(syntax, &crate::hi::THEME_SET.themes["base16-ocean-dark"]); // inspired-github
|
||||||
let tokens = h.highlight(line, &crate::hi::SYNTAX_SET);
|
let tokens = { h.highlight(line, &crate::hi::syntax_set::load()) };
|
||||||
|
|
||||||
let parts: Vec<String> = tokens
|
for (style, token) in tokens.into_iter() {
|
||||||
.into_iter()
|
let Style {
|
||||||
.map(|(style, token)| {
|
foreground: f,
|
||||||
let Style {
|
background: b,
|
||||||
foreground: f,
|
font_style,
|
||||||
background: b,
|
} = style;
|
||||||
font_style,
|
let fs = if font_style == FontStyle::BOLD {
|
||||||
} = style;
|
"font-weight: bold"
|
||||||
let fs = if font_style == FontStyle::BOLD {
|
} else if font_style == FontStyle::ITALIC {
|
||||||
"font-weight: bold"
|
"font-style: italic"
|
||||||
} else if font_style == FontStyle::ITALIC {
|
} else if font_style == FontStyle::UNDERLINE {
|
||||||
"font-style: italic"
|
"text-decoration: underline"
|
||||||
} else if font_style == FontStyle::UNDERLINE {
|
} else {
|
||||||
"text-decoration: underline"
|
""
|
||||||
} else {
|
};
|
||||||
""
|
|
||||||
};
|
buffer.push(format!(
|
||||||
let f = format!("rgba({}, {}, {}, {})", f.r, f.g, f.b, f.a);
|
r#"<span style="color: rgba({f_r}, {f_g}, {f_b}, {f_a});background:rgba({b_r}, {b_g}, {b_b}, {b_a}); {fs}">{t}</span>"#,
|
||||||
let b = format!("rgba({}, {}, {}, {})", b.r, b.g, b.b, b.a);
|
t = if token.is_empty() { " " } else { token },
|
||||||
format!(
|
f_r = f.r, f_g = f.g, f_b = f.b, f_a = f.a, b_r = b.r, b_g = b.g, b_b = b.b, b_a = b.a,
|
||||||
r#"<span style="color: {f};background:{b}; {fs}">{t}</span>"#,
|
fs = fs
|
||||||
t = if token.is_empty() { " " } else { token },
|
));
|
||||||
f = f,
|
}
|
||||||
b = b,
|
|
||||||
fs = fs
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
parts.join("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,6 +87,7 @@ pub fn define() {
|
|||||||
const lang = this.getAttribute('lang') || '';
|
const lang = this.getAttribute('lang') || '';
|
||||||
setTimeout(() => {{
|
setTimeout(() => {{
|
||||||
const code = (this.innerHTML || '').trim();
|
const code = (this.innerHTML || '').trim();
|
||||||
|
console.log('connected');
|
||||||
shadow.querySelector('#view').innerHTML = runtime.hi_code(lang, code);
|
shadow.querySelector('#view').innerHTML = runtime.hi_code(lang, code);
|
||||||
}}, 1);
|
}}, 1);
|
||||||
"#,
|
"#,
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use syntect::dumps::from_binary;
|
use syntect::{dumps::*, highlighting::ThemeSet};
|
||||||
use syntect::highlighting::ThemeSet;
|
|
||||||
use syntect::parsing::SyntaxSet;
|
pub mod syntax_set;
|
||||||
|
//
|
||||||
|
// pub use syntax::get_syntax;
|
||||||
|
// pub use syntax::SYNTAX_LIST;
|
||||||
|
// pub use syntax::SYNTAX_NAMES;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref SYNTAX_SET: SyntaxSet = from_binary(include_bytes!("./newlines.packdump"));
|
|
||||||
pub static ref THEME_SET: ThemeSet = from_binary(include_bytes!("./all.themedump"));
|
pub static ref THEME_SET: ThemeSet = from_binary(include_bytes!("./all.themedump"));
|
||||||
|
// pub static ref SYNTAX_SET: SyntaxSet = syntax_set::load();
|
||||||
}
|
}
|
||||||
|
BIN
jirs-client/src/hi/syntax_set.cbor
Normal file
BIN
jirs-client/src/hi/syntax_set.cbor
Normal file
Binary file not shown.
10
jirs-client/src/hi/syntax_set.rs
Normal file
10
jirs-client/src/hi/syntax_set.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
|
|
||||||
|
static mut SET: Option<SyntaxSet> = None;
|
||||||
|
|
||||||
|
pub fn load() -> &'static SyntaxSet {
|
||||||
|
if unsafe { SET.as_ref() }.is_none() {
|
||||||
|
unsafe { SET = serde_cbor::from_slice(include_bytes!("./syntax_set.cbor")).ok() };
|
||||||
|
}
|
||||||
|
unsafe { SET.as_ref() }.unwrap()
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#![feature(or_patterns)]
|
#![feature(or_patterns, type_ascription)]
|
||||||
|
|
||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
use web_sys::File;
|
use web_sys::File;
|
||||||
@ -33,6 +33,11 @@ mod users;
|
|||||||
pub mod validations;
|
pub mod validations;
|
||||||
mod ws;
|
mod ws;
|
||||||
|
|
||||||
|
#[link(wasm_import_module = "__wbindgen_thread_xform__")]
|
||||||
|
extern "C" {
|
||||||
|
fn __wbindgen_thread_id() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
@ -80,7 +85,7 @@ pub enum Msg {
|
|||||||
StrInputChanged(FieldId, String),
|
StrInputChanged(FieldId, String),
|
||||||
U32InputChanged(FieldId, u32),
|
U32InputChanged(FieldId, u32),
|
||||||
FileInputChanged(FieldId, Vec<File>),
|
FileInputChanged(FieldId, Vec<File>),
|
||||||
Rte(RteMsg, FieldId),
|
Rte(FieldId, RteMsg),
|
||||||
|
|
||||||
// issues
|
// issues
|
||||||
AddIssue,
|
AddIssue,
|
||||||
@ -143,7 +148,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
WebSocketChanged::WebSocketMessageLoaded(v) => {
|
||||||
match bincode::deserialize(v.as_slice()) {
|
match bincode::deserialize(v.as_slice()) {
|
||||||
Ok(WsMsg::Ping | WsMsg::Pong) => {
|
Ok(WsMsg::Ping | WsMsg::Pong) => {
|
||||||
orders.perform_cmd(cmds::timeout(1000, || {
|
orders.perform_cmd(cmds::timeout(300, || {
|
||||||
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
Msg::WebSocketChange(WebSocketChanged::SendPing)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -267,11 +272,14 @@ pub static mut WS_URL: String = String::new();
|
|||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn render(host_url: String, ws_url: String) {
|
pub fn render(host_url: String, ws_url: String) {
|
||||||
elements::define();
|
|
||||||
unsafe {
|
unsafe {
|
||||||
HOST_URL = host_url;
|
HOST_URL = host_url;
|
||||||
WS_URL = ws_url;
|
WS_URL = ws_url;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// crate::hi::syntax_set::load();
|
||||||
|
}
|
||||||
|
elements::define();
|
||||||
|
|
||||||
let _app = seed::App::builder(update, view)
|
let _app = seed::App::builder(update, view)
|
||||||
.routes(routes)
|
.routes(routes)
|
||||||
|
@ -73,10 +73,10 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for Type {
|
impl<'l> ToChild<'l> for Type {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let name = match self {
|
let name = match self {
|
||||||
Type::Task => "Task",
|
Type::Task => "Task",
|
||||||
Type::Bug => "Bug",
|
Type::Bug => "Bug",
|
||||||
|
@ -426,6 +426,7 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
let issue_types = IssueType::ordered();
|
||||||
let issue_type_select = StyledSelect::build()
|
let issue_type_select = StyledSelect::build()
|
||||||
.dropdown_width(150)
|
.dropdown_width(150)
|
||||||
.name("type")
|
.name("type")
|
||||||
@ -433,8 +434,8 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
.opened(top_type_state.opened)
|
.opened(top_type_state.opened)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.options(
|
.options(
|
||||||
IssueType::ordered()
|
issue_types
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|t| t.to_child().name("type"))
|
.map(|t| t.to_child().name("type"))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
@ -444,7 +445,7 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
issue_type
|
issue_type
|
||||||
.to_child()
|
.to_child()
|
||||||
.name("type")
|
.name("type")
|
||||||
.text(format!("{} - {}", issue_type, id))
|
.text_owned(format!("{} - {}", issue_type, id))
|
||||||
}])
|
}])
|
||||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
IssueFieldId::Type,
|
IssueFieldId::Type,
|
||||||
@ -500,7 +501,7 @@ fn left_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
model
|
model
|
||||||
.user
|
.user
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|u| u.avatar_url.clone())
|
.and_then(|u| u.avatar_url.as_deref())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
@ -597,7 +598,7 @@ fn comment(model: &Model, modal: &EditIssueModal, comment: &Comment) -> Option<N
|
|||||||
|
|
||||||
let avatar = StyledAvatar::build()
|
let avatar = StyledAvatar::build()
|
||||||
.size(32)
|
.size(32)
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned()?)
|
.avatar_url(user.avatar_url.as_deref()?)
|
||||||
.add_class("userAvatar")
|
.add_class("userAvatar")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
@ -751,14 +752,15 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
let issue_priorities = IssuePriority::ordered();
|
||||||
let priority = StyledSelect::build()
|
let priority = StyledSelect::build()
|
||||||
.name("priority")
|
.name("priority")
|
||||||
.opened(priority_state.opened)
|
.opened(priority_state.opened)
|
||||||
.empty()
|
.empty()
|
||||||
.text_filter(priority_state.text_filter.as_str())
|
.text_filter(priority_state.text_filter.as_str())
|
||||||
.options(
|
.options(
|
||||||
IssuePriority::ordered()
|
issue_priorities
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|p| p.to_child().name("priority"))
|
.map(|p| p.to_child().name("priority"))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
|
@ -89,6 +89,7 @@ pub fn time_tracking_field(
|
|||||||
input_state: &StyledInputState,
|
input_state: &StyledInputState,
|
||||||
select_state: &StyledSelectState,
|
select_state: &StyledSelectState,
|
||||||
) -> Node<Msg> {
|
) -> Node<Msg> {
|
||||||
|
let fibonacci_values = fibonacci_values();
|
||||||
let input = match time_tracking_type {
|
let input = match time_tracking_type {
|
||||||
TimeTracking::Untracked => empty![],
|
TimeTracking::Untracked => empty![],
|
||||||
TimeTracking::Fibonacci => StyledSelect::build()
|
TimeTracking::Fibonacci => StyledSelect::build()
|
||||||
@ -100,12 +101,7 @@ pub fn time_tracking_field(
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.state(select_state)
|
.state(select_state)
|
||||||
.options(
|
.options(fibonacci_values.iter().map(|v| v.to_child()).collect())
|
||||||
fibonacci_values()
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| v.to_child())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.build(field_id)
|
.build(field_id)
|
||||||
.into_node(),
|
.into_node(),
|
||||||
TimeTracking::Hourly => StyledInput::build()
|
TimeTracking::Hourly => StyledInput::build()
|
||||||
|
@ -8,16 +8,16 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use jirs_data::*;
|
use jirs_data::*;
|
||||||
|
|
||||||
use crate::modal::time_tracking::value_for_time_tracking;
|
use crate::{
|
||||||
use crate::shared::drag::DragState;
|
modal::time_tracking::value_for_time_tracking,
|
||||||
use crate::shared::styled_checkbox::StyledCheckboxState;
|
shared::{
|
||||||
use crate::shared::styled_date_time_input::StyledDateTimeInputState;
|
drag::DragState, styled_checkbox::StyledCheckboxState,
|
||||||
use crate::shared::styled_editor::Mode;
|
styled_date_time_input::StyledDateTimeInputState, styled_editor::Mode,
|
||||||
use crate::shared::styled_image_input::StyledImageInputState;
|
styled_image_input::StyledImageInputState, styled_input::StyledInputState,
|
||||||
use crate::shared::styled_input::StyledInputState;
|
styled_rte::StyledRteState, styled_select::StyledSelectState,
|
||||||
use crate::shared::styled_rte::StyledRteState;
|
},
|
||||||
use crate::shared::styled_select::StyledSelectState;
|
EditIssueModalSection, FieldId, Msg, ProjectFieldId,
|
||||||
use crate::{EditIssueModalSection, FieldId, Msg, ProjectFieldId};
|
};
|
||||||
|
|
||||||
pub trait IssueModal {
|
pub trait IssueModal {
|
||||||
fn epic_id_value(&self) -> Option<u32>;
|
fn epic_id_value(&self) -> Option<u32>;
|
||||||
|
11
jirs-client/src/print.rs
Normal file
11
jirs-client/src/print.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
std::fs::create_dir_all("./jirs-client/src/hi/syntax").unwrap();
|
||||||
|
|
||||||
|
use syntect::{dumps::*, parsing::*};
|
||||||
|
let syntax_set: SyntaxSet = from_binary(include_bytes!("./hi/newlines.packdump"));
|
||||||
|
std::fs::write(
|
||||||
|
"./jirs-client/src/hi/syntax_set.cbor",
|
||||||
|
serde_cbor::ser::to_vec(&syntax_set).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
@ -41,7 +41,7 @@ fn breadcrumbs(model: &Model) -> Node<Msg> {
|
|||||||
fn header() -> Node<Msg> {
|
fn header() -> Node<Msg> {
|
||||||
let button = StyledButton::build()
|
let button = StyledButton::build()
|
||||||
.secondary()
|
.secondary()
|
||||||
.text("Github Repo".to_string())
|
.text("Github Repo")
|
||||||
.icon(Icon::Github)
|
.icon(Icon::Github)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
@ -124,7 +124,7 @@ fn avatars_filters(model: &Model) -> Node<Msg> {
|
|||||||
class_list.push("isActive");
|
class_list.push("isActive");
|
||||||
}
|
}
|
||||||
let styled_avatar = StyledAvatar::build()
|
let styled_avatar = StyledAvatar::build()
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(user.avatar_url.as_deref().unwrap_or_default())
|
||||||
.on_click(mouse_ev(Ev::Click, move |_| {
|
.on_click(mouse_ev(Ev::Click, move |_| {
|
||||||
Msg::ProjectAvatarFilterChanged(user_id, active)
|
Msg::ProjectAvatarFilterChanged(user_id, active)
|
||||||
}))
|
}))
|
||||||
@ -270,7 +270,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
|||||||
StyledAvatar::build()
|
StyledAvatar::build()
|
||||||
.size(24)
|
.size(24)
|
||||||
.name(user.name.as_str())
|
.name(user.name.as_str())
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(user.avatar_url.as_deref().unwrap_or_default())
|
||||||
.user_index(idx)
|
.user_index(idx)
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
@ -279,10 +279,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
|||||||
|
|
||||||
let issue_type_icon = {
|
let issue_type_icon = {
|
||||||
StyledIcon::build(issue.issue_type.clone().into())
|
StyledIcon::build(issue.issue_type.clone().into())
|
||||||
.add_style(format!(
|
.with_color(issue.issue_type.to_str())
|
||||||
"color: var(--{issue_type})",
|
|
||||||
issue_type = issue.issue_type.to_string()
|
|
||||||
))
|
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
};
|
};
|
||||||
@ -292,7 +289,7 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
|
|||||||
_ => Icon::ArrowUp,
|
_ => Icon::ArrowUp,
|
||||||
};
|
};
|
||||||
StyledIcon::build(icon)
|
StyledIcon::build(icon)
|
||||||
.add_style(format!("color: var(--{})", issue.priority))
|
.with_color(issue.priority.to_str())
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Tracking employees’ time carries the risk of having them feel like they are being spied on. This is one of the most common fears that employees have when a time tracking system is implemented. No one likes to feel like they’re always being watched.
|
@ -0,0 +1 @@
|
|||||||
|
Employees may feel intimidated by demands to track their time. Or they could feel that they’re constantly being watched and evaluated. And for overly ambitious managers, employee time tracking may open the doors to excessive micromanaging.
|
@ -18,8 +18,8 @@ use crate::shared::styled_textarea::StyledTextarea;
|
|||||||
use crate::shared::{inner_layout, ToChild, ToNode};
|
use crate::shared::{inner_layout, ToChild, ToNode};
|
||||||
use crate::{model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange};
|
use crate::{model, FieldId, Msg, PageChanged, ProjectFieldId, ProjectPageChange};
|
||||||
|
|
||||||
static TIME_TRACKING_FIBONACCI: &str = "Tracking employees’ time carries the risk of having them feel like they are being spied on. This is one of the most common fears that employees have when a time tracking system is implemented. No one likes to feel like they’re always being watched.";
|
static TIME_TRACKING_FIBONACCI: &str = include_str!("./time_tracking_fibonacci.txt");
|
||||||
static TIME_TRACKING_HOURLY: &str = "Employees may feel intimidated by demands to track their time. Or they could feel that they’re constantly being watched and evaluated. And for overly ambitious managers, employee time tracking may open the doors to excessive micromanaging.";
|
static TIME_TRACKING_HOURLY: &str = include_str!("./time_tracking_hourly.txt");
|
||||||
|
|
||||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||||
let page = match &model.page_content {
|
let page = match &model.page_content {
|
||||||
@ -106,7 +106,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
/// Build project name input with styled field wrapper
|
/// Build project name input with styled field wrapper
|
||||||
fn name_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
fn name_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
||||||
let name = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Name))
|
let name = StyledTextarea::build(FieldId::ProjectSettings(ProjectFieldId::Name))
|
||||||
.value(page.payload.name.as_ref().cloned().unwrap_or_default())
|
.value(page.payload.name.as_deref().unwrap_or_default())
|
||||||
.height(39)
|
.height(39)
|
||||||
.max_height(39)
|
.max_height(39)
|
||||||
.disable_auto_resize()
|
.disable_auto_resize()
|
||||||
@ -126,7 +126,7 @@ fn url_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
|||||||
.height(39)
|
.height(39)
|
||||||
.max_height(39)
|
.max_height(39)
|
||||||
.disable_auto_resize()
|
.disable_auto_resize()
|
||||||
.value(page.payload.url.as_ref().cloned().unwrap_or_default())
|
.value(page.payload.url.as_deref().unwrap_or_default())
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
@ -161,17 +161,13 @@ fn description_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
|||||||
|
|
||||||
/// Build project category dropdown with styled field wrapper
|
/// Build project category dropdown with styled field wrapper
|
||||||
fn category_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
fn category_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
||||||
|
let project_categories = ProjectCategory::ordered();
|
||||||
let category = StyledSelect::build()
|
let category = StyledSelect::build()
|
||||||
.opened(page.project_category_state.opened)
|
.opened(page.project_category_state.opened)
|
||||||
.text_filter(page.project_category_state.text_filter.as_str())
|
.text_filter(page.project_category_state.text_filter.as_str())
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.normal()
|
.normal()
|
||||||
.options(
|
.options(project_categories.iter().map(|c| c.to_child()).collect())
|
||||||
ProjectCategory::ordered()
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| c.to_child())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.selected(vec![page
|
.selected(vec![page
|
||||||
.payload
|
.payload
|
||||||
.category
|
.category
|
||||||
|
@ -14,8 +14,8 @@ pub fn update(msg: &Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
vec![
|
vec![
|
||||||
WsMsg::UserProjectsLoad,
|
WsMsg::UserProjectsLoad,
|
||||||
WsMsg::ProjectsLoad,
|
WsMsg::ProjectsLoad,
|
||||||
WsMsg::MessagesLoad,
|
|
||||||
WsMsg::ProjectUsersLoad,
|
WsMsg::ProjectUsersLoad,
|
||||||
|
WsMsg::MessagesLoad,
|
||||||
],
|
],
|
||||||
model.ws.as_ref(),
|
model.ws.as_ref(),
|
||||||
orders,
|
orders,
|
||||||
@ -85,16 +85,17 @@ pub fn render(model: &Model) -> Node<Msg> {
|
|||||||
|
|
||||||
fn sidebar_link_item(model: &Model, name: &str, icon: Icon, page: Option<Page>) -> Node<Msg> {
|
fn sidebar_link_item(model: &Model, name: &str, icon: Icon, page: Option<Page>) -> Node<Msg> {
|
||||||
let path = page.map(|ref p| p.to_path()).unwrap_or_default();
|
let path = page.map(|ref p| p.to_path()).unwrap_or_default();
|
||||||
let mut class_list = vec!["linkItem".to_string(), icon.to_string()];
|
let mut class_list = vec![];
|
||||||
if page.is_none() {
|
if page.is_none() {
|
||||||
class_list.push("notAllowed".to_string());
|
class_list.push("notAllowed");
|
||||||
};
|
};
|
||||||
if Some(model.page) == page {
|
if Some(model.page) == page {
|
||||||
class_list.push("active".to_string());
|
class_list.push("active");
|
||||||
}
|
}
|
||||||
let icon_node = StyledIcon::build(icon).build().into_node();
|
let icon_node = StyledIcon::build(icon).build().into_node();
|
||||||
li![
|
li![
|
||||||
attrs![At::Class => class_list.join(" ")],
|
class!["linkItem"],
|
||||||
|
class![icon.to_str()],
|
||||||
a![
|
a![
|
||||||
attrs![At::Href => path],
|
attrs![At::Href => path],
|
||||||
icon_node,
|
icon_node,
|
||||||
|
@ -31,10 +31,10 @@ pub mod styled_textarea;
|
|||||||
pub mod styled_tooltip;
|
pub mod styled_tooltip;
|
||||||
pub mod tracking_widget;
|
pub mod tracking_widget;
|
||||||
|
|
||||||
pub trait ToChild {
|
pub trait ToChild<'l> {
|
||||||
type Builder;
|
type Builder: 'l;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder;
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn go_to_board(orders: &mut impl Orders<Msg>) {
|
pub fn go_to_board(orders: &mut impl Orders<Msg>) {
|
||||||
@ -51,7 +51,7 @@ pub fn go_to(url: &str) {
|
|||||||
seed::push_route(Url::from_str(url).unwrap());
|
seed::push_route(Url::from_str(url).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_issue(model: &Model, issue_id: IssueId) -> Option<&Issue> {
|
pub fn find_issue<'l>(model: &'l Model, issue_id: IssueId) -> Option<&'l Issue> {
|
||||||
model.issues.iter().find(|issue| issue.id == issue_id)
|
model.issues.iter().find(|issue| issue.id == issue_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ pub fn render(model: &Model) -> Vec<Node<Msg>> {
|
|||||||
StyledAvatar::build()
|
StyledAvatar::build()
|
||||||
.size(27)
|
.size(27)
|
||||||
.name(user.name.as_str())
|
.name(user.name.as_str())
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(user.avatar_url.as_deref().unwrap_or_default())
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
],
|
],
|
||||||
@ -257,12 +257,12 @@ fn message_ui(model: &Model, message: &Message) -> Option<Node<Msg>> {
|
|||||||
|
|
||||||
fn about_tooltip_popup(model: &Model) -> Node<Msg> {
|
fn about_tooltip_popup(model: &Model) -> Node<Msg> {
|
||||||
let visit_website = StyledButton::build()
|
let visit_website = StyledButton::build()
|
||||||
.text("Visit Website".to_string())
|
.text("Visit Website")
|
||||||
.primary()
|
.primary()
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let github_repo = StyledButton::build()
|
let github_repo = StyledButton::build()
|
||||||
.text("Github Repo".to_string())
|
.text("Github Repo")
|
||||||
.secondary()
|
.secondary()
|
||||||
.icon(Icon::Github)
|
.icon(Icon::Github)
|
||||||
.build()
|
.build()
|
||||||
@ -332,7 +332,7 @@ fn parse_description(model: &Model, desc: &str) -> Node<Msg> {
|
|||||||
})
|
})
|
||||||
.map(|(index, user)| {
|
.map(|(index, user)| {
|
||||||
let avatar = StyledAvatar::build()
|
let avatar = StyledAvatar::build()
|
||||||
.avatar_url(user.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(user.avatar_url.as_deref().unwrap_or_default())
|
||||||
.user_index(index)
|
.user_index(index)
|
||||||
.size(16)
|
.size(16)
|
||||||
.build()
|
.build()
|
||||||
|
@ -3,21 +3,21 @@ use seed::{prelude::*, *};
|
|||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
pub struct StyledAvatar {
|
pub struct StyledAvatar<'l> {
|
||||||
avatar_url: Option<String>,
|
avatar_url: Option<&'l str>,
|
||||||
size: u32,
|
size: u32,
|
||||||
name: String,
|
name: &'l str,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
user_index: usize,
|
user_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for StyledAvatar {
|
impl<'l> Default for StyledAvatar<'l> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
avatar_url: None,
|
avatar_url: None,
|
||||||
size: 32,
|
size: 32,
|
||||||
name: "".to_string(),
|
name: "",
|
||||||
on_click: None,
|
on_click: None,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
user_index: 0,
|
user_index: 0,
|
||||||
@ -25,12 +25,12 @@ impl Default for StyledAvatar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledAvatar {
|
impl<'l> StyledAvatar<'l> {
|
||||||
pub fn build() -> StyledAvatarBuilder {
|
pub fn build() -> StyledAvatarBuilder<'l> {
|
||||||
StyledAvatarBuilder {
|
StyledAvatarBuilder {
|
||||||
avatar_url: None,
|
avatar_url: None,
|
||||||
size: None,
|
size: None,
|
||||||
name: "".to_string(),
|
name: "",
|
||||||
on_click: None,
|
on_click: None,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
user_index: 0,
|
user_index: 0,
|
||||||
@ -38,29 +38,25 @@ impl StyledAvatar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledAvatar {
|
impl<'l> ToNode for StyledAvatar<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledAvatarBuilder {
|
pub struct StyledAvatarBuilder<'l> {
|
||||||
avatar_url: Option<String>,
|
avatar_url: Option<&'l str>,
|
||||||
size: Option<u32>,
|
size: Option<u32>,
|
||||||
name: String,
|
name: &'l str,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
user_index: usize,
|
user_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledAvatarBuilder {
|
impl<'l> StyledAvatarBuilder<'l> {
|
||||||
pub fn avatar_url<S>(mut self, avatar_url: S) -> Self
|
pub fn avatar_url<'m: 'l>(mut self, avatar_url: &'m str) -> Self {
|
||||||
where
|
if !avatar_url.is_empty() {
|
||||||
S: Into<String>,
|
self.avatar_url = Some(avatar_url);
|
||||||
{
|
|
||||||
let url = avatar_url.into();
|
|
||||||
if !url.is_empty() {
|
|
||||||
self.avatar_url = Some(url);
|
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -70,11 +66,8 @@ impl StyledAvatarBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name<S>(mut self, name: S) -> Self
|
pub fn name<'m: 'l>(mut self, name: &'m str) -> Self {
|
||||||
where
|
self.name = name;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.name = name.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,11 +76,8 @@ impl StyledAvatarBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class<'m: 'l>(mut self, name: &'m str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +86,7 @@ impl StyledAvatarBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledAvatar {
|
pub fn build(self) -> StyledAvatar<'l> {
|
||||||
StyledAvatar {
|
StyledAvatar {
|
||||||
avatar_url: self.avatar_url,
|
avatar_url: self.avatar_url,
|
||||||
size: self.size.unwrap_or(32),
|
size: self.size.unwrap_or(32),
|
||||||
@ -120,10 +110,10 @@ pub fn render(values: StyledAvatar) -> Node<Msg> {
|
|||||||
|
|
||||||
let index = user_index % 8;
|
let index = user_index % 8;
|
||||||
|
|
||||||
class_list.push("styledAvatar".to_string());
|
class_list.push("styledAvatar");
|
||||||
match avatar_url {
|
match avatar_url {
|
||||||
Some(_) => class_list.push("image".to_string()),
|
Some(_) => class_list.push("image"),
|
||||||
_ => class_list.push("letter".to_string()),
|
_ => class_list.push("letter"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let shared_style = format!("width: {size}px; height: {size}px", size = size);
|
let shared_style = format!("width: {size}px; height: {size}px", size = size);
|
||||||
@ -155,10 +145,13 @@ pub fn render(values: StyledAvatar) -> Node<Msg> {
|
|||||||
shared = shared_style,
|
shared = shared_style,
|
||||||
size = size
|
size = size
|
||||||
);
|
);
|
||||||
class_list.push("letter".to_string());
|
|
||||||
class_list.push(format!("avatarColor{}", index + 1));
|
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => class_list.join(" "), At::Style => style],
|
class!["letter"],
|
||||||
|
attrs![
|
||||||
|
At::Class => format!("avatarColor{}", index + 1),
|
||||||
|
At::Class => class_list.join(" "),
|
||||||
|
At::Style => style
|
||||||
|
],
|
||||||
span![letter],
|
span![letter],
|
||||||
handler,
|
handler,
|
||||||
]
|
]
|
||||||
|
@ -12,8 +12,8 @@ enum Variant {
|
|||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Variant {
|
impl Variant {
|
||||||
fn to_string(&self) -> String {
|
fn to_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Variant::Primary => "primary",
|
Variant::Primary => "primary",
|
||||||
Variant::Success => "success",
|
Variant::Success => "success",
|
||||||
@ -21,24 +21,29 @@ impl ToString for Variant {
|
|||||||
Variant::Secondary => "secondary",
|
Variant::Secondary => "secondary",
|
||||||
Variant::Empty => "empty",
|
Variant::Empty => "empty",
|
||||||
}
|
}
|
||||||
.to_string()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Variant {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.to_str().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledButtonBuilder {
|
pub struct StyledButtonBuilder<'l> {
|
||||||
variant: Option<Variant>,
|
variant: Option<Variant>,
|
||||||
disabled: Option<bool>,
|
disabled: Option<bool>,
|
||||||
active: Option<bool>,
|
active: Option<bool>,
|
||||||
text: Option<String>,
|
text: Option<&'l str>,
|
||||||
icon: Option<Node<Msg>>,
|
icon: Option<Node<Msg>>,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
children: Option<Vec<Node<Msg>>>,
|
children: Option<Vec<Node<Msg>>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
button_type: Option<String>,
|
button_type: Option<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButtonBuilder {
|
impl<'l> StyledButtonBuilder<'l> {
|
||||||
fn variant(mut self, value: Variant) -> Self {
|
fn variant(mut self, value: Variant) -> Self {
|
||||||
self.variant = Some(value);
|
self.variant = Some(value);
|
||||||
self
|
self
|
||||||
@ -74,11 +79,8 @@ impl StyledButtonBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text<S>(mut self, value: S) -> Self
|
pub fn text(mut self, value: &'l str) -> Self {
|
||||||
where
|
self.text = Some(value);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.text = Some(value.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,20 +102,17 @@ impl StyledButtonBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_type_reset(mut self) -> Self {
|
pub fn set_type_reset(mut self) -> Self {
|
||||||
self.button_type = Some("reset".to_string());
|
self.button_type = Some("reset");
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledButton {
|
pub fn build(self) -> StyledButton<'l> {
|
||||||
StyledButton {
|
StyledButton {
|
||||||
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
||||||
disabled: self.disabled.unwrap_or_else(|| false),
|
disabled: self.disabled.unwrap_or_else(|| false),
|
||||||
@ -123,30 +122,30 @@ impl StyledButtonBuilder {
|
|||||||
on_click: self.on_click,
|
on_click: self.on_click,
|
||||||
children: self.children.unwrap_or_default(),
|
children: self.children.unwrap_or_default(),
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
button_type: self.button_type.unwrap_or_else(|| "submit".to_string()),
|
button_type: self.button_type.unwrap_or_else(|| "submit"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledButton {
|
pub struct StyledButton<'l> {
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
active: bool,
|
active: bool,
|
||||||
text: Option<String>,
|
text: Option<&'l str>,
|
||||||
icon: Option<Node<Msg>>,
|
icon: Option<Node<Msg>>,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
button_type: String,
|
button_type: &'l str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButton {
|
impl<'l> StyledButton<'l> {
|
||||||
pub fn build() -> StyledButtonBuilder {
|
pub fn build() -> StyledButtonBuilder<'l> {
|
||||||
StyledButtonBuilder::default()
|
StyledButtonBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledButton {
|
impl<'l> ToNode for StyledButton<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
@ -164,16 +163,16 @@ pub fn render(values: StyledButton) -> Node<Msg> {
|
|||||||
mut class_list,
|
mut class_list,
|
||||||
button_type,
|
button_type,
|
||||||
} = values;
|
} = values;
|
||||||
class_list.push("styledButton".to_string());
|
class_list.push("styledButton");
|
||||||
class_list.push(variant.to_string());
|
class_list.push(variant.to_str());
|
||||||
if children.is_empty() && text.is_none() {
|
if children.is_empty() && text.is_none() {
|
||||||
class_list.push("iconOnly".to_string());
|
class_list.push("iconOnly");
|
||||||
}
|
}
|
||||||
if active {
|
if active {
|
||||||
class_list.push("isActive".to_string());
|
class_list.push("isActive");
|
||||||
}
|
}
|
||||||
if icon.is_some() {
|
if icon.is_some() {
|
||||||
class_list.push("withIcon".to_string());
|
class_list.push("withIcon");
|
||||||
}
|
}
|
||||||
let handler = match on_click {
|
let handler = match on_click {
|
||||||
Some(h) if !disabled => vec![h],
|
Some(h) if !disabled => vec![h],
|
||||||
|
@ -27,21 +27,21 @@ impl StyledCheckboxState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ChildBuilder {
|
pub struct ChildBuilder<'l> {
|
||||||
field_id: Option<FieldId>,
|
field_id: Option<FieldId>,
|
||||||
name: String,
|
name: &'l str,
|
||||||
label: String,
|
label: &'l str,
|
||||||
value: u32,
|
value: u32,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChildBuilder {
|
impl<'l> Default for ChildBuilder<'l> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
field_id: None,
|
field_id: None,
|
||||||
name: "".to_string(),
|
name: "",
|
||||||
label: "".to_string(),
|
label: "",
|
||||||
value: 0,
|
value: 0,
|
||||||
selected: false,
|
selected: false,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
@ -49,25 +49,19 @@ impl Default for ChildBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChildBuilder {
|
impl<'l> ChildBuilder<'l> {
|
||||||
pub fn value(mut self, value: u32) -> Self {
|
pub fn value(mut self, value: u32) -> Self {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name<S>(mut self, name: S) -> Self
|
pub fn name(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.name = name;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.name = name.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn label<S>(mut self, label: S) -> Self
|
pub fn label(mut self, label: &'l str) -> Self {
|
||||||
where
|
self.label = label;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.label = label.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +84,7 @@ impl ChildBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for ChildBuilder {
|
impl<'l> ToNode for ChildBuilder<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
let ChildBuilder {
|
let ChildBuilder {
|
||||||
field_id,
|
field_id,
|
||||||
@ -111,9 +105,9 @@ impl ToNode for ChildBuilder {
|
|||||||
class_list.push(if selected { "selected" } else { "" }.to_string());
|
class_list.push(if selected { "selected" } else { "" }.to_string());
|
||||||
|
|
||||||
let input_attrs = if selected {
|
let input_attrs = if selected {
|
||||||
attrs![At::Type => "radio", At::Name => name.as_str(), At::Checked => selected, At::Id => format!("{}-{}", id, name)]
|
attrs![At::Type => "radio", At::Name => name, At::Checked => selected, At::Id => format!("{}-{}", id, name)]
|
||||||
} else {
|
} else {
|
||||||
attrs![At::Type => "radio", At::Name => name.as_str(), At::Id => format!("{}-{}", id, name)]
|
attrs![At::Type => "radio", At::Name => name, At::Id => format!("{}-{}", id, name)]
|
||||||
};
|
};
|
||||||
|
|
||||||
div![
|
div![
|
||||||
@ -126,21 +120,21 @@ impl ToNode for ChildBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledCheckbox {
|
pub struct StyledCheckbox<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
options: Vec<ChildBuilder>,
|
options: Vec<ChildBuilder<'l>>,
|
||||||
selected: u32,
|
selected: u32,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledCheckbox {
|
impl<'l> ToNode for StyledCheckbox<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledCheckbox {
|
impl<'l> StyledCheckbox<'l> {
|
||||||
pub fn build() -> StyledCheckboxBuilder {
|
pub fn build() -> StyledCheckboxBuilder<'l> {
|
||||||
StyledCheckboxBuilder {
|
StyledCheckboxBuilder {
|
||||||
options: vec![],
|
options: vec![],
|
||||||
selected: 0,
|
selected: 0,
|
||||||
@ -149,32 +143,29 @@ impl StyledCheckbox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledCheckboxBuilder {
|
pub struct StyledCheckboxBuilder<'l> {
|
||||||
options: Vec<ChildBuilder>,
|
options: Vec<ChildBuilder<'l>>,
|
||||||
selected: u32,
|
selected: u32,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledCheckboxBuilder {
|
impl<'l> StyledCheckboxBuilder<'l> {
|
||||||
pub fn state(mut self, state: &StyledCheckboxState) -> Self {
|
pub fn state(mut self, state: &StyledCheckboxState) -> Self {
|
||||||
self.selected = state.value;
|
self.selected = state.value;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn options(mut self, options: Vec<ChildBuilder>) -> Self {
|
pub fn options(mut self, options: Vec<ChildBuilder<'l>>) -> Self {
|
||||||
self.options = options;
|
self.options = options;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self, field_id: FieldId) -> StyledCheckbox {
|
pub fn build(self, field_id: FieldId) -> StyledCheckbox<'l> {
|
||||||
StyledCheckbox {
|
StyledCheckbox {
|
||||||
id: field_id,
|
id: field_id,
|
||||||
options: self.options,
|
options: self.options,
|
||||||
@ -204,10 +195,10 @@ fn render(values: StyledCheckbox) -> Node<Msg> {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for TimeTracking {
|
impl<'l> ToChild<'l> for TimeTracking {
|
||||||
type Builder = ChildBuilder;
|
type Builder = ChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
Self::Builder::default()
|
Self::Builder::default()
|
||||||
.label(match self {
|
.label(match self {
|
||||||
TimeTracking::Untracked => "No tracking",
|
TimeTracking::Untracked => "No tracking",
|
||||||
|
@ -12,65 +12,53 @@ const CONFIRM_TEXT: &str = "Confirm";
|
|||||||
const CANCEL_TEXT: &str = "Cancel";
|
const CANCEL_TEXT: &str = "Cancel";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledConfirmModal {
|
pub struct StyledConfirmModal<'l> {
|
||||||
title: String,
|
title: &'l str,
|
||||||
message: String,
|
message: &'l str,
|
||||||
confirm_text: String,
|
confirm_text: &'l str,
|
||||||
cancel_text: String,
|
cancel_text: &'l str,
|
||||||
on_confirm: Option<EventHandler<Msg>>,
|
on_confirm: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledConfirmModal {
|
impl<'l> StyledConfirmModal<'l> {
|
||||||
pub fn build() -> StyledConfirmModalBuilder {
|
pub fn build() -> StyledConfirmModalBuilder<'l> {
|
||||||
StyledConfirmModalBuilder::default()
|
StyledConfirmModalBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledConfirmModal {
|
impl<'l> ToNode for StyledConfirmModal<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledConfirmModalBuilder {
|
pub struct StyledConfirmModalBuilder<'l> {
|
||||||
title: Option<String>,
|
title: Option<&'l str>,
|
||||||
message: Option<String>,
|
message: Option<&'l str>,
|
||||||
confirm_text: Option<String>,
|
confirm_text: Option<&'l str>,
|
||||||
cancel_text: Option<String>,
|
cancel_text: Option<&'l str>,
|
||||||
on_confirm: Option<EventHandler<Msg>>,
|
on_confirm: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledConfirmModalBuilder {
|
impl<'l> StyledConfirmModalBuilder<'l> {
|
||||||
pub fn title<S>(mut self, title: S) -> Self
|
pub fn title(mut self, title: &'l str) -> Self {
|
||||||
where
|
self.title = Some(title);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.title = Some(title.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message<S>(mut self, message: S) -> Self
|
pub fn message(mut self, message: &'l str) -> Self {
|
||||||
where
|
self.message = Some(message);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.message = Some(message.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn confirm_text<S>(mut self, confirm_text: S) -> Self
|
pub fn confirm_text(mut self, confirm_text: &'l str) -> Self {
|
||||||
where
|
self.confirm_text = Some(confirm_text);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.confirm_text = Some(confirm_text.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel_text<S>(mut self, cancel_text: S) -> Self
|
pub fn cancel_text(mut self, cancel_text: &'l str) -> Self {
|
||||||
where
|
self.cancel_text = Some(cancel_text);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.cancel_text = Some(cancel_text.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,14 +67,12 @@ impl StyledConfirmModalBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledConfirmModal {
|
pub fn build(self) -> StyledConfirmModal<'l> {
|
||||||
StyledConfirmModal {
|
StyledConfirmModal {
|
||||||
title: self.title.unwrap_or_else(|| TITLE.to_string()),
|
title: self.title.unwrap_or_else(|| TITLE),
|
||||||
message: self.message.unwrap_or_else(|| MESSAGE.to_string()),
|
message: self.message.unwrap_or_else(|| MESSAGE),
|
||||||
confirm_text: self
|
confirm_text: self.confirm_text.unwrap_or_else(|| CONFIRM_TEXT),
|
||||||
.confirm_text
|
cancel_text: self.cancel_text.unwrap_or_else(|| CANCEL_TEXT),
|
||||||
.unwrap_or_else(|| CONFIRM_TEXT.to_string()),
|
|
||||||
cancel_text: self.cancel_text.unwrap_or_else(|| CANCEL_TEXT.to_string()),
|
|
||||||
on_confirm: self.on_confirm,
|
on_confirm: self.on_confirm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +118,7 @@ pub fn render(values: StyledConfirmModal) -> Node<Msg> {
|
|||||||
cancel_button
|
cancel_button
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
.add_class("confirmModal".to_string())
|
.add_class("confirmModal")
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ fn render(values: StyledDateTimeInput) -> Node<Msg> {
|
|||||||
.to_string();
|
.to_string();
|
||||||
StyledButton::build()
|
StyledButton::build()
|
||||||
.on_click(on_focus)
|
.on_click(on_focus)
|
||||||
.text(text)
|
.text(text.as_str())
|
||||||
.empty()
|
.empty()
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
|
@ -4,47 +4,41 @@ use crate::shared::ToNode;
|
|||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledField {
|
pub struct StyledField<'l> {
|
||||||
label: String,
|
label: &'l str,
|
||||||
tip: Option<String>,
|
tip: Option<&'l str>,
|
||||||
input: Node<Msg>,
|
input: Node<Msg>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledField {
|
impl<'l> StyledField<'l> {
|
||||||
pub fn build() -> StyledFieldBuilder {
|
pub fn build() -> StyledFieldBuilder<'l> {
|
||||||
StyledFieldBuilder::default()
|
StyledFieldBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledField {
|
impl<'l> ToNode for StyledField<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StyledFieldBuilder {
|
pub struct StyledFieldBuilder<'l> {
|
||||||
label: Option<String>,
|
label: Option<&'l str>,
|
||||||
tip: Option<String>,
|
tip: Option<&'l str>,
|
||||||
input: Option<Node<Msg>>,
|
input: Option<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledFieldBuilder {
|
impl<'l> StyledFieldBuilder<'l> {
|
||||||
pub fn label<S>(mut self, label: S) -> Self
|
pub fn label(mut self, label: &'l str) -> Self {
|
||||||
where
|
self.label = Some(label);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.label = Some(label.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tip<S>(mut self, tip: S) -> Self
|
pub fn tip(mut self, tip: &'l str) -> Self {
|
||||||
where
|
self.tip = Some(tip);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.tip = Some(tip.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,15 +47,12 @@ impl StyledFieldBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledField {
|
pub fn build(self) -> StyledField<'l> {
|
||||||
StyledField {
|
StyledField {
|
||||||
label: self.label.unwrap_or_default(),
|
label: self.label.unwrap_or_default(),
|
||||||
tip: self.tip,
|
tip: self.tip,
|
||||||
@ -76,16 +67,15 @@ pub fn render(values: StyledField) -> Node<Msg> {
|
|||||||
label,
|
label,
|
||||||
tip,
|
tip,
|
||||||
input,
|
input,
|
||||||
mut class_list,
|
class_list,
|
||||||
} = values;
|
} = values;
|
||||||
let tip_node = match tip {
|
let tip_node = match tip {
|
||||||
Some(s) => div![attrs![At::Class => "styledTip"], s],
|
Some(s) => div![attrs![At::Class => "styledTip"], s],
|
||||||
_ => empty![],
|
_ => empty![],
|
||||||
};
|
};
|
||||||
class_list.push("styledField".to_string());
|
|
||||||
|
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => class_list.join(" ")],
|
attrs![At::Class => class_list.join(" "), At::Class => "styledField"],
|
||||||
seed::label![attrs![At::Class => "styledLabel"], label],
|
seed::label![attrs![At::Class => "styledLabel"], label],
|
||||||
input,
|
input,
|
||||||
tip_node,
|
tip_node,
|
||||||
|
@ -4,32 +4,32 @@ use crate::shared::ToNode;
|
|||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StyledForm {
|
pub struct StyledForm<'l> {
|
||||||
heading: String,
|
heading: &'l str,
|
||||||
fields: Vec<Node<Msg>>,
|
fields: Vec<Node<Msg>>,
|
||||||
on_submit: Option<EventHandler<Msg>>,
|
on_submit: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledForm {
|
impl<'l> StyledForm<'l> {
|
||||||
pub fn build() -> StyledFormBuilder {
|
pub fn build() -> StyledFormBuilder<'l> {
|
||||||
StyledFormBuilder::default()
|
StyledFormBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledForm {
|
impl<'l> ToNode for StyledForm<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct StyledFormBuilder {
|
pub struct StyledFormBuilder<'l> {
|
||||||
fields: Vec<Node<Msg>>,
|
fields: Vec<Node<Msg>>,
|
||||||
heading: String,
|
heading: &'l str,
|
||||||
on_submit: Option<EventHandler<Msg>>,
|
on_submit: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledFormBuilder {
|
impl<'l> StyledFormBuilder<'l> {
|
||||||
pub fn add_field(mut self, node: Node<Msg>) -> Self {
|
pub fn add_field(mut self, node: Node<Msg>) -> Self {
|
||||||
self.fields.push(node);
|
self.fields.push(node);
|
||||||
self
|
self
|
||||||
@ -42,11 +42,8 @@ impl StyledFormBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn heading<S>(mut self, heading: S) -> Self
|
pub fn heading(mut self, heading: &'l str) -> Self {
|
||||||
where
|
self.heading = heading;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.heading = heading.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +52,7 @@ impl StyledFormBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledForm {
|
pub fn build(self) -> StyledForm<'l> {
|
||||||
StyledForm {
|
StyledForm {
|
||||||
heading: self.heading,
|
heading: self.heading,
|
||||||
fields: self.fields,
|
fields: self.fields,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use jirs_data::{IssuePriority, IssueType};
|
use jirs_data::{IssuePriority, IssueType};
|
||||||
@ -100,21 +102,19 @@ pub enum Icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
pub fn to_color(self) -> Option<String> {
|
pub fn to_color(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Icon::Bug | Icon::Task | Icon::Story | Icon::Epic => Some(format!("var(--{})", self)),
|
Icon::Bug | Icon::Task | Icon::Story | Icon::Epic => Some(format!("var(--{})", self)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_styled_builder(self) -> StyledIconBuilder {
|
pub fn into_styled_builder<'l>(self) -> StyledIconBuilder<'l> {
|
||||||
StyledIcon::build(self)
|
StyledIcon::build(self)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Icon {
|
pub fn to_str<'l>(&self) -> &'l str {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
match self {
|
||||||
let code = match self {
|
|
||||||
Icon::Bug => "bug",
|
Icon::Bug => "bug",
|
||||||
Icon::Stopwatch => "stopwatch",
|
Icon::Stopwatch => "stopwatch",
|
||||||
Icon::Task => "task",
|
Icon::Task => "task",
|
||||||
@ -202,8 +202,13 @@ impl std::fmt::Display for Icon {
|
|||||||
|
|
||||||
Icon::DoubleLeft => "double-left",
|
Icon::DoubleLeft => "double-left",
|
||||||
Icon::DoubleRight => "double-right",
|
Icon::DoubleRight => "double-right",
|
||||||
};
|
}
|
||||||
f.write_str(code)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Icon {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.to_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,8 +235,8 @@ impl From<IssuePriority> for Icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<StyledIcon> for Icon {
|
impl<'l> Into<StyledIcon<'l>> for Icon {
|
||||||
fn into(self) -> StyledIcon {
|
fn into(self) -> StyledIcon<'l> {
|
||||||
StyledIcon::build(self).build()
|
StyledIcon::build(self).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,33 +248,28 @@ impl ToNode for Icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledIconBuilder {
|
pub struct StyledIconBuilder<'l> {
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
size: Option<i32>,
|
size: Option<i32>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<Cow<'l, str>>,
|
||||||
style_list: Vec<String>,
|
style_list: Vec<Cow<'l, str>>,
|
||||||
|
color: Option<Cow<'l, str>>,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledIconBuilder {
|
impl<'l> StyledIconBuilder<'l> {
|
||||||
pub fn size(mut self, size: i32) -> Self {
|
pub fn size(mut self, size: i32) -> Self {
|
||||||
self.size = Some(size);
|
self.size = Some(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(Cow::Borrowed(name));
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_style<S>(mut self, name: S) -> Self
|
pub fn with_color(mut self, color: &'l str) -> Self {
|
||||||
where
|
self.color = Some(Cow::Borrowed(color));
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.style_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,10 +278,11 @@ impl StyledIconBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledIcon {
|
pub fn build(self) -> StyledIcon<'l> {
|
||||||
StyledIcon {
|
StyledIcon {
|
||||||
icon: self.icon,
|
icon: self.icon,
|
||||||
size: self.size,
|
size: self.size,
|
||||||
|
color: self.color,
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
style_list: self.style_list,
|
style_list: self.style_list,
|
||||||
on_click: self.on_click,
|
on_click: self.on_click,
|
||||||
@ -289,27 +290,29 @@ impl StyledIconBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledIcon {
|
pub struct StyledIcon<'l> {
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
size: Option<i32>,
|
size: Option<i32>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<Cow<'l, str>>,
|
||||||
style_list: Vec<String>,
|
style_list: Vec<Cow<'l, str>>,
|
||||||
|
color: Option<Cow<'l, str>>,
|
||||||
on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledIcon {
|
impl<'l> StyledIcon<'l> {
|
||||||
pub fn build(icon: Icon) -> StyledIconBuilder {
|
pub fn build(icon: Icon) -> StyledIconBuilder<'l> {
|
||||||
StyledIconBuilder {
|
StyledIconBuilder {
|
||||||
icon,
|
icon,
|
||||||
size: None,
|
size: None,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
style_list: vec![],
|
style_list: vec![],
|
||||||
|
color: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledIcon {
|
impl<'l> ToNode for StyledIcon<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
@ -319,23 +322,56 @@ pub fn render(values: StyledIcon) -> Node<Msg> {
|
|||||||
let StyledIcon {
|
let StyledIcon {
|
||||||
icon,
|
icon,
|
||||||
size,
|
size,
|
||||||
mut class_list,
|
color,
|
||||||
mut style_list,
|
class_list,
|
||||||
|
style_list,
|
||||||
on_click,
|
on_click,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
if let Some(s) = icon.to_color() {
|
let styles: Vec<Attrs> = vec![
|
||||||
style_list.push(format!("color: {}", s));
|
size.map(|s| {
|
||||||
}
|
let font_size = format!("font-size: {}", s);
|
||||||
|
attrs![At::Style => font_size]
|
||||||
|
}),
|
||||||
|
icon.to_color().map(|s| {
|
||||||
|
let color = format!("color: {}", s);
|
||||||
|
attrs![At::Style => color]
|
||||||
|
}),
|
||||||
|
color.map(|s| {
|
||||||
|
let s = match s {
|
||||||
|
Cow::Owned(s) => format!("color: var(--{})", s.as_str()),
|
||||||
|
Cow::Borrowed(s) => format!("color: var(--{})", s),
|
||||||
|
};
|
||||||
|
attrs![At::Style => s]
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.filter(Option::is_some)
|
||||||
|
.map(|o| o.unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
if let Some(size) = size {
|
let class_list: Vec<seed::Attrs> = class_list
|
||||||
style_list.push(format!("font-size: {s}px", s = size));
|
.into_iter()
|
||||||
}
|
.map(|s| match s {
|
||||||
|
Cow::Borrowed(s) => class![s],
|
||||||
class_list.push(format!("styledIcon {}", icon));
|
Cow::Owned(s) => class![s.as_str()],
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let style_list = style_list
|
||||||
|
.iter()
|
||||||
|
.map(|s| match s {
|
||||||
|
Cow::Borrowed(s) => s,
|
||||||
|
Cow::Owned(s) => s.as_str(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<&str>>()
|
||||||
|
.join(";");
|
||||||
|
|
||||||
i![
|
i![
|
||||||
attrs![At::Class => class_list.join(" "), At::Style => style_list.join(";")],
|
class!["styledIcon"],
|
||||||
|
class_list,
|
||||||
|
class![icon.to_str()],
|
||||||
|
styles,
|
||||||
|
attrs![ At::Style => style_list ],
|
||||||
on_click,
|
on_click,
|
||||||
""
|
""
|
||||||
]
|
]
|
||||||
|
@ -30,14 +30,14 @@ impl StyledImageInputState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledImageInput {
|
pub struct StyledImageInput<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledImageInput {
|
impl<'l> StyledImageInput<'l> {
|
||||||
pub fn build(field_id: FieldId) -> StyledInputInputBuilder {
|
pub fn build(field_id: FieldId) -> StyledInputInputBuilder<'l> {
|
||||||
StyledInputInputBuilder {
|
StyledInputInputBuilder {
|
||||||
id: field_id,
|
id: field_id,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
@ -46,24 +46,21 @@ impl StyledImageInput {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledImageInput {
|
impl<'l> ToNode for StyledImageInput<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledInputInputBuilder {
|
pub struct StyledInputInputBuilder<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
url: Option<String>,
|
url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledInputInputBuilder {
|
impl<'l> StyledInputInputBuilder<'l> {
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +69,7 @@ impl StyledInputInputBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledImageInput {
|
pub fn build(self) -> StyledImageInput<'l> {
|
||||||
StyledImageInput {
|
StyledImageInput {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
|
@ -10,13 +10,18 @@ pub enum Variant {
|
|||||||
Primary,
|
Primary,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Variant {
|
impl Variant {
|
||||||
fn to_string(&self) -> String {
|
pub fn to_str<'l>(&self) -> &'l str {
|
||||||
match self {
|
match self {
|
||||||
Variant::Normal => "normal",
|
Variant::Normal => "normal",
|
||||||
Variant::Primary => "primary",
|
Variant::Primary => "primary",
|
||||||
}
|
}
|
||||||
.to_string()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for Variant {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.to_str().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,21 +72,21 @@ impl StyledInputState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledInput {
|
pub struct StyledInput<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
icon: Option<Icon>,
|
icon: Option<Icon>,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
value: Option<String>,
|
value: Option<String>,
|
||||||
input_type: Option<String>,
|
input_type: Option<&'l str>,
|
||||||
input_class_list: Vec<String>,
|
input_class_list: Vec<&'l str>,
|
||||||
wrapper_class_list: Vec<String>,
|
wrapper_class_list: Vec<&'l str>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
auto_focus: bool,
|
auto_focus: bool,
|
||||||
input_handlers: Vec<EventHandler<Msg>>,
|
input_handlers: Vec<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledInput {
|
impl<'l> StyledInput<'l> {
|
||||||
pub fn build() -> StyledInputBuilder {
|
pub fn build() -> StyledInputBuilder<'l> {
|
||||||
StyledInputBuilder {
|
StyledInputBuilder {
|
||||||
icon: None,
|
icon: None,
|
||||||
valid: None,
|
valid: None,
|
||||||
@ -97,19 +102,19 @@ impl StyledInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledInputBuilder {
|
pub struct StyledInputBuilder<'l> {
|
||||||
icon: Option<Icon>,
|
icon: Option<Icon>,
|
||||||
valid: Option<bool>,
|
valid: Option<bool>,
|
||||||
value: Option<String>,
|
value: Option<String>,
|
||||||
input_type: Option<String>,
|
input_type: Option<&'l str>,
|
||||||
input_class_list: Vec<String>,
|
input_class_list: Vec<&'l str>,
|
||||||
wrapper_class_list: Vec<String>,
|
wrapper_class_list: Vec<&'l str>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
auto_focus: bool,
|
auto_focus: bool,
|
||||||
input_handlers: Vec<EventHandler<Msg>>,
|
input_handlers: Vec<EventHandler<Msg>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledInputBuilder {
|
impl<'l> StyledInputBuilder<'l> {
|
||||||
pub fn icon(mut self, icon: Icon) -> Self {
|
pub fn icon(mut self, icon: Icon) -> Self {
|
||||||
self.icon = Some(icon);
|
self.icon = Some(icon);
|
||||||
self
|
self
|
||||||
@ -133,19 +138,13 @@ impl StyledInputBuilder {
|
|||||||
.valid(!state.touched || !state.value.is_empty())
|
.valid(!state.touched || !state.value.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_input_class<S>(mut self, name: S) -> Self
|
pub fn add_input_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.input_class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.input_class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_wrapper_class<S>(mut self, name: S) -> Self
|
pub fn add_wrapper_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.wrapper_class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.wrapper_class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +163,7 @@ impl StyledInputBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self, id: FieldId) -> StyledInput {
|
pub fn build(self, id: FieldId) -> StyledInput<'l> {
|
||||||
StyledInput {
|
StyledInput {
|
||||||
id,
|
id,
|
||||||
icon: self.icon,
|
icon: self.icon,
|
||||||
@ -180,7 +179,7 @@ impl StyledInputBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledInput {
|
impl<'l> ToNode for StyledInput<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
@ -200,15 +199,14 @@ pub fn render(values: StyledInput) -> Node<Msg> {
|
|||||||
input_handlers,
|
input_handlers,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
wrapper_class_list.push(variant.to_string());
|
wrapper_class_list.push(variant.to_str());
|
||||||
wrapper_class_list.push(format!("{}", id));
|
|
||||||
if !valid {
|
if !valid {
|
||||||
wrapper_class_list.push("invalid".to_string());
|
wrapper_class_list.push("invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
input_class_list.push(variant.to_string());
|
input_class_list.push(variant.to_str());
|
||||||
if icon.is_some() {
|
if icon.is_some() {
|
||||||
input_class_list.push("withIcon".to_string());
|
input_class_list.push("withIcon");
|
||||||
}
|
}
|
||||||
|
|
||||||
let icon = match icon {
|
let icon = match icon {
|
||||||
@ -240,7 +238,10 @@ pub fn render(values: StyledInput) -> Node<Msg> {
|
|||||||
|
|
||||||
div![
|
div![
|
||||||
C!["styledInput"],
|
C!["styledInput"],
|
||||||
attrs!(At::Class => wrapper_class_list.join(" ")),
|
attrs!(
|
||||||
|
At::Class => wrapper_class_list.join(" "),
|
||||||
|
At::Class => format!("{}", id),
|
||||||
|
),
|
||||||
icon,
|
icon,
|
||||||
on_click,
|
on_click,
|
||||||
on_keyup,
|
on_keyup,
|
||||||
@ -250,7 +251,7 @@ pub fn render(values: StyledInput) -> Node<Msg> {
|
|||||||
At::Id => format!("{}", id),
|
At::Id => format!("{}", id),
|
||||||
At::Class => input_class_list.join(" "),
|
At::Class => input_class_list.join(" "),
|
||||||
At::Value => value.unwrap_or_default(),
|
At::Value => value.unwrap_or_default(),
|
||||||
At::Type => input_type.unwrap_or_else(|| "text".to_string()),
|
At::Type => input_type.unwrap_or_else(|| "text"),
|
||||||
|
|
||||||
],
|
],
|
||||||
if auto_focus {
|
if auto_focus {
|
||||||
|
@ -3,56 +3,46 @@ use seed::{prelude::*, *};
|
|||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
pub struct StyledLink {
|
pub struct StyledLink<'l> {
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
href: String,
|
href: &'l str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledLink {
|
impl<'l> StyledLink<'l> {
|
||||||
pub fn build() -> StyledLinkBuilder {
|
pub fn build() -> StyledLinkBuilder<'l> {
|
||||||
StyledLinkBuilder::default()
|
StyledLinkBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledLinkBuilder {
|
pub struct StyledLinkBuilder<'l> {
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
href: String,
|
href: &'l str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledLinkBuilder {
|
impl<'l> StyledLinkBuilder<'l> {
|
||||||
pub fn add_child(mut self, child: Node<Msg>) -> Self {
|
pub fn add_child(mut self, child: Node<Msg>) -> Self {
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn href<S>(mut self, href: S) -> Self
|
pub fn href(mut self, href: &'l str) -> Self {
|
||||||
where
|
self.href = href;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.href = href.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text<S>(self, s: S) -> Self
|
pub fn text(self, s: &'l str) -> Self {
|
||||||
where
|
self.add_child(span![s])
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
let text: String = s.into();
|
|
||||||
self.add_child(span![text])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledLink {
|
pub fn build(self) -> StyledLink<'l> {
|
||||||
StyledLink {
|
StyledLink {
|
||||||
children: self.children,
|
children: self.children,
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
@ -61,7 +51,7 @@ impl StyledLinkBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledLink {
|
impl<'l> ToNode for StyledLink<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
@ -70,12 +60,12 @@ impl ToNode for StyledLink {
|
|||||||
pub fn render(values: StyledLink) -> Node<Msg> {
|
pub fn render(values: StyledLink) -> Node<Msg> {
|
||||||
let StyledLink {
|
let StyledLink {
|
||||||
children,
|
children,
|
||||||
mut class_list,
|
class_list,
|
||||||
href,
|
href,
|
||||||
} = values;
|
} = values;
|
||||||
class_list.push("styledLink".to_string());
|
|
||||||
|
|
||||||
a![
|
a![
|
||||||
|
class!["styledLink"],
|
||||||
attrs![
|
attrs![
|
||||||
At::Class => class_list.join(" "),
|
At::Class => class_list.join(" "),
|
||||||
At::Href => href,
|
At::Href => href,
|
||||||
|
@ -28,36 +28,36 @@ impl Variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledModal {
|
pub struct StyledModal<'l> {
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
width: Option<usize>,
|
width: Option<usize>,
|
||||||
with_icon: bool,
|
with_icon: bool,
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledModal {
|
impl<'l> ToNode for StyledModal<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledModal {
|
impl<'l> StyledModal<'l> {
|
||||||
pub fn build() -> StyledModalBuilder {
|
pub fn build() -> StyledModalBuilder<'l> {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledModalBuilder {
|
pub struct StyledModalBuilder<'l> {
|
||||||
variant: Option<Variant>,
|
variant: Option<Variant>,
|
||||||
width: Option<usize>,
|
width: Option<usize>,
|
||||||
with_icon: Option<bool>,
|
with_icon: Option<bool>,
|
||||||
children: Option<Vec<Node<Msg>>>,
|
children: Option<Vec<Node<Msg>>>,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledModalBuilder {
|
impl<'l> StyledModalBuilder<'l> {
|
||||||
pub fn variant(mut self, variant: Variant) -> Self {
|
pub fn variant(mut self, variant: Variant) -> Self {
|
||||||
self.variant = Some(variant);
|
self.variant = Some(variant);
|
||||||
self
|
self
|
||||||
@ -82,15 +82,12 @@ impl StyledModalBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledModal {
|
pub fn build(self) -> StyledModal<'l> {
|
||||||
StyledModal {
|
StyledModal {
|
||||||
variant: self.variant.unwrap_or_else(|| Variant::Center),
|
variant: self.variant.unwrap_or_else(|| Variant::Center),
|
||||||
width: self.width,
|
width: self.width,
|
||||||
@ -126,7 +123,8 @@ pub fn render(values: StyledModal) -> Node<Msg> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
||||||
class_list.push(format!("styledModal {}", variant.to_class_name()));
|
class_list.push("styledModal");
|
||||||
|
class_list.push(variant.to_class_name());
|
||||||
let styled_modal_style = match width {
|
let styled_modal_style = match width {
|
||||||
Some(0) => "".to_string(),
|
Some(0) => "".to_string(),
|
||||||
Some(n) => format!("max-width: {width}px", width = n),
|
Some(n) => format!("max-width: {width}px", width = n),
|
||||||
|
@ -26,8 +26,8 @@ impl HeadingSize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for HeadingSize {
|
impl HeadingSize {
|
||||||
fn to_string(&self) -> String {
|
fn as_str<'l>(&self) -> &'l str {
|
||||||
use HeadingSize::*;
|
use HeadingSize::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
@ -39,7 +39,6 @@ impl ToString for HeadingSize {
|
|||||||
H5 => "H5",
|
H5 => "H5",
|
||||||
H6 => "H6",
|
H6 => "H6",
|
||||||
}
|
}
|
||||||
.to_string()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,28 +87,18 @@ pub enum RteMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ExecCommand {
|
pub struct ExecCommand<'l> {
|
||||||
pub(crate) name: String,
|
pub(crate) name: &'l str,
|
||||||
pub(crate) param: String,
|
pub(crate) param: &'l str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecCommand {
|
impl<'l> ExecCommand<'l> {
|
||||||
pub fn new<S>(name: S) -> Self
|
pub fn new(name: &'l str) -> Self {
|
||||||
where
|
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
Self::new_with_param(name, "")
|
Self::new_with_param(name, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_param<S1, S2>(name: S1, param: S2) -> Self
|
pub fn new_with_param(name: &'l str, param: &'l str) -> Self {
|
||||||
where
|
Self { name, param }
|
||||||
S1: Into<String>,
|
|
||||||
S2: Into<String>,
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
name: name.into(),
|
|
||||||
param: param.into(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,9 +125,7 @@ impl RteMsg {
|
|||||||
| HeadingSize::H3
|
| HeadingSize::H3
|
||||||
| HeadingSize::H4
|
| HeadingSize::H4
|
||||||
| HeadingSize::H5
|
| HeadingSize::H5
|
||||||
| HeadingSize::H6 => {
|
| HeadingSize::H6 => Some(ExecCommand::new_with_param("heading", heading.as_str())),
|
||||||
Some(ExecCommand::new_with_param("heading", heading.to_string()))
|
|
||||||
}
|
|
||||||
HeadingSize::Normal => Some(ExecCommand::new_with_param("formatBlock", "div")),
|
HeadingSize::Normal => Some(ExecCommand::new_with_param("formatBlock", "div")),
|
||||||
},
|
},
|
||||||
RteMsg::InsertUnorderedList => Some(ExecCommand::new("insertUnorderedList")),
|
RteMsg::InsertUnorderedList => Some(ExecCommand::new("insertUnorderedList")),
|
||||||
@ -193,7 +180,7 @@ pub struct StyledRteCodeState {
|
|||||||
|
|
||||||
impl StyledRteCodeState {
|
impl StyledRteCodeState {
|
||||||
pub fn new(field_id: FieldId) -> Self {
|
pub fn new(field_id: FieldId) -> Self {
|
||||||
let mut languages: Vec<String> = crate::hi::SYNTAX_SET
|
let mut languages: Vec<String> = crate::hi::syntax_set::load()
|
||||||
.syntaxes()
|
.syntaxes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.name.clone())
|
.map(|s| s.name.clone())
|
||||||
@ -202,7 +189,7 @@ impl StyledRteCodeState {
|
|||||||
Self {
|
Self {
|
||||||
visible: false,
|
visible: false,
|
||||||
lang: StyledSelectState::new(
|
lang: StyledSelectState::new(
|
||||||
FieldId::Rte(RteField::CodeLang(Box::new(field_id.clone()))),
|
FieldId::Rte(RteField::CodeLang(Box::new(field_id))),
|
||||||
vec![],
|
vec![],
|
||||||
),
|
),
|
||||||
code: "".to_string(),
|
code: "".to_string(),
|
||||||
@ -221,6 +208,33 @@ impl StyledRteCodeState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RteTableBodyBuilder {
|
||||||
|
cols: u16,
|
||||||
|
rows: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RteTableBodyBuilder {
|
||||||
|
pub fn new(cols: u16, rows: u16) -> Self {
|
||||||
|
Self { cols, rows }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for RteTableBodyBuilder {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
let RteTableBodyBuilder { cols, rows } = self;
|
||||||
|
let mut buff = "<tbody>".to_string();
|
||||||
|
for _c in 0..(*cols) {
|
||||||
|
buff.push_str("<tr>");
|
||||||
|
for _r in 0..(*rows) {
|
||||||
|
buff.push_str("<td> </td>")
|
||||||
|
}
|
||||||
|
buff.push_str("</tr>");
|
||||||
|
}
|
||||||
|
buff.push_str("</tbody>");
|
||||||
|
buff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledRteState {
|
pub struct StyledRteState {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
@ -250,17 +264,19 @@ impl StyledRteState {
|
|||||||
pub fn update(&mut self, msg: &Msg, orders: &mut impl Orders<Msg>) {
|
pub fn update(&mut self, msg: &Msg, orders: &mut impl Orders<Msg>) {
|
||||||
self.code_tooltip.lang.update(msg, orders);
|
self.code_tooltip.lang.update(msg, orders);
|
||||||
let m = match msg {
|
let m = match msg {
|
||||||
Msg::Rte(m, field) if field == &self.field_id => m,
|
Msg::Rte(field, m) if field == &self.field_id => m,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
match m.to_command() {
|
match m.to_command() {
|
||||||
Some(ExecCommand { name, param }) => {
|
Some(ExecCommand { name, param }) => {
|
||||||
self.store_range();
|
self.store_range();
|
||||||
match seed::html_document().exec_command_with_show_ui_and_value(
|
let doc = match web_sys::window().and_then(|w| w.document()).map(|d| {
|
||||||
name.as_str(),
|
wasm_bindgen::JsValue::from(d).unchecked_into::<web_sys::HtmlDocument>()
|
||||||
false,
|
}) {
|
||||||
param.as_str(),
|
Some(doc) => doc,
|
||||||
) {
|
_ => return,
|
||||||
|
};
|
||||||
|
match doc.exec_command_with_show_ui_and_value(name, false, param) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => log!(e),
|
Err(e) => log!(e),
|
||||||
}
|
}
|
||||||
@ -345,16 +361,9 @@ impl StyledRteState {
|
|||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let mut buff = "<tbody>".to_string();
|
table.set_inner_html(
|
||||||
for _c in 0..(*cols) {
|
RteTableBodyBuilder::new(*cols, *rows).to_string().as_str(),
|
||||||
buff.push_str("<tr>");
|
);
|
||||||
for _r in 0..(*rows) {
|
|
||||||
buff.push_str("<td> </td>")
|
|
||||||
}
|
|
||||||
buff.push_str("</tr>");
|
|
||||||
}
|
|
||||||
buff.push_str("</tbody>");
|
|
||||||
table.set_inner_html(buff.as_str());
|
|
||||||
if let Err(e) = r.insert_node(&table) {
|
if let Err(e) = r.insert_node(&table) {
|
||||||
log!(e);
|
log!(e);
|
||||||
}
|
}
|
||||||
@ -363,6 +372,10 @@ impl StyledRteState {
|
|||||||
_ => log!(m),
|
_ => log!(m),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// orders.skip().send_msg(Msg::StrInputChanged(
|
||||||
|
// self.field_id.clone(),
|
||||||
|
// self.value.clone(),
|
||||||
|
// ));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_range(&mut self) {
|
fn store_range(&mut self) {
|
||||||
@ -396,58 +409,54 @@ impl StyledRteState {
|
|||||||
let field_id = self.field_id.clone();
|
let field_id = self.field_id.clone();
|
||||||
let identifier = self.identifier;
|
let identifier = self.identifier;
|
||||||
orders.perform_cmd(cmds::timeout(200, move || {
|
orders.perform_cmd(cmds::timeout(200, move || {
|
||||||
Msg::Rte(RteMsg::RequestFocus(identifier), field_id)
|
Msg::Rte(field_id, RteMsg::RequestFocus(identifier))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledRte {
|
pub struct StyledRte<'component> {
|
||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
table_tooltip: StyledRteTableState,
|
table_tooltip: Option<&'component StyledRteTableState>,
|
||||||
identifier: Option<uuid::Uuid>,
|
identifier: Option<uuid::Uuid>,
|
||||||
code_tooltip: StyledRteCodeState,
|
code_tooltip: Option<&'component StyledRteCodeState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledRte {
|
impl<'component> StyledRte<'component> {
|
||||||
pub fn build(field_id: FieldId) -> StyledRteBuilder {
|
pub fn build(field_id: FieldId) -> StyledRteBuilder<'component> {
|
||||||
StyledRteBuilder {
|
StyledRteBuilder {
|
||||||
field_id: field_id.clone(),
|
field_id: field_id.clone(),
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
table_tooltip: StyledRteTableState {
|
table_tooltip: None,
|
||||||
visible: false,
|
code_tooltip: None,
|
||||||
rows: 0,
|
|
||||||
cols: 0,
|
|
||||||
},
|
|
||||||
code_tooltip: StyledRteCodeState::new(field_id),
|
|
||||||
identifier: None,
|
identifier: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledRte {
|
impl<'component> ToNode for StyledRte<'component> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledRteBuilder {
|
pub struct StyledRteBuilder<'outer> {
|
||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
value: String,
|
value: String,
|
||||||
table_tooltip: StyledRteTableState,
|
table_tooltip: Option<&'outer StyledRteTableState>,
|
||||||
code_tooltip: StyledRteCodeState,
|
code_tooltip: Option<&'outer StyledRteCodeState>,
|
||||||
identifier: Option<uuid::Uuid>,
|
identifier: Option<uuid::Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledRteBuilder {
|
impl<'outer> StyledRteBuilder<'outer> {
|
||||||
pub fn state(mut self, state: &StyledRteState) -> Self {
|
pub fn state(mut self, state: &'outer StyledRteState) -> Self {
|
||||||
self.value = state.value.clone();
|
self.value = state.value.clone();
|
||||||
self.table_tooltip = state.table_tooltip.clone();
|
self.table_tooltip = Some(&state.table_tooltip);
|
||||||
self.code_tooltip = state.code_tooltip.clone();
|
self.code_tooltip = Some(&state.code_tooltip);
|
||||||
self.identifier = Some(state.identifier);
|
self.identifier = Some(state.identifier);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledRte {
|
pub fn build(self) -> StyledRte<'outer> {
|
||||||
StyledRte {
|
StyledRte {
|
||||||
field_id: self.field_id,
|
field_id: self.field_id,
|
||||||
table_tooltip: self.table_tooltip,
|
table_tooltip: self.table_tooltip,
|
||||||
@ -458,7 +467,7 @@ impl StyledRteBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(values: StyledRte) -> Node<Msg> {
|
pub fn render(values: StyledRte) -> Node<Msg> {
|
||||||
{
|
/*{
|
||||||
let _brush_button = styled_rte_button(
|
let _brush_button = styled_rte_button(
|
||||||
"Brush",
|
"Brush",
|
||||||
Icon::Brush,
|
Icon::Brush,
|
||||||
@ -533,12 +542,17 @@ pub fn render(values: StyledRte) -> Node<Msg> {
|
|||||||
None as Option<Msg>
|
None as Option<Msg>
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
// let field_id = values.field_id.clone();
|
||||||
let capture_event = ev(Ev::KeyDown, |ev| {
|
let capture_event = ev(Ev::KeyDown, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
None as Option<Msg>
|
None as Option<Msg>
|
||||||
});
|
});
|
||||||
|
// let capture_change = ev(Ev::Input, |ev| {
|
||||||
|
// ev.stop_propagation();
|
||||||
|
// Some(Msg::StrInputChanged(field_id, "".to_string()))
|
||||||
|
// });
|
||||||
|
|
||||||
let id = values.identifier.unwrap_or_default().to_string();
|
let id = values.identifier.unwrap_or_default().to_string();
|
||||||
|
|
||||||
@ -563,7 +577,8 @@ pub fn render(values: StyledRte) -> Node<Msg> {
|
|||||||
div![
|
div![
|
||||||
C!["editor"],
|
C!["editor"],
|
||||||
attrs![At::ContentEditable => true],
|
attrs![At::ContentEditable => true],
|
||||||
capture_event
|
capture_event,
|
||||||
|
// capture_change
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -578,7 +593,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
|
|
||||||
Some(Msg::Rte(RteMsg::JustifyFull, field_id))
|
Some(Msg::Rte(field_id, RteMsg::JustifyFull))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -587,7 +602,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::JustifyCenter,
|
Icon::JustifyCenter,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::JustifyCenter, field_id))
|
Some(Msg::Rte(field_id, RteMsg::JustifyCenter))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -596,7 +611,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::JustifyLeft,
|
Icon::JustifyLeft,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::JustifyLeft, field_id))
|
Some(Msg::Rte(field_id, RteMsg::JustifyLeft))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -606,7 +621,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
|
|
||||||
Some(Msg::Rte(RteMsg::JustifyRight, field_id))
|
Some(Msg::Rte(field_id, RteMsg::JustifyRight))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
div![
|
div![
|
||||||
@ -625,7 +640,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Redo,
|
Icon::Redo,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Redo, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Redo))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -634,7 +649,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Undo,
|
Icon::Undo,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Undo, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Undo))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
/*let field_id = values.field_id.clone();
|
/*let field_id = values.field_id.clone();
|
||||||
@ -681,7 +696,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::EraserAlt,
|
Icon::EraserAlt,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::RemoveFormat, field_id))
|
Some(Msg::Rte(field_id, RteMsg::RemoveFormat))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -690,7 +705,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Bold,
|
Icon::Bold,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Bold, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Bold))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -699,7 +714,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Italic,
|
Icon::Italic,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Italic, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Italic))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -710,7 +725,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Underline,
|
Icon::Underline,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Underscore, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Underscore))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -722,7 +737,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::StrikeThrough,
|
Icon::StrikeThrough,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Strikethrough, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Strikethrough))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -734,7 +749,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Subscript,
|
Icon::Subscript,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Subscript, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Subscript))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -746,7 +761,7 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Superscript,
|
Icon::Superscript,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::Superscript, field_id))
|
Some(Msg::Rte(field_id, RteMsg::Superscript))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -818,10 +833,10 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
.map(|h| {
|
.map(|h| {
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
let button = StyledButton::build()
|
let button = StyledButton::build()
|
||||||
.text(h.to_string())
|
.text(h.as_str())
|
||||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertHeading(h), field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertHeading(h)))
|
||||||
}))
|
}))
|
||||||
.empty()
|
.empty()
|
||||||
.build()
|
.build()
|
||||||
@ -867,7 +882,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::ListingDots,
|
Icon::ListingDots,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertUnorderedList, field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertUnorderedList))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
@ -876,7 +891,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::ListingNumber,
|
Icon::ListingNumber,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertOrderedList, field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertOrderedList))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
/*let field_id = values.field_id.clone();
|
/*let field_id = values.field_id.clone();
|
||||||
@ -896,7 +911,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Table,
|
Icon::Table,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::TableSetVisibility(true), field_id))
|
Some(Msg::Rte(field_id, RteMsg::TableSetVisibility(true)))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -909,7 +924,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::Paragraph,
|
Icon::Paragraph,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertParagraph, field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertParagraph))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -920,7 +935,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Icon::CodeAlt,
|
Icon::CodeAlt,
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertCode(true), field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertCode(true)))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -945,8 +960,8 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(
|
Some(Msg::Rte(
|
||||||
RteMsg::ChangeIndent(RteIndentMsg::Increase),
|
|
||||||
field_id,
|
field_id,
|
||||||
|
RteMsg::ChangeIndent(RteIndentMsg::Increase),
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -957,8 +972,8 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(
|
Some(Msg::Rte(
|
||||||
RteMsg::ChangeIndent(RteIndentMsg::Decrease),
|
|
||||||
field_id,
|
field_id,
|
||||||
|
RteMsg::ChangeIndent(RteIndentMsg::Decrease),
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -975,18 +990,23 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
||||||
let StyledRteTableState {
|
let (visible, rows, cols) = values
|
||||||
visible,
|
.table_tooltip
|
||||||
rows,
|
.map(
|
||||||
cols,
|
|StyledRteTableState {
|
||||||
} = values.table_tooltip;
|
visible,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
}| (*visible, *rows, *cols),
|
||||||
|
)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let on_rows_change = {
|
let on_rows_change = {
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
input_ev(Ev::Change, move |v| {
|
input_ev(Ev::Change, move |v| {
|
||||||
v.parse::<u16>()
|
v.parse::<u16>()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
.map(|n| Msg::Rte(field_id, RteMsg::TableSetRows(n)))
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -995,7 +1015,7 @@ fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
input_ev(Ev::Change, move |v| {
|
input_ev(Ev::Change, move |v| {
|
||||||
v.parse::<u16>()
|
v.parse::<u16>()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
.map(|n| Msg::Rte(field_id, RteMsg::TableSetColumns(n)))
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1006,7 +1026,7 @@ fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
.icon(Icon::Close)
|
.icon(Icon::Close)
|
||||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::TableSetVisibility(false), field_id))
|
Some(Msg::Rte(field_id, RteMsg::TableSetVisibility(false)))
|
||||||
}))
|
}))
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
@ -1016,7 +1036,7 @@ fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertTable { rows, cols }, field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertTable { rows, cols }))
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1070,9 +1090,16 @@ fn styled_rte_button(title: &str, icon: Icon, handler: EventHandler<Msg>) -> Nod
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
||||||
let StyledRteCodeState { visible, lang, .. } = &values.code_tooltip;
|
let (visible, lang) = values
|
||||||
|
.code_tooltip
|
||||||
|
.as_ref()
|
||||||
|
.map(|StyledRteCodeState { visible, lang, .. }| (*visible, Some(lang)))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let languages = values.code_tooltip.languages();
|
let languages = values
|
||||||
|
.code_tooltip
|
||||||
|
.map(|ct| ct.languages().as_slice())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let options: Vec<(String, u32)> = languages
|
let options: Vec<(String, u32)> = languages
|
||||||
.iter()
|
.iter()
|
||||||
@ -1081,15 +1108,14 @@ fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let select_lang_node = StyledSelect::build()
|
let select_lang_node = StyledSelect::build()
|
||||||
.state(lang)
|
.try_state(lang)
|
||||||
.selected(
|
.selected(
|
||||||
lang.values
|
lang.and_then(|l| l.values.get(0))
|
||||||
.get(0)
|
|
||||||
.and_then(|n| options.get(*n as usize))
|
.and_then(|n| options.get(*n as usize))
|
||||||
.map(|v| vec![v.to_child()])
|
.map(|v| vec![v.to_child()])
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
.options(options.into_iter().map(|opt| opt.to_child()).collect())
|
.options(options.iter().map(|opt| opt.to_child()).collect())
|
||||||
.normal()
|
.normal()
|
||||||
.build(FieldId::Rte(RteField::CodeLang(Box::new(
|
.build(FieldId::Rte(RteField::CodeLang(Box::new(
|
||||||
values.field_id.clone(),
|
values.field_id.clone(),
|
||||||
@ -1103,7 +1129,7 @@ fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
.icon(Icon::Close)
|
.icon(Icon::Close)
|
||||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::InsertCode(false), field_id))
|
Some(Msg::Rte(field_id, RteMsg::InsertCode(false)))
|
||||||
}))
|
}))
|
||||||
.build()
|
.build()
|
||||||
.into_node()
|
.into_node()
|
||||||
@ -1116,7 +1142,7 @@ fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
let target = ev.target().unwrap();
|
let target = ev.target().unwrap();
|
||||||
let textarea = seed::to_textarea(&target);
|
let textarea = seed::to_textarea(&target);
|
||||||
let code = textarea.value();
|
let code = textarea.value();
|
||||||
Msg::Rte(RteMsg::CodeChanged(code), field_id)
|
Msg::Rte(field_id, RteMsg::CodeChanged(code))
|
||||||
});
|
});
|
||||||
seed::textarea![on_change]
|
seed::textarea![on_change]
|
||||||
};
|
};
|
||||||
@ -1126,7 +1152,8 @@ fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
let on_insert = ev(Ev::Click, move |ev| {
|
let on_insert = ev(Ev::Click, move |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Msg::Rte(RteMsg::InjectCode, field_id)
|
ev.target().unwrap();
|
||||||
|
Msg::Rte(field_id, RteMsg::InjectCode)
|
||||||
});
|
});
|
||||||
let insert = StyledButton::build()
|
let insert = StyledButton::build()
|
||||||
.on_click(on_insert)
|
.on_click(on_insert)
|
||||||
@ -1138,7 +1165,7 @@ fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
|
|
||||||
StyledTooltip::build()
|
StyledTooltip::build()
|
||||||
.code_tooltip()
|
.code_tooltip()
|
||||||
.visible(*visible)
|
.visible(visible)
|
||||||
.add_child(h2!["Insert Code", close_tooltip])
|
.add_child(h2!["Insert Code", close_tooltip])
|
||||||
.add_child(select_lang_node)
|
.add_child(select_lang_node)
|
||||||
.add_child(input)
|
.add_child(input)
|
||||||
|
@ -25,12 +25,18 @@ impl Default for Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Variant {
|
||||||
|
pub fn to_str<'l>(&self) -> &'l str {
|
||||||
|
match self {
|
||||||
|
Variant::Empty => "empty",
|
||||||
|
Variant::Normal => "normal",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Variant {
|
impl std::fmt::Display for Variant {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(self.to_str())
|
||||||
Variant::Empty => f.write_str("empty"),
|
|
||||||
Variant::Normal => f.write_str("normal"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,34 +67,30 @@ impl StyledSelectState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, msg: &Msg, _orders: &mut impl Orders<Msg>) {
|
pub fn update(&mut self, msg: &Msg, _orders: &mut impl Orders<Msg>) {
|
||||||
let ref id = self.field_id;
|
let field_id = match msg {
|
||||||
|
Msg::StyledSelectChanged(field_id, ..) => field_id,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if &self.field_id != field_id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match msg {
|
match msg {
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChanged::DropDownVisibility(b))
|
Msg::StyledSelectChanged(_, StyledSelectChanged::DropDownVisibility(b)) => {
|
||||||
if field_id == id =>
|
|
||||||
{
|
|
||||||
self.opened = *b;
|
self.opened = *b;
|
||||||
if !self.opened {
|
if !self.opened {
|
||||||
self.text_filter.clear();
|
self.text_filter.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChanged::Text(text))
|
Msg::StyledSelectChanged(_, StyledSelectChanged::Text(text)) => {
|
||||||
if field_id == id =>
|
|
||||||
{
|
|
||||||
self.text_filter = text.clone();
|
self.text_filter = text.clone();
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChanged::Changed(Some(v)))
|
Msg::StyledSelectChanged(_, StyledSelectChanged::Changed(Some(v))) => {
|
||||||
if field_id == id =>
|
|
||||||
{
|
|
||||||
self.values = vec![*v];
|
self.values = vec![*v];
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChanged::Changed(None))
|
Msg::StyledSelectChanged(_, StyledSelectChanged::Changed(None)) => {
|
||||||
if field_id == id =>
|
|
||||||
{
|
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChanged::RemoveMulti(v))
|
Msg::StyledSelectChanged(_, StyledSelectChanged::RemoveMulti(v)) => {
|
||||||
if field_id == id =>
|
|
||||||
{
|
|
||||||
let mut old = vec![];
|
let mut old = vec![];
|
||||||
std::mem::swap(&mut old, &mut self.values);
|
std::mem::swap(&mut old, &mut self.values);
|
||||||
|
|
||||||
@ -103,28 +105,28 @@ impl StyledSelectState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledSelect {
|
pub struct StyledSelect<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
dropdown_width: Option<usize>,
|
dropdown_width: Option<usize>,
|
||||||
name: Option<String>,
|
name: Option<&'l str>,
|
||||||
valid: bool,
|
valid: bool,
|
||||||
is_multi: bool,
|
is_multi: bool,
|
||||||
options: Vec<StyledSelectChildBuilder>,
|
options: Vec<StyledSelectChildBuilder<'l>>,
|
||||||
selected: Vec<StyledSelectChildBuilder>,
|
selected: Vec<StyledSelectChildBuilder<'l>>,
|
||||||
text_filter: String,
|
text_filter: &'l str,
|
||||||
opened: bool,
|
opened: bool,
|
||||||
clearable: bool,
|
clearable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledSelect {
|
impl<'l> ToNode for StyledSelect<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelect {
|
impl<'l> StyledSelect<'l> {
|
||||||
pub fn build() -> StyledSelectBuilder {
|
pub fn build() -> StyledSelectBuilder<'l> {
|
||||||
StyledSelectBuilder {
|
StyledSelectBuilder {
|
||||||
variant: None,
|
variant: None,
|
||||||
dropdown_width: None,
|
dropdown_width: None,
|
||||||
@ -141,21 +143,21 @@ impl StyledSelect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledSelectBuilder {
|
pub struct StyledSelectBuilder<'l> {
|
||||||
variant: Option<Variant>,
|
variant: Option<Variant>,
|
||||||
dropdown_width: Option<usize>,
|
dropdown_width: Option<usize>,
|
||||||
name: Option<String>,
|
name: Option<&'l str>,
|
||||||
valid: Option<bool>,
|
valid: Option<bool>,
|
||||||
is_multi: Option<bool>,
|
is_multi: Option<bool>,
|
||||||
options: Option<Vec<StyledSelectChildBuilder>>,
|
options: Option<Vec<StyledSelectChildBuilder<'l>>>,
|
||||||
selected: Option<Vec<StyledSelectChildBuilder>>,
|
selected: Option<Vec<StyledSelectChildBuilder<'l>>>,
|
||||||
text_filter: Option<String>,
|
text_filter: Option<&'l str>,
|
||||||
opened: Option<bool>,
|
opened: Option<bool>,
|
||||||
clearable: bool,
|
clearable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelectBuilder {
|
impl<'l> StyledSelectBuilder<'l> {
|
||||||
pub fn build(self, id: FieldId) -> StyledSelect {
|
pub fn build(self, id: FieldId) -> StyledSelect<'l> {
|
||||||
StyledSelect {
|
StyledSelect {
|
||||||
id,
|
id,
|
||||||
variant: self.variant.unwrap_or_default(),
|
variant: self.variant.unwrap_or_default(),
|
||||||
@ -171,7 +173,15 @@ impl StyledSelectBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(self, state: &StyledSelectState) -> Self {
|
pub fn try_state<'state: 'l>(self, state: Option<&'state StyledSelectState>) -> Self {
|
||||||
|
if let Some(s) = state {
|
||||||
|
self.state(s)
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state<'state: 'l>(self, state: &'state StyledSelectState) -> Self {
|
||||||
self.opened(state.opened)
|
self.opened(state.opened)
|
||||||
.text_filter(state.text_filter.as_str())
|
.text_filter(state.text_filter.as_str())
|
||||||
}
|
}
|
||||||
@ -181,19 +191,13 @@ impl StyledSelectBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name<S>(mut self, name: S) -> Self
|
pub fn name(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.name = Some(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.name = Some(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_filter<S>(mut self, text_filter: S) -> Self
|
pub fn text_filter(mut self, text_filter: &'l str) -> Self {
|
||||||
where
|
self.text_filter = Some(text_filter);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.text_filter = Some(text_filter.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,12 +211,12 @@ impl StyledSelectBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn options(mut self, options: Vec<StyledSelectChildBuilder>) -> Self {
|
pub fn options(mut self, options: Vec<StyledSelectChildBuilder<'l>>) -> Self {
|
||||||
self.options = Some(options);
|
self.options = Some(options);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected(mut self, selected: Vec<StyledSelectChildBuilder>) -> Self {
|
pub fn selected(mut self, selected: Vec<StyledSelectChildBuilder<'l>>) -> Self {
|
||||||
self.selected = Some(selected);
|
self.selected = Some(selected);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -301,7 +305,7 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
|||||||
|
|
||||||
let children: Vec<Node<Msg>> = options
|
let children: Vec<Node<Msg>> = options
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|o| !selected.contains(&o) && o.match_text(text_filter.as_str()))
|
.filter(|o| !selected.contains(&o) && o.match_text(text_filter))
|
||||||
.map(|child| {
|
.map(|child| {
|
||||||
let child = child.build(DisplayType::SelectOption);
|
let child = child.build(DisplayType::SelectOption);
|
||||||
let value = child.value();
|
let value = child.value();
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use crate::shared::styled_select::Variant;
|
use crate::shared::styled_select::Variant;
|
||||||
@ -9,18 +11,18 @@ pub enum DisplayType {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledSelectChild {
|
pub struct StyledSelectChild<'l> {
|
||||||
name: Option<String>,
|
name: Option<&'l str>,
|
||||||
icon: Option<Node<Msg>>,
|
icon: Option<Node<Msg>>,
|
||||||
text: Option<String>,
|
text: Option<std::borrow::Cow<'l, str>>,
|
||||||
display_type: DisplayType,
|
display_type: DisplayType,
|
||||||
value: u32,
|
value: u32,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<std::borrow::Cow<'l, str>>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelectChild {
|
impl<'l> StyledSelectChild<'l> {
|
||||||
pub fn build() -> StyledSelectChildBuilder {
|
pub fn build() -> StyledSelectChildBuilder<'l> {
|
||||||
StyledSelectChildBuilder {
|
StyledSelectChildBuilder {
|
||||||
icon: None,
|
icon: None,
|
||||||
text: None,
|
text: None,
|
||||||
@ -37,47 +39,46 @@ impl StyledSelectChild {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledSelectChild {
|
impl<'l> ToNode for StyledSelectChild<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledSelectChildBuilder {
|
pub struct StyledSelectChildBuilder<'l> {
|
||||||
icon: Option<Node<Msg>>,
|
icon: Option<Node<Msg>>,
|
||||||
text: Option<String>,
|
text: Option<std::borrow::Cow<'l, str>>,
|
||||||
name: Option<String>,
|
name: Option<&'l str>,
|
||||||
value: u32,
|
value: u32,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<std::borrow::Cow<'l, str>>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for StyledSelectChildBuilder {
|
impl<'l> PartialEq for StyledSelectChildBuilder<'l> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.value == other.value
|
self.value == other.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelectChildBuilder {
|
impl<'l> StyledSelectChildBuilder<'l> {
|
||||||
pub fn icon(mut self, icon: Node<Msg>) -> Self {
|
pub fn icon(mut self, icon: Node<Msg>) -> Self {
|
||||||
self.icon = Some(icon);
|
self.icon = Some(icon);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text<S>(mut self, text: S) -> Self
|
pub fn text<'m: 'l>(mut self, text: &'m str) -> Self {
|
||||||
where
|
self.text = Some(std::borrow::Cow::Borrowed(text));
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.text = Some(text.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name<S>(mut self, name: S) -> Self
|
pub fn text_owned(mut self, text: String) -> Self {
|
||||||
where
|
self.text = Some(std::borrow::Cow::Owned(text));
|
||||||
S: Into<String>,
|
self
|
||||||
{
|
}
|
||||||
self.name = Some(name.into());
|
|
||||||
|
pub fn name(mut self, name: &'l str) -> Self {
|
||||||
|
self.name = Some(name);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,15 +94,12 @@ impl StyledSelectChildBuilder {
|
|||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class<'m: 'l>(mut self, name: &'m str) -> Self {
|
||||||
where
|
self.class_list.push(Cow::Borrowed(name));
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self, display_type: DisplayType) -> StyledSelectChild {
|
pub fn build(self, display_type: DisplayType) -> StyledSelectChild<'l> {
|
||||||
StyledSelectChild {
|
StyledSelectChild {
|
||||||
name: self.name,
|
name: self.name,
|
||||||
icon: self.icon,
|
icon: self.icon,
|
||||||
@ -121,45 +119,27 @@ pub fn render(values: StyledSelectChild) -> Node<Msg> {
|
|||||||
text,
|
text,
|
||||||
display_type,
|
display_type,
|
||||||
value: _,
|
value: _,
|
||||||
mut class_list,
|
class_list,
|
||||||
variant,
|
variant,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
class_list.push(format!("{}", variant));
|
|
||||||
|
|
||||||
let label_class = match display_type {
|
let label_class = match display_type {
|
||||||
DisplayType::SelectOption => vec![
|
DisplayType::SelectOption => vec![
|
||||||
"optionLabel".to_string(),
|
"optionLabel",
|
||||||
variant.to_string(),
|
variant.to_str(),
|
||||||
name.as_ref().cloned().unwrap_or_default(),
|
name.as_deref().unwrap_or_default(),
|
||||||
name.as_ref()
|
|
||||||
.map(|s| format!("{}Label", s))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
class_list.join(" "),
|
|
||||||
],
|
],
|
||||||
DisplayType::SelectValue => vec![
|
DisplayType::SelectValue => vec![
|
||||||
"selectItemLabel".to_string(),
|
"selectItemLabel",
|
||||||
variant.to_string(),
|
variant.to_str(),
|
||||||
name.as_ref().cloned().unwrap_or_default(),
|
name.as_deref().unwrap_or_default(),
|
||||||
name.as_ref()
|
|
||||||
.map(|s| format!("{}Label", s))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
class_list.join(" "),
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
let wrapper_class = match display_type {
|
let wrapper_class = match display_type {
|
||||||
DisplayType::SelectOption => vec![
|
DisplayType::SelectOption => vec!["optionItem", name.as_deref().unwrap_or_default()],
|
||||||
"optionItem".to_string(),
|
DisplayType::SelectValue => vec!["selectItem", name.as_deref().unwrap_or_default()],
|
||||||
name.as_ref().cloned().unwrap_or_default(),
|
|
||||||
class_list.join(" "),
|
|
||||||
],
|
|
||||||
DisplayType::SelectValue => vec![
|
|
||||||
"selectItem".to_string(),
|
|
||||||
name.as_ref().cloned().unwrap_or_default(),
|
|
||||||
class_list.join(" "),
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
@ -169,19 +149,32 @@ pub fn render(values: StyledSelectChild) -> Node<Msg> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let label_node = match text {
|
let label_node = match text {
|
||||||
Some(text) => div![class![label_class.as_str()], text],
|
Some(text) => div![
|
||||||
|
attrs![
|
||||||
|
At::Class => name.as_deref().map(|s| format!("{}Label", s)).unwrap_or_default(),
|
||||||
|
At::Class => class_list.join(" "),
|
||||||
|
],
|
||||||
|
class![label_class.as_str()],
|
||||||
|
text
|
||||||
|
],
|
||||||
_ => empty![],
|
_ => empty![],
|
||||||
};
|
};
|
||||||
|
|
||||||
div![class![wrapper_class.as_str()], icon_node, label_node]
|
div![
|
||||||
|
class![variant.to_str()],
|
||||||
|
class![wrapper_class.as_str()],
|
||||||
|
attrs![At::Class => class_list.join(" ")],
|
||||||
|
icon_node,
|
||||||
|
label_node
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::User {
|
impl<'l> ToChild<'l> for jirs_data::User {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let avatar = crate::shared::styled_avatar::StyledAvatar::build()
|
let avatar = crate::shared::styled_avatar::StyledAvatar::build()
|
||||||
.avatar_url(self.avatar_url.as_ref().cloned().unwrap_or_default())
|
.avatar_url(self.avatar_url.as_deref().unwrap_or_default())
|
||||||
.size(20)
|
.size(20)
|
||||||
.name(self.name.as_str())
|
.name(self.name.as_str())
|
||||||
.build()
|
.build()
|
||||||
@ -193,110 +186,106 @@ impl ToChild for jirs_data::User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::IssuePriority {
|
impl<'l> ToChild<'l> for jirs_data::IssuePriority {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
fn to_child(&self) -> StyledSelectChildBuilder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let icon = crate::shared::styled_icon::StyledIcon::build(self.clone().into())
|
let icon = crate::shared::styled_icon::StyledIcon::build(self.clone().into())
|
||||||
.add_class(self.to_string())
|
.add_class(self.to_str())
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let text = self.to_string();
|
let text = self.to_str();
|
||||||
|
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.icon(icon)
|
.icon(icon)
|
||||||
.value(self.clone().into())
|
.value(self.clone().into())
|
||||||
.text(text)
|
.text(text)
|
||||||
.add_class(format!("{}", self))
|
.add_class(self.to_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::IssueStatus {
|
impl<'l> ToChild<'l> for jirs_data::IssueStatus {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> StyledSelectChildBuilder {
|
|
||||||
let text = &self.name;
|
|
||||||
|
|
||||||
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.value(self.id as u32)
|
.value(self.id as u32)
|
||||||
.add_class(text)
|
.add_class(self.name.as_str())
|
||||||
.text(text.as_str())
|
.text(self.name.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::IssueType {
|
impl<'l> ToChild<'l> for jirs_data::IssueType {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> StyledSelectChildBuilder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let name = self.to_label().to_owned();
|
let name = self.to_label();
|
||||||
|
|
||||||
let type_icon = crate::shared::styled_icon::StyledIcon::build(self.clone().into())
|
let type_icon = crate::shared::styled_icon::StyledIcon::build(self.clone().into())
|
||||||
.add_class(name.as_str())
|
.add_class(name)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.add_class(name.as_str())
|
.add_class(name)
|
||||||
.text(name)
|
.text(name)
|
||||||
.icon(type_icon)
|
.icon(type_icon)
|
||||||
.value(self.clone().into())
|
.value(self.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::ProjectCategory {
|
impl<'l> ToChild<'l> for jirs_data::ProjectCategory {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> StyledSelectChildBuilder {
|
|
||||||
let name = self.to_string();
|
|
||||||
|
|
||||||
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.add_class(name.as_str())
|
.add_class(self.to_str())
|
||||||
.text(name)
|
.text(self.to_str())
|
||||||
.value(self.clone().into())
|
.value(self.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::UserRole {
|
impl<'l> ToChild<'l> for jirs_data::UserRole {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> StyledSelectChildBuilder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let name = self.to_string();
|
let name = self.to_str();
|
||||||
|
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.add_class(name.as_str())
|
.add_class(name)
|
||||||
.add_class("capitalize")
|
.add_class("capitalize")
|
||||||
.text(name)
|
.text(name)
|
||||||
.value(self.clone().into())
|
.value(self.clone().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::Project {
|
impl<'l> ToChild<'l> for jirs_data::Project {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.text(self.name.as_str())
|
.text(self.name.as_str())
|
||||||
.value(self.id as u32)
|
.value(self.id as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for jirs_data::Epic {
|
impl<'l> ToChild<'l> for jirs_data::Epic {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.text(self.name.as_str())
|
.text(self.name.as_str())
|
||||||
.value(self.id as u32)
|
.value(self.id as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToChild for u32 {
|
impl<'l> ToChild<'l> for u32 {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
let name = self.to_string();
|
let name = stringify!(self);
|
||||||
|
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.add_class(name.as_str())
|
.add_class(name)
|
||||||
.text(name)
|
.text(name)
|
||||||
.value(*self)
|
.value(*self)
|
||||||
}
|
}
|
||||||
@ -305,10 +294,10 @@ impl ToChild for u32 {
|
|||||||
pub type Label = String;
|
pub type Label = String;
|
||||||
pub type Value = u32;
|
pub type Value = u32;
|
||||||
|
|
||||||
impl ToChild for (Label, Value) {
|
impl<'l> ToChild<'l> for (Label, Value) {
|
||||||
type Builder = StyledSelectChildBuilder;
|
type Builder = StyledSelectChildBuilder<'l>;
|
||||||
|
|
||||||
fn to_child(&self) -> Self::Builder {
|
fn to_child<'m: 'l>(&'m self) -> Self::Builder {
|
||||||
StyledSelectChild::build()
|
StyledSelectChild::build()
|
||||||
.text(self.0.as_str())
|
.text(self.0.as_str())
|
||||||
.value(self.1)
|
.value(self.1)
|
||||||
|
@ -4,31 +4,31 @@ use crate::shared::ToNode;
|
|||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledTextarea {
|
pub struct StyledTextarea<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
height: usize,
|
height: usize,
|
||||||
max_height: usize,
|
max_height: usize,
|
||||||
value: String,
|
value: &'l str,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
update_event: Ev,
|
update_event: Ev,
|
||||||
placeholder: Option<String>,
|
placeholder: Option<&'l str>,
|
||||||
disable_auto_resize: bool,
|
disable_auto_resize: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledTextarea {
|
impl<'l> ToNode for StyledTextarea<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledTextarea {
|
impl<'l> StyledTextarea<'l> {
|
||||||
pub fn build(field_id: FieldId) -> StyledTextareaBuilder {
|
pub fn build(field_id: FieldId) -> StyledTextareaBuilder<'l> {
|
||||||
StyledTextareaBuilder {
|
StyledTextareaBuilder {
|
||||||
id: field_id,
|
id: field_id,
|
||||||
height: None,
|
height: None,
|
||||||
max_height: None,
|
max_height: None,
|
||||||
on_change: None,
|
on_change: None,
|
||||||
value: "".to_string(),
|
value: "",
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
update_event: None,
|
update_event: None,
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
@ -38,19 +38,19 @@ impl StyledTextarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledTextareaBuilder {
|
pub struct StyledTextareaBuilder<'l> {
|
||||||
id: FieldId,
|
id: FieldId,
|
||||||
height: Option<usize>,
|
height: Option<usize>,
|
||||||
max_height: Option<usize>,
|
max_height: Option<usize>,
|
||||||
on_change: Option<EventHandler<Msg>>,
|
on_change: Option<EventHandler<Msg>>,
|
||||||
value: String,
|
value: &'l str,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
update_event: Option<Ev>,
|
update_event: Option<Ev>,
|
||||||
placeholder: Option<String>,
|
placeholder: Option<&'l str>,
|
||||||
disable_auto_resize: bool,
|
disable_auto_resize: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledTextareaBuilder {
|
impl<'l> StyledTextareaBuilder<'l> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn height(mut self, height: usize) -> Self {
|
pub fn height(mut self, height: usize) -> Self {
|
||||||
self.height = Some(height);
|
self.height = Some(height);
|
||||||
@ -64,20 +64,14 @@ impl StyledTextareaBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn value<S>(mut self, value: S) -> Self
|
pub fn value(mut self, value: &'l str) -> Self {
|
||||||
where
|
self.value = value;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.value = value.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_class<S>(mut self, value: S) -> Self
|
pub fn add_class(mut self, value: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(value);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(value.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +80,8 @@ impl StyledTextareaBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn placeholder<S>(mut self, placeholder: S) -> Self
|
pub fn placeholder(mut self, placeholder: &'l str) -> Self {
|
||||||
where
|
self.placeholder = Some(placeholder);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.placeholder = Some(placeholder.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +91,7 @@ impl StyledTextareaBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(self) -> StyledTextarea {
|
pub fn build(self) -> StyledTextarea<'l> {
|
||||||
StyledTextarea {
|
StyledTextarea {
|
||||||
id: self.id,
|
id: self.id,
|
||||||
value: self.value,
|
value: self.value,
|
||||||
@ -140,7 +131,7 @@ pub fn render(values: StyledTextarea) -> Node<Msg> {
|
|||||||
} = values;
|
} = values;
|
||||||
let mut style_list = vec![];
|
let mut style_list = vec![];
|
||||||
|
|
||||||
let min_height = get_min_height(value.as_str(), height as f64, disable_auto_resize);
|
let min_height = get_min_height(value, height as f64, disable_auto_resize);
|
||||||
if min_height > 0f64 {
|
if min_height > 0f64 {
|
||||||
style_list.push(format!("min-height: {}px", min_height));
|
style_list.push(format!("min-height: {}px", min_height));
|
||||||
}
|
}
|
||||||
@ -197,7 +188,7 @@ pub fn render(values: StyledTextarea) -> Node<Msg> {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
class_list.push("textAreaInput".to_string());
|
class_list.push("textAreaInput");
|
||||||
|
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "styledTextArea"],
|
attrs![At::Class => "styledTextArea"],
|
||||||
|
@ -18,56 +18,59 @@ impl Default for Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Variant {
|
impl Variant {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub fn to_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Variant::About => f.write_str("about"),
|
Variant::About => "about",
|
||||||
Variant::Messages => f.write_str("messages"),
|
Variant::Messages => "messages",
|
||||||
Variant::TableBuilder => f.write_str("tableTooltip"),
|
Variant::TableBuilder => "tableTooltip",
|
||||||
Variant::CodeBuilder => f.write_str("codeTooltip"),
|
Variant::CodeBuilder => "codeTooltip",
|
||||||
Variant::DateTimeBuilder => f.write_str("dateTimeTooltip"),
|
Variant::DateTimeBuilder => "dateTimeTooltip",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledTooltip {
|
impl std::fmt::Display for Variant {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.to_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StyledTooltip<'l> {
|
||||||
visible: bool,
|
visible: bool,
|
||||||
class_name: String,
|
class_list: Vec<&'l str>,
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToNode for StyledTooltip {
|
impl<'l> ToNode for StyledTooltip<'l> {
|
||||||
fn into_node(self) -> Node<Msg> {
|
fn into_node(self) -> Node<Msg> {
|
||||||
render(self)
|
render(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledTooltip {
|
impl<'l> StyledTooltip<'l> {
|
||||||
pub fn build() -> StyledTooltipBuilder {
|
pub fn build() -> StyledTooltipBuilder<'l> {
|
||||||
StyledTooltipBuilder::default()
|
StyledTooltipBuilder::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledTooltipBuilder {
|
pub struct StyledTooltipBuilder<'l> {
|
||||||
visible: bool,
|
visible: bool,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<&'l str>,
|
||||||
children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledTooltipBuilder {
|
impl<'l> StyledTooltipBuilder<'l> {
|
||||||
pub fn visible(mut self, b: bool) -> Self {
|
pub fn visible(mut self, b: bool) -> Self {
|
||||||
self.visible = b;
|
self.visible = b;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class<S>(mut self, name: S) -> Self
|
pub fn add_class(mut self, name: &'l str) -> Self {
|
||||||
where
|
self.class_list.push(name);
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.class_list.push(name.into());
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,10 +104,10 @@ impl StyledTooltipBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledTooltip {
|
pub fn build(self) -> StyledTooltip<'l> {
|
||||||
StyledTooltip {
|
StyledTooltip {
|
||||||
visible: self.visible,
|
visible: self.visible,
|
||||||
class_name: self.class_list.join(" "),
|
class_list: self.class_list,
|
||||||
children: self.children,
|
children: self.children,
|
||||||
variant: self.variant,
|
variant: self.variant,
|
||||||
}
|
}
|
||||||
@ -114,13 +117,13 @@ impl StyledTooltipBuilder {
|
|||||||
pub fn render(values: StyledTooltip) -> Node<Msg> {
|
pub fn render(values: StyledTooltip) -> Node<Msg> {
|
||||||
let StyledTooltip {
|
let StyledTooltip {
|
||||||
visible,
|
visible,
|
||||||
class_name,
|
class_list,
|
||||||
children,
|
children,
|
||||||
variant,
|
variant,
|
||||||
} = values;
|
} = values;
|
||||||
if visible {
|
if visible {
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => format!("styledTooltip {} {}", class_name, variant)],
|
attrs![At::Class => format!("styledTooltip {} {}", class_list.join(" "), variant)],
|
||||||
children
|
children
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
|
@ -44,18 +44,14 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
let roles = UserRole::ordered();
|
||||||
let user_role = StyledSelect::build()
|
let user_role = StyledSelect::build()
|
||||||
.name("user_role")
|
.name("user_role")
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.normal()
|
.normal()
|
||||||
.state(&page.user_role_state)
|
.state(&page.user_role_state)
|
||||||
.selected(vec![page.user_role.to_child()])
|
.selected(vec![page.user_role.to_child()])
|
||||||
.options(
|
.options(roles.iter().map(|role| role.to_child()).collect())
|
||||||
UserRole::ordered()
|
|
||||||
.into_iter()
|
|
||||||
.map(|role| role.to_child())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.build(FieldId::Users(UsersFieldId::UserRole))
|
.build(FieldId::Users(UsersFieldId::UserRole))
|
||||||
.into_node();
|
.into_node();
|
||||||
let user_role_field = StyledField::build()
|
let user_role_field = StyledField::build()
|
||||||
|
@ -42,9 +42,12 @@ pub fn send_ws_msg(msg: WsMsg, ws: Option<&WebSocket>, orders: &mut impl Orders<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// orders.perform_cmd(seed::app::cmds::timeout(10, move || {
|
||||||
|
// let ws = ws.clone();
|
||||||
let binary = bincode::serialize(&msg).unwrap();
|
let binary = bincode::serialize(&msg).unwrap();
|
||||||
ws.send_bytes(binary.as_slice())
|
ws.send_bytes(binary.as_slice())
|
||||||
.expect("Failed to send ws msg");
|
.expect("Failed to send ws msg");
|
||||||
|
// }));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
||||||
@ -64,10 +67,21 @@ pub fn open_socket(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
let url = model.ws_url.as_str();
|
let url = model.ws_url.as_str();
|
||||||
|
|
||||||
model.ws = WebSocket::builder(url, orders)
|
model.ws = WebSocket::builder(url, orders)
|
||||||
.on_message(|msg| Msg::WebSocketChange(WebSocketChanged::WebSocketMessage(msg)))
|
.on_message(|msg| {
|
||||||
.on_open(|| Msg::WebSocketChange(WebSocketChanged::WebSocketOpened))
|
Some(Msg::WebSocketChange(WebSocketChanged::WebSocketMessage(
|
||||||
.on_close(|_| Msg::WebSocketChange(WebSocketChanged::WebSocketClosed))
|
msg,
|
||||||
.on_error(|| {})
|
)))
|
||||||
|
})
|
||||||
|
.on_open(|| {
|
||||||
|
log!("open_socket opened");
|
||||||
|
Some(Msg::WebSocketChange(WebSocketChanged::WebSocketOpened))
|
||||||
|
})
|
||||||
|
.on_close(|_| Some(Msg::WebSocketChange(WebSocketChanged::WebSocketClosed)))
|
||||||
|
.on_error(|| {
|
||||||
|
error!("Failed to open WebSocket");
|
||||||
|
None as Option<Msg>
|
||||||
|
})
|
||||||
|
.protocols(&["jirs"])
|
||||||
.build_and_open()
|
.build_and_open()
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,11 @@ const getProtocol = () => window.location.protocol.replace(/^http/, 'ws');
|
|||||||
const wsUrl = () => `${getProtocol()}//${getWsHostName()}:${process.env.JIRS_SERVER_PORT}/ws/`;
|
const wsUrl = () => `${getProtocol()}//${getWsHostName()}:${process.env.JIRS_SERVER_PORT}/ws/`;
|
||||||
|
|
||||||
import("/jirs.js").then(async module => {
|
import("/jirs.js").then(async module => {
|
||||||
window.module = module;
|
// window.module = module;
|
||||||
await module.default();
|
await module.default();
|
||||||
const host_url = `${location.protocol}//${process.env.JIRS_SERVER_BIND}:${process.env.JIRS_SERVER_PORT}`;
|
const host_url = `${location.protocol}//${process.env.JIRS_SERVER_BIND}:${process.env.JIRS_SERVER_PORT}`;
|
||||||
module.render(host_url, wsUrl());
|
module.render(host_url, wsUrl());
|
||||||
|
document.querySelector('main').className = '';
|
||||||
|
document.querySelector('.spinner').remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,13 +101,19 @@ impl Into<IssueType> for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IssueType {
|
||||||
|
pub fn to_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
IssueType::Task => "task",
|
||||||
|
IssueType::Bug => "bug",
|
||||||
|
IssueType::Story => "story",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for IssueType {
|
impl std::fmt::Display for IssueType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(self.to_str())
|
||||||
IssueType::Task => f.write_str("task"),
|
|
||||||
IssueType::Bug => f.write_str("bug"),
|
|
||||||
IssueType::Story => f.write_str("story"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,15 +163,21 @@ impl Default for IssuePriority {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IssuePriority {
|
||||||
|
pub fn to_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
IssuePriority::Highest => "highest",
|
||||||
|
IssuePriority::High => "high",
|
||||||
|
IssuePriority::Medium => "medium",
|
||||||
|
IssuePriority::Low => "low",
|
||||||
|
IssuePriority::Lowest => "lowest",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for IssuePriority {
|
impl std::fmt::Display for IssuePriority {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(self.to_str())
|
||||||
IssuePriority::Highest => f.write_str("highest"),
|
|
||||||
IssuePriority::High => f.write_str("high"),
|
|
||||||
IssuePriority::Medium => f.write_str("medium"),
|
|
||||||
IssuePriority::Low => f.write_str("low"),
|
|
||||||
IssuePriority::Lowest => f.write_str("lowest"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,13 +257,19 @@ impl Default for UserRole {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UserRole {
|
||||||
|
pub fn to_str<'l>(&self) -> &'l str {
|
||||||
|
match self {
|
||||||
|
UserRole::User => "user",
|
||||||
|
UserRole::Manager => "manager",
|
||||||
|
UserRole::Owner => "owner",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for UserRole {
|
impl std::fmt::Display for UserRole {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(self.to_str())
|
||||||
UserRole::User => f.write_str("user"),
|
|
||||||
UserRole::Manager => f.write_str("manager"),
|
|
||||||
UserRole::Owner => f.write_str("owner"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,13 +334,19 @@ impl Default for ProjectCategory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ProjectCategory {
|
||||||
|
pub fn to_str<'l>(&self) -> &'l str {
|
||||||
|
match self {
|
||||||
|
ProjectCategory::Software => "software",
|
||||||
|
ProjectCategory::Marketing => "marketing",
|
||||||
|
ProjectCategory::Business => "business",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ProjectCategory {
|
impl std::fmt::Display for ProjectCategory {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
f.write_str(self.to_str())
|
||||||
ProjectCategory::Software => f.write_str("software"),
|
|
||||||
ProjectCategory::Marketing => f.write_str("marketing"),
|
|
||||||
ProjectCategory::Business => f.write_str("business"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ default = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "*", features = ["derive"] }
|
|
||||||
actix = { version = "*" }
|
actix = { version = "*" }
|
||||||
actix-web = { version = "*" }
|
actix-web = { version = "*" }
|
||||||
actix-cors = { version = "*" }
|
actix-cors = { version = "*" }
|
||||||
@ -35,14 +34,14 @@ actix-rt = "1"
|
|||||||
actix-web-actors = "*"
|
actix-web-actors = "*"
|
||||||
actix-multipart = { version = "*" }
|
actix-multipart = { version = "*" }
|
||||||
|
|
||||||
dotenv = { version = "*" }
|
|
||||||
byteorder = "1.0"
|
|
||||||
chrono = { version = "0.4", features = [ "serde" ] }
|
|
||||||
libc = { version = "0.2.0" }
|
|
||||||
pq-sys = { version = ">=0.3.0, <0.5.0" }
|
pq-sys = { version = ">=0.3.0, <0.5.0" }
|
||||||
serde_json = { version = ">=0.8.0, <2.0" }
|
r2d2 = { version = ">= 0.8, < 0.9" }
|
||||||
toml = "0.5.6"
|
|
||||||
bincode = "1.2.1"
|
dotenv = { version = "*" }
|
||||||
|
|
||||||
|
byteorder = "1.0"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
libc = { version = "0.2.0" }
|
||||||
time = { version = "0.1" }
|
time = { version = "0.1" }
|
||||||
url = { version = "2.1.0" }
|
url = { version = "2.1.0" }
|
||||||
percent-encoding = { version = "2.1.0" }
|
percent-encoding = { version = "2.1.0" }
|
||||||
@ -53,14 +52,23 @@ num-traits = { version = "0.2" }
|
|||||||
num-integer = { version = "0.1.32" }
|
num-integer = { version = "0.1.32" }
|
||||||
bigdecimal = { version = ">= 0.0.10, <= 0.1.0" }
|
bigdecimal = { version = ">= 0.0.10, <= 0.1.0" }
|
||||||
bitflags = { version = "1.0" }
|
bitflags = { version = "1.0" }
|
||||||
r2d2 = { version = ">= 0.8, < 0.9" }
|
|
||||||
|
serde = { version = "*", features = ["derive"] }
|
||||||
|
serde_json = { version = ">=0.8.0, <2.0" }
|
||||||
|
toml = "0.5.6"
|
||||||
|
bincode = "1.2.1"
|
||||||
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.4"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
|
|
||||||
|
async-trait = { version = "*" }
|
||||||
|
|
||||||
futures = { version = "*" }
|
futures = { version = "*" }
|
||||||
|
openssl-sys = { version = "*", features = ["vendored"] }
|
||||||
|
|
||||||
lettre = { version = "*" }
|
lettre = { version = "*" }
|
||||||
lettre_email = { version = "*" }
|
lettre_email = { version = "*" }
|
||||||
openssl-sys = { version = "*", features = ["vendored"] }
|
|
||||||
|
|
||||||
[dependencies.diesel]
|
[dependencies.diesel]
|
||||||
version = "1.4.4"
|
version = "1.4.4"
|
||||||
@ -82,4 +90,4 @@ version = "0.43.0"
|
|||||||
# Local storage
|
# Local storage
|
||||||
[dependencies.actix-files]
|
[dependencies.actix-files]
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.2.1"
|
version = "*"
|
||||||
|
@ -53,7 +53,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||||
let pool: Db = match req.app_data() {
|
let pool: &Db = match req.app_data::<Db>() {
|
||||||
Some(d) => d,
|
Some(d) => d,
|
||||||
_ => {
|
_ => {
|
||||||
return async move {
|
return async move {
|
||||||
@ -66,7 +66,7 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match check_token(req.headers(), pool) {
|
match check_token(req.headers(), pool.clone()) {
|
||||||
std::result::Result::Err(e) => {
|
std::result::Result::Err(e) => {
|
||||||
return async move {
|
return async move {
|
||||||
let res = e.into_http_response().into_body();
|
let res = e.into_http_response().into_body();
|
||||||
|
@ -82,7 +82,7 @@ impl WsMessageSender for ws::WebsocketContext<WebSocketActor> {
|
|||||||
impl Handler<InnerMsg> for WebSocketActor {
|
impl Handler<InnerMsg> for WebSocketActor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: InnerMsg, ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: InnerMsg, ctx: &mut <Self as Actor>::Context) -> Self::Result {
|
||||||
if let InnerMsg::Transfer(msg) = msg {
|
if let InnerMsg::Transfer(msg) = msg {
|
||||||
ctx.send_msg(&msg)
|
ctx.send_msg(&msg)
|
||||||
};
|
};
|
||||||
@ -252,10 +252,10 @@ impl WebSocketActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn require_user(&self) -> Result<&User, WsMsg> {
|
fn require_user(&self) -> Result<&User, WsMsg> {
|
||||||
self.current_user.as_ref().map(|u| u).ok_or_else(|| {
|
self.current_user
|
||||||
let _x = 1;
|
.as_ref()
|
||||||
WsMsg::AuthorizeExpired
|
.map(|u| u)
|
||||||
})
|
.ok_or_else(|| WsMsg::AuthorizeExpired)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_user_project(&self) -> Result<&UserProject, WsMsg> {
|
fn require_user_project(&self) -> Result<&UserProject, WsMsg> {
|
||||||
@ -300,7 +300,11 @@ impl WebSocketActor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
||||||
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
msg: Result<ws::Message, ws::ProtocolError>,
|
||||||
|
ctx: &mut <Self as Actor>::Context,
|
||||||
|
) {
|
||||||
match msg {
|
match msg {
|
||||||
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
Ok(ws::Message::Ping(msg)) => ctx.pong(&msg),
|
||||||
Ok(ws::Message::Text(text)) => ctx.text(text),
|
Ok(ws::Message::Text(text)) => ctx.text(text),
|
||||||
@ -322,7 +326,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finished(&mut self, ctx: &mut Self::Context) {
|
fn finished(&mut self, ctx: &mut <Self as Actor>::Context) {
|
||||||
info!("Disconnected");
|
info!("Disconnected");
|
||||||
if let (Some(user), Some(up)) = (
|
if let (Some(user), Some(up)) = (
|
||||||
self.current_user.as_ref(),
|
self.current_user.as_ref(),
|
||||||
@ -380,7 +384,7 @@ impl Actor for WsServer {
|
|||||||
impl Handler<InnerMsg> for WsServer {
|
impl Handler<InnerMsg> for WsServer {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
|
|
||||||
fn handle(&mut self, msg: InnerMsg, _ctx: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: InnerMsg, _ctx: &mut <Self as Actor>::Context) -> Self::Result {
|
||||||
debug!("receive {:?}", msg);
|
debug!("receive {:?}", msg);
|
||||||
match msg {
|
match msg {
|
||||||
InnerMsg::Join(project_id, user_id, recipient) => {
|
InnerMsg::Join(project_id, user_id, recipient) => {
|
||||||
@ -408,8 +412,12 @@ impl Handler<InnerMsg> for WsServer {
|
|||||||
self.sessions.remove(&user_id);
|
self.sessions.remove(&user_id);
|
||||||
} else {
|
} else {
|
||||||
let v = self.sessions.entry(user_id).or_insert_with(Vec::new);
|
let v = self.sessions.entry(user_id).or_insert_with(Vec::new);
|
||||||
if v.remove_item(&recipient).is_none() {
|
let mut old = vec![];
|
||||||
debug!("Can't remove recipient");
|
std::mem::swap(&mut old, v);
|
||||||
|
for r in old {
|
||||||
|
if r != recipient {
|
||||||
|
v.push(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user