Add modal and initial select
This commit is contained in:
parent
4a9ba8e2a3
commit
28d35026ef
38
jirs-client/js/css/issue.css
Normal file
38
jirs-client/js/css/issue.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
.issueDetails > .content {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 30px 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .left {
|
||||||
|
width: 65%;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .content > .right {
|
||||||
|
width: 35%;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .topActions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 21px 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .topActions > .topActionsRight {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .topActions > .topActionsRight > * {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issueDetails > .sectionTitle {
|
||||||
|
margin: 24px 0 5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--textMedium);
|
||||||
|
font-size: 12.5px;
|
||||||
|
font-family: "CircularStdBold", serif;
|
||||||
|
font-weight: normal
|
||||||
|
}
|
98
jirs-client/js/css/styledSelect.css
Normal file
98
jirs-client/js/css/styledSelect.css
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
.styledSelect {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect.normal {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--borderLightest);
|
||||||
|
background: var(--backgroundLightest);
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect.empty {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect:hover {
|
||||||
|
background: var(--backgroundLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect.normal:focus {
|
||||||
|
border: 1px solid var(--borderInputFocus);
|
||||||
|
box-shadow: 0 0 0 1px var(--borderInputFocus);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect.invalid, .styledSelect.invalid:focus {
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .dropDownInput {
|
||||||
|
padding: 10px 14px 8px;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
color: var(--textDarkest);
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .dropDownInput:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options::-webkit-scrollbar-track {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 99px;
|
||||||
|
background: var(--backgroundMedium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options > .option {
|
||||||
|
padding: 8px 14px;
|
||||||
|
word-break: break-word;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options > .option:last-of-type {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .options > .option.jira-select-option-is-active {
|
||||||
|
background: var(--backgroundLightPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .noOptions {
|
||||||
|
padding: 5px 15px 15px;
|
||||||
|
color: var(--textLight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.styledSelect > .styledIcon {
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 7px;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--textMedium);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
@ -4,10 +4,12 @@
|
|||||||
@import "css/global.css";
|
@import "css/global.css";
|
||||||
@import "css/sidebar.css";
|
@import "css/sidebar.css";
|
||||||
@import "css/aside.css";
|
@import "css/aside.css";
|
||||||
@import "css/icon.css";
|
@import "css/styledIcon.css";
|
||||||
@import "css/shared.css";
|
@import "css/shared.css";
|
||||||
@import "css/styledTooltip.css";
|
@import "css/styledTooltip.css";
|
||||||
@import "css/styledAvatar.css";
|
@import "css/styledAvatar.css";
|
||||||
|
@import "css/styledSelect.css";
|
||||||
@import "css/app.css";
|
@import "css/app.css";
|
||||||
@import "css/modal.css";
|
@import "css/modal.css";
|
||||||
|
@import "css/issue.css";
|
||||||
@import "css/project.css";
|
@import "css/project.css";
|
||||||
|
@ -37,11 +37,13 @@ pub enum Msg {
|
|||||||
// dragging
|
// dragging
|
||||||
IssueDragStarted(IssueId),
|
IssueDragStarted(IssueId),
|
||||||
IssueDragStopped(IssueId),
|
IssueDragStopped(IssueId),
|
||||||
IssueDragOver(f64, f64),
|
|
||||||
IssueDropZone(IssueStatus),
|
IssueDropZone(IssueStatus),
|
||||||
|
|
||||||
// issues
|
// issues
|
||||||
IssueUpdateResult(FetchObject<String>),
|
IssueUpdateResult(FetchObject<String>),
|
||||||
|
|
||||||
|
// modals
|
||||||
|
CloseModal,
|
||||||
}
|
}
|
||||||
|
|
||||||
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>) {
|
||||||
@ -52,6 +54,9 @@ fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
|||||||
Msg::ChangePage(page) => {
|
Msg::ChangePage(page) => {
|
||||||
model.page = page;
|
model.page = page;
|
||||||
}
|
}
|
||||||
|
Msg::CloseModal => {
|
||||||
|
model.modal = None;
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
crate::shared::update(&msg, model, orders);
|
crate::shared::update(&msg, model, orders);
|
||||||
|
@ -9,6 +9,11 @@ use crate::{IssueId, UserId, HOST_URL};
|
|||||||
|
|
||||||
pub type ProjectId = i32;
|
pub type ProjectId = i32;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
|
pub enum ModalType {
|
||||||
|
EditIssue(IssueId),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
@ -47,12 +52,6 @@ pub struct UpdateProjectForm {
|
|||||||
pub fields: UpdateProjectPayload,
|
pub fields: UpdateProjectPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
|
||||||
pub struct Point {
|
|
||||||
pub x: f64,
|
|
||||||
pub y: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct ProjectPage {
|
pub struct ProjectPage {
|
||||||
pub about_tooltip_visible: bool,
|
pub about_tooltip_visible: bool,
|
||||||
@ -61,7 +60,6 @@ pub struct ProjectPage {
|
|||||||
pub only_my_filter: bool,
|
pub only_my_filter: bool,
|
||||||
pub recently_updated_filter: bool,
|
pub recently_updated_filter: bool,
|
||||||
pub dragged_issue_id: Option<IssueId>,
|
pub dragged_issue_id: Option<IssueId>,
|
||||||
pub drag_point: Point,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -77,6 +75,7 @@ pub struct Model {
|
|||||||
pub page: Page,
|
pub page: Page,
|
||||||
pub host_url: String,
|
pub host_url: String,
|
||||||
pub project_page: ProjectPage,
|
pub project_page: ProjectPage,
|
||||||
|
pub modal: Option<ModalType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Model {
|
impl Default for Model {
|
||||||
@ -100,8 +99,8 @@ impl Default for Model {
|
|||||||
only_my_filter: false,
|
only_my_filter: false,
|
||||||
recently_updated_filter: false,
|
recently_updated_filter: false,
|
||||||
dragged_issue_id: None,
|
dragged_issue_id: None,
|
||||||
drag_point: Point::default(),
|
|
||||||
},
|
},
|
||||||
|
modal: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@ use seed::{prelude::*, *};
|
|||||||
|
|
||||||
use jirs_data::*;
|
use jirs_data::*;
|
||||||
|
|
||||||
use crate::model::{Icon, Model, Page};
|
use crate::model::{Icon, ModalType, Model, Page};
|
||||||
|
use crate::shared::modal::{Modal, Variant as ModalVariant};
|
||||||
use crate::shared::styled_avatar::StyledAvatar;
|
use crate::shared::styled_avatar::StyledAvatar;
|
||||||
use crate::shared::styled_button::{StyledButton, Variant};
|
use crate::shared::styled_button::{StyledButton, Variant as ButtonVariant};
|
||||||
use crate::shared::styled_input::StyledInput;
|
use crate::shared::styled_input::StyledInput;
|
||||||
use crate::shared::{drag_ev, inner_layout, ToNode};
|
use crate::shared::styled_select::StyledSelect;
|
||||||
|
use crate::shared::{drag_ev, find_issue, inner_layout, ToNode};
|
||||||
use crate::Msg;
|
use crate::Msg;
|
||||||
|
|
||||||
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>) {
|
||||||
@ -19,6 +21,15 @@ 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::ChangePage(Page::EditIssue(issue_id)) => {
|
||||||
|
orders
|
||||||
|
.skip()
|
||||||
|
.perform_cmd(crate::api::fetch_current_project(model.host_url.clone()));
|
||||||
|
orders
|
||||||
|
.skip()
|
||||||
|
.perform_cmd(crate::api::fetch_current_user(model.host_url.clone()));
|
||||||
|
model.modal = Some(ModalType::EditIssue(issue_id));
|
||||||
|
}
|
||||||
Msg::ToggleAboutTooltip => {
|
Msg::ToggleAboutTooltip => {
|
||||||
model.project_page.about_tooltip_visible = !model.project_page.about_tooltip_visible;
|
model.project_page.about_tooltip_visible = !model.project_page.about_tooltip_visible;
|
||||||
}
|
}
|
||||||
@ -58,10 +69,6 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
Msg::IssueDragStopped(_) => {
|
Msg::IssueDragStopped(_) => {
|
||||||
model.project_page.dragged_issue_id = None;
|
model.project_page.dragged_issue_id = None;
|
||||||
}
|
}
|
||||||
Msg::IssueDragOver(x, y) => {
|
|
||||||
model.project_page.drag_point.x = x;
|
|
||||||
model.project_page.drag_point.y = y;
|
|
||||||
}
|
|
||||||
Msg::IssueDropZone(status) => {
|
Msg::IssueDropZone(status) => {
|
||||||
match (
|
match (
|
||||||
model.project_page.dragged_issue_id.as_ref().cloned(),
|
model.project_page.dragged_issue_id.as_ref().cloned(),
|
||||||
@ -116,6 +123,25 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(model: &Model) -> Node<Msg> {
|
pub fn view(model: &Model) -> Node<Msg> {
|
||||||
|
let modal = match model.modal {
|
||||||
|
Some(ModalType::EditIssue(issue_id)) => {
|
||||||
|
if let Some(issue) = find_issue(model, issue_id) {
|
||||||
|
let details = issue_details(model, issue);
|
||||||
|
let modal = Modal {
|
||||||
|
variant: ModalVariant::Center,
|
||||||
|
width: 1040,
|
||||||
|
with_icon: false,
|
||||||
|
children: vec![details],
|
||||||
|
}
|
||||||
|
.into_node();
|
||||||
|
Some(modal)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let project_section = vec![
|
let project_section = vec![
|
||||||
breadcrumbs(model),
|
breadcrumbs(model),
|
||||||
header(),
|
header(),
|
||||||
@ -123,7 +149,7 @@ pub fn view(model: &Model) -> Node<Msg> {
|
|||||||
project_board_lists(model),
|
project_board_lists(model),
|
||||||
];
|
];
|
||||||
|
|
||||||
inner_layout(model, "projectPage", project_section)
|
inner_layout(model, "projectPage", project_section, modal)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn breadcrumbs(model: &Model) -> Node<Msg> {
|
fn breadcrumbs(model: &Model) -> Node<Msg> {
|
||||||
@ -144,7 +170,7 @@ fn breadcrumbs(model: &Model) -> Node<Msg> {
|
|||||||
|
|
||||||
fn header() -> Node<Msg> {
|
fn header() -> Node<Msg> {
|
||||||
let button = StyledButton {
|
let button = StyledButton {
|
||||||
variant: Variant::Secondary,
|
variant: ButtonVariant::Secondary,
|
||||||
icon_only: false,
|
icon_only: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
active: false,
|
active: false,
|
||||||
@ -175,7 +201,7 @@ 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 {
|
||||||
variant: Variant::Empty,
|
variant: ButtonVariant::Empty,
|
||||||
icon_only: false,
|
icon_only: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
active: model.project_page.only_my_filter,
|
active: model.project_page.only_my_filter,
|
||||||
@ -186,7 +212,7 @@ fn project_board_filters(model: &Model) -> Node<Msg> {
|
|||||||
.into_node();
|
.into_node();
|
||||||
|
|
||||||
let recently_updated = StyledButton {
|
let recently_updated = StyledButton {
|
||||||
variant: Variant::Empty,
|
variant: ButtonVariant::Empty,
|
||||||
icon_only: false,
|
icon_only: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
active: model.project_page.recently_updated_filter,
|
active: model.project_page.recently_updated_filter,
|
||||||
@ -345,8 +371,10 @@ fn project_issue(model: &Model, project: &FullProject, issue: &Issue) -> Node<Ms
|
|||||||
class_list.push("hidden");
|
class_list.push("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let href = format!("/issues/{id}", id = issue_id);
|
||||||
|
|
||||||
a![
|
a![
|
||||||
attrs![At::Class => "issueLink"],
|
attrs![At::Class => "issueLink"; At::Href => href],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => class_list.join(" "), At::Draggable => true],
|
attrs![At::Class => class_list.join(" "), At::Draggable => true],
|
||||||
drag_started,
|
drag_started,
|
||||||
@ -363,3 +391,43 @@ fn project_issue(model: &Model, project: &FullProject, issue: &Issue) -> Node<Ms
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToNode for IssueType {
|
||||||
|
fn into_node(self) -> Node<Msg> {
|
||||||
|
div![self.to_string()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn issue_details(_model: &Model, _issue: &Issue) -> Node<Msg> {
|
||||||
|
let issue_type_select = StyledSelect {
|
||||||
|
on_change: mouse_ev(Ev::Click, |_| Msg::NoOp),
|
||||||
|
variant: crate::shared::styled_select::Variant::Empty,
|
||||||
|
width: 150,
|
||||||
|
name: None,
|
||||||
|
placeholder: None,
|
||||||
|
valid: false,
|
||||||
|
is_multi: false,
|
||||||
|
allow_clear: true,
|
||||||
|
options: vec![IssueType::Story, IssueType::Task, IssueType::Bug],
|
||||||
|
}
|
||||||
|
.into_node();
|
||||||
|
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "issueDetails"],
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "topActions"],
|
||||||
|
issue_type_select,
|
||||||
|
div![attrs![At::Class => "topActionsRight"]],
|
||||||
|
],
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "content"],
|
||||||
|
div![
|
||||||
|
attrs![At::Class => "left"],
|
||||||
|
div![attrs![At::Class => "title"]],
|
||||||
|
div![attrs![At::Class => "description"]],
|
||||||
|
div![attrs![At::Class => "comments"]],
|
||||||
|
],
|
||||||
|
div![attrs![At::Class => "right"]],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -8,5 +8,5 @@ pub fn update(_msg: Msg, _model: &mut model::Model, _orders: &mut impl Orders<Ms
|
|||||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||||
let project_section = vec![];
|
let project_section = vec![];
|
||||||
|
|
||||||
inner_layout(model, "projectSettings", project_section)
|
inner_layout(model, "projectSettings", project_section, None)
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use seed::{prelude::*, *};
|
use seed::{prelude::*, *};
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
use jirs_data::Issue;
|
||||||
|
|
||||||
use crate::model::{Icon, Model};
|
use crate::model::{Icon, Model};
|
||||||
use crate::Msg;
|
use crate::{IssueId, Msg};
|
||||||
|
|
||||||
pub mod aside;
|
pub mod aside;
|
||||||
pub mod modal;
|
pub mod modal;
|
||||||
@ -10,8 +12,16 @@ pub mod navbar_left;
|
|||||||
pub mod styled_avatar;
|
pub mod styled_avatar;
|
||||||
pub mod styled_button;
|
pub mod styled_button;
|
||||||
pub mod styled_input;
|
pub mod styled_input;
|
||||||
|
pub mod styled_select;
|
||||||
pub mod styled_tooltip;
|
pub mod styled_tooltip;
|
||||||
|
|
||||||
|
pub fn find_issue(model: &Model, issue_id: IssueId) -> Option<&Issue> {
|
||||||
|
match model.project.as_ref() {
|
||||||
|
Some(p) => p.issues.iter().find(|issue| issue.id == issue_id),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ToNode {
|
pub trait ToNode {
|
||||||
fn into_node(self) -> Node<Msg>;
|
fn into_node(self) -> Node<Msg>;
|
||||||
}
|
}
|
||||||
@ -24,8 +34,18 @@ pub fn divider() -> Node<Msg> {
|
|||||||
div![attrs![At::Class => "divider"], ""]
|
div![attrs![At::Class => "divider"], ""]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_layout(model: &Model, page_name: &str, children: Vec<Node<Msg>>) -> Node<Msg> {
|
pub fn inner_layout(
|
||||||
|
model: &Model,
|
||||||
|
page_name: &str,
|
||||||
|
children: Vec<Node<Msg>>,
|
||||||
|
modal: Option<Node<Msg>>,
|
||||||
|
) -> Node<Msg> {
|
||||||
|
let modal_node = match modal {
|
||||||
|
Some(modal) => vec![modal],
|
||||||
|
_ => vec![],
|
||||||
|
};
|
||||||
article![
|
article![
|
||||||
|
modal_node,
|
||||||
attrs![At::Class => "inner-layout"],
|
attrs![At::Class => "inner-layout"],
|
||||||
id![page_name],
|
id![page_name],
|
||||||
navbar_left::render(model),
|
navbar_left::render(model),
|
||||||
|
@ -48,6 +48,7 @@ pub fn render(values: Modal) -> Node<Msg> {
|
|||||||
with_icon,
|
with_icon,
|
||||||
children,
|
children,
|
||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
let icon = if with_icon {
|
let icon = if with_icon {
|
||||||
let mut styled_icon = styled_icon(Icon::Close);
|
let mut styled_icon = styled_icon(Icon::Close);
|
||||||
styled_icon.add_class(variant.to_icon_class_name().to_string());
|
styled_icon.add_class(variant.to_icon_class_name().to_string());
|
||||||
@ -56,6 +57,12 @@ pub fn render(values: Modal) -> Node<Msg> {
|
|||||||
empty![]
|
empty![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let close_handler = mouse_ev(Ev::Click, |_| Msg::CloseModal);
|
||||||
|
let body_handler = mouse_ev(Ev::Click, |ev| {
|
||||||
|
ev.stop_propagation();
|
||||||
|
Msg::NoOp
|
||||||
|
});
|
||||||
|
|
||||||
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
let clickable_class = format!("clickableOverlay {}", variant.to_class_name());
|
||||||
let styled_modal_class = format!("styledModal {}", variant.to_class_name());
|
let styled_modal_class = format!("styledModal {}", variant.to_class_name());
|
||||||
let styled_modal_style = format!("max-width: {width}px", width = width);
|
let styled_modal_style = format!("max-width: {width}px", width = width);
|
||||||
@ -63,8 +70,10 @@ pub fn render(values: Modal) -> Node<Msg> {
|
|||||||
attrs![At::Class => "modal"],
|
attrs![At::Class => "modal"],
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => clickable_class],
|
attrs![At::Class => clickable_class],
|
||||||
|
close_handler,
|
||||||
div![
|
div![
|
||||||
attrs![At::Class => styled_modal_class, At::Style => styled_modal_style],
|
attrs![At::Class => styled_modal_class, At::Style => styled_modal_style],
|
||||||
|
body_handler,
|
||||||
icon,
|
icon,
|
||||||
children
|
children
|
||||||
]
|
]
|
||||||
|
93
jirs-client/src/shared/styled_select.rs
Normal file
93
jirs-client/src/shared/styled_select.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use seed::{prelude::*, *};
|
||||||
|
|
||||||
|
use crate::shared::ToNode;
|
||||||
|
use crate::Msg;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Variant {
|
||||||
|
Empty,
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StyledSelect<Child>
|
||||||
|
where
|
||||||
|
Child: ToNode,
|
||||||
|
{
|
||||||
|
pub on_change: EventHandler<Msg>,
|
||||||
|
pub variant: Variant,
|
||||||
|
pub width: usize,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub placeholder: Option<String>,
|
||||||
|
pub valid: bool,
|
||||||
|
pub is_multi: bool,
|
||||||
|
pub allow_clear: bool,
|
||||||
|
pub options: Vec<Child>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Child> ToNode for StyledSelect<Child>
|
||||||
|
where
|
||||||
|
Child: ToNode,
|
||||||
|
{
|
||||||
|
fn into_node(self) -> Node<Msg> {
|
||||||
|
render(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<Child>(values: StyledSelect<Child>) -> Node<Msg>
|
||||||
|
where
|
||||||
|
Child: ToNode,
|
||||||
|
{
|
||||||
|
let StyledSelect {
|
||||||
|
on_change,
|
||||||
|
variant,
|
||||||
|
width,
|
||||||
|
name,
|
||||||
|
placeholder,
|
||||||
|
valid,
|
||||||
|
is_multi,
|
||||||
|
allow_clear,
|
||||||
|
options,
|
||||||
|
} = values;
|
||||||
|
|
||||||
|
let select_style = format!("width: {width}px", width = width);
|
||||||
|
let mut select_class = vec!["styledSelect"];
|
||||||
|
if !valid {
|
||||||
|
select_class.push("invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
let children: Vec<Node<Msg>> = options
|
||||||
|
.into_iter()
|
||||||
|
.map(|child| render_option(child.into_node()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let clear_icon = match allow_clear {
|
||||||
|
true => crate::shared::styled_icon(crate::model::Icon::Close),
|
||||||
|
false => empty![],
|
||||||
|
};
|
||||||
|
|
||||||
|
seed::div![
|
||||||
|
on_change.clone(),
|
||||||
|
attrs![At::Class => "styledSelect", At::Style => select_style],
|
||||||
|
seed::input![
|
||||||
|
attrs![
|
||||||
|
At::Class => "dropDownInput",
|
||||||
|
At::Type => "text"
|
||||||
|
At::Placeholder => "Search"
|
||||||
|
At::AutoFocus => true,
|
||||||
|
],
|
||||||
|
on_change,
|
||||||
|
],
|
||||||
|
clear_icon,
|
||||||
|
seed::div![
|
||||||
|
attrs![
|
||||||
|
At::Class => "options",
|
||||||
|
],
|
||||||
|
children
|
||||||
|
],
|
||||||
|
seed::div![attrs![At::Class => "noOptions"], "No results"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_option(content: Node<Msg>) -> Node<Msg> {
|
||||||
|
seed::div![attrs![At::Class => "option"], content,]
|
||||||
|
}
|
2
react-client/src/App/BaseStyles.js
vendored
2
react-client/src/App/BaseStyles.js
vendored
@ -49,7 +49,7 @@ export default createGlobalStyle`
|
|||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, strong {
|
h1, h2, h3, h4, h5, h6, strong {
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { color, font } from 'shared/utils/styles';
|
import { color } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const Tip = styled.div`
|
export const Tip = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -22,6 +22,6 @@ export const TipLetter = styled.span`
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
color: ${color.textDarkest};
|
color: ${color.textDarkest};
|
||||||
background: ${color.backgroundMedium};
|
background: ${color.backgroundMedium};
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
font-size: 12px
|
font-size: 12px
|
||||||
`;
|
`;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { color, font } from 'shared/utils/styles';
|
import { color } from 'shared/utils/styles';
|
||||||
|
|
||||||
export const Content = styled.div`
|
export const Content = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -36,5 +36,5 @@ export const SectionTitle = styled.div`
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: ${color.textMedium};
|
color: ${color.textMedium};
|
||||||
font-size: 12.5px
|
font-size: 12.5px
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
`;
|
`;
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { IssueType, IssueTypeCopy } from 'shared/constants/issues';
|
import { IssueType, IssueTypeCopy } from '../../../../shared/constants/issues';
|
||||||
import { IssueTypeIcon, Select } from 'shared/components';
|
import { IssueTypeIcon, Select } from '../../../../shared/components';
|
||||||
|
|
||||||
import { TypeButton, Type, TypeLabel } from './Styles';
|
import { Type, TypeButton, TypeLabel } from './Styles';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
issue: PropTypes.object.isRequired,
|
issue: PropTypes.object.isRequired,
|
||||||
updateIssue: PropTypes.func.isRequired,
|
updateIssue: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProjectBoardIssueDetailsType = ({ issue, updateIssue }) => (
|
const ProjectBoardIssueDetailsType = ({ issue, updateIssue }) => (
|
||||||
<Select
|
<Select
|
||||||
variant="empty"
|
variant="empty"
|
||||||
dropdownWidth={150}
|
dropdownWidth={150}
|
||||||
withClearValue={false}
|
withClearValue={false}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { color, font, mixin } from 'shared/utils/styles';
|
import { color, font } from 'shared/utils/styles';
|
||||||
import { InputDebounced, Spinner, Icon } from 'shared/components';
|
import { Icon, InputDebounced, Spinner } from 'shared/components';
|
||||||
|
|
||||||
export const IssueSearch = styled.div`
|
export const IssueSearch = styled.div`
|
||||||
padding: 25px 35px 60px;
|
padding: 25px 35px 60px;
|
||||||
@ -76,7 +76,7 @@ export const SectionTitle = styled.div`
|
|||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color: ${color.textMedium};
|
color: ${color.textMedium};
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
font-size: 11.5px
|
font-size: 11.5px
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { font, sizes, color, mixin, zIndexValues } from 'shared/utils/styles';
|
import { color, mixin, sizes, zIndexValues } from 'shared/utils/styles';
|
||||||
import { Logo } from 'shared/components';
|
import { Logo } from 'shared/components';
|
||||||
|
|
||||||
export const NavLeft = styled.aside`
|
export const NavLeft = styled.aside`
|
||||||
z-index: ${zIndexValues.navLeft};
|
z-index: ${zIndexValues.navLeft};
|
||||||
@ -71,7 +71,7 @@ export const ItemText = styled.div`
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
transition: all 0.1s;
|
transition: all 0.1s;
|
||||||
transition-property: right, visibility, opacity;
|
transition-property: right, visibility, opacity;
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
font-size: 12px
|
font-size: 12px
|
||||||
${NavLeft}:hover & {
|
${NavLeft}:hover & {
|
||||||
right: 0;
|
right: 0;
|
||||||
|
2
react-client/src/Project/Sidebar/Styles.js
vendored
2
react-client/src/Project/Sidebar/Styles.js
vendored
@ -90,7 +90,7 @@ export const NotImplemented = styled.div`
|
|||||||
background: ${color.backgroundMedium};
|
background: ${color.backgroundMedium};
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
${LinkItem}:hover & {
|
${LinkItem}:hover & {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export const DateSection = styled.div`
|
|||||||
export const SelectedMonthYear = styled.div`
|
export const SelectedMonthYear = styled.div`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-left: 7px;
|
padding-left: 7px;
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
font-size: 16px
|
font-size: 16px
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import React, { useState, useRef, useLayoutEffect } from 'react';
|
import React, { useLayoutEffect, useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
|
|
||||||
import { KeyCodes } from 'shared/constants/keyCodes';
|
import { KeyCodes } from '../../../shared/constants/keyCodes';
|
||||||
|
|
||||||
import { ClearIcon, Dropdown, DropdownInput, Options, Option, OptionsNoResults } from './Styles';
|
import { ClearIcon, Dropdown, DropdownInput, Option, Options, OptionsNoResults } from './Styles';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
dropdownWidth: PropTypes.number,
|
dropdownWidth: PropTypes.number,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
isValueEmpty: PropTypes.bool.isRequired,
|
isValueEmpty: PropTypes.bool.isRequired,
|
||||||
searchValue: PropTypes.string.isRequired,
|
searchValue: PropTypes.string.isRequired,
|
||||||
setSearchValue: PropTypes.func.isRequired,
|
setSearchValue: PropTypes.func.isRequired,
|
||||||
$inputRef: PropTypes.object.isRequired,
|
$inputRef: PropTypes.object.isRequired,
|
||||||
deactivateDropdown: PropTypes.func.isRequired,
|
deactivateDropdown: PropTypes.func.isRequired,
|
||||||
@ -177,12 +177,12 @@ const SelectDropdown = ({
|
|||||||
return (
|
return (
|
||||||
<Dropdown width={dropdownWidth}>
|
<Dropdown width={dropdownWidth}>
|
||||||
<DropdownInput
|
<DropdownInput
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
ref={$inputRef}
|
autoFocus
|
||||||
autoFocus
|
ref={$inputRef}
|
||||||
onKeyDown={handleInputKeyDown}
|
onKeyDown={handleInputKeyDown}
|
||||||
onChange={event => setSearchValue(event.target.value)}
|
onChange={event => setSearchValue(event.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isValueEmpty && withClearValue && <ClearIcon type="close" onClick={clearOptionValues} />}
|
{!isValueEmpty && withClearValue && <ClearIcon type="close" onClick={clearOptionValues} />}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
import { color, mixin, zIndexValues } from 'shared/utils/styles';
|
import { color, mixin, zIndexValues } from 'shared/utils/styles';
|
||||||
import Icon from 'shared/components/Icon';
|
import Icon from 'shared/components/Icon';
|
||||||
|
|
||||||
export const StyledSelect = styled.div`
|
export const StyledSelect = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -119,14 +119,14 @@ export const ClearIcon = styled(Icon)`
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: ${color.textMedium};
|
color: ${color.textMedium};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Options = styled.div`
|
export const Options = styled.div`
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
-webkit-overflow-scrolling: touch;;
|
-webkit-overflow-scrolling: touch;
|
||||||
${mixin.customScrollbar()};
|
${mixin.customScrollbar()};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,64 +1,22 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||||
import { KeyCodes } from 'shared/constants/keyCodes';
|
import { KeyCodes } from 'shared/constants/keyCodes';
|
||||||
import Icon from 'shared/components/Icon';
|
import Icon from 'shared/components/Icon';
|
||||||
|
|
||||||
import Dropdown from './Dropdown';
|
import Dropdown from './Dropdown';
|
||||||
import {
|
import { AddMore, ChevronIcon, Placeholder, StyledSelect, ValueContainer, ValueMulti, ValueMultiItem, } from './Styles';
|
||||||
StyledSelect,
|
|
||||||
ValueContainer,
|
|
||||||
ChevronIcon,
|
|
||||||
Placeholder,
|
|
||||||
ValueMulti,
|
|
||||||
ValueMultiItem,
|
|
||||||
AddMore,
|
|
||||||
} from './Styles';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
className: PropTypes.string,
|
|
||||||
variant: PropTypes.oneOf(['normal', 'empty']),
|
|
||||||
dropdownWidth: PropTypes.number,
|
|
||||||
name: PropTypes.string,
|
|
||||||
value: PropTypes.oneOfType([PropTypes.array, PropTypes.string, PropTypes.number]),
|
|
||||||
defaultValue: PropTypes.any,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
invalid: PropTypes.bool,
|
|
||||||
options: PropTypes.array.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onCreate: PropTypes.func,
|
|
||||||
isMulti: PropTypes.bool,
|
|
||||||
withClearValue: PropTypes.bool,
|
|
||||||
renderValue: PropTypes.func,
|
|
||||||
renderOption: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultProps = {
|
|
||||||
className: undefined,
|
|
||||||
variant: 'normal',
|
|
||||||
dropdownWidth: undefined,
|
|
||||||
name: undefined,
|
|
||||||
value: undefined,
|
|
||||||
defaultValue: undefined,
|
|
||||||
placeholder: 'Select',
|
|
||||||
invalid: false,
|
|
||||||
onCreate: undefined,
|
|
||||||
isMulti: false,
|
|
||||||
withClearValue: true,
|
|
||||||
renderValue: undefined,
|
|
||||||
renderOption: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const Select = ({
|
const Select = ({
|
||||||
className,
|
className,
|
||||||
variant,
|
variant,
|
||||||
dropdownWidth,
|
dropdownWidth,
|
||||||
name,
|
name,
|
||||||
value: propsValue,
|
value: propsValue,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
placeholder,
|
placeholder,
|
||||||
invalid,
|
invalid,
|
||||||
options,
|
options,
|
||||||
onChange,
|
onChange,
|
||||||
onCreate,
|
onCreate,
|
||||||
@ -183,27 +141,57 @@ const Select = ({
|
|||||||
|
|
||||||
{isDropdownOpen && (
|
{isDropdownOpen && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownWidth={dropdownWidth}
|
dropdownWidth={dropdownWidth}
|
||||||
value={value}
|
value={value}
|
||||||
isValueEmpty={isValueEmpty}
|
isValueEmpty={isValueEmpty}
|
||||||
searchValue={searchValue}
|
searchValue={searchValue}
|
||||||
setSearchValue={setSearchValue}
|
setSearchValue={setSearchValue}
|
||||||
$selectRef={$selectRef}
|
$selectRef={$selectRef}
|
||||||
$inputRef={$inputRef}
|
$inputRef={$inputRef}
|
||||||
deactivateDropdown={deactivateDropdown}
|
deactivateDropdown={deactivateDropdown}
|
||||||
options={options}
|
options={options}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onCreate={onCreate}
|
onCreate={onCreate}
|
||||||
isMulti={isMulti}
|
isMulti={isMulti}
|
||||||
withClearValue={withClearValue}
|
withClearValue={withClearValue}
|
||||||
propsRenderOption={propsRenderOption}
|
propsRenderOption={propsRenderOption}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</StyledSelect>
|
</StyledSelect>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Select.propTypes = propTypes;
|
Select.propTypes = {
|
||||||
Select.defaultProps = defaultProps;
|
className: PropTypes.string,
|
||||||
|
variant: PropTypes.oneOf(['normal', 'empty']),
|
||||||
|
dropdownWidth: PropTypes.number,
|
||||||
|
name: PropTypes.string,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.array, PropTypes.string, PropTypes.number]),
|
||||||
|
defaultValue: PropTypes.any,
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
invalid: PropTypes.bool,
|
||||||
|
options: PropTypes.array.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onCreate: PropTypes.func,
|
||||||
|
isMulti: PropTypes.bool,
|
||||||
|
withClearValue: PropTypes.bool,
|
||||||
|
renderValue: PropTypes.func,
|
||||||
|
renderOption: PropTypes.func,
|
||||||
|
};
|
||||||
|
Select.defaultProps = {
|
||||||
|
className: undefined,
|
||||||
|
variant: 'normal',
|
||||||
|
dropdownWidth: undefined,
|
||||||
|
name: undefined,
|
||||||
|
value: undefined,
|
||||||
|
defaultValue: undefined,
|
||||||
|
placeholder: 'Select',
|
||||||
|
invalid: false,
|
||||||
|
onCreate: undefined,
|
||||||
|
isMulti: false,
|
||||||
|
withClearValue: true,
|
||||||
|
renderValue: undefined,
|
||||||
|
renderOption: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
export default Select;
|
export default Select;
|
||||||
|
6
react-client/src/shared/utils/styles.js
vendored
6
react-client/src/shared/utils/styles.js
vendored
@ -1,7 +1,7 @@
|
|||||||
import { css } from 'styled-components';
|
import { css } from 'styled-components';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
|
|
||||||
import { IssueType, IssueStatus, IssuePriority } from 'shared/constants/issues';
|
import { IssuePriority, IssueStatus, IssueType } from 'shared/constants/issues';
|
||||||
|
|
||||||
export const color = {
|
export const color = {
|
||||||
primary: '#0052cc', // Blue
|
primary: '#0052cc', // Blue
|
||||||
@ -174,7 +174,7 @@ export const mixin = {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
color: ${colorValue};
|
color: ${colorValue};
|
||||||
background: ${background};
|
background: ${background};
|
||||||
${font.bold}
|
font-family: "CircularStdBold"; font-weight: normal
|
||||||
font-size: 12px
|
font-size: 12px
|
||||||
i {
|
i {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
Loading…
Reference in New Issue
Block a user