Better select lang (broken text filter), style for code. Move to and then field_id
This commit is contained in:
parent
8deab1c2d5
commit
5267ce8a11
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -1080,8 +1080,8 @@ version = "0.1.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
@ -1790,10 +1790,10 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd02480f8dcf48798e62113974d6ccca2129a51d241fa20f1ea349c8a42559d5"
|
checksum = "fd02480f8dcf48798e62113974d6ccca2129a51d241fa20f1ea349c8a42559d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.10.1",
|
"base64 0.10.1",
|
||||||
"email",
|
"email",
|
||||||
"lettre",
|
"lettre",
|
||||||
"mime",
|
"mime",
|
||||||
"time 0.1.43",
|
"time 0.1.43",
|
||||||
"uuid 0.7.4",
|
"uuid 0.7.4",
|
||||||
]
|
]
|
||||||
@ -3078,7 +3078,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"yaml-rust",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3090,9 +3089,9 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"remove_dir_all",
|
"remove_dir_all",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3768,15 +3767,6 @@ version = "0.8.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
|
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "yaml-rust"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
|
|
||||||
dependencies = [
|
|
||||||
"linked-hash-map",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
157
README.md
157
README.md
@ -51,6 +51,7 @@ https://git.sr.ht/~tsumanu/jirs
|
|||||||
* [X] Grouping by Epic
|
* [X] Grouping by Epic
|
||||||
* [X] Basic Rich Text Editor
|
* [X] Basic Rich Text Editor
|
||||||
* [ ] Insert Code in Rich Text Editor
|
* [ ] Insert Code in Rich Text Editor
|
||||||
|
* [X] Code syntax
|
||||||
* [ ] Personal settings to choose MDE (Markdown Editor) or RTE
|
* [ ] Personal settings to choose MDE (Markdown Editor) or RTE
|
||||||
* [ ] Issues and filters view
|
* [ ] Issues and filters view
|
||||||
* [ ] Issues and filters working filters
|
* [ ] Issues and filters working filters
|
||||||
@ -180,3 +181,159 @@ sudo nginx -s reload
|
|||||||
## Issue trackers
|
## Issue trackers
|
||||||
|
|
||||||
https://todo.sr.ht/~tsumanu/JIRS
|
https://todo.sr.ht/~tsumanu/JIRS
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
### Display code syntax
|
||||||
|
|
||||||
|
Custom element glued with WASM
|
||||||
|
|
||||||
|
* `file-path` have connected on attr changed callback and will change displayed path
|
||||||
|
* `lang` does not have callback and it's used only on `connectedCallback`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<jirs-code-view lang="Rust" file-path="/some/path.rs">
|
||||||
|
struct Foo {
|
||||||
|
}
|
||||||
|
</jirs-code-view>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported languages
|
||||||
|
|
||||||
|
* ASP
|
||||||
|
* AWK
|
||||||
|
* ActionScript
|
||||||
|
* Advanced CSV
|
||||||
|
* AppleScript
|
||||||
|
* Assembly x86 (NASM)
|
||||||
|
* Batch File
|
||||||
|
* BibTeX
|
||||||
|
* Bourne Again Shell (bash)
|
||||||
|
* C
|
||||||
|
* C#
|
||||||
|
* C++
|
||||||
|
* CMake
|
||||||
|
* CMake C Header
|
||||||
|
* CMake C++ Header
|
||||||
|
* CMakeCache
|
||||||
|
* CMakeCommands
|
||||||
|
* CSS
|
||||||
|
* Cargo Build Results
|
||||||
|
* Clojure
|
||||||
|
* Crystal
|
||||||
|
* D
|
||||||
|
* DMD Output
|
||||||
|
* Dart
|
||||||
|
* Diff
|
||||||
|
* Dockerfile
|
||||||
|
* Elixir
|
||||||
|
* Elm
|
||||||
|
* Elm Compile Messages
|
||||||
|
* Elm Documentation
|
||||||
|
* Erlang
|
||||||
|
* F#
|
||||||
|
* Fortran (Fixed Form)
|
||||||
|
* Fortran (Modern)
|
||||||
|
* Fortran Namelist
|
||||||
|
* Friendly Interactive Shell (fish)
|
||||||
|
* GFortran Build Results
|
||||||
|
* Generic Config
|
||||||
|
* Git Attributes
|
||||||
|
* Git Commit
|
||||||
|
* Git Common
|
||||||
|
* Git Config
|
||||||
|
* Git Ignore
|
||||||
|
* Git Link
|
||||||
|
* Git Log
|
||||||
|
* Git Mailmap
|
||||||
|
* Git Rebase Todo
|
||||||
|
* Go
|
||||||
|
* GraphQL
|
||||||
|
* Graphviz (DOT)
|
||||||
|
* Groovy
|
||||||
|
* HTML
|
||||||
|
* HTML (ASP)
|
||||||
|
* HTML (EEx)
|
||||||
|
* HTML (Erlang)
|
||||||
|
* HTML (Jinja2)
|
||||||
|
* HTML (Rails)
|
||||||
|
* HTML (Tcl)
|
||||||
|
* Handlebars
|
||||||
|
* Haskell
|
||||||
|
* JSON
|
||||||
|
* Java
|
||||||
|
* Java Properties
|
||||||
|
* Java Server Page (JSP)
|
||||||
|
* JavaScript
|
||||||
|
* JavaScript (Rails)
|
||||||
|
* Javadoc
|
||||||
|
* Jinja2
|
||||||
|
* Julia
|
||||||
|
* Kotlin
|
||||||
|
* LaTeX
|
||||||
|
* LaTeX Log
|
||||||
|
* Less
|
||||||
|
* Linker Script
|
||||||
|
* Lisp
|
||||||
|
* Literate Haskell
|
||||||
|
* Lua
|
||||||
|
* MATLAB
|
||||||
|
* Make Output
|
||||||
|
* Makefile
|
||||||
|
* Markdown
|
||||||
|
* MiniZinc (MZN)
|
||||||
|
* MultiMarkdown
|
||||||
|
* NAnt Build File
|
||||||
|
* Nim
|
||||||
|
* Nix
|
||||||
|
* OCaml
|
||||||
|
* OCamllex
|
||||||
|
* OCamlyacc
|
||||||
|
* Objective-C
|
||||||
|
* Objective-C++
|
||||||
|
* OpenMP (Fortran)
|
||||||
|
* PHP
|
||||||
|
* PHP Source
|
||||||
|
* Pascal
|
||||||
|
* Perl
|
||||||
|
* Plain Text
|
||||||
|
* PowerShell
|
||||||
|
* PureScript
|
||||||
|
* Python
|
||||||
|
* R
|
||||||
|
* R Console
|
||||||
|
* Racket
|
||||||
|
* Rd (R Documentation)
|
||||||
|
* Reason
|
||||||
|
* Regular Expression
|
||||||
|
* Regular Expressions (Elixir)
|
||||||
|
* Regular Expressions (Javascript)
|
||||||
|
* Regular Expressions (PHP)
|
||||||
|
* Regular Expressions (Python)
|
||||||
|
* Ruby
|
||||||
|
* Ruby Haml
|
||||||
|
* Ruby on Rails
|
||||||
|
* Rust
|
||||||
|
* SCSS
|
||||||
|
* SQL
|
||||||
|
* SQL (Rails)
|
||||||
|
* SWI-Prolog
|
||||||
|
* Sass
|
||||||
|
* Scala
|
||||||
|
* Shell-Unix-Generic
|
||||||
|
* Stylus
|
||||||
|
* Swift
|
||||||
|
* TOML
|
||||||
|
* Tcl
|
||||||
|
* TeX
|
||||||
|
* Textile
|
||||||
|
* TypeScript
|
||||||
|
* TypeScriptReact
|
||||||
|
* VimL
|
||||||
|
* XML
|
||||||
|
* YAML
|
||||||
|
* camlp4
|
||||||
|
* commands-builtin-shell-bash
|
||||||
|
* lrc
|
||||||
|
* reStructuredText
|
||||||
|
* srt
|
||||||
|
@ -33,7 +33,7 @@ comrak = "*"
|
|||||||
wee_alloc = "*"
|
wee_alloc = "*"
|
||||||
|
|
||||||
lazy_static = "*"
|
lazy_static = "*"
|
||||||
syntect = { version = "*", default-features = false, features = ["default-fancy"] }
|
syntect = { version = "*", default-features = false, features = ["html", "regex-fancy", "dump-load-rs"] }
|
||||||
|
|
||||||
[dependencies.wasm-bindgen]
|
[dependencies.wasm-bindgen]
|
||||||
version = "0.2.66"
|
version = "0.2.66"
|
||||||
|
@ -161,3 +161,54 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**********************************************************/
|
||||||
|
/* Code tooltip */
|
||||||
|
/**********************************************************/
|
||||||
|
|
||||||
|
.codeTooltip {
|
||||||
|
min-width: 336px;
|
||||||
|
padding: 15px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 800px) {
|
||||||
|
.codeTooltip {
|
||||||
|
min-width: 636px;
|
||||||
|
left: calc(100% / 2 - 318px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.codeTooltip {
|
||||||
|
min-width: 836px;
|
||||||
|
left: calc(100% / 2 - 418px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeTooltip > h2 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeTooltip > select {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--borderLightest);
|
||||||
|
margin: var(--rte-indent) 0;
|
||||||
|
min-height: 1rem;
|
||||||
|
background: var(--backgroundLightest);
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeTooltip > select > option {
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeTooltip > textarea {
|
||||||
|
border: 1px solid var(--borderLightest);
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeTooltip > textarea:focus {
|
||||||
|
border: 1px solid var(--borderInputFocus);
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<link href="/logo2.svg" rel="icon">
|
<link href="/logo2.svg" rel="icon">
|
||||||
<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">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main id="app"></main>
|
<main id="app"></main>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use syntect::easy::HighlightLines;
|
use syntect::easy::HighlightLines;
|
||||||
use syntect::highlighting::{FontStyle, Style};
|
use syntect::highlighting::{FontStyle, Style};
|
||||||
|
use syntect::parsing::SyntaxReference;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen(final, js_name = JirsCodeBuilder)]
|
#[wasm_bindgen(final, js_name = JirsCodeBuilder)]
|
||||||
@ -14,13 +15,22 @@ impl JirsCodeBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn hi(&mut self, lang: &str, line: &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 = match crate::hi::SYNTAX_SET.find_syntax_by_name(lang) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
_ => {
|
_ => {
|
||||||
return line.to_string();
|
return code.to_string();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut buffer = String::new();
|
||||||
|
for line in code.lines() {
|
||||||
|
buffer.push_str(self.hi(syntax, line).as_str());
|
||||||
|
buffer.push_str("<br />");
|
||||||
|
}
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hi(&mut self, syntax: &SyntaxReference, line: &str) -> 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);
|
||||||
|
|
||||||
@ -36,8 +46,10 @@ impl JirsCodeBuilder {
|
|||||||
"font-weight: bold"
|
"font-weight: bold"
|
||||||
} else if font_style == FontStyle::ITALIC {
|
} else if font_style == FontStyle::ITALIC {
|
||||||
"font-style: italic"
|
"font-style: italic"
|
||||||
|
} else if font_style == FontStyle::UNDERLINE {
|
||||||
|
"text-decoration: underline"
|
||||||
} else {
|
} else {
|
||||||
"font-decoration: underline"
|
""
|
||||||
};
|
};
|
||||||
let f = format!("rgba({}, {}, {}, {})", f.r, f.g, f.b, f.a);
|
let f = format!("rgba({}, {}, {}, {})", f.r, f.g, f.b, f.a);
|
||||||
let b = format!("rgba({}, {}, {}, {})", b.r, b.g, b.b, b.a);
|
let b = format!("rgba({}, {}, {}, {})", b.r, b.g, b.b, b.a);
|
||||||
@ -55,71 +67,225 @@ impl JirsCodeBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn define() {
|
pub fn define() {
|
||||||
create_custom_element(
|
{
|
||||||
"jirs-code-view",
|
let el_name = "JirsCodeView";
|
||||||
"JirsCodeView",
|
let tag = "jirs-code-view";
|
||||||
r#"
|
|
||||||
<style>
|
ElementBuilder::default()
|
||||||
:host { display: block; border: 1px solid black; background: rgba(43, 48, 59, 255); padding: 1rem; }
|
.identifier(el_name, tag)
|
||||||
|
.runtime("JirsCodeBuilder")
|
||||||
|
.body(
|
||||||
|
r#"
|
||||||
|
<style>
|
||||||
|
:host { display: block; border: 1px solid black; }
|
||||||
:host { margin-left: 400px; }
|
:host { margin-left: 400px; }
|
||||||
#view span { white-space: pre; }
|
#view { background: rgba(43, 48, 59, 255); padding: 1rem; }
|
||||||
|
#view span { white-space: pre; font-family: 'Source Code Pro', monospace; }
|
||||||
</style>
|
</style>
|
||||||
|
<div id='file-name'></div>
|
||||||
<div id='view'></div>
|
<div id='view'></div>
|
||||||
"#,
|
"#,
|
||||||
);
|
)
|
||||||
|
.on_connected(FillShadowElement::new(el_name, "#view", ""))
|
||||||
|
.on_connected(
|
||||||
|
r#"
|
||||||
|
const lang = this.getAttribute('lang') || '';
|
||||||
|
setTimeout(() => {{
|
||||||
|
const code = (this.innerHTML || '').trim();
|
||||||
|
shadow.querySelector('#view').innerHTML = runtime.hi_code(lang, code);
|
||||||
|
}}, 1);
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.on_attr_changed("lang", r#""#)
|
||||||
|
.on_attr_changed(
|
||||||
|
"file-path",
|
||||||
|
r#"
|
||||||
|
shadow.querySelector('#file-name').innerText = newV;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.mount();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_custom_element(tag: &str, name: &str, html: &str) {
|
trait ToJs {
|
||||||
let source = format!(
|
fn to_js(&self) -> String;
|
||||||
r#"
|
}
|
||||||
|
|
||||||
|
impl ToJs for &str {
|
||||||
|
fn to_js(&self) -> String {
|
||||||
|
self.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FillShadowElement {
|
||||||
|
el_name: String,
|
||||||
|
target: String,
|
||||||
|
source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FillShadowElement {
|
||||||
|
pub fn new<N: Into<String>, S: Into<String>, Source: ToJs>(
|
||||||
|
el_name: N,
|
||||||
|
target: S,
|
||||||
|
source: Source,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
el_name: el_name.into(),
|
||||||
|
target: target.into(),
|
||||||
|
source: source.to_js(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJs for FillShadowElement {
|
||||||
|
fn to_js(&self) -> String {
|
||||||
|
let shadow = ElementBuilder::shadow_handle(&self.el_name);
|
||||||
|
format!(
|
||||||
|
"{shadow}.querySelector('{selector}').innerHTML = `{content}`;",
|
||||||
|
shadow = shadow,
|
||||||
|
selector = self.target,
|
||||||
|
content = self.source
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ElementBuilder {
|
||||||
|
name: String,
|
||||||
|
tag: String,
|
||||||
|
body: String,
|
||||||
|
runtime: String,
|
||||||
|
on_connected: Vec<String>,
|
||||||
|
on_attr_changed: std::collections::HashMap<String, Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJs for ElementBuilder {
|
||||||
|
fn to_js(&self) -> String {
|
||||||
|
let shadow = Self::shadow_handle(&self.name);
|
||||||
|
let runtime = Self::runtime_handle(&self.name);
|
||||||
|
let (observe, attr_body) = if self.on_attr_changed.is_empty() {
|
||||||
|
("".to_string(), "".to_string())
|
||||||
|
} else {
|
||||||
|
let observe = self
|
||||||
|
.on_attr_changed
|
||||||
|
.keys()
|
||||||
|
.map(|s| format!("'{}'", s))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(",");
|
||||||
|
let mut on_changed = "switch (name) {".to_string();
|
||||||
|
for (k, v) in self.on_attr_changed.iter() {
|
||||||
|
let body = v.join(";");
|
||||||
|
on_changed.push_str(
|
||||||
|
format!("case '{attr}': {{ {body}; break; }}", attr = k, body = body).as_str(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
on_changed.push_str("}");
|
||||||
|
|
||||||
|
(
|
||||||
|
format!(
|
||||||
|
"static get observedAttributes() {{ return [{}]; }}",
|
||||||
|
observe
|
||||||
|
),
|
||||||
|
on_changed,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let on_connected: String = self.on_connected.join(";");
|
||||||
|
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
class {name} extends HTMLElement {{
|
class {name} extends HTMLElement {{
|
||||||
static RUNTIME = Symbol();
|
static RUNTIME = Symbol();
|
||||||
static SHADOW = Symbol();
|
static SHADOW = Symbol();
|
||||||
|
|
||||||
static get observedAttributes() {{ return ['lang']; }}
|
{observe}
|
||||||
|
|
||||||
constructor() {{
|
constructor() {{
|
||||||
super();
|
super();
|
||||||
this[ {name} . SHADOW] = this.attachShadow({{ 'mode': 'closed' }});
|
{shadow} = this.attachShadow({{ 'mode': 'closed' }});
|
||||||
this[ {name} . SHADOW].innerHTML = `{html}`;
|
{shadow}.innerHTML = `{html}`;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
connectedCallback() {{
|
connectedCallback() {{
|
||||||
this[ {name} . RUNTIME] = new JirsCodeBuilder();
|
const runtime = {runtime} = new JirsCodeBuilder();
|
||||||
const view = this[ {name} . SHADOW].querySelector('#view');
|
const shadow = {shadow};
|
||||||
view.innerHTML = '';
|
|
||||||
const lang = this.getAttribute('lang') || '';
|
|
||||||
|
|
||||||
setTimeout(() => {{
|
{on_connected}
|
||||||
const hi = () => {{
|
|
||||||
const line = code.shift();
|
|
||||||
if (line === undefined) return;
|
|
||||||
const s = this[ {name} . RUNTIME].hi(lang, line);
|
|
||||||
view.innerHTML += `${{s}}<br />`;
|
|
||||||
setTimeout(() => hi(), 10);
|
|
||||||
}};
|
|
||||||
hi();
|
|
||||||
}}, 10);
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
disconnectedCallback() {{
|
disconnectedCallback() {{
|
||||||
this[ {name} . RUNTIME].free();
|
{runtime}.free();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
attributeChangedCallback(name, oldV, newV) {{
|
attributeChangedCallback(name, oldV, newV) {{
|
||||||
|
const runtime = {runtime};
|
||||||
|
const shadow = {shadow};
|
||||||
|
{attr_body}
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
customElements.define( '{tag}', {name});
|
customElements.define( '{tag}', {name});
|
||||||
"#,
|
"#,
|
||||||
name = name,
|
name = self.name,
|
||||||
tag = tag,
|
tag = self.tag,
|
||||||
html = html,
|
html = self.body,
|
||||||
);
|
shadow = shadow,
|
||||||
{
|
runtime = runtime,
|
||||||
|
observe = observe,
|
||||||
|
attr_body = attr_body,
|
||||||
|
on_connected = on_connected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElementBuilder {
|
||||||
|
pub fn identifier<N: Into<String>, T: Into<String>>(mut self, name: N, tag: T) -> Self {
|
||||||
|
self.name = name.into();
|
||||||
|
self.tag = tag.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime<S: Into<String>>(mut self, runtime: S) -> Self {
|
||||||
|
self.runtime = runtime.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body<S: Into<String>>(mut self, body: S) -> Self {
|
||||||
|
self.body = body.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_connected<B: ToJs>(mut self, c: B) -> Self {
|
||||||
|
self.on_connected.push(c.to_js());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_attr_changed<N: Into<String>, R: Into<String>>(mut self, attr: N, run: R) -> Self {
|
||||||
|
let a = attr.into();
|
||||||
|
let r = run.into();
|
||||||
|
self.on_attr_changed
|
||||||
|
.entry(a)
|
||||||
|
.or_insert_with(|| vec![])
|
||||||
|
.push(r);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shadow_handle(name: &str) -> String {
|
||||||
|
format!("this[ {name} . SHADOW]", name = name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_handle(name: &str) -> String {
|
||||||
|
format!("this[ {name} . RUNTIME]", name = name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount(&self) {
|
||||||
|
let source = self.to_js();
|
||||||
|
{
|
||||||
|
use seed::*;
|
||||||
|
log!(source);
|
||||||
|
}
|
||||||
use seed::*;
|
use seed::*;
|
||||||
match js_sys::eval(source.as_str()) {
|
match js_sys::eval(source.as_str()) {
|
||||||
Ok(_v) => (),
|
Ok(_v) => (),
|
||||||
Err(e) => error!(e),
|
Err(e) => error!(e),
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,11 @@ pub enum EditIssueModalSection {
|
|||||||
Comment(CommentFieldId),
|
Comment(CommentFieldId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
|
pub enum RteField {
|
||||||
|
CodeLang(Box<FieldId>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||||
pub enum FieldId {
|
pub enum FieldId {
|
||||||
SignIn(SignInFieldId),
|
SignIn(SignInFieldId),
|
||||||
@ -26,6 +31,7 @@ pub enum FieldId {
|
|||||||
CopyButtonLabel,
|
CopyButtonLabel,
|
||||||
|
|
||||||
ProjectSettings(ProjectFieldId),
|
ProjectSettings(ProjectFieldId),
|
||||||
|
Rte(RteField),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for FieldId {
|
impl std::fmt::Display for FieldId {
|
||||||
@ -120,6 +126,7 @@ impl std::fmt::Display for FieldId {
|
|||||||
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
||||||
UsersFieldId::CurrentProject => f.write_str("profile-currentProject"),
|
UsersFieldId::CurrentProject => f.write_str("profile-currentProject"),
|
||||||
},
|
},
|
||||||
|
FieldId::Rte(..) => f.write_str("rte"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,10 @@ fn submit(_page: &InvitePage) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn token_field(page: &InvitePage) -> Node<Msg> {
|
fn token_field(page: &InvitePage) -> Node<Msg> {
|
||||||
let token = StyledInput::build(FieldId::Invite(InviteFieldId::Token))
|
let token = StyledInput::build()
|
||||||
.valid(!page.token_touched || is_token(page.token.as_str()) && page.error.is_none())
|
.valid(!page.token_touched || is_token(page.token.as_str()) && page.error.is_none())
|
||||||
.value(page.token.as_str())
|
.value(page.token.as_str())
|
||||||
.build()
|
.build(FieldId::Invite(InviteFieldId::Token))
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
|
@ -191,6 +191,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
styled_tooltip::Variant::Messages => {
|
styled_tooltip::Variant::Messages => {
|
||||||
model.messages_tooltip_visible = !model.messages_tooltip_visible;
|
model.messages_tooltip_visible = !model.messages_tooltip_visible;
|
||||||
}
|
}
|
||||||
|
styled_tooltip::Variant::CodeBuilder => {}
|
||||||
Variant::TableBuilder => {}
|
Variant::TableBuilder => {}
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -23,7 +23,7 @@ where
|
|||||||
.and_then(|id| model.epics.iter().find(|epic| epic.id == id as EpicId))
|
.and_then(|id| model.epics.iter().find(|epic| epic.id == id as EpicId))
|
||||||
.map(|epic| vec![epic.to_child()])
|
.map(|epic| vec![epic.to_child()])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let input = StyledSelect::build(field_id)
|
let input = StyledSelect::build()
|
||||||
.name("epic")
|
.name("epic")
|
||||||
.selected(selected)
|
.selected(selected)
|
||||||
.options(model.epics.iter().map(|epic| epic.to_child()).collect())
|
.options(model.epics.iter().map(|epic| epic.to_child()).collect())
|
||||||
@ -32,7 +32,7 @@ where
|
|||||||
.text_filter(modal.epic_state().text_filter.as_str())
|
.text_filter(modal.epic_state().text_filter.as_str())
|
||||||
.opened(modal.epic_state().opened)
|
.opened(modal.epic_state().opened)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build(field_id)
|
||||||
.into_node();
|
.into_node();
|
||||||
Some(
|
Some(
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
|
@ -300,7 +300,7 @@ pub fn view(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let select_type = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Type))
|
let select_type = StyledSelect::build()
|
||||||
.name("type")
|
.name("type")
|
||||||
.normal()
|
.normal()
|
||||||
.text_filter(modal.type_state.text_filter.as_str())
|
.text_filter(modal.type_state.text_filter.as_str())
|
||||||
@ -317,7 +317,7 @@ fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
)
|
)
|
||||||
.to_child()
|
.to_child()
|
||||||
.name("type")])
|
.name("type")])
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Type))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.label("Issue Type")
|
.label("Issue Type")
|
||||||
@ -328,9 +328,9 @@ fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn short_summary_field(modal: &AddIssueModal) -> Node<Msg> {
|
fn short_summary_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let short_summary = StyledInput::build(FieldId::AddIssueModal(IssueFieldId::Title))
|
let short_summary = StyledInput::build()
|
||||||
.state(&modal.title_state)
|
.state(&modal.title_state)
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.label("Short Summary")
|
.label("Short Summary")
|
||||||
@ -359,7 +359,7 @@ fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.reporter_id
|
.reporter_id
|
||||||
.or_else(|| model.user.as_ref().map(|u| u.id))
|
.or_else(|| model.user.as_ref().map(|u| u.id))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let reporter = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Reporter))
|
let reporter = StyledSelect::build()
|
||||||
.normal()
|
.normal()
|
||||||
.text_filter(modal.reporter_state.text_filter.as_str())
|
.text_filter(modal.reporter_state.text_filter.as_str())
|
||||||
.opened(modal.reporter_state.opened)
|
.opened(modal.reporter_state.opened)
|
||||||
@ -384,7 +384,7 @@ fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Reporter))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.input(reporter)
|
.input(reporter)
|
||||||
@ -395,7 +395,7 @@ fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let assignees = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Assignees))
|
let assignees = StyledSelect::build()
|
||||||
.normal()
|
.normal()
|
||||||
.multi()
|
.multi()
|
||||||
.text_filter(modal.assignees_state.text_filter.as_str())
|
.text_filter(modal.assignees_state.text_filter.as_str())
|
||||||
@ -421,7 +421,7 @@ fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Assignees))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.input(assignees)
|
.input(assignees)
|
||||||
@ -432,7 +432,7 @@ fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let select_priority = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Priority))
|
let select_priority = StyledSelect::build()
|
||||||
.name("priority")
|
.name("priority")
|
||||||
.normal()
|
.normal()
|
||||||
.text_filter(modal.priority_state.text_filter.as_str())
|
.text_filter(modal.priority_state.text_filter.as_str())
|
||||||
@ -445,7 +445,7 @@ fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.selected(vec![modal.priority.to_child().name("priority")])
|
.selected(vec![modal.priority.to_child().name("priority")])
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Priority))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.label("Issue Type")
|
.label("Issue Type")
|
||||||
@ -456,9 +456,9 @@ fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn name_field(modal: &AddIssueModal) -> Node<Msg> {
|
fn name_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let name = StyledInput::build(FieldId::AddIssueModal(IssueFieldId::Title))
|
let name = StyledInput::build()
|
||||||
.state(&modal.title_state)
|
.state(&modal.title_state)
|
||||||
.build()
|
.build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.label("Epic name")
|
.label("Epic name")
|
||||||
|
@ -426,30 +426,30 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let issue_type_select = StyledSelect::build(FieldId::EditIssueModal(
|
let issue_type_select = StyledSelect::build()
|
||||||
EditIssueModalSection::Issue(IssueFieldId::Type),
|
.dropdown_width(150)
|
||||||
))
|
.name("type")
|
||||||
.dropdown_width(150)
|
.text_filter(top_type_state.text_filter.as_str())
|
||||||
.name("type")
|
.opened(top_type_state.opened)
|
||||||
.text_filter(top_type_state.text_filter.as_str())
|
.valid(true)
|
||||||
.opened(top_type_state.opened)
|
.options(
|
||||||
.valid(true)
|
IssueType::ordered()
|
||||||
.options(
|
.into_iter()
|
||||||
IssueType::ordered()
|
.map(|t| t.to_child().name("type"))
|
||||||
.into_iter()
|
.collect(),
|
||||||
.map(|t| t.to_child().name("type"))
|
)
|
||||||
.collect(),
|
.selected(vec![{
|
||||||
)
|
let id = modal.id;
|
||||||
.selected(vec![{
|
let issue_type = &payload.issue_type;
|
||||||
let id = modal.id;
|
issue_type
|
||||||
let issue_type = &payload.issue_type;
|
.to_child()
|
||||||
issue_type
|
.name("type")
|
||||||
.to_child()
|
.text(format!("{} - {}", issue_type, id))
|
||||||
.name("type")
|
}])
|
||||||
.text(format!("{} - {}", issue_type, id))
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
}])
|
IssueFieldId::Type,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "topActions"],
|
attrs![At::Class => "topActions"],
|
||||||
@ -471,16 +471,16 @@ fn left_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
..
|
..
|
||||||
} = modal;
|
} = modal;
|
||||||
|
|
||||||
let title = StyledInput::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let title = StyledInput::build()
|
||||||
IssueFieldId::Title,
|
.add_input_class("issueSummary")
|
||||||
)))
|
.add_wrapper_class("issueSummary")
|
||||||
.add_input_class("issueSummary")
|
.add_wrapper_class("textarea")
|
||||||
.add_wrapper_class("issueSummary")
|
.value(payload.title.as_str())
|
||||||
.add_wrapper_class("textarea")
|
.valid(payload.title.len() >= 3)
|
||||||
.value(payload.title.as_str())
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
.valid(payload.title.len() >= 3)
|
IssueFieldId::Title,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let description_text = payload.description.as_ref().cloned().unwrap_or_default();
|
let description_text = payload.description.as_ref().cloned().unwrap_or_default();
|
||||||
let description = StyledEditor::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let description = StyledEditor::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
@ -659,114 +659,114 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
|||||||
..
|
..
|
||||||
} = modal;
|
} = modal;
|
||||||
|
|
||||||
let status = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let status = StyledSelect::build()
|
||||||
IssueFieldId::IssueStatusId,
|
.name("status")
|
||||||
)))
|
.opened(status_state.opened)
|
||||||
.name("status")
|
.normal()
|
||||||
.opened(status_state.opened)
|
.text_filter(status_state.text_filter.as_str())
|
||||||
.normal()
|
.options(
|
||||||
.text_filter(status_state.text_filter.as_str())
|
model
|
||||||
.options(
|
.issue_statuses
|
||||||
model
|
.iter()
|
||||||
.issue_statuses
|
.map(|opt| opt.to_child().name("status"))
|
||||||
.iter()
|
.collect(),
|
||||||
.map(|opt| opt.to_child().name("status"))
|
)
|
||||||
.collect(),
|
.selected(
|
||||||
)
|
model
|
||||||
.selected(
|
.issue_statuses
|
||||||
model
|
.iter()
|
||||||
.issue_statuses
|
.filter(|is| is.id == payload.issue_status_id)
|
||||||
.iter()
|
.map(|is| is.to_child().name("status"))
|
||||||
.filter(|is| is.id == payload.issue_status_id)
|
.collect(),
|
||||||
.map(|is| is.to_child().name("status"))
|
)
|
||||||
.collect(),
|
.valid(true)
|
||||||
)
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
.valid(true)
|
IssueFieldId::IssueStatusId,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
let status_field = StyledField::build()
|
let status_field = StyledField::build()
|
||||||
.input(status)
|
.input(status)
|
||||||
.label("Status")
|
.label("Status")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let assignees = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let assignees = StyledSelect::build()
|
||||||
IssueFieldId::Assignees,
|
.name("assignees")
|
||||||
)))
|
.opened(assignees_state.opened)
|
||||||
.name("assignees")
|
.empty()
|
||||||
.opened(assignees_state.opened)
|
.multi()
|
||||||
.empty()
|
.text_filter(assignees_state.text_filter.as_str())
|
||||||
.multi()
|
.options(
|
||||||
.text_filter(assignees_state.text_filter.as_str())
|
model
|
||||||
.options(
|
.users
|
||||||
model
|
.iter()
|
||||||
.users
|
.map(|user| user.to_child().name("assignees"))
|
||||||
.iter()
|
.collect(),
|
||||||
.map(|user| user.to_child().name("assignees"))
|
)
|
||||||
.collect(),
|
.selected(
|
||||||
)
|
model
|
||||||
.selected(
|
.users
|
||||||
model
|
.iter()
|
||||||
.users
|
.filter(|user| payload.user_ids.contains(&user.id))
|
||||||
.iter()
|
.map(|user| user.to_child().name("assignees"))
|
||||||
.filter(|user| payload.user_ids.contains(&user.id))
|
.collect(),
|
||||||
.map(|user| user.to_child().name("assignees"))
|
)
|
||||||
.collect(),
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
)
|
IssueFieldId::Assignees,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
let assignees_field = StyledField::build()
|
let assignees_field = StyledField::build()
|
||||||
.input(assignees)
|
.input(assignees)
|
||||||
.label("Assignees")
|
.label("Assignees")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let reporter = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let reporter = StyledSelect::build()
|
||||||
IssueFieldId::Reporter,
|
.name("reporter")
|
||||||
)))
|
.opened(reporter_state.opened)
|
||||||
.name("reporter")
|
.empty()
|
||||||
.opened(reporter_state.opened)
|
.text_filter(reporter_state.text_filter.as_str())
|
||||||
.empty()
|
.options(
|
||||||
.text_filter(reporter_state.text_filter.as_str())
|
model
|
||||||
.options(
|
.users
|
||||||
model
|
.iter()
|
||||||
.users
|
.map(|user| user.to_child().name("reporter"))
|
||||||
.iter()
|
.collect(),
|
||||||
.map(|user| user.to_child().name("reporter"))
|
)
|
||||||
.collect(),
|
.selected(
|
||||||
)
|
model
|
||||||
.selected(
|
.users
|
||||||
model
|
.iter()
|
||||||
.users
|
.filter(|user| payload.reporter_id == user.id)
|
||||||
.iter()
|
.map(|user| user.to_child().name("reporter"))
|
||||||
.filter(|user| payload.reporter_id == user.id)
|
.collect(),
|
||||||
.map(|user| user.to_child().name("reporter"))
|
)
|
||||||
.collect(),
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
)
|
IssueFieldId::Reporter,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
let reporter_field = StyledField::build()
|
let reporter_field = StyledField::build()
|
||||||
.input(reporter)
|
.input(reporter)
|
||||||
.label("Reporter")
|
.label("Reporter")
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let priority = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
let priority = StyledSelect::build()
|
||||||
IssueFieldId::Priority,
|
.name("priority")
|
||||||
)))
|
.opened(priority_state.opened)
|
||||||
.name("priority")
|
.empty()
|
||||||
.opened(priority_state.opened)
|
.text_filter(priority_state.text_filter.as_str())
|
||||||
.empty()
|
.options(
|
||||||
.text_filter(priority_state.text_filter.as_str())
|
IssuePriority::ordered()
|
||||||
.options(
|
.into_iter()
|
||||||
IssuePriority::ordered()
|
.map(|p| p.to_child().name("priority"))
|
||||||
.into_iter()
|
.collect(),
|
||||||
.map(|p| p.to_child().name("priority"))
|
)
|
||||||
.collect(),
|
.selected(vec![payload.priority.to_child().name("priority")])
|
||||||
)
|
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||||
.selected(vec![payload.priority.to_child().name("priority")])
|
IssueFieldId::Priority,
|
||||||
.build()
|
)))
|
||||||
.into_node();
|
.into_node();
|
||||||
let priority_field = StyledField::build()
|
let priority_field = StyledField::build()
|
||||||
.input(priority)
|
.input(priority)
|
||||||
.label("Priority")
|
.label("Priority")
|
||||||
|
@ -91,7 +91,7 @@ pub fn time_tracking_field(
|
|||||||
) -> Node<Msg> {
|
) -> Node<Msg> {
|
||||||
let input = match time_tracking_type {
|
let input = match time_tracking_type {
|
||||||
TimeTracking::Untracked => empty![],
|
TimeTracking::Untracked => empty![],
|
||||||
TimeTracking::Fibonacci => StyledSelect::build(field_id)
|
TimeTracking::Fibonacci => StyledSelect::build()
|
||||||
.selected(
|
.selected(
|
||||||
select_state
|
select_state
|
||||||
.values
|
.values
|
||||||
@ -106,12 +106,12 @@ pub fn time_tracking_field(
|
|||||||
.map(|v| v.to_child())
|
.map(|v| v.to_child())
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.build()
|
.build(field_id)
|
||||||
.into_node(),
|
.into_node(),
|
||||||
TimeTracking::Hourly => StyledInput::build(field_id)
|
TimeTracking::Hourly => StyledInput::build()
|
||||||
.state(input_state)
|
.state(input_state)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.build()
|
.build(field_id)
|
||||||
.into_node(),
|
.into_node(),
|
||||||
};
|
};
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
|
@ -26,11 +26,11 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let username = StyledInput::build(FieldId::Profile(UsersFieldId::Username))
|
let username = StyledInput::build()
|
||||||
.state(&page.name)
|
.state(&page.name)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.primary()
|
.primary()
|
||||||
.build()
|
.build(FieldId::Profile(UsersFieldId::Username))
|
||||||
.into_node();
|
.into_node();
|
||||||
let username_field = StyledField::build()
|
let username_field = StyledField::build()
|
||||||
.label("Username")
|
.label("Username")
|
||||||
@ -38,11 +38,11 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let email = StyledInput::build(FieldId::Profile(UsersFieldId::Username))
|
let email = StyledInput::build()
|
||||||
.state(&page.email)
|
.state(&page.email)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.primary()
|
.primary()
|
||||||
.build()
|
.build(FieldId::Profile(UsersFieldId::Username))
|
||||||
.into_node();
|
.into_node();
|
||||||
let email_field = StyledField::build()
|
let email_field = StyledField::build()
|
||||||
.label("E-Mail")
|
.label("E-Mail")
|
||||||
@ -97,7 +97,7 @@ fn build_current_project(model: &Model, page: &ProfilePage) -> Node<Msg> {
|
|||||||
joined_projects.insert(p.project_id, p);
|
joined_projects.insert(p.project_id, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledSelect::build(FieldId::Profile(UsersFieldId::CurrentProject))
|
StyledSelect::build()
|
||||||
.name("current_project")
|
.name("current_project")
|
||||||
.normal()
|
.normal()
|
||||||
.options(
|
.options(
|
||||||
@ -117,7 +117,7 @@ fn build_current_project(model: &Model, page: &ProfilePage) -> Node<Msg> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.state(&page.current_project)
|
.state(&page.current_project)
|
||||||
.build()
|
.build(FieldId::Profile(UsersFieldId::CurrentProject))
|
||||||
.into_node()
|
.into_node()
|
||||||
};
|
};
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
|
@ -61,11 +61,11 @@ fn project_board_filters(model: &Model) -> Node<Msg> {
|
|||||||
_ => return empty![],
|
_ => return empty![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let search_input = StyledInput::build(FieldId::TextFilterBoard)
|
let search_input = StyledInput::build()
|
||||||
.icon(Icon::Search)
|
.icon(Icon::Search)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.value(project_page.text_filter.as_str())
|
.value(project_page.text_filter.as_str())
|
||||||
.build()
|
.build(FieldId::TextFilterBoard)
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let only_my = StyledButton::build()
|
let only_my = StyledButton::build()
|
||||||
|
@ -44,17 +44,16 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
|
|
||||||
let category_field = category_field(page);
|
let category_field = category_field(page);
|
||||||
|
|
||||||
let time_tracking =
|
let time_tracking = StyledCheckbox::build()
|
||||||
StyledCheckbox::build(FieldId::ProjectSettings(ProjectFieldId::TimeTracking))
|
.options(vec![
|
||||||
.options(vec![
|
TimeTracking::Untracked.to_child(),
|
||||||
TimeTracking::Untracked.to_child(),
|
TimeTracking::Fibonacci.to_child(),
|
||||||
TimeTracking::Fibonacci.to_child(),
|
TimeTracking::Hourly.to_child(),
|
||||||
TimeTracking::Hourly.to_child(),
|
])
|
||||||
])
|
.state(&page.time_tracking)
|
||||||
.state(&page.time_tracking)
|
.add_class("timeTracking")
|
||||||
.add_class("timeTracking")
|
.build(FieldId::ProjectSettings(ProjectFieldId::TimeTracking))
|
||||||
.build()
|
.into_node();
|
||||||
.into_node();
|
|
||||||
let time_tracking_type: TimeTracking = page.time_tracking.value.into();
|
let time_tracking_type: TimeTracking = page.time_tracking.value.into();
|
||||||
let time_tracking_field = StyledField::build()
|
let time_tracking_field = StyledField::build()
|
||||||
.input(time_tracking)
|
.input(time_tracking)
|
||||||
@ -162,7 +161,7 @@ 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 category = StyledSelect::build(FieldId::ProjectSettings(ProjectFieldId::Category))
|
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)
|
||||||
@ -180,7 +179,7 @@ fn category_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_child()])
|
.to_child()])
|
||||||
.build()
|
.build(FieldId::ProjectSettings(ProjectFieldId::Category))
|
||||||
.into_node();
|
.into_node();
|
||||||
StyledField::build()
|
StyledField::build()
|
||||||
.label("Project Category")
|
.label("Project Category")
|
||||||
@ -242,12 +241,12 @@ fn add_column(page: &ProjectSettingsPage, column_style: &str) -> Node<Msg> {
|
|||||||
)))
|
)))
|
||||||
});
|
});
|
||||||
|
|
||||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
let input = StyledInput::build()
|
||||||
.state(&page.name)
|
.state(&page.name)
|
||||||
.primary()
|
.primary()
|
||||||
.auto_focus()
|
.auto_focus()
|
||||||
.on_input_ev(blur)
|
.on_input_ev(blur)
|
||||||
.build()
|
.build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
div![
|
div![
|
||||||
@ -277,12 +276,12 @@ fn column_preview(
|
|||||||
ProjectPageChange::EditIssueStatusName(None),
|
ProjectPageChange::EditIssueStatusName(None),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
let input = StyledInput::build()
|
||||||
.state(&page.name)
|
.state(&page.name)
|
||||||
.primary()
|
.primary()
|
||||||
.auto_focus()
|
.auto_focus()
|
||||||
.on_input_ev(blur)
|
.on_input_ev(blur)
|
||||||
.build()
|
.build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
div![class!["columnPreview"], div![class!["columnName"], input]]
|
div![class!["columnPreview"], div![class!["columnName"], input]]
|
||||||
|
@ -140,9 +140,8 @@ impl ToNode for StyledCheckbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StyledCheckbox {
|
impl StyledCheckbox {
|
||||||
pub fn build(field_id: FieldId) -> StyledCheckboxBuilder {
|
pub fn build() -> StyledCheckboxBuilder {
|
||||||
StyledCheckboxBuilder {
|
StyledCheckboxBuilder {
|
||||||
id: field_id,
|
|
||||||
options: vec![],
|
options: vec![],
|
||||||
selected: 0,
|
selected: 0,
|
||||||
class_list: vec![],
|
class_list: vec![],
|
||||||
@ -151,7 +150,6 @@ impl StyledCheckbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledCheckboxBuilder {
|
pub struct StyledCheckboxBuilder {
|
||||||
id: FieldId,
|
|
||||||
options: Vec<ChildBuilder>,
|
options: Vec<ChildBuilder>,
|
||||||
selected: u32,
|
selected: u32,
|
||||||
class_list: Vec<String>,
|
class_list: Vec<String>,
|
||||||
@ -176,9 +174,9 @@ impl StyledCheckboxBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledCheckbox {
|
pub fn build(self, field_id: FieldId) -> StyledCheckbox {
|
||||||
StyledCheckbox {
|
StyledCheckbox {
|
||||||
id: self.id,
|
id: field_id,
|
||||||
options: self.options,
|
options: self.options,
|
||||||
selected: self.selected,
|
selected: self.selected,
|
||||||
class_list: self.class_list,
|
class_list: self.class_list,
|
||||||
|
@ -81,9 +81,8 @@ pub struct StyledInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StyledInput {
|
impl StyledInput {
|
||||||
pub fn build(id: FieldId) -> StyledInputBuilder {
|
pub fn build() -> StyledInputBuilder {
|
||||||
StyledInputBuilder {
|
StyledInputBuilder {
|
||||||
id,
|
|
||||||
icon: None,
|
icon: None,
|
||||||
valid: None,
|
valid: None,
|
||||||
value: None,
|
value: None,
|
||||||
@ -99,7 +98,6 @@ impl StyledInput {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledInputBuilder {
|
pub struct StyledInputBuilder {
|
||||||
id: FieldId,
|
|
||||||
icon: Option<Icon>,
|
icon: Option<Icon>,
|
||||||
valid: Option<bool>,
|
valid: Option<bool>,
|
||||||
value: Option<String>,
|
value: Option<String>,
|
||||||
@ -166,9 +164,9 @@ impl StyledInputBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledInput {
|
pub fn build(self, id: FieldId) -> StyledInput {
|
||||||
StyledInput {
|
StyledInput {
|
||||||
id: self.id,
|
id,
|
||||||
icon: self.icon,
|
icon: self.icon,
|
||||||
valid: self.valid.unwrap_or_default(),
|
valid: self.valid.unwrap_or_default(),
|
||||||
value: self.value,
|
value: self.value,
|
||||||
|
@ -2,9 +2,10 @@ use seed::{prelude::*, *};
|
|||||||
|
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
|
use crate::shared::styled_select::{StyledSelect, StyledSelectState};
|
||||||
use crate::shared::styled_tooltip::StyledTooltip;
|
use crate::shared::styled_tooltip::StyledTooltip;
|
||||||
use crate::shared::ToNode;
|
use crate::shared::{ToChild, ToNode};
|
||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg, RteField};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum HeadingSize {
|
pub enum HeadingSize {
|
||||||
@ -80,6 +81,7 @@ pub enum RteMsg {
|
|||||||
|
|
||||||
// code
|
// code
|
||||||
InsertCode(bool),
|
InsertCode(bool),
|
||||||
|
CodeSetLang(String),
|
||||||
|
|
||||||
RequestFocus(uuid::Uuid),
|
RequestFocus(uuid::Uuid),
|
||||||
}
|
}
|
||||||
@ -146,6 +148,7 @@ impl RteMsg {
|
|||||||
RteMsg::InsertTable { .. } => None,
|
RteMsg::InsertTable { .. } => None,
|
||||||
// code
|
// code
|
||||||
RteMsg::InsertCode(_) => None,
|
RteMsg::InsertCode(_) => None,
|
||||||
|
RteMsg::CodeSetLang(_) => None,
|
||||||
|
|
||||||
// indent
|
// indent
|
||||||
RteMsg::ChangeIndent(RteIndentMsg::Increase) => Some(ExecCommand::new("indent")),
|
RteMsg::ChangeIndent(RteIndentMsg::Increase) => Some(ExecCommand::new("indent")),
|
||||||
@ -181,7 +184,7 @@ pub struct StyledRteTableState {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StyledRteCodeState {
|
pub struct StyledRteCodeState {
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
pub lang: String,
|
pub lang: StyledSelectState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -197,7 +200,7 @@ pub struct StyledRteState {
|
|||||||
impl StyledRteState {
|
impl StyledRteState {
|
||||||
pub fn new(field_id: FieldId) -> Self {
|
pub fn new(field_id: FieldId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
field_id,
|
field_id: field_id.clone(),
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
table_tooltip: StyledRteTableState {
|
table_tooltip: StyledRteTableState {
|
||||||
visible: false,
|
visible: false,
|
||||||
@ -206,7 +209,10 @@ impl StyledRteState {
|
|||||||
},
|
},
|
||||||
code_tooltip: StyledRteCodeState {
|
code_tooltip: StyledRteCodeState {
|
||||||
visible: false,
|
visible: false,
|
||||||
lang: "".to_string(),
|
lang: StyledSelectState::new(
|
||||||
|
FieldId::Rte(RteField::CodeLang(Box::new(field_id.clone()))),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
range: None,
|
range: None,
|
||||||
identifier: uuid::Uuid::new_v4(),
|
identifier: uuid::Uuid::new_v4(),
|
||||||
@ -214,6 +220,7 @@ 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);
|
||||||
let m = match msg {
|
let m = match msg {
|
||||||
Msg::Rte(m, field) if field == &self.field_id => m,
|
Msg::Rte(m, field) if field == &self.field_id => m,
|
||||||
_ => return,
|
_ => return,
|
||||||
@ -235,12 +242,14 @@ impl StyledRteState {
|
|||||||
self.schedule_focus(orders);
|
self.schedule_focus(orders);
|
||||||
}
|
}
|
||||||
_ => match m {
|
_ => match m {
|
||||||
|
// code
|
||||||
RteMsg::InsertCode(b) => {
|
RteMsg::InsertCode(b) => {
|
||||||
if *b {
|
if *b {
|
||||||
self.store_range();
|
self.store_range();
|
||||||
}
|
}
|
||||||
self.code_tooltip.visible = *b;
|
self.code_tooltip.visible = *b;
|
||||||
}
|
}
|
||||||
|
// table
|
||||||
RteMsg::TableSetRows(n) => {
|
RteMsg::TableSetRows(n) => {
|
||||||
self.table_tooltip.rows = *n;
|
self.table_tooltip.rows = *n;
|
||||||
}
|
}
|
||||||
@ -329,19 +338,24 @@ pub struct StyledRte {
|
|||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
table_tooltip: StyledRteTableState,
|
table_tooltip: StyledRteTableState,
|
||||||
identifier: Option<uuid::Uuid>,
|
identifier: Option<uuid::Uuid>,
|
||||||
|
code_tooltip: StyledRteCodeState,
|
||||||
// value: String,
|
// value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledRte {
|
impl StyledRte {
|
||||||
pub fn build(field_id: FieldId) -> StyledRteBuilder {
|
pub fn build(field_id: FieldId) -> StyledRteBuilder {
|
||||||
StyledRteBuilder {
|
StyledRteBuilder {
|
||||||
field_id,
|
field_id: field_id.clone(),
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
table_tooltip: StyledRteTableState {
|
table_tooltip: StyledRteTableState {
|
||||||
visible: false,
|
visible: false,
|
||||||
rows: 0,
|
rows: 0,
|
||||||
cols: 0,
|
cols: 0,
|
||||||
},
|
},
|
||||||
|
code_tooltip: StyledRteCodeState {
|
||||||
|
visible: false,
|
||||||
|
lang: StyledSelectState::new(field_id.clone(), vec![]),
|
||||||
|
},
|
||||||
identifier: None,
|
identifier: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,6 +371,7 @@ pub struct StyledRteBuilder {
|
|||||||
field_id: FieldId,
|
field_id: FieldId,
|
||||||
value: String,
|
value: String,
|
||||||
table_tooltip: StyledRteTableState,
|
table_tooltip: StyledRteTableState,
|
||||||
|
code_tooltip: StyledRteCodeState,
|
||||||
identifier: Option<uuid::Uuid>,
|
identifier: Option<uuid::Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +379,8 @@ impl StyledRteBuilder {
|
|||||||
pub fn state(mut self, state: &StyledRteState) -> Self {
|
pub fn state(mut self, state: &StyledRteState) -> Self {
|
||||||
self.value = state.value.clone();
|
self.value = state.value.clone();
|
||||||
self.table_tooltip = state.table_tooltip.clone();
|
self.table_tooltip = state.table_tooltip.clone();
|
||||||
self.identifier = Some(state.identifier.clone());
|
self.code_tooltip = state.code_tooltip.clone();
|
||||||
|
self.identifier = Some(state.identifier);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,6 +390,7 @@ impl StyledRteBuilder {
|
|||||||
// value: self.value,
|
// value: self.value,
|
||||||
table_tooltip: self.table_tooltip,
|
table_tooltip: self.table_tooltip,
|
||||||
identifier: self.identifier,
|
identifier: self.identifier,
|
||||||
|
code_tooltip: self.code_tooltip,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,42 +640,55 @@ fn first_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
Some(Msg::Rte(RteMsg::Italic, field_id))
|
Some(Msg::Rte(RteMsg::Italic, field_id))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let field_id = values.field_id.clone();
|
|
||||||
let underline_button = styled_rte_button(
|
let underline_button = {
|
||||||
"Underline",
|
let field_id = values.field_id.clone();
|
||||||
Icon::Underline,
|
styled_rte_button(
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
"Underline",
|
||||||
ev.prevent_default();
|
Icon::Underline,
|
||||||
Some(Msg::Rte(RteMsg::Underscore, field_id))
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
}),
|
ev.prevent_default();
|
||||||
);
|
Some(Msg::Rte(RteMsg::Underscore, field_id))
|
||||||
let field_id = values.field_id.clone();
|
}),
|
||||||
let strike_through_button = styled_rte_button(
|
)
|
||||||
"StrikeThrough",
|
};
|
||||||
Icon::StrikeThrough,
|
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
let strike_through_button = {
|
||||||
ev.prevent_default();
|
let field_id = values.field_id.clone();
|
||||||
Some(Msg::Rte(RteMsg::Strikethrough, field_id))
|
styled_rte_button(
|
||||||
}),
|
"StrikeThrough",
|
||||||
);
|
Icon::StrikeThrough,
|
||||||
let field_id = values.field_id.clone();
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
let subscript_button = styled_rte_button(
|
ev.prevent_default();
|
||||||
"Subscript",
|
Some(Msg::Rte(RteMsg::Strikethrough, field_id))
|
||||||
Icon::Subscript,
|
}),
|
||||||
mouse_ev(Ev::Click, move |ev| {
|
)
|
||||||
ev.prevent_default();
|
};
|
||||||
Some(Msg::Rte(RteMsg::Subscript, field_id))
|
|
||||||
}),
|
let subscript_button = {
|
||||||
);
|
let field_id = values.field_id.clone();
|
||||||
let field_id = values.field_id.clone();
|
styled_rte_button(
|
||||||
let superscript_button = styled_rte_button(
|
"Subscript",
|
||||||
"Superscript",
|
Icon::Subscript,
|
||||||
Icon::Superscript,
|
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(RteMsg::Superscript, field_id))
|
}),
|
||||||
}),
|
)
|
||||||
);
|
};
|
||||||
|
|
||||||
|
let superscript_button = {
|
||||||
|
let field_id = values.field_id.clone();
|
||||||
|
styled_rte_button(
|
||||||
|
"Superscript",
|
||||||
|
Icon::Superscript,
|
||||||
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
Some(Msg::Rte(RteMsg::Superscript, field_id))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
div![
|
div![
|
||||||
class!["group formatting"],
|
class!["group formatting"],
|
||||||
bold_button,
|
bold_button,
|
||||||
@ -821,7 +851,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let code_alt_button = {
|
let mut code_alt_button = {
|
||||||
let field_id = values.field_id.clone();
|
let field_id = values.field_id.clone();
|
||||||
styled_rte_button(
|
styled_rte_button(
|
||||||
"Insert code",
|
"Insert code",
|
||||||
@ -832,6 +862,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
code_alt_button.add_child(code_tooltip(values));
|
||||||
|
|
||||||
div![
|
div![
|
||||||
class!["group insert"],
|
class!["group insert"],
|
||||||
@ -887,33 +918,46 @@ fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
|||||||
rows,
|
rows,
|
||||||
cols,
|
cols,
|
||||||
} = values.table_tooltip;
|
} = values.table_tooltip;
|
||||||
let field_id = values.field_id.clone();
|
|
||||||
let on_rows_change = input_ev(Ev::Change, move |v| {
|
let on_rows_change = {
|
||||||
v.parse::<u16>()
|
let field_id = values.field_id.clone();
|
||||||
.ok()
|
input_ev(Ev::Change, move |v| {
|
||||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
v.parse::<u16>()
|
||||||
});
|
.ok()
|
||||||
let field_id = values.field_id.clone();
|
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
||||||
let on_cols_change = input_ev(Ev::Change, move |v| {
|
})
|
||||||
v.parse::<u16>()
|
};
|
||||||
.ok()
|
|
||||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
let on_cols_change = {
|
||||||
});
|
let field_id = values.field_id.clone();
|
||||||
let field_id = values.field_id.clone();
|
input_ev(Ev::Change, move |v| {
|
||||||
let close_table_tooltip = StyledButton::build()
|
v.parse::<u16>()
|
||||||
.empty()
|
.ok()
|
||||||
.icon(Icon::Close)
|
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
||||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let close_table_tooltip = {
|
||||||
|
let field_id = values.field_id.clone();
|
||||||
|
StyledButton::build()
|
||||||
|
.empty()
|
||||||
|
.icon(Icon::Close)
|
||||||
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
Some(Msg::Rte(RteMsg::TableSetVisibility(false), field_id))
|
||||||
|
}))
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_submit = {
|
||||||
|
let field_id = values.field_id.clone();
|
||||||
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
Some(Msg::Rte(RteMsg::TableSetVisibility(false), field_id))
|
Some(Msg::Rte(RteMsg::InsertTable { rows, cols }, field_id))
|
||||||
}))
|
})
|
||||||
.build()
|
};
|
||||||
.into_node();
|
|
||||||
let field_id = values.field_id.clone();
|
|
||||||
let on_submit = mouse_ev(Ev::Click, move |ev| {
|
|
||||||
ev.prevent_default();
|
|
||||||
Some(Msg::Rte(RteMsg::InsertTable { rows, cols }, field_id))
|
|
||||||
});
|
|
||||||
StyledTooltip::build()
|
StyledTooltip::build()
|
||||||
.table_tooltip()
|
.table_tooltip()
|
||||||
.visible(visible)
|
.visible(visible)
|
||||||
@ -963,19 +1007,59 @@ fn styled_rte_button(title: &str, icon: Icon, handler: EventHandler<Msg>) -> Nod
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_code() -> Node<Msg> {
|
fn code_tooltip(values: &StyledRte) -> Node<Msg> {
|
||||||
|
let StyledRteCodeState { visible, lang } = &values.code_tooltip;
|
||||||
|
|
||||||
let mut languages: Vec<&str> = crate::hi::SYNTAX_SET
|
let mut languages: Vec<&str> = crate::hi::SYNTAX_SET
|
||||||
.syntaxes()
|
.syntaxes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.name.as_str())
|
.map(|s| s.name.as_str())
|
||||||
.collect();
|
.collect();
|
||||||
languages.sort();
|
languages.sort();
|
||||||
let options: Vec<Node<Msg>> = languages
|
|
||||||
|
let options: Vec<(String, u32)> = languages
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| option![attrs![At::Value => name], name])
|
.enumerate()
|
||||||
|
.map(|(idx, label)| (label.to_string(), idx as u32))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
seed::select![options]
|
let select_lang_node = StyledSelect::build()
|
||||||
|
.state(lang)
|
||||||
|
.selected(
|
||||||
|
lang.values
|
||||||
|
.get(0)
|
||||||
|
.and_then(|n| options.get(*n as usize))
|
||||||
|
.map(|v| vec![v.to_child()])
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
.options(options.into_iter().map(|opt| opt.to_child()).collect())
|
||||||
|
.normal()
|
||||||
|
.build(FieldId::Rte(RteField::CodeLang(Box::new(
|
||||||
|
values.field_id.clone(),
|
||||||
|
))))
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
let close_tooltip = {
|
||||||
|
let field_id = values.field_id.clone();
|
||||||
|
StyledButton::build()
|
||||||
|
.empty()
|
||||||
|
.icon(Icon::Close)
|
||||||
|
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||||
|
ev.prevent_default();
|
||||||
|
Some(Msg::Rte(RteMsg::InsertCode(false), field_id))
|
||||||
|
}))
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
|
};
|
||||||
|
|
||||||
|
StyledTooltip::build()
|
||||||
|
.code_tooltip()
|
||||||
|
.visible(*visible)
|
||||||
|
.add_child(h2!["Insert Code", close_tooltip])
|
||||||
|
.add_child(select_lang_node)
|
||||||
|
.add_child(seed::textarea![])
|
||||||
|
.build()
|
||||||
|
.into_node()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn code_to_tag(code: &str) -> Node<Msg> {
|
pub fn code_to_tag(code: &str) -> Node<Msg> {
|
||||||
|
@ -61,9 +61,10 @@ 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;
|
||||||
match msg {
|
match msg {
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::DropDownVisibility(b))
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::DropDownVisibility(b))
|
||||||
if *field_id == self.field_id =>
|
if field_id == id =>
|
||||||
{
|
{
|
||||||
self.opened = *b;
|
self.opened = *b;
|
||||||
if !self.opened {
|
if !self.opened {
|
||||||
@ -71,22 +72,22 @@ impl StyledSelectState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Text(text))
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::Text(text))
|
||||||
if *field_id == self.field_id =>
|
if field_id == id =>
|
||||||
{
|
{
|
||||||
self.text_filter = text.clone();
|
self.text_filter = text.clone();
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(v)))
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(v)))
|
||||||
if field_id == &self.field_id =>
|
if field_id == id =>
|
||||||
{
|
{
|
||||||
self.values = vec![*v];
|
self.values = vec![*v];
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
||||||
if field_id == &self.field_id =>
|
if field_id == id =>
|
||||||
{
|
{
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
}
|
}
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(v))
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(v))
|
||||||
if field_id == &self.field_id =>
|
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);
|
||||||
@ -123,9 +124,8 @@ impl ToNode for StyledSelect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelect {
|
impl StyledSelect {
|
||||||
pub fn build(id: FieldId) -> StyledSelectBuilder {
|
pub fn build() -> StyledSelectBuilder {
|
||||||
StyledSelectBuilder {
|
StyledSelectBuilder {
|
||||||
id,
|
|
||||||
variant: None,
|
variant: None,
|
||||||
dropdown_width: None,
|
dropdown_width: None,
|
||||||
name: None,
|
name: None,
|
||||||
@ -142,7 +142,6 @@ impl StyledSelect {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StyledSelectBuilder {
|
pub struct StyledSelectBuilder {
|
||||||
id: FieldId,
|
|
||||||
variant: Option<Variant>,
|
variant: Option<Variant>,
|
||||||
dropdown_width: Option<usize>,
|
dropdown_width: Option<usize>,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
@ -156,9 +155,9 @@ pub struct StyledSelectBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StyledSelectBuilder {
|
impl StyledSelectBuilder {
|
||||||
pub fn build(self) -> StyledSelect {
|
pub fn build(self, id: FieldId) -> StyledSelect {
|
||||||
StyledSelect {
|
StyledSelect {
|
||||||
id: self.id,
|
id,
|
||||||
variant: self.variant.unwrap_or_default(),
|
variant: self.variant.unwrap_or_default(),
|
||||||
dropdown_width: self.dropdown_width,
|
dropdown_width: self.dropdown_width,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
@ -278,12 +277,14 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let action_icon = if clearable && !selected.is_empty() {
|
let action_icon = if clearable && !selected.is_empty() {
|
||||||
let field_id = id.clone();
|
let on_click = {
|
||||||
let on_click = mouse_ev(Ev::Click, move |ev| {
|
let field_id = id.clone();
|
||||||
ev.stop_propagation();
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
ev.prevent_default();
|
ev.stop_propagation();
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
ev.prevent_default();
|
||||||
});
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
||||||
|
})
|
||||||
|
};
|
||||||
StyledIcon::build(Icon::Close)
|
StyledIcon::build(Icon::Close)
|
||||||
.add_class("chevronIcon")
|
.add_class("chevronIcon")
|
||||||
.on_click(on_click)
|
.on_click(on_click)
|
||||||
@ -305,10 +306,13 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
|||||||
let child = child.build(DisplayType::SelectOption);
|
let child = child.build(DisplayType::SelectOption);
|
||||||
let value = child.value();
|
let value = child.value();
|
||||||
let node = child.into_node();
|
let node = child.into_node();
|
||||||
let field_id = id.clone();
|
|
||||||
let on_change = mouse_ev(Ev::Click, move |_| {
|
let on_change = {
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(value)))
|
let field_id = id.clone();
|
||||||
});
|
mouse_ev(Ev::Click, move |_| {
|
||||||
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(value)))
|
||||||
|
})
|
||||||
|
};
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "option"],
|
attrs![At::Class => "option"],
|
||||||
on_change,
|
on_change,
|
||||||
@ -386,7 +390,7 @@ fn render_value(mut content: Node<Msg>) -> Node<Msg> {
|
|||||||
content
|
content
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_multi_value(opt: StyledSelectChildBuilder, field_id: FieldId) -> Node<Msg> {
|
fn into_multi_value(opt: StyledSelectChildBuilder, id: FieldId) -> Node<Msg> {
|
||||||
let close_icon = StyledIcon::build(Icon::Close).size(14).build().into_node();
|
let close_icon = StyledIcon::build(Icon::Close).size(14).build().into_node();
|
||||||
let child = opt.build(DisplayType::SelectValue);
|
let child = opt.build(DisplayType::SelectValue);
|
||||||
let value = child.value();
|
let value = child.value();
|
||||||
@ -395,10 +399,13 @@ fn into_multi_value(opt: StyledSelectChildBuilder, field_id: FieldId) -> Node<Ms
|
|||||||
opt.add_class("value");
|
opt.add_class("value");
|
||||||
opt.add_child(close_icon);
|
opt.add_child(close_icon);
|
||||||
|
|
||||||
let handler = mouse_ev(Ev::Click, move |ev| {
|
let handler = {
|
||||||
ev.stop_propagation();
|
let field_id = id.clone();
|
||||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(value))
|
mouse_ev(Ev::Click, move |ev| {
|
||||||
});
|
ev.stop_propagation();
|
||||||
|
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(value))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
div![attrs![At::Class => "valueMultiItem"], opt, handler]
|
div![attrs![At::Class => "valueMultiItem"], opt, handler]
|
||||||
}
|
}
|
||||||
|
@ -301,3 +301,16 @@ impl ToChild for u32 {
|
|||||||
.value(*self)
|
.value(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Label = String;
|
||||||
|
pub type Value = u32;
|
||||||
|
|
||||||
|
impl ToChild for (Label, Value) {
|
||||||
|
type Builder = StyledSelectChildBuilder;
|
||||||
|
|
||||||
|
fn to_child(&self) -> Self::Builder {
|
||||||
|
StyledSelectChild::build()
|
||||||
|
.text(self.0.as_str())
|
||||||
|
.value(self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ pub enum Variant {
|
|||||||
About,
|
About,
|
||||||
Messages,
|
Messages,
|
||||||
TableBuilder,
|
TableBuilder,
|
||||||
|
CodeBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Variant {
|
impl Default for Variant {
|
||||||
@ -22,6 +23,7 @@ impl std::fmt::Display for Variant {
|
|||||||
Variant::About => f.write_str("about"),
|
Variant::About => f.write_str("about"),
|
||||||
Variant::Messages => f.write_str("messages"),
|
Variant::Messages => f.write_str("messages"),
|
||||||
Variant::TableBuilder => f.write_str("tableTooltip"),
|
Variant::TableBuilder => f.write_str("tableTooltip"),
|
||||||
|
Variant::CodeBuilder => f.write_str("codeTooltip"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +89,11 @@ impl StyledTooltipBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn code_tooltip(mut self) -> Self {
|
||||||
|
self.variant = Variant::CodeBuilder;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledTooltip {
|
pub fn build(self) -> StyledTooltip {
|
||||||
StyledTooltip {
|
StyledTooltip {
|
||||||
visible: self.visible,
|
visible: self.visible,
|
||||||
|
@ -92,10 +92,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
_ => return empty![],
|
_ => return empty![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let username = StyledInput::build(FieldId::SignIn(SignInFieldId::Username))
|
let username = StyledInput::build()
|
||||||
.value(page.username.as_str())
|
.value(page.username.as_str())
|
||||||
.valid(!page.username_touched || page.username.len() > 1)
|
.valid(!page.username_touched || page.username.len() > 1)
|
||||||
.build()
|
.build(FieldId::SignIn(SignInFieldId::Username))
|
||||||
.into_node();
|
.into_node();
|
||||||
let username_field = StyledField::build()
|
let username_field = StyledField::build()
|
||||||
.label("Username")
|
.label("Username")
|
||||||
@ -103,10 +103,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let email = StyledInput::build(FieldId::SignIn(SignInFieldId::Email))
|
let email = StyledInput::build()
|
||||||
.value(page.email.as_str())
|
.value(page.email.as_str())
|
||||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||||
.build()
|
.build(FieldId::SignIn(SignInFieldId::Email))
|
||||||
.into_node();
|
.into_node();
|
||||||
let email_field = StyledField::build()
|
let email_field = StyledField::build()
|
||||||
.label("E-Mail")
|
.label("E-Mail")
|
||||||
@ -164,10 +164,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let token = StyledInput::build(FieldId::SignIn(SignInFieldId::Token))
|
let token = StyledInput::build()
|
||||||
.value(page.token.as_str())
|
.value(page.token.as_str())
|
||||||
.valid(!page.token_touched || is_token(page.token.as_str()))
|
.valid(!page.token_touched || is_token(page.token.as_str()))
|
||||||
.build()
|
.build(FieldId::SignIn(SignInFieldId::Token))
|
||||||
.into_node();
|
.into_node();
|
||||||
let token_field = StyledField::build()
|
let token_field = StyledField::build()
|
||||||
.label("Single use token")
|
.label("Single use token")
|
||||||
|
@ -68,10 +68,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
_ => return empty![],
|
_ => return empty![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let username = StyledInput::build(FieldId::SignUp(SignUpFieldId::Username))
|
let username = StyledInput::build()
|
||||||
.value(page.username.as_str())
|
.value(page.username.as_str())
|
||||||
.valid(!page.username_touched || page.username.len() > 1)
|
.valid(!page.username_touched || page.username.len() > 1)
|
||||||
.build()
|
.build(FieldId::SignUp(SignUpFieldId::Username))
|
||||||
.into_node();
|
.into_node();
|
||||||
let username_field = StyledField::build()
|
let username_field = StyledField::build()
|
||||||
.label("Username")
|
.label("Username")
|
||||||
@ -79,10 +79,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let email = StyledInput::build(FieldId::SignUp(SignUpFieldId::Email))
|
let email = StyledInput::build()
|
||||||
.value(page.email.as_str())
|
.value(page.email.as_str())
|
||||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||||
.build()
|
.build(FieldId::SignUp(SignUpFieldId::Email))
|
||||||
.into_node();
|
.into_node();
|
||||||
let email_field = StyledField::build()
|
let email_field = StyledField::build()
|
||||||
.label("E-Mail")
|
.label("E-Mail")
|
||||||
|
@ -22,10 +22,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
_ => return empty![],
|
_ => return empty![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = StyledInput::build(FieldId::Users(UsersFieldId::Username))
|
let name = StyledInput::build()
|
||||||
.valid(!page.name_touched || page.name.len() >= 3)
|
.valid(!page.name_touched || page.name.len() >= 3)
|
||||||
.value(page.name.as_str())
|
.value(page.name.as_str())
|
||||||
.build()
|
.build(FieldId::Users(UsersFieldId::Username))
|
||||||
.into_node();
|
.into_node();
|
||||||
let name_field = StyledField::build()
|
let name_field = StyledField::build()
|
||||||
.input(name)
|
.input(name)
|
||||||
@ -33,10 +33,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let email = StyledInput::build(FieldId::Users(UsersFieldId::Email))
|
let email = StyledInput::build()
|
||||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||||
.value(page.email.as_str())
|
.value(page.email.as_str())
|
||||||
.build()
|
.build(FieldId::Users(UsersFieldId::Email))
|
||||||
.into_node();
|
.into_node();
|
||||||
let email_field = StyledField::build()
|
let email_field = StyledField::build()
|
||||||
.input(email)
|
.input(email)
|
||||||
@ -44,7 +44,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let user_role = StyledSelect::build(FieldId::Users(UsersFieldId::UserRole))
|
let user_role = StyledSelect::build()
|
||||||
.name("user_role")
|
.name("user_role")
|
||||||
.valid(true)
|
.valid(true)
|
||||||
.normal()
|
.normal()
|
||||||
@ -56,7 +56,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
.map(|role| role.to_child())
|
.map(|role| role.to_child())
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.build()
|
.build(FieldId::Users(UsersFieldId::UserRole))
|
||||||
.into_node();
|
.into_node();
|
||||||
let user_role_field = StyledField::build()
|
let user_role_field = StyledField::build()
|
||||||
.input(user_role)
|
.input(user_role)
|
||||||
|
Loading…
Reference in New Issue
Block a user