Finish visual aside

This commit is contained in:
Adrian Wozniak 2020-03-30 23:19:00 +02:00
parent c58d9fb9f1
commit aa5216a547
11 changed files with 362 additions and 39 deletions

View File

@ -95,12 +95,11 @@ aside#navbar-left:hover .item > .itemText {
opacity: 1; opacity: 1;
} }
aside#navbar-left .styledTooltip { .styledTooltip.aboutTooltipPopup {
z-index: calc(var(--modal) + 1); bottom: 48px;
position: fixed; left: 120px;
width: 300px; }
border-radius: 3px;
background: #fff; .styledTooltip.aboutTooltipPopup #about-github-button {
transform: translateZ(0); margin-left: 10px;
box-shadow: rgba(9, 30, 66, 0.25) 0 4px 8px -2px, rgba(9, 30, 66, 0.31) 0 0 1px;
} }

View File

@ -3,3 +3,104 @@
padding-top: 18px; padding-top: 18px;
border-top: 1px solid var(--borderLight); border-top: 1px solid var(--borderLight);
} }
.styledButton {
display: inline-flex;
align-items: center;
justify-content: center;
height: 32px;
vertical-align: middle;
line-height: 1;
white-space: nowrap;
border-radius: 3px;
transition: all 0.1s;
appearance: none;
cursor: pointer;
user-select: none;
font-size: 14.5px;
}
.styledButton.withIcon > span.text {
margin-left: 7px;
}
.styledButton:disabled {
opacity: 0.6;
cursor: default;
}
.styledButton:not(.onlyIcon) {
padding: 0 12px;
}
.styledButton.onlyIcon {
padding: 0 9px;
}
.styledButton.primary {
color: #fff;
background: var(--primary);
font-family: var(--font-medium);
}
.styledButton.primary:not(:disabled):hover {
filter: brightness(115%);
}
.styledButton.primary:not(:disabled):active {
filter: brightness(110%);
}
.styledButton.primary:not(:disabled).isActive {
filter: brightness(110%);
}
.styledButton.success {
color: #fff;
background: var(--success);
}
.styledButton.danger {
color: #fff;
background: var(--danger);
}
.styledButton.secondary {
color: var(--textDark);
background: var(--secondary);
font-family: var(--font-regular);
}
.styledButton.secondary:not(:disabled):hover {
background: var(--backgroundLight);
}
.styledButton.secondary:not(:disabled):active {
color: var(--primary);
background: var(--backgroundLightPrimary);
}
.styledButton.secondary:not(:disabled).isActive {
color: var(--primary);
background: var(--backgroundLightPrimary);
}
.styledButton.empty {
background: #fff;
color: var(--textDark);
font-family: var(--font-regular);
}
.styledButton.empty:not(:disabled):hover {
background: var(--backgroundLight);
}
.styledButton.empty:not(:disabled):active {
color: var(--primary);
background: var(--backgroundLightPrimary);
}
.styledButton.empty:not(:disabled).isActive {
color: var(--primary);
background: var(--backgroundLightPrimary);
}

View File

@ -0,0 +1,30 @@
.styledTooltip {
z-index: calc(var(--modal) + 1);
position: fixed;
width: 300px;
border-radius: 3px;
background: #fff;
transform: translateZ(0);
box-shadow: rgba(9, 30, 66, 0.25) 0 4px 8px -2px, rgba(9, 30, 66, 0.31) 0 0 1px;
}
.styledTooltip .feedbackDropdown {
padding: 16px 24px 24px;
}
.styledTooltip .feedbackImageCont {
padding: 24px 56px 20px;
}
.styledTooltip .feedbackImage {
width: 100%;
}
.styledTooltip .feedbackParagraph {
margin-bottom: 12px;
font-size: 15px;
}
.styledTooltip .feedbackParagraph:last-of-type {
margin-bottom: 22px;
}

View File

@ -6,4 +6,5 @@
@import "css/aside.css"; @import "css/aside.css";
@import "css/icon.css"; @import "css/icon.css";
@import "css/shared.css"; @import "css/shared.css";
@import "css/styledTooltip.css";
@import "css/app.css"; @import "css/app.css";

View File

@ -1,7 +1,8 @@
use crate::model::Page;
use seed::fetch::FetchObject; use seed::fetch::FetchObject;
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use crate::model::Page;
mod api; mod api;
mod login; mod login;
mod model; mod model;
@ -16,6 +17,7 @@ pub enum Msg {
CurrentProjectResult(FetchObject<String>), CurrentProjectResult(FetchObject<String>),
CurrentUserResult(FetchObject<String>), CurrentUserResult(FetchObject<String>),
InternalFailure(String), InternalFailure(String),
ToggleAboutTooltip,
} }
fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) { fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {

View File

@ -59,6 +59,8 @@ pub struct Model {
pub comments_by_project_id: HashMap<ProjectId, Vec<Comment>>, pub comments_by_project_id: HashMap<ProjectId, Vec<Comment>>,
pub page: Page, pub page: Page,
pub host_url: String, pub host_url: String,
//
pub about_tooltip_visible: bool,
} }
impl Default for Model { impl Default for Model {
@ -75,6 +77,7 @@ impl Default for Model {
comments_by_project_id: Default::default(), comments_by_project_id: Default::default(),
page: Page::Project, page: Page::Project,
host_url, host_url,
about_tooltip_visible: false,
} }
} }
} }

View File

@ -1,7 +1,8 @@
use seed::{prelude::*, *};
use crate::model::Page; use crate::model::Page;
use crate::shared::{host_client, inner_layout}; use crate::shared::{host_client, inner_layout};
use crate::Msg; use crate::Msg;
use seed::{prelude::*, *};
pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) { pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Orders<Msg>) {
match msg { match msg {
@ -13,6 +14,9 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
.skip() .skip()
.perform_cmd(crate::api::fetch_current_user(model.host_url.clone())); .perform_cmd(crate::api::fetch_current_user(model.host_url.clone()));
} }
Msg::ToggleAboutTooltip => {
model.about_tooltip_visible = !model.about_tooltip_visible;
}
_ => (), _ => (),
} }
} }

View File

@ -1,27 +1,33 @@
use seed::fetch::{FetchObject, FetchResult, ResponseWithDataResult}; use seed::fetch::{FetchObject, ResponseWithDataResult};
use seed::{prelude::*, *}; use seed::{prelude::*, *};
use serde::Deserialize;
use jirs_data::FullProjectResponse; use jirs_data::FullProjectResponse;
use styled_button::*;
use crate::model::{Icon, Model, Page}; use crate::model::{Icon, Model, Page};
use crate::Msg; use crate::Msg;
pub fn navbar_left(model: &Model) -> Node<Msg> { pub mod styled_button;
let mut logo_svg = Node::from_html(include_str!("../static/logo.svg")); pub mod styled_tooltip;
aside![ pub fn navbar_left(model: &Model) -> Vec<Node<Msg>> {
id!["navbar-left"], let logo_svg = Node::from_html(include_str!("../../static/logo.svg"));
a![
attrs![At::Class => "logoLink", At::Href => "/"], vec![
div![attrs![At::Class => "styledLogo"], logo_svg] about_tooltip_popup(model),
aside![
id!["navbar-left"],
a![
attrs![At::Class => "logoLink", At::Href => "/"],
div![attrs![At::Class => "styledLogo"], logo_svg]
],
navbar_left_item(model, "Search issues", Icon::Search),
navbar_left_item(model, "Create Issue", Icon::Plus),
div![
attrs![At::Class => "bottom"],
about_tooltip(model, navbar_left_item(model, "About", Icon::Help)),
],
], ],
navbar_left_item(model, "Search issues", Icon::Search),
navbar_left_item(model, "Create Issue", Icon::Plus),
div![
attrs![At::Class => "bottom"],
about_tooltip(model, navbar_left_item(model, "About", Icon::Help))
]
] ]
} }
@ -33,16 +39,74 @@ fn navbar_left_item(_model: &Model, text: &str, logo: Icon) -> Node<Msg> {
] ]
} }
pub fn about_tooltip(_model: &Model, children: Node<Msg>) -> Node<Msg> { pub fn about_tooltip(model: &Model, children: Node<Msg>) -> Node<Msg> {
div![attrs![At::Class => "aboutTooltip"], children] div![
attrs![At::Class => "aboutTooltip"],
ev(Ev::Click, |_| Msg::ToggleAboutTooltip),
children
]
} }
pub fn styled_tooltip() -> Node<Msg> { fn about_tooltip_popup(model: &Model) -> Node<Msg> {
div![attrs![At::Class => "styledTooltip"]] styled_tooltip::StyledTooltip {
visible: model.about_tooltip_visible,
class_name: "aboutTooltipPopup".to_string(),
children: div![
ev(Ev::Click, |_| Msg::ToggleAboutTooltip),
attrs![At::Class => "feedbackDropdown"],
div![
attrs![At::Class => "feedbackImageCont"],
img![attrs![At::Src => "/feedback.png", At::Class => "feedbackImage"]]
],
div![
attrs![At::Class => "feedbackParagraph"],
"This simplified Jira clone is built with Seed.rs on the front-end and Actix-Web on the back-end."
],
div![
attrs![At::Class => "feedbackParagraph"],
"Read more on my website or reach out via ",
a![
attrs![At::Href => "mailto:adrian.wozniak@ita-prog.pl"],
strong!["adrian.wozniak@ita-prog.pl"]
]
],
a![
attrs![
At::Href => "https://gitlab.com/adrian.wozniak/jirs",
At::Target => "_blank",
At::Rel => "noreferrer noopener",
],
StyledButton {
text: Some("Visit Website".to_string()),
variant: Variant::Primary,
disabled: false,
active: false,
icon_only: false,
icon: None,
}.into_node(),
],
a![
id!["about-github-button"],
attrs![
At::Href => "https://gitlab.com/adrian.wozniak/jirs",
At::Target => "_blank",
At::Rel => "noreferrer noopener",
],
StyledButton {
text: Some("Github Repo".to_string()),
variant: Variant::Secondary,
disabled: false,
active: false,
icon_only: false,
icon: Some(Icon::Github),
}.into_node()
]
],
}.into_node()
} }
pub fn sidebar(model: &Model) -> Node<Msg> { pub fn sidebar(model: &Model) -> Node<Msg> {
let project_icon = Node::from_html(include_str!("../static/project-avatar.svg")); let project_icon = Node::from_html(include_str!("../../static/project-avatar.svg"));
let project_info = match model.project.as_ref() { let project_info = match model.project.as_ref() {
Some(project) => li![ Some(project) => li![
id!["projectInfo"], id!["projectInfo"],
@ -96,12 +160,16 @@ fn sidebar_link_item(model: &Model, name: &str, icon: Icon, page: Option<Page>)
attrs![At::Class => class_list.join(" ")], attrs![At::Class => class_list.join(" ")],
a![ a![
attrs![At::Href => path], attrs![At::Href => path],
i![attrs![At::Class => format!("styledIcon {}", icon)], ""], styled_icon(icon),
div![attrs![At::Class => "linkText"], name], div![attrs![At::Class => "linkText"], name],
] ]
] ]
} }
pub fn styled_icon(icon: Icon) -> Node<Msg> {
i![attrs![At::Class => format!("styledIcon {}", icon)], ""]
}
pub fn divider() -> Node<Msg> { pub fn divider() -> Node<Msg> {
div![attrs![At::Class => "divider"], ""] div![attrs![At::Class => "divider"], ""]
} }

View File

@ -0,0 +1,85 @@
use seed::{prelude::*, *};
use crate::model::Icon;
use crate::shared::styled_icon;
use crate::Msg;
pub enum Variant {
Primary,
Success,
Danger,
Secondary,
Empty,
}
impl ToString for Variant {
fn to_string(&self) -> String {
match self {
Variant::Primary => "primary",
Variant::Success => "success",
Variant::Danger => "danger",
Variant::Secondary => "secondary",
Variant::Empty => "empty",
}
.to_string()
}
}
pub struct StyledButton {
pub variant: Variant,
pub icon_only: bool,
pub disabled: bool,
pub active: bool,
pub text: Option<String>,
pub icon: Option<Icon>,
}
impl Into<Node<Msg>> for StyledButton {
fn into(self) -> Node<Msg> {
styled_button(self)
}
}
impl StyledButton {
pub fn into_node(self) -> Node<Msg> {
self.into()
}
}
pub fn styled_button(values: StyledButton) -> Node<Msg> {
let StyledButton {
text,
variant,
icon_only,
disabled,
active,
icon,
} = values;
let mut class_list = vec!["styledButton".to_string(), variant.to_string()];
if icon_only {
class_list.push("iconOnly".to_string());
}
if active {
class_list.push("isActive".to_string());
}
if icon.is_some() {
class_list.push("withIcon".to_string());
}
let icon_node = match icon {
None => empty![],
Some(i) => styled_icon(i),
};
button![
attrs![
At::Class => class_list.join(" "),
],
match disabled {
true => vec![attrs![At::Disabled => true]],
false => vec![],
},
icon_node,
span![attrs![At::Class => "text"], text.unwrap_or_default()],
]
}

View File

@ -0,0 +1,37 @@
use seed::{prelude::*, *};
use crate::Msg;
pub struct StyledTooltip {
pub visible: bool,
pub class_name: String,
pub children: Node<Msg>,
}
impl Into<Node<Msg>> for StyledTooltip {
fn into(self) -> Node<Msg> {
styled_tooltip(self)
}
}
impl StyledTooltip {
pub fn into_node(self) -> Node<Msg> {
self.into()
}
}
pub fn styled_tooltip(values: StyledTooltip) -> Node<Msg> {
let StyledTooltip {
visible,
class_name,
children,
} = values;
if visible {
div![
attrs![At::Class => format!("styledTooltip {}", class_name)],
children
]
} else {
empty!()
}
}

View File

@ -83,11 +83,4 @@ joinable!(issues -> users (reporter_id));
joinable!(tokens -> users (user_id)); joinable!(tokens -> users (user_id));
joinable!(users -> projects (project_id)); joinable!(users -> projects (project_id));
allow_tables_to_appear_in_same_query!( allow_tables_to_appear_in_same_query!(comments, issue_assignees, issues, projects, tokens, users,);
comments,
issue_assignees,
issues,
projects,
tokens,
users,
);