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"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
@ -1790,10 +1790,10 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd02480f8dcf48798e62113974d6ccca2129a51d241fa20f1ea349c8a42559d5"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"email",
|
||||
"lettre",
|
||||
"mime",
|
||||
"base64 0.10.1",
|
||||
"email",
|
||||
"lettre",
|
||||
"mime",
|
||||
"time 0.1.43",
|
||||
"uuid 0.7.4",
|
||||
]
|
||||
@ -3078,7 +3078,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"walkdir",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3090,9 +3089,9 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi 0.3.9",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3768,15 +3767,6 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "zeroize"
|
||||
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] Basic Rich Text Editor
|
||||
* [ ] Insert Code in Rich Text Editor
|
||||
* [X] Code syntax
|
||||
* [ ] Personal settings to choose MDE (Markdown Editor) or RTE
|
||||
* [ ] Issues and filters view
|
||||
* [ ] Issues and filters working filters
|
||||
@ -180,3 +181,159 @@ sudo nginx -s reload
|
||||
## Issue trackers
|
||||
|
||||
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 = "*"
|
||||
|
||||
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]
|
||||
version = "0.2.66"
|
||||
|
@ -161,3 +161,54 @@
|
||||
display: flex;
|
||||
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">
|
||||
<title>JIRS</title>
|
||||
<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>
|
||||
<body>
|
||||
<main id="app"></main>
|
||||
|
@ -1,5 +1,6 @@
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::{FontStyle, Style};
|
||||
use syntect::parsing::SyntaxReference;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(final, js_name = JirsCodeBuilder)]
|
||||
@ -14,13 +15,22 @@ impl JirsCodeBuilder {
|
||||
}
|
||||
|
||||
#[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) {
|
||||
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 tokens = h.highlight(line, &crate::hi::SYNTAX_SET);
|
||||
|
||||
@ -36,8 +46,10 @@ impl JirsCodeBuilder {
|
||||
"font-weight: bold"
|
||||
} else if font_style == FontStyle::ITALIC {
|
||||
"font-style: italic"
|
||||
} else if font_style == FontStyle::UNDERLINE {
|
||||
"text-decoration: underline"
|
||||
} else {
|
||||
"font-decoration: underline"
|
||||
""
|
||||
};
|
||||
let f = format!("rgba({}, {}, {}, {})", f.r, f.g, f.b, f.a);
|
||||
let b = format!("rgba({}, {}, {}, {})", b.r, b.g, b.b, b.a);
|
||||
@ -55,71 +67,225 @@ impl JirsCodeBuilder {
|
||||
}
|
||||
|
||||
pub fn define() {
|
||||
create_custom_element(
|
||||
"jirs-code-view",
|
||||
"JirsCodeView",
|
||||
r#"
|
||||
<style>
|
||||
:host { display: block; border: 1px solid black; background: rgba(43, 48, 59, 255); padding: 1rem; }
|
||||
{
|
||||
let el_name = "JirsCodeView";
|
||||
let tag = "jirs-code-view";
|
||||
|
||||
ElementBuilder::default()
|
||||
.identifier(el_name, tag)
|
||||
.runtime("JirsCodeBuilder")
|
||||
.body(
|
||||
r#"
|
||||
<style>
|
||||
:host { display: block; border: 1px solid black; }
|
||||
: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>
|
||||
<div id='file-name'></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) {
|
||||
let source = format!(
|
||||
r#"
|
||||
trait ToJs {
|
||||
fn to_js(&self) -> String;
|
||||
}
|
||||
|
||||
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 {{
|
||||
static RUNTIME = Symbol();
|
||||
static SHADOW = Symbol();
|
||||
|
||||
static get observedAttributes() {{ return ['lang']; }}
|
||||
{observe}
|
||||
|
||||
constructor() {{
|
||||
super();
|
||||
this[ {name} . SHADOW] = this.attachShadow({{ 'mode': 'closed' }});
|
||||
this[ {name} . SHADOW].innerHTML = `{html}`;
|
||||
{shadow} = this.attachShadow({{ 'mode': 'closed' }});
|
||||
{shadow}.innerHTML = `{html}`;
|
||||
}}
|
||||
|
||||
connectedCallback() {{
|
||||
this[ {name} . RUNTIME] = new JirsCodeBuilder();
|
||||
const view = this[ {name} . SHADOW].querySelector('#view');
|
||||
view.innerHTML = '';
|
||||
const lang = this.getAttribute('lang') || '';
|
||||
const runtime = {runtime} = new JirsCodeBuilder();
|
||||
const shadow = {shadow};
|
||||
|
||||
setTimeout(() => {{
|
||||
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);
|
||||
{on_connected}
|
||||
}}
|
||||
|
||||
disconnectedCallback() {{
|
||||
this[ {name} . RUNTIME].free();
|
||||
{runtime}.free();
|
||||
}}
|
||||
|
||||
attributeChangedCallback(name, oldV, newV) {{
|
||||
const runtime = {runtime};
|
||||
const shadow = {shadow};
|
||||
{attr_body}
|
||||
}}
|
||||
}}
|
||||
customElements.define( '{tag}', {name});
|
||||
"#,
|
||||
name = name,
|
||||
tag = tag,
|
||||
html = html,
|
||||
);
|
||||
{
|
||||
name = self.name,
|
||||
tag = self.tag,
|
||||
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::*;
|
||||
match js_sys::eval(source.as_str()) {
|
||||
Ok(_v) => (),
|
||||
Err(e) => error!(e),
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,11 @@ pub enum EditIssueModalSection {
|
||||
Comment(CommentFieldId),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum RteField {
|
||||
CodeLang(Box<FieldId>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
|
||||
pub enum FieldId {
|
||||
SignIn(SignInFieldId),
|
||||
@ -26,6 +31,7 @@ pub enum FieldId {
|
||||
CopyButtonLabel,
|
||||
|
||||
ProjectSettings(ProjectFieldId),
|
||||
Rte(RteField),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FieldId {
|
||||
@ -120,6 +126,7 @@ impl std::fmt::Display for FieldId {
|
||||
UsersFieldId::Avatar => f.write_str("profile-avatar"),
|
||||
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> {
|
||||
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())
|
||||
.value(page.token.as_str())
|
||||
.build()
|
||||
.build(FieldId::Invite(InviteFieldId::Token))
|
||||
.into_node();
|
||||
|
||||
StyledField::build()
|
||||
|
@ -191,6 +191,7 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
styled_tooltip::Variant::Messages => {
|
||||
model.messages_tooltip_visible = !model.messages_tooltip_visible;
|
||||
}
|
||||
styled_tooltip::Variant::CodeBuilder => {}
|
||||
Variant::TableBuilder => {}
|
||||
},
|
||||
_ => (),
|
||||
|
@ -23,7 +23,7 @@ where
|
||||
.and_then(|id| model.epics.iter().find(|epic| epic.id == id as EpicId))
|
||||
.map(|epic| vec![epic.to_child()])
|
||||
.unwrap_or_default();
|
||||
let input = StyledSelect::build(field_id)
|
||||
let input = StyledSelect::build()
|
||||
.name("epic")
|
||||
.selected(selected)
|
||||
.options(model.epics.iter().map(|epic| epic.to_child()).collect())
|
||||
@ -32,7 +32,7 @@ where
|
||||
.text_filter(modal.epic_state().text_filter.as_str())
|
||||
.opened(modal.epic_state().opened)
|
||||
.valid(true)
|
||||
.build()
|
||||
.build(field_id)
|
||||
.into_node();
|
||||
Some(
|
||||
StyledField::build()
|
||||
|
@ -300,7 +300,7 @@ pub fn view(model: &Model, 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")
|
||||
.normal()
|
||||
.text_filter(modal.type_state.text_filter.as_str())
|
||||
@ -317,7 +317,7 @@ fn issue_type_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||
)
|
||||
.to_child()
|
||||
.name("type")])
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Type))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.label("Issue Type")
|
||||
@ -328,9 +328,9 @@ fn issue_type_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)
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.label("Short Summary")
|
||||
@ -359,7 +359,7 @@ fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||
.reporter_id
|
||||
.or_else(|| model.user.as_ref().map(|u| u.id))
|
||||
.unwrap_or_default();
|
||||
let reporter = StyledSelect::build(FieldId::AddIssueModal(IssueFieldId::Reporter))
|
||||
let reporter = StyledSelect::build()
|
||||
.normal()
|
||||
.text_filter(modal.reporter_state.text_filter.as_str())
|
||||
.opened(modal.reporter_state.opened)
|
||||
@ -384,7 +384,7 @@ fn reporter_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||
.collect(),
|
||||
)
|
||||
.valid(true)
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Reporter))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.input(reporter)
|
||||
@ -395,7 +395,7 @@ fn reporter_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()
|
||||
.multi()
|
||||
.text_filter(modal.assignees_state.text_filter.as_str())
|
||||
@ -421,7 +421,7 @@ fn assignees_field(model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||
.collect(),
|
||||
)
|
||||
.valid(true)
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Assignees))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.input(assignees)
|
||||
@ -432,7 +432,7 @@ fn assignees_field(model: &Model, 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")
|
||||
.normal()
|
||||
.text_filter(modal.priority_state.text_filter.as_str())
|
||||
@ -445,7 +445,7 @@ fn issue_priority_field(modal: &AddIssueModal) -> Node<Msg> {
|
||||
.collect(),
|
||||
)
|
||||
.selected(vec![modal.priority.to_child().name("priority")])
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Priority))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.label("Issue Type")
|
||||
@ -456,9 +456,9 @@ fn issue_priority_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)
|
||||
.build()
|
||||
.build(FieldId::AddIssueModal(IssueFieldId::Title))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.label("Epic name")
|
||||
|
@ -426,30 +426,30 @@ fn top_modal_row(_model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let issue_type_select = StyledSelect::build(FieldId::EditIssueModal(
|
||||
EditIssueModalSection::Issue(IssueFieldId::Type),
|
||||
))
|
||||
.dropdown_width(150)
|
||||
.name("type")
|
||||
.text_filter(top_type_state.text_filter.as_str())
|
||||
.opened(top_type_state.opened)
|
||||
.valid(true)
|
||||
.options(
|
||||
IssueType::ordered()
|
||||
.into_iter()
|
||||
.map(|t| t.to_child().name("type"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(vec![{
|
||||
let id = modal.id;
|
||||
let issue_type = &payload.issue_type;
|
||||
issue_type
|
||||
.to_child()
|
||||
.name("type")
|
||||
.text(format!("{} - {}", issue_type, id))
|
||||
}])
|
||||
.build()
|
||||
.into_node();
|
||||
let issue_type_select = StyledSelect::build()
|
||||
.dropdown_width(150)
|
||||
.name("type")
|
||||
.text_filter(top_type_state.text_filter.as_str())
|
||||
.opened(top_type_state.opened)
|
||||
.valid(true)
|
||||
.options(
|
||||
IssueType::ordered()
|
||||
.into_iter()
|
||||
.map(|t| t.to_child().name("type"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(vec![{
|
||||
let id = modal.id;
|
||||
let issue_type = &payload.issue_type;
|
||||
issue_type
|
||||
.to_child()
|
||||
.name("type")
|
||||
.text(format!("{} - {}", issue_type, id))
|
||||
}])
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Type,
|
||||
)))
|
||||
.into_node();
|
||||
|
||||
div![
|
||||
attrs![At::Class => "topActions"],
|
||||
@ -471,16 +471,16 @@ fn left_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
..
|
||||
} = modal;
|
||||
|
||||
let title = StyledInput::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Title,
|
||||
)))
|
||||
.add_input_class("issueSummary")
|
||||
.add_wrapper_class("issueSummary")
|
||||
.add_wrapper_class("textarea")
|
||||
.value(payload.title.as_str())
|
||||
.valid(payload.title.len() >= 3)
|
||||
.build()
|
||||
.into_node();
|
||||
let title = StyledInput::build()
|
||||
.add_input_class("issueSummary")
|
||||
.add_wrapper_class("issueSummary")
|
||||
.add_wrapper_class("textarea")
|
||||
.value(payload.title.as_str())
|
||||
.valid(payload.title.len() >= 3)
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Title,
|
||||
)))
|
||||
.into_node();
|
||||
|
||||
let description_text = payload.description.as_ref().cloned().unwrap_or_default();
|
||||
let description = StyledEditor::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
@ -659,114 +659,114 @@ fn right_modal_column(model: &Model, modal: &EditIssueModal) -> Node<Msg> {
|
||||
..
|
||||
} = modal;
|
||||
|
||||
let status = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::IssueStatusId,
|
||||
)))
|
||||
.name("status")
|
||||
.opened(status_state.opened)
|
||||
.normal()
|
||||
.text_filter(status_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.map(|opt| opt.to_child().name("status"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.filter(|is| is.id == payload.issue_status_id)
|
||||
.map(|is| is.to_child().name("status"))
|
||||
.collect(),
|
||||
)
|
||||
.valid(true)
|
||||
.build()
|
||||
.into_node();
|
||||
let status = StyledSelect::build()
|
||||
.name("status")
|
||||
.opened(status_state.opened)
|
||||
.normal()
|
||||
.text_filter(status_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.map(|opt| opt.to_child().name("status"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.issue_statuses
|
||||
.iter()
|
||||
.filter(|is| is.id == payload.issue_status_id)
|
||||
.map(|is| is.to_child().name("status"))
|
||||
.collect(),
|
||||
)
|
||||
.valid(true)
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::IssueStatusId,
|
||||
)))
|
||||
.into_node();
|
||||
let status_field = StyledField::build()
|
||||
.input(status)
|
||||
.label("Status")
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let assignees = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Assignees,
|
||||
)))
|
||||
.name("assignees")
|
||||
.opened(assignees_state.opened)
|
||||
.empty()
|
||||
.multi()
|
||||
.text_filter(assignees_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.map(|user| user.to_child().name("assignees"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.filter(|user| payload.user_ids.contains(&user.id))
|
||||
.map(|user| user.to_child().name("assignees"))
|
||||
.collect(),
|
||||
)
|
||||
.build()
|
||||
.into_node();
|
||||
let assignees = StyledSelect::build()
|
||||
.name("assignees")
|
||||
.opened(assignees_state.opened)
|
||||
.empty()
|
||||
.multi()
|
||||
.text_filter(assignees_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.map(|user| user.to_child().name("assignees"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.filter(|user| payload.user_ids.contains(&user.id))
|
||||
.map(|user| user.to_child().name("assignees"))
|
||||
.collect(),
|
||||
)
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Assignees,
|
||||
)))
|
||||
.into_node();
|
||||
let assignees_field = StyledField::build()
|
||||
.input(assignees)
|
||||
.label("Assignees")
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let reporter = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Reporter,
|
||||
)))
|
||||
.name("reporter")
|
||||
.opened(reporter_state.opened)
|
||||
.empty()
|
||||
.text_filter(reporter_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.map(|user| user.to_child().name("reporter"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.filter(|user| payload.reporter_id == user.id)
|
||||
.map(|user| user.to_child().name("reporter"))
|
||||
.collect(),
|
||||
)
|
||||
.build()
|
||||
.into_node();
|
||||
let reporter = StyledSelect::build()
|
||||
.name("reporter")
|
||||
.opened(reporter_state.opened)
|
||||
.empty()
|
||||
.text_filter(reporter_state.text_filter.as_str())
|
||||
.options(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.map(|user| user.to_child().name("reporter"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(
|
||||
model
|
||||
.users
|
||||
.iter()
|
||||
.filter(|user| payload.reporter_id == user.id)
|
||||
.map(|user| user.to_child().name("reporter"))
|
||||
.collect(),
|
||||
)
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Reporter,
|
||||
)))
|
||||
.into_node();
|
||||
let reporter_field = StyledField::build()
|
||||
.input(reporter)
|
||||
.label("Reporter")
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let priority = StyledSelect::build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Priority,
|
||||
)))
|
||||
.name("priority")
|
||||
.opened(priority_state.opened)
|
||||
.empty()
|
||||
.text_filter(priority_state.text_filter.as_str())
|
||||
.options(
|
||||
IssuePriority::ordered()
|
||||
.into_iter()
|
||||
.map(|p| p.to_child().name("priority"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(vec![payload.priority.to_child().name("priority")])
|
||||
.build()
|
||||
.into_node();
|
||||
let priority = StyledSelect::build()
|
||||
.name("priority")
|
||||
.opened(priority_state.opened)
|
||||
.empty()
|
||||
.text_filter(priority_state.text_filter.as_str())
|
||||
.options(
|
||||
IssuePriority::ordered()
|
||||
.into_iter()
|
||||
.map(|p| p.to_child().name("priority"))
|
||||
.collect(),
|
||||
)
|
||||
.selected(vec![payload.priority.to_child().name("priority")])
|
||||
.build(FieldId::EditIssueModal(EditIssueModalSection::Issue(
|
||||
IssueFieldId::Priority,
|
||||
)))
|
||||
.into_node();
|
||||
let priority_field = StyledField::build()
|
||||
.input(priority)
|
||||
.label("Priority")
|
||||
|
@ -91,7 +91,7 @@ pub fn time_tracking_field(
|
||||
) -> Node<Msg> {
|
||||
let input = match time_tracking_type {
|
||||
TimeTracking::Untracked => empty![],
|
||||
TimeTracking::Fibonacci => StyledSelect::build(field_id)
|
||||
TimeTracking::Fibonacci => StyledSelect::build()
|
||||
.selected(
|
||||
select_state
|
||||
.values
|
||||
@ -106,12 +106,12 @@ pub fn time_tracking_field(
|
||||
.map(|v| v.to_child())
|
||||
.collect(),
|
||||
)
|
||||
.build()
|
||||
.build(field_id)
|
||||
.into_node(),
|
||||
TimeTracking::Hourly => StyledInput::build(field_id)
|
||||
TimeTracking::Hourly => StyledInput::build()
|
||||
.state(input_state)
|
||||
.valid(true)
|
||||
.build()
|
||||
.build(field_id)
|
||||
.into_node(),
|
||||
};
|
||||
StyledField::build()
|
||||
|
@ -26,11 +26,11 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let username = StyledInput::build(FieldId::Profile(UsersFieldId::Username))
|
||||
let username = StyledInput::build()
|
||||
.state(&page.name)
|
||||
.valid(true)
|
||||
.primary()
|
||||
.build()
|
||||
.build(FieldId::Profile(UsersFieldId::Username))
|
||||
.into_node();
|
||||
let username_field = StyledField::build()
|
||||
.label("Username")
|
||||
@ -38,11 +38,11 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let email = StyledInput::build(FieldId::Profile(UsersFieldId::Username))
|
||||
let email = StyledInput::build()
|
||||
.state(&page.email)
|
||||
.valid(true)
|
||||
.primary()
|
||||
.build()
|
||||
.build(FieldId::Profile(UsersFieldId::Username))
|
||||
.into_node();
|
||||
let email_field = StyledField::build()
|
||||
.label("E-Mail")
|
||||
@ -97,7 +97,7 @@ fn build_current_project(model: &Model, page: &ProfilePage) -> Node<Msg> {
|
||||
joined_projects.insert(p.project_id, p);
|
||||
}
|
||||
|
||||
StyledSelect::build(FieldId::Profile(UsersFieldId::CurrentProject))
|
||||
StyledSelect::build()
|
||||
.name("current_project")
|
||||
.normal()
|
||||
.options(
|
||||
@ -117,7 +117,7 @@ fn build_current_project(model: &Model, page: &ProfilePage) -> Node<Msg> {
|
||||
.collect(),
|
||||
)
|
||||
.state(&page.current_project)
|
||||
.build()
|
||||
.build(FieldId::Profile(UsersFieldId::CurrentProject))
|
||||
.into_node()
|
||||
};
|
||||
StyledField::build()
|
||||
|
@ -61,11 +61,11 @@ fn project_board_filters(model: &Model) -> Node<Msg> {
|
||||
_ => return empty![],
|
||||
};
|
||||
|
||||
let search_input = StyledInput::build(FieldId::TextFilterBoard)
|
||||
let search_input = StyledInput::build()
|
||||
.icon(Icon::Search)
|
||||
.valid(true)
|
||||
.value(project_page.text_filter.as_str())
|
||||
.build()
|
||||
.build(FieldId::TextFilterBoard)
|
||||
.into_node();
|
||||
|
||||
let only_my = StyledButton::build()
|
||||
|
@ -44,17 +44,16 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
|
||||
let category_field = category_field(page);
|
||||
|
||||
let time_tracking =
|
||||
StyledCheckbox::build(FieldId::ProjectSettings(ProjectFieldId::TimeTracking))
|
||||
.options(vec![
|
||||
TimeTracking::Untracked.to_child(),
|
||||
TimeTracking::Fibonacci.to_child(),
|
||||
TimeTracking::Hourly.to_child(),
|
||||
])
|
||||
.state(&page.time_tracking)
|
||||
.add_class("timeTracking")
|
||||
.build()
|
||||
.into_node();
|
||||
let time_tracking = StyledCheckbox::build()
|
||||
.options(vec![
|
||||
TimeTracking::Untracked.to_child(),
|
||||
TimeTracking::Fibonacci.to_child(),
|
||||
TimeTracking::Hourly.to_child(),
|
||||
])
|
||||
.state(&page.time_tracking)
|
||||
.add_class("timeTracking")
|
||||
.build(FieldId::ProjectSettings(ProjectFieldId::TimeTracking))
|
||||
.into_node();
|
||||
let time_tracking_type: TimeTracking = page.time_tracking.value.into();
|
||||
let time_tracking_field = StyledField::build()
|
||||
.input(time_tracking)
|
||||
@ -162,7 +161,7 @@ fn description_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
||||
|
||||
/// Build project category dropdown with styled field wrapper
|
||||
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)
|
||||
.text_filter(page.project_category_state.text_filter.as_str())
|
||||
.valid(true)
|
||||
@ -180,7 +179,7 @@ fn category_field(page: &ProjectSettingsPage) -> Node<Msg> {
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.to_child()])
|
||||
.build()
|
||||
.build(FieldId::ProjectSettings(ProjectFieldId::Category))
|
||||
.into_node();
|
||||
StyledField::build()
|
||||
.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)
|
||||
.primary()
|
||||
.auto_focus()
|
||||
.on_input_ev(blur)
|
||||
.build()
|
||||
.build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
.into_node();
|
||||
|
||||
div![
|
||||
@ -277,12 +276,12 @@ fn column_preview(
|
||||
ProjectPageChange::EditIssueStatusName(None),
|
||||
))
|
||||
});
|
||||
let input = StyledInput::build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
let input = StyledInput::build()
|
||||
.state(&page.name)
|
||||
.primary()
|
||||
.auto_focus()
|
||||
.on_input_ev(blur)
|
||||
.build()
|
||||
.build(FieldId::ProjectSettings(ProjectFieldId::IssueStatusName))
|
||||
.into_node();
|
||||
|
||||
div![class!["columnPreview"], div![class!["columnName"], input]]
|
||||
|
@ -140,9 +140,8 @@ impl ToNode for StyledCheckbox {
|
||||
}
|
||||
|
||||
impl StyledCheckbox {
|
||||
pub fn build(field_id: FieldId) -> StyledCheckboxBuilder {
|
||||
pub fn build() -> StyledCheckboxBuilder {
|
||||
StyledCheckboxBuilder {
|
||||
id: field_id,
|
||||
options: vec![],
|
||||
selected: 0,
|
||||
class_list: vec![],
|
||||
@ -151,7 +150,6 @@ impl StyledCheckbox {
|
||||
}
|
||||
|
||||
pub struct StyledCheckboxBuilder {
|
||||
id: FieldId,
|
||||
options: Vec<ChildBuilder>,
|
||||
selected: u32,
|
||||
class_list: Vec<String>,
|
||||
@ -176,9 +174,9 @@ impl StyledCheckboxBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> StyledCheckbox {
|
||||
pub fn build(self, field_id: FieldId) -> StyledCheckbox {
|
||||
StyledCheckbox {
|
||||
id: self.id,
|
||||
id: field_id,
|
||||
options: self.options,
|
||||
selected: self.selected,
|
||||
class_list: self.class_list,
|
||||
|
@ -81,9 +81,8 @@ pub struct StyledInput {
|
||||
}
|
||||
|
||||
impl StyledInput {
|
||||
pub fn build(id: FieldId) -> StyledInputBuilder {
|
||||
pub fn build() -> StyledInputBuilder {
|
||||
StyledInputBuilder {
|
||||
id,
|
||||
icon: None,
|
||||
valid: None,
|
||||
value: None,
|
||||
@ -99,7 +98,6 @@ impl StyledInput {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledInputBuilder {
|
||||
id: FieldId,
|
||||
icon: Option<Icon>,
|
||||
valid: Option<bool>,
|
||||
value: Option<String>,
|
||||
@ -166,9 +164,9 @@ impl StyledInputBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> StyledInput {
|
||||
pub fn build(self, id: FieldId) -> StyledInput {
|
||||
StyledInput {
|
||||
id: self.id,
|
||||
id,
|
||||
icon: self.icon,
|
||||
valid: self.valid.unwrap_or_default(),
|
||||
value: self.value,
|
||||
|
@ -2,9 +2,10 @@ use seed::{prelude::*, *};
|
||||
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
use crate::shared::styled_select::{StyledSelect, StyledSelectState};
|
||||
use crate::shared::styled_tooltip::StyledTooltip;
|
||||
use crate::shared::ToNode;
|
||||
use crate::{FieldId, Msg};
|
||||
use crate::shared::{ToChild, ToNode};
|
||||
use crate::{FieldId, Msg, RteField};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum HeadingSize {
|
||||
@ -80,6 +81,7 @@ pub enum RteMsg {
|
||||
|
||||
// code
|
||||
InsertCode(bool),
|
||||
CodeSetLang(String),
|
||||
|
||||
RequestFocus(uuid::Uuid),
|
||||
}
|
||||
@ -146,6 +148,7 @@ impl RteMsg {
|
||||
RteMsg::InsertTable { .. } => None,
|
||||
// code
|
||||
RteMsg::InsertCode(_) => None,
|
||||
RteMsg::CodeSetLang(_) => None,
|
||||
|
||||
// indent
|
||||
RteMsg::ChangeIndent(RteIndentMsg::Increase) => Some(ExecCommand::new("indent")),
|
||||
@ -181,7 +184,7 @@ pub struct StyledRteTableState {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StyledRteCodeState {
|
||||
pub visible: bool,
|
||||
pub lang: String,
|
||||
pub lang: StyledSelectState,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -197,7 +200,7 @@ pub struct StyledRteState {
|
||||
impl StyledRteState {
|
||||
pub fn new(field_id: FieldId) -> Self {
|
||||
Self {
|
||||
field_id,
|
||||
field_id: field_id.clone(),
|
||||
value: String::new(),
|
||||
table_tooltip: StyledRteTableState {
|
||||
visible: false,
|
||||
@ -206,7 +209,10 @@ impl StyledRteState {
|
||||
},
|
||||
code_tooltip: StyledRteCodeState {
|
||||
visible: false,
|
||||
lang: "".to_string(),
|
||||
lang: StyledSelectState::new(
|
||||
FieldId::Rte(RteField::CodeLang(Box::new(field_id.clone()))),
|
||||
vec![],
|
||||
),
|
||||
},
|
||||
range: None,
|
||||
identifier: uuid::Uuid::new_v4(),
|
||||
@ -214,6 +220,7 @@ impl StyledRteState {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, msg: &Msg, orders: &mut impl Orders<Msg>) {
|
||||
self.code_tooltip.lang.update(msg, orders);
|
||||
let m = match msg {
|
||||
Msg::Rte(m, field) if field == &self.field_id => m,
|
||||
_ => return,
|
||||
@ -235,12 +242,14 @@ impl StyledRteState {
|
||||
self.schedule_focus(orders);
|
||||
}
|
||||
_ => match m {
|
||||
// code
|
||||
RteMsg::InsertCode(b) => {
|
||||
if *b {
|
||||
self.store_range();
|
||||
}
|
||||
self.code_tooltip.visible = *b;
|
||||
}
|
||||
// table
|
||||
RteMsg::TableSetRows(n) => {
|
||||
self.table_tooltip.rows = *n;
|
||||
}
|
||||
@ -329,19 +338,24 @@ pub struct StyledRte {
|
||||
field_id: FieldId,
|
||||
table_tooltip: StyledRteTableState,
|
||||
identifier: Option<uuid::Uuid>,
|
||||
code_tooltip: StyledRteCodeState,
|
||||
// value: String,
|
||||
}
|
||||
|
||||
impl StyledRte {
|
||||
pub fn build(field_id: FieldId) -> StyledRteBuilder {
|
||||
StyledRteBuilder {
|
||||
field_id,
|
||||
field_id: field_id.clone(),
|
||||
value: String::new(),
|
||||
table_tooltip: StyledRteTableState {
|
||||
visible: false,
|
||||
rows: 0,
|
||||
cols: 0,
|
||||
},
|
||||
code_tooltip: StyledRteCodeState {
|
||||
visible: false,
|
||||
lang: StyledSelectState::new(field_id.clone(), vec![]),
|
||||
},
|
||||
identifier: None,
|
||||
}
|
||||
}
|
||||
@ -357,6 +371,7 @@ pub struct StyledRteBuilder {
|
||||
field_id: FieldId,
|
||||
value: String,
|
||||
table_tooltip: StyledRteTableState,
|
||||
code_tooltip: StyledRteCodeState,
|
||||
identifier: Option<uuid::Uuid>,
|
||||
}
|
||||
|
||||
@ -364,7 +379,8 @@ impl StyledRteBuilder {
|
||||
pub fn state(mut self, state: &StyledRteState) -> Self {
|
||||
self.value = state.value.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
|
||||
}
|
||||
|
||||
@ -374,6 +390,7 @@ impl StyledRteBuilder {
|
||||
// value: self.value,
|
||||
table_tooltip: self.table_tooltip,
|
||||
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))
|
||||
}),
|
||||
);
|
||||
let field_id = values.field_id.clone();
|
||||
let underline_button = styled_rte_button(
|
||||
"Underline",
|
||||
Icon::Underline,
|
||||
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| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Strikethrough, field_id))
|
||||
}),
|
||||
);
|
||||
let field_id = values.field_id.clone();
|
||||
let subscript_button = styled_rte_button(
|
||||
"Subscript",
|
||||
Icon::Subscript,
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Subscript, field_id))
|
||||
}),
|
||||
);
|
||||
let field_id = values.field_id.clone();
|
||||
let superscript_button = styled_rte_button(
|
||||
"Superscript",
|
||||
Icon::Superscript,
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Superscript, field_id))
|
||||
}),
|
||||
);
|
||||
|
||||
let underline_button = {
|
||||
let field_id = values.field_id.clone();
|
||||
styled_rte_button(
|
||||
"Underline",
|
||||
Icon::Underline,
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Underscore, field_id))
|
||||
}),
|
||||
)
|
||||
};
|
||||
|
||||
let strike_through_button = {
|
||||
let field_id = values.field_id.clone();
|
||||
styled_rte_button(
|
||||
"StrikeThrough",
|
||||
Icon::StrikeThrough,
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Strikethrough, field_id))
|
||||
}),
|
||||
)
|
||||
};
|
||||
|
||||
let subscript_button = {
|
||||
let field_id = values.field_id.clone();
|
||||
styled_rte_button(
|
||||
"Subscript",
|
||||
Icon::Subscript,
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.prevent_default();
|
||||
Some(Msg::Rte(RteMsg::Subscript, 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![
|
||||
class!["group formatting"],
|
||||
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();
|
||||
styled_rte_button(
|
||||
"Insert code",
|
||||
@ -832,6 +862,7 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
|
||||
}),
|
||||
)
|
||||
};
|
||||
code_alt_button.add_child(code_tooltip(values));
|
||||
|
||||
div![
|
||||
class!["group insert"],
|
||||
@ -887,33 +918,46 @@ fn table_tooltip(values: &StyledRte) -> Node<Msg> {
|
||||
rows,
|
||||
cols,
|
||||
} = values.table_tooltip;
|
||||
let field_id = values.field_id.clone();
|
||||
let on_rows_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let on_cols_change = input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
||||
});
|
||||
let field_id = values.field_id.clone();
|
||||
let close_table_tooltip = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::Close)
|
||||
.on_click(mouse_ev(Ev::Click, move |ev| {
|
||||
|
||||
let on_rows_change = {
|
||||
let field_id = values.field_id.clone();
|
||||
input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetRows(n), field_id))
|
||||
})
|
||||
};
|
||||
|
||||
let on_cols_change = {
|
||||
let field_id = values.field_id.clone();
|
||||
input_ev(Ev::Change, move |v| {
|
||||
v.parse::<u16>()
|
||||
.ok()
|
||||
.map(|n| Msg::Rte(RteMsg::TableSetColumns(n), field_id))
|
||||
})
|
||||
};
|
||||
|
||||
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();
|
||||
Some(Msg::Rte(RteMsg::TableSetVisibility(false), 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))
|
||||
});
|
||||
Some(Msg::Rte(RteMsg::InsertTable { rows, cols }, field_id))
|
||||
})
|
||||
};
|
||||
|
||||
StyledTooltip::build()
|
||||
.table_tooltip()
|
||||
.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
|
||||
.syntaxes()
|
||||
.iter()
|
||||
.map(|s| s.name.as_str())
|
||||
.collect();
|
||||
languages.sort();
|
||||
let options: Vec<Node<Msg>> = languages
|
||||
|
||||
let options: Vec<(String, u32)> = languages
|
||||
.into_iter()
|
||||
.map(|name| option![attrs![At::Value => name], name])
|
||||
.enumerate()
|
||||
.map(|(idx, label)| (label.to_string(), idx as u32))
|
||||
.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> {
|
||||
|
@ -61,9 +61,10 @@ impl StyledSelectState {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, msg: &Msg, _orders: &mut impl Orders<Msg>) {
|
||||
let ref id = self.field_id;
|
||||
match msg {
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::DropDownVisibility(b))
|
||||
if *field_id == self.field_id =>
|
||||
if field_id == id =>
|
||||
{
|
||||
self.opened = *b;
|
||||
if !self.opened {
|
||||
@ -71,22 +72,22 @@ impl StyledSelectState {
|
||||
}
|
||||
}
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Text(text))
|
||||
if *field_id == self.field_id =>
|
||||
if field_id == id =>
|
||||
{
|
||||
self.text_filter = text.clone();
|
||||
}
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(v)))
|
||||
if field_id == &self.field_id =>
|
||||
if field_id == id =>
|
||||
{
|
||||
self.values = vec![*v];
|
||||
}
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
||||
if field_id == &self.field_id =>
|
||||
if field_id == id =>
|
||||
{
|
||||
self.values.clear();
|
||||
}
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(v))
|
||||
if field_id == &self.field_id =>
|
||||
if field_id == id =>
|
||||
{
|
||||
let mut old = vec![];
|
||||
std::mem::swap(&mut old, &mut self.values);
|
||||
@ -123,9 +124,8 @@ impl ToNode for StyledSelect {
|
||||
}
|
||||
|
||||
impl StyledSelect {
|
||||
pub fn build(id: FieldId) -> StyledSelectBuilder {
|
||||
pub fn build() -> StyledSelectBuilder {
|
||||
StyledSelectBuilder {
|
||||
id,
|
||||
variant: None,
|
||||
dropdown_width: None,
|
||||
name: None,
|
||||
@ -142,7 +142,6 @@ impl StyledSelect {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledSelectBuilder {
|
||||
id: FieldId,
|
||||
variant: Option<Variant>,
|
||||
dropdown_width: Option<usize>,
|
||||
name: Option<String>,
|
||||
@ -156,9 +155,9 @@ pub struct StyledSelectBuilder {
|
||||
}
|
||||
|
||||
impl StyledSelectBuilder {
|
||||
pub fn build(self) -> StyledSelect {
|
||||
pub fn build(self, id: FieldId) -> StyledSelect {
|
||||
StyledSelect {
|
||||
id: self.id,
|
||||
id,
|
||||
variant: self.variant.unwrap_or_default(),
|
||||
dropdown_width: self.dropdown_width,
|
||||
name: self.name,
|
||||
@ -278,12 +277,14 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
||||
}
|
||||
|
||||
let action_icon = if clearable && !selected.is_empty() {
|
||||
let field_id = id.clone();
|
||||
let on_click = mouse_ev(Ev::Click, move |ev| {
|
||||
ev.stop_propagation();
|
||||
ev.prevent_default();
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
||||
});
|
||||
let on_click = {
|
||||
let field_id = id.clone();
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.stop_propagation();
|
||||
ev.prevent_default();
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(None))
|
||||
})
|
||||
};
|
||||
StyledIcon::build(Icon::Close)
|
||||
.add_class("chevronIcon")
|
||||
.on_click(on_click)
|
||||
@ -305,10 +306,13 @@ pub fn render(values: StyledSelect) -> Node<Msg> {
|
||||
let child = child.build(DisplayType::SelectOption);
|
||||
let value = child.value();
|
||||
let node = child.into_node();
|
||||
let field_id = id.clone();
|
||||
let on_change = mouse_ev(Ev::Click, move |_| {
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(value)))
|
||||
});
|
||||
|
||||
let on_change = {
|
||||
let field_id = id.clone();
|
||||
mouse_ev(Ev::Click, move |_| {
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::Changed(Some(value)))
|
||||
})
|
||||
};
|
||||
div![
|
||||
attrs![At::Class => "option"],
|
||||
on_change,
|
||||
@ -386,7 +390,7 @@ fn render_value(mut content: Node<Msg>) -> Node<Msg> {
|
||||
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 child = opt.build(DisplayType::SelectValue);
|
||||
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_child(close_icon);
|
||||
|
||||
let handler = mouse_ev(Ev::Click, move |ev| {
|
||||
ev.stop_propagation();
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(value))
|
||||
});
|
||||
let handler = {
|
||||
let field_id = id.clone();
|
||||
mouse_ev(Ev::Click, move |ev| {
|
||||
ev.stop_propagation();
|
||||
Msg::StyledSelectChanged(field_id, StyledSelectChange::RemoveMulti(value))
|
||||
})
|
||||
};
|
||||
|
||||
div![attrs![At::Class => "valueMultiItem"], opt, handler]
|
||||
}
|
||||
|
@ -301,3 +301,16 @@ impl ToChild for u32 {
|
||||
.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,
|
||||
Messages,
|
||||
TableBuilder,
|
||||
CodeBuilder,
|
||||
}
|
||||
|
||||
impl Default for Variant {
|
||||
@ -22,6 +23,7 @@ impl std::fmt::Display for Variant {
|
||||
Variant::About => f.write_str("about"),
|
||||
Variant::Messages => f.write_str("messages"),
|
||||
Variant::TableBuilder => f.write_str("tableTooltip"),
|
||||
Variant::CodeBuilder => f.write_str("codeTooltip"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,6 +89,11 @@ impl StyledTooltipBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn code_tooltip(mut self) -> Self {
|
||||
self.variant = Variant::CodeBuilder;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> StyledTooltip {
|
||||
StyledTooltip {
|
||||
visible: self.visible,
|
||||
|
@ -92,10 +92,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
_ => return empty![],
|
||||
};
|
||||
|
||||
let username = StyledInput::build(FieldId::SignIn(SignInFieldId::Username))
|
||||
let username = StyledInput::build()
|
||||
.value(page.username.as_str())
|
||||
.valid(!page.username_touched || page.username.len() > 1)
|
||||
.build()
|
||||
.build(FieldId::SignIn(SignInFieldId::Username))
|
||||
.into_node();
|
||||
let username_field = StyledField::build()
|
||||
.label("Username")
|
||||
@ -103,10 +103,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let email = StyledInput::build(FieldId::SignIn(SignInFieldId::Email))
|
||||
let email = StyledInput::build()
|
||||
.value(page.email.as_str())
|
||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||
.build()
|
||||
.build(FieldId::SignIn(SignInFieldId::Email))
|
||||
.into_node();
|
||||
let email_field = StyledField::build()
|
||||
.label("E-Mail")
|
||||
@ -164,10 +164,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let token = StyledInput::build(FieldId::SignIn(SignInFieldId::Token))
|
||||
let token = StyledInput::build()
|
||||
.value(page.token.as_str())
|
||||
.valid(!page.token_touched || is_token(page.token.as_str()))
|
||||
.build()
|
||||
.build(FieldId::SignIn(SignInFieldId::Token))
|
||||
.into_node();
|
||||
let token_field = StyledField::build()
|
||||
.label("Single use token")
|
||||
|
@ -68,10 +68,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
_ => return empty![],
|
||||
};
|
||||
|
||||
let username = StyledInput::build(FieldId::SignUp(SignUpFieldId::Username))
|
||||
let username = StyledInput::build()
|
||||
.value(page.username.as_str())
|
||||
.valid(!page.username_touched || page.username.len() > 1)
|
||||
.build()
|
||||
.build(FieldId::SignUp(SignUpFieldId::Username))
|
||||
.into_node();
|
||||
let username_field = StyledField::build()
|
||||
.label("Username")
|
||||
@ -79,10 +79,10 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let email = StyledInput::build(FieldId::SignUp(SignUpFieldId::Email))
|
||||
let email = StyledInput::build()
|
||||
.value(page.email.as_str())
|
||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||
.build()
|
||||
.build(FieldId::SignUp(SignUpFieldId::Email))
|
||||
.into_node();
|
||||
let email_field = StyledField::build()
|
||||
.label("E-Mail")
|
||||
|
@ -22,10 +22,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
_ => return empty![],
|
||||
};
|
||||
|
||||
let name = StyledInput::build(FieldId::Users(UsersFieldId::Username))
|
||||
let name = StyledInput::build()
|
||||
.valid(!page.name_touched || page.name.len() >= 3)
|
||||
.value(page.name.as_str())
|
||||
.build()
|
||||
.build(FieldId::Users(UsersFieldId::Username))
|
||||
.into_node();
|
||||
let name_field = StyledField::build()
|
||||
.input(name)
|
||||
@ -33,10 +33,10 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let email = StyledInput::build(FieldId::Users(UsersFieldId::Email))
|
||||
let email = StyledInput::build()
|
||||
.valid(!page.email_touched || is_email(page.email.as_str()))
|
||||
.value(page.email.as_str())
|
||||
.build()
|
||||
.build(FieldId::Users(UsersFieldId::Email))
|
||||
.into_node();
|
||||
let email_field = StyledField::build()
|
||||
.input(email)
|
||||
@ -44,7 +44,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.build()
|
||||
.into_node();
|
||||
|
||||
let user_role = StyledSelect::build(FieldId::Users(UsersFieldId::UserRole))
|
||||
let user_role = StyledSelect::build()
|
||||
.name("user_role")
|
||||
.valid(true)
|
||||
.normal()
|
||||
@ -56,7 +56,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
||||
.map(|role| role.to_child())
|
||||
.collect(),
|
||||
)
|
||||
.build()
|
||||
.build(FieldId::Users(UsersFieldId::UserRole))
|
||||
.into_node();
|
||||
let user_role_field = StyledField::build()
|
||||
.input(user_role)
|
||||
|
Loading…
Reference in New Issue
Block a user