Finish visual aside
This commit is contained in:
parent
c58d9fb9f1
commit
aa5216a547
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
30
jirs-client/js/css/styledTooltip.css
Normal file
30
jirs-client/js/css/styledTooltip.css
Normal 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;
|
||||||
|
}
|
@ -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";
|
||||||
|
@ -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>) {
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"], ""]
|
||||||
}
|
}
|
85
jirs-client/src/shared/styled_button.rs
Normal file
85
jirs-client/src/shared/styled_button.rs
Normal 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()],
|
||||||
|
]
|
||||||
|
}
|
37
jirs-client/src/shared/styled_tooltip.rs
Normal file
37
jirs-client/src/shared/styled_tooltip.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
|
||||||
);
|
|
||||||
|
Loading…
Reference in New Issue
Block a user