Add select tip. Add action buttons. Fix select styles
This commit is contained in:
parent
8950277149
commit
72c894fa77
@ -10,3 +10,32 @@
|
|||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
font-size: 21px;
|
font-size: 21px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement .selectItem {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement .selectItem.withBottomMargin {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement .selectItem > .selectItemLabel {
|
||||||
|
padding: 0 3px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement .divider {
|
||||||
|
margin-top: 22px;
|
||||||
|
border-top: 1px solid var(--borderLightest);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement > .actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledForm > .formElement > .actions > .actionButton {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
@ -46,6 +46,18 @@
|
|||||||
padding: 5px 5px 5px 10px;
|
padding: 5px 5px 5px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.styledSelect > .valueContainer > .chevronIcon {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--textMedium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelectTip {
|
||||||
|
padding-top: 6px;
|
||||||
|
color: var(--textMedium);
|
||||||
|
font-size: 12.5px;
|
||||||
|
}
|
||||||
|
|
||||||
.styledSelect > .dropDown {
|
.styledSelect > .dropDown {
|
||||||
z-index: var(--dropdown);
|
z-index: var(--dropdown);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -53,7 +65,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px -2px, rgba(9, 30, 66, 0.31) 0px 0px 1px;
|
box-shadow: rgba(9, 30, 66, 0.25) 0 4px 8px -2px, rgba(9, 30, 66, 0.31) 0 0 1px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
use jirs_data::IssueType;
|
||||||
|
|
||||||
use crate::model::{AddIssueModal, Model};
|
use crate::model::{AddIssueModal, Model};
|
||||||
use crate::shared::styled_button::StyledButton;
|
use crate::shared::styled_button::StyledButton;
|
||||||
use crate::shared::styled_form::StyledForm;
|
use crate::shared::styled_form::StyledForm;
|
||||||
@ -6,8 +10,6 @@ use crate::shared::styled_modal::{StyledModal, Variant as ModalVariant};
|
|||||||
use crate::shared::styled_select::StyledSelect;
|
use crate::shared::styled_select::StyledSelect;
|
||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::{FieldId, Msg};
|
use crate::{FieldId, Msg};
|
||||||
use jirs_data::IssueType;
|
|
||||||
use seed::{prelude::*, *};
|
|
||||||
|
|
||||||
pub fn view(_model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
pub fn view(_model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
||||||
let select_type = StyledSelect::build(FieldId::IssueTypeAddIssueModal)
|
let select_type = StyledSelect::build(FieldId::IssueTypeAddIssueModal)
|
||||||
@ -16,6 +18,7 @@ pub fn view(_model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.text_filter(modal.type_select_filter.as_str())
|
.text_filter(modal.type_select_filter.as_str())
|
||||||
.opened(modal.type_select_opened)
|
.opened(modal.type_select_opened)
|
||||||
.valid(true)
|
.valid(true)
|
||||||
|
.tip("Start typing to get a list of possible matches.")
|
||||||
.options(vec![
|
.options(vec![
|
||||||
IssueTypeOption(IssueType::Story),
|
IssueTypeOption(IssueType::Story),
|
||||||
IssueTypeOption(IssueType::Task),
|
IssueTypeOption(IssueType::Task),
|
||||||
@ -24,9 +27,25 @@ pub fn view(_model: &Model, modal: &AddIssueModal) -> Node<Msg> {
|
|||||||
.selected(vec![IssueTypeOption(modal.issue_type.clone())])
|
.selected(vec![IssueTypeOption(modal.issue_type.clone())])
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
|
let submit = StyledButton::build()
|
||||||
|
.primary()
|
||||||
|
.text("Create Issue")
|
||||||
|
.add_class("action")
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
let cancel = StyledButton::build()
|
||||||
|
.empty()
|
||||||
|
.add_class("action")
|
||||||
|
.text("Cancel")
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
let actions = div![attrs![At::Class => "actions"], submit, cancel];
|
||||||
|
|
||||||
let form = StyledForm::build()
|
let form = StyledForm::build()
|
||||||
.heading("Create issue")
|
.heading("Create issue")
|
||||||
.add_field(select_type)
|
.add_field(select_type)
|
||||||
|
.add_field(actions)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
@ -61,14 +80,12 @@ impl crate::shared::styled_select::SelectOption for IssueTypeOption {
|
|||||||
let name = self.0.to_label().to_owned();
|
let name = self.0.to_label().to_owned();
|
||||||
|
|
||||||
let type_icon = StyledIcon::build(self.0.into()).build().into_node();
|
let type_icon = StyledIcon::build(self.0.into()).build().into_node();
|
||||||
let chevron_icon = StyledIcon::build(Icon::ChevronDown).build().into_node();
|
|
||||||
div![attrs![At::Class => "option"], type_icon, name, chevron_icon]
|
div![
|
||||||
// StyledButton::build()
|
attrs![At::Class => "selectItem"],
|
||||||
// .secondary()
|
type_icon,
|
||||||
// .children(vec![span![format!("{}", name)]])
|
div![attrs![At::Class => "selectItemLabel"], name]
|
||||||
// .icon(StyledIcon::build(self.0.into()).build())
|
]
|
||||||
// .build()
|
|
||||||
// .into_node()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_text_filter(&self, text_filter: &str) -> bool {
|
fn match_text_filter(&self, text_filter: &str) -> bool {
|
||||||
|
@ -182,29 +182,20 @@ fn project_board_filters(model: &Model) -> Node<Msg> {
|
|||||||
|
|
||||||
let project_page = &model.project_page;
|
let project_page = &model.project_page;
|
||||||
|
|
||||||
let only_my = StyledButton {
|
let only_my = StyledButton::build()
|
||||||
variant: ButtonVariant::Empty,
|
.empty()
|
||||||
|
.active(model.project_page.only_my_filter)
|
||||||
|
.text("Only My Issues")
|
||||||
|
.on_click(mouse_ev(Ev::Click, |_| Msg::ProjectToggleOnlyMy))
|
||||||
|
.build()
|
||||||
|
.into_node();
|
||||||
|
|
||||||
disabled: false,
|
let recently_updated = StyledButton::build()
|
||||||
active: model.project_page.only_my_filter,
|
.empty()
|
||||||
text: Some("Only My Issues".to_string()),
|
.text("Recently Updated")
|
||||||
icon: None,
|
.on_click(mouse_ev(Ev::Click, |_| Msg::ProjectToggleRecentlyUpdated))
|
||||||
on_click: Some(mouse_ev(Ev::Click, |_| Msg::ProjectToggleOnlyMy)),
|
.build()
|
||||||
children: vec![],
|
.into_node();
|
||||||
}
|
|
||||||
.into_node();
|
|
||||||
|
|
||||||
let recently_updated = StyledButton {
|
|
||||||
variant: ButtonVariant::Empty,
|
|
||||||
|
|
||||||
disabled: false,
|
|
||||||
active: model.project_page.recently_updated_filter,
|
|
||||||
text: Some("Recently Updated".to_string()),
|
|
||||||
icon: None,
|
|
||||||
on_click: Some(mouse_ev(Ev::Click, |_| Msg::ProjectToggleRecentlyUpdated)),
|
|
||||||
children: vec![],
|
|
||||||
}
|
|
||||||
.into_node();
|
|
||||||
|
|
||||||
let clear_all = match project_page.only_my_filter
|
let clear_all = match project_page.only_my_filter
|
||||||
|| project_page.recently_updated_filter
|
|| project_page.recently_updated_filter
|
||||||
|
@ -27,13 +27,14 @@ impl ToString for Variant {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StyledButtonBuilder {
|
pub struct StyledButtonBuilder {
|
||||||
pub variant: Option<Variant>,
|
variant: Option<Variant>,
|
||||||
pub disabled: Option<bool>,
|
disabled: Option<bool>,
|
||||||
pub active: Option<bool>,
|
active: Option<bool>,
|
||||||
pub text: Option<Option<String>>,
|
text: Option<Option<String>>,
|
||||||
pub icon: Option<Option<Node<Msg>>>,
|
icon: Option<Option<Node<Msg>>>,
|
||||||
pub on_click: Option<Option<EventHandler<Msg>>>,
|
on_click: Option<Option<EventHandler<Msg>>>,
|
||||||
pub children: Option<Vec<Node<Msg>>>,
|
children: Option<Vec<Node<Msg>>>,
|
||||||
|
class_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButtonBuilder {
|
impl StyledButtonBuilder {
|
||||||
@ -67,13 +68,16 @@ impl StyledButtonBuilder {
|
|||||||
// self
|
// self
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// pub fn active(mut self, value: bool) -> Self {
|
pub fn active(mut self, value: bool) -> Self {
|
||||||
// self.active = Some(value);
|
self.active = Some(value);
|
||||||
// self
|
self
|
||||||
// }
|
}
|
||||||
|
|
||||||
pub fn text(mut self, value: String) -> Self {
|
pub fn text<S>(mut self, value: S) -> Self
|
||||||
self.text = Some(Some(value));
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.text = Some(Some(value.into()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +99,14 @@ impl StyledButtonBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_class<S>(mut self, name: S) -> Self
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.class_list.push(name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> StyledButton {
|
pub fn build(self) -> StyledButton {
|
||||||
StyledButton {
|
StyledButton {
|
||||||
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
||||||
@ -104,18 +116,20 @@ impl StyledButtonBuilder {
|
|||||||
icon: self.icon.unwrap_or_else(|| None),
|
icon: self.icon.unwrap_or_else(|| None),
|
||||||
on_click: self.on_click.unwrap_or_else(|| None),
|
on_click: self.on_click.unwrap_or_else(|| None),
|
||||||
children: self.children.unwrap_or_default(),
|
children: self.children.unwrap_or_default(),
|
||||||
|
class_list: self.class_list,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StyledButton {
|
pub struct StyledButton {
|
||||||
pub variant: Variant,
|
variant: Variant,
|
||||||
pub disabled: bool,
|
disabled: bool,
|
||||||
pub active: bool,
|
active: bool,
|
||||||
pub text: Option<String>,
|
text: Option<String>,
|
||||||
pub icon: Option<Node<Msg>>,
|
icon: Option<Node<Msg>>,
|
||||||
pub on_click: Option<EventHandler<Msg>>,
|
on_click: Option<EventHandler<Msg>>,
|
||||||
pub children: Vec<Node<Msg>>,
|
children: Vec<Node<Msg>>,
|
||||||
|
class_list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledButton {
|
impl StyledButton {
|
||||||
@ -139,8 +153,10 @@ pub fn render(values: StyledButton) -> Node<Msg> {
|
|||||||
icon,
|
icon,
|
||||||
on_click,
|
on_click,
|
||||||
children,
|
children,
|
||||||
|
mut class_list,
|
||||||
} = values;
|
} = values;
|
||||||
let mut class_list = vec!["styledButton".to_string(), variant.to_string()];
|
class_list.push("styledButton".to_string());
|
||||||
|
class_list.push(variant.to_string());
|
||||||
if children.is_empty() && text.is_none() {
|
if children.is_empty() && text.is_none() {
|
||||||
class_list.push("iconOnly".to_string());
|
class_list.push("iconOnly".to_string());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
use seed::{prelude::*, *};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StyledForm {
|
pub struct StyledForm {
|
||||||
@ -54,7 +55,7 @@ pub fn render(values: StyledForm) -> Node<Msg> {
|
|||||||
attrs![At::Class => "styledForm"],
|
attrs![At::Class => "styledForm"],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => "formElement"],
|
attrs![At::Class => "formElement"],
|
||||||
div![attrs![At::Class => "heading"], heading],
|
div![attrs![At::Class => "formHeading"], heading],
|
||||||
fields
|
fields
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
@ -131,13 +131,19 @@ impl StyledIconBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_class(mut self, name: String) -> Self {
|
pub fn add_class<S>(mut self, name: S) -> Self
|
||||||
self.class_list.push(name);
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.class_list.push(name.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_style(mut self, name: String) -> Self {
|
pub fn add_style<S>(mut self, name: S) -> Self
|
||||||
self.style_list.push(name);
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.style_list.push(name.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ where
|
|||||||
selected: Vec<Child>,
|
selected: Vec<Child>,
|
||||||
text_filter: String,
|
text_filter: String,
|
||||||
opened: bool,
|
opened: bool,
|
||||||
|
tip: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Child> ToNode for StyledSelect<Child>
|
impl<Child> ToNode for StyledSelect<Child>
|
||||||
@ -88,6 +89,7 @@ where
|
|||||||
selected: None,
|
selected: None,
|
||||||
text_filter: None,
|
text_filter: None,
|
||||||
opened: None,
|
opened: None,
|
||||||
|
tip: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +111,7 @@ where
|
|||||||
selected: Option<Vec<Child>>,
|
selected: Option<Vec<Child>>,
|
||||||
text_filter: Option<String>,
|
text_filter: Option<String>,
|
||||||
opened: Option<bool>,
|
opened: Option<bool>,
|
||||||
|
tip: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Child> StyledSelectBuilder<Child>
|
impl<Child> StyledSelectBuilder<Child>
|
||||||
@ -129,6 +132,7 @@ where
|
|||||||
selected: self.selected.unwrap_or_default(),
|
selected: self.selected.unwrap_or_default(),
|
||||||
text_filter: self.text_filter.unwrap_or_default(),
|
text_filter: self.text_filter.unwrap_or_default(),
|
||||||
opened: self.opened.unwrap_or_default(),
|
opened: self.opened.unwrap_or_default(),
|
||||||
|
tip: self.tip,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +177,14 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tip<S>(mut self, tip: S) -> Self
|
||||||
|
where
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
self.tip = Some(tip.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn normal(mut self) -> Self {
|
pub fn normal(mut self) -> Self {
|
||||||
self.variant = Some(Variant::Normal);
|
self.variant = Some(Variant::Normal);
|
||||||
self
|
self
|
||||||
@ -190,12 +202,13 @@ where
|
|||||||
name,
|
name,
|
||||||
placeholder: _,
|
placeholder: _,
|
||||||
valid,
|
valid,
|
||||||
is_multi: _,
|
is_multi,
|
||||||
allow_clear,
|
allow_clear,
|
||||||
options,
|
options,
|
||||||
selected,
|
selected,
|
||||||
text_filter,
|
text_filter,
|
||||||
opened,
|
opened,
|
||||||
|
tip,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
let on_text = input_ev(Ev::KeyUp, |value| {
|
let on_text = input_ev(Ev::KeyUp, |value| {
|
||||||
@ -211,13 +224,21 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
let dropdown_style = dropdown_width
|
let dropdown_style = dropdown_width
|
||||||
.map(|n| format!("width: {}px", n))
|
.map(|n| format!("width: {}px;", n))
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|| format!("width: 100%;"));
|
||||||
let mut select_class = vec!["styledSelect".to_string(), format!("{}", variant)];
|
let mut select_class = vec!["styledSelect".to_string(), format!("{}", variant)];
|
||||||
if !valid {
|
if !valid {
|
||||||
select_class.push("invalid".to_string());
|
select_class.push("invalid".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let chevron_down = match (selected.is_empty() || !is_multi) && variant != Variant::Empty {
|
||||||
|
true => StyledIcon::build(Icon::ChevronDown)
|
||||||
|
.add_class("chevronIcon")
|
||||||
|
.build()
|
||||||
|
.into_node(),
|
||||||
|
_ => empty![],
|
||||||
|
};
|
||||||
|
|
||||||
let children: Vec<Node<Msg>> = options
|
let children: Vec<Node<Msg>> = options
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|o| !selected.contains(&o) && o.match_text_filter(text_filter.as_str()))
|
.filter(|o| !selected.contains(&o) && o.match_text_filter(text_filter.as_str()))
|
||||||
@ -237,6 +258,10 @@ where
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let value = selected.into_iter().map(|m| render_value(m.into_value()));
|
let value = selected.into_iter().map(|m| render_value(m.into_value()));
|
||||||
|
let tip_node = match tip {
|
||||||
|
Some(s) => div![attrs![At::Class => "styledSelectTip"], s],
|
||||||
|
_ => empty![],
|
||||||
|
};
|
||||||
|
|
||||||
let text_input = match opened {
|
let text_input = match opened {
|
||||||
true => seed::input![
|
true => seed::input![
|
||||||
@ -263,19 +288,23 @@ where
|
|||||||
_ => seed::div![attrs![ At::Class => "options" ], children],
|
_ => seed::div![attrs![ At::Class => "options" ], children],
|
||||||
};
|
};
|
||||||
|
|
||||||
seed::div![
|
div![
|
||||||
attrs![At::Class => select_class.join(" ")],
|
seed::div![
|
||||||
div![
|
attrs![At::Class => select_class.join(" ")],
|
||||||
attrs![At::Class => format!("valueContainer {}", variant)],
|
div![
|
||||||
visibility_handler,
|
attrs![At::Class => format!("valueContainer {}", variant)],
|
||||||
value,
|
visibility_handler,
|
||||||
|
value,
|
||||||
|
chevron_down,
|
||||||
|
],
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "dropDown", At::Style => dropdown_style],
|
||||||
|
text_input,
|
||||||
|
clear_icon,
|
||||||
|
option_list
|
||||||
|
]
|
||||||
],
|
],
|
||||||
div![
|
tip_node,
|
||||||
attrs![At::Class => "dropDown", At::Style => dropdown_style],
|
|
||||||
text_input,
|
|
||||||
clear_icon,
|
|
||||||
option_list
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user