Add edit epic and delete epic, refactor, fix drag sorting

This commit is contained in:
Adrian Woźniak 2021-01-15 22:57:26 +01:00
parent 7803e0fc3d
commit 1b8069103f
103 changed files with 17024 additions and 16197 deletions

125
Cargo.lock generated
View File

@ -1018,6 +1018,7 @@ dependencies = [
"bitflags",
"byteorder",
"chrono",
"derive_db_execute",
"diesel",
"dotenv",
"env_logger",
@ -1042,10 +1043,6 @@ dependencies = [
"uuid 0.8.1",
]
[[package]]
name = "database-actor-derive"
version = "0.1.0"
[[package]]
name = "dbg"
version = "1.0.4"
@ -1055,6 +1052,10 @@ dependencies = [
"version_check 0.1.5",
]
[[package]]
name = "derive_db_execute"
version = "0.1.0"
[[package]]
name = "derive_enum_iter"
version = "0.1.0"
@ -1353,18 +1354,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "filetime"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"winapi 0.3.9",
]
[[package]]
name = "flate2"
version = "1.0.19"
@ -1408,25 +1397,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fsevent"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
dependencies = [
"bitflags",
"fsevent-sys",
]
[[package]]
name = "fsevent-sys"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
dependencies = [
"libc",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@ -1605,12 +1575,6 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gloo-events"
version = "0.1.1"
@ -1647,26 +1611,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "gumdrop"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b"
dependencies = [
"gumdrop_derive",
]
[[package]]
name = "gumdrop_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "h2"
version = "0.2.7"
@ -1867,26 +1811,6 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "inotify"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
dependencies = [
"bitflags",
"inotify-sys",
"libc",
]
[[package]]
name = "inotify-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
dependencies = [
"libc",
]
[[package]]
name = "instant"
version = "0.1.9"
@ -1963,15 +1887,6 @@ dependencies = [
"toml",
]
[[package]]
name = "jirs-css"
version = "0.1.0"
dependencies = [
"glob",
"gumdrop",
"notify",
]
[[package]]
name = "jirs-data"
version = "0.1.0"
@ -2285,18 +2200,6 @@ dependencies = [
"winapi 0.2.8",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio",
"slab",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.7"
@ -2381,24 +2284,6 @@ dependencies = [
"version_check 0.1.5",
]
[[package]]
name = "notify"
version = "4.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd"
dependencies = [
"bitflags",
"filetime",
"fsevent",
"fsevent-sys",
"inotify",
"libc",
"mio",
"mio-extras",
"walkdir",
"winapi 0.3.9",
]
[[package]]
name = "num-bigint"
version = "0.2.6"

View File

@ -12,15 +12,14 @@
members = [
"./jirs-cli",
"./jirs-server",
"./jirs-css",
"./shared/jirs-config",
"./shared/jirs-data",
"./derive/derive_enum_iter",
"./derive/derive_enum_primitive",
"./derive/derive_enum_sql",
"./derive/derive_db_execute",
"./actors/highlight-actor",
"./actors/database-actor",
"./actors/database-actor/database_actor-derive",
"./actors/web-actor",
"./actors/websocket-actor",
"./actors/mail-actor",

View File

@ -54,6 +54,9 @@ features = ["database"]
path = "../../shared/jirs-data"
features = ["backend"]
[dependencies.derive_db_execute]
path = "../../derive/derive_db_execute"
[dependencies.diesel]
version = "1.4.5"
features = ["unstable", "postgres", "numeric", "extras", "uuidv07"]

View File

@ -1,29 +1,54 @@
use {
crate::{
db_create_with_conn, db_delete_with_conn, db_find, db_load, db_update_with_conn,
models::Issue,
},
crate::models::Issue,
derive_db_execute::Execute,
diesel::{expression::sql_literal::sql, prelude::*},
jirs_data::{IssueId, IssuePriority, IssueStatusId, IssueType, ProjectId, UserId},
};
db_find! {
LoadIssue,
msg => issues => issues.filter(id.eq(msg.issue_id)).distinct(),
Issue,
issue_id => IssueId
#[derive(Default, Execute)]
#[db_exec(
result = "Issue",
schema = "issues",
find = "issues.filter(id.eq(msg.issue_id)).distinct()"
)]
pub struct LoadIssue {
pub issue_id: IssueId,
}
db_load! {
LoadProjectIssues,
msg => issues => issues.filter(project_id.eq(msg.project_id)).distinct(),
Issue,
project_id => ProjectId
#[derive(Execute)]
#[db_exec(
result = "Issue",
schema = "issues",
load = "issues.filter(project_id.eq(msg.project_id)).distinct()"
)]
pub struct LoadProjectIssues {
pub project_id: ProjectId,
}
db_update_with_conn! {
UpdateIssue,
msg => conn => issues => {
#[derive(Default, Execute)]
#[db_exec(result = "Issue", schema = "issues")]
pub struct UpdateIssue {
pub issue_id: i32,
pub title: Option<String>,
pub issue_type: Option<IssueType>,
pub priority: Option<IssuePriority>,
pub list_position: Option<i32>,
pub description: Option<String>,
pub description_text: Option<String>,
pub estimate: Option<i32>,
pub time_spent: Option<i32>,
pub time_remaining: Option<i32>,
pub project_id: Option<i32>,
pub user_ids: Option<Vec<i32>>,
pub reporter_id: Option<i32>,
pub issue_status_id: Option<i32>,
pub epic_id: Option<Option<i32>>,
}
impl UpdateIssue {
fn execute(self, conn: &crate::DbPooledConn) -> Result<Issue, crate::DatabaseError> {
let msg = self;
use crate::schema::issues::dsl::*;
if let Some(user_ids) = msg.user_ids {
crate::issue_assignees::DropIssueAssignees {
issue_id: msg.issue_id,
@ -44,119 +69,144 @@ db_update_with_conn! {
}
.execute(conn)?;
}
diesel::update(issues.find(msg.issue_id)).set((
msg.title.map(|v| title.eq(v)),
msg.issue_type.map(|v| issue_type.eq(v)),
msg.issue_status_id.map(|v| issue_status_id.eq(v)),
msg.priority.map(|p| priority.eq(p)),
msg.list_position.map(|pos| list_position.eq(pos)),
msg.description.map(|desc| description.eq(desc)),
msg.description_text.map(|t| description_text.eq(t)),
msg.estimate.map(|v| estimate.eq(v)),
msg.time_spent.map(|v| time_spent.eq(v)),
msg.time_remaining.map(|v| time_remaining.eq(v)),
msg.project_id.map(|v| project_id.eq(v)),
msg.reporter_id.map(|v| reporter_id.eq(v)),
msg.epic_id.map(|v| epic_id.eq(v)),
updated_at.eq(chrono::Utc::now().naive_utc()),
))
},
Issue,
issue_id => i32,
title => Option<String>,
issue_type => Option<IssueType>,
priority => Option<IssuePriority>,
list_position => Option<i32>,
description => Option<String>,
description_text => Option<String>,
estimate => Option<i32>,
time_spent => Option<i32>,
time_remaining => Option<i32>,
project_id => Option<i32>,
user_ids => Option<Vec<i32>>,
reporter_id => Option<i32>,
issue_status_id => Option<i32>,
epic_id => Option<Option<i32>>
diesel::update(issues.find(msg.issue_id))
.set((
msg.title.map(|v| title.eq(v)),
msg.issue_type.map(|v| issue_type.eq(v)),
msg.issue_status_id.map(|v| issue_status_id.eq(v)),
msg.priority.map(|p| priority.eq(p)),
msg.list_position.map(|pos| list_position.eq(pos)),
msg.description.map(|desc| description.eq(desc)),
msg.description_text.map(|t| description_text.eq(t)),
msg.estimate.map(|v| estimate.eq(v)),
msg.time_spent.map(|v| time_spent.eq(v)),
msg.time_remaining.map(|v| time_remaining.eq(v)),
msg.project_id.map(|v| project_id.eq(v)),
msg.reporter_id.map(|v| reporter_id.eq(v)),
msg.epic_id.map(|v| epic_id.eq(v)),
updated_at.eq(chrono::Utc::now().naive_utc()),
))
.get_result(conn)
.map_err(|e| {
log::debug!("{:?}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::Create,
crate::ResourceKind::Issue,
)
})
}
}
db_delete_with_conn! {
DeleteIssue,
msg => conn => issues => {
#[derive(Execute)]
#[db_exec(
result = "Issue",
schema = "issues",
destroy = r#"{
crate::issue_assignees::DeleteIssueAssignees { issue_id: msg.issue_id }
.execute(conn)?;
diesel::delete(issues.find(msg.issue_id))
},
Issue,
issue_id => IssueId
}"#
)]
pub struct DeleteIssue {
pub issue_id: IssueId,
}
mod inner {
use {
crate::{db_create, models::Issue},
crate::models::Issue,
derive_db_execute::Execute,
diesel::prelude::*,
jirs_data::{IssuePriority, IssueStatusId, IssueType},
};
db_create! {
CreateIssue,
msg => issues => diesel::insert_into(issues)
.values((
title.eq(msg.title),
issue_type.eq(msg.issue_type),
issue_status_id.eq(msg.issue_status_id),
priority.eq(msg.priority),
list_position.eq(msg.list_position),
description.eq(msg.description),
description_text.eq(msg.description_text),
estimate.eq(msg.estimate),
time_spent.eq(msg.time_spent),
time_remaining.eq(msg.time_remaining),
reporter_id.eq(msg.reporter_id),
project_id.eq(msg.project_id),
epic_id.eq(msg.epic_id)
))
.on_conflict_do_nothing(),
Issue,
title => String,
list_position => i32,
issue_type => IssueType,
issue_status_id => IssueStatusId,
priority => IssuePriority,
description => Option<String>,
description_text => Option<String>,
estimate => Option<i32>,
time_spent => Option<i32>,
time_remaining => Option<i32>,
project_id => jirs_data::ProjectId,
reporter_id => jirs_data::UserId,
epic_id => Option<jirs_data::EpicId>
#[derive(Default, Execute)]
#[db_exec(
result = "Issue",
schema = "issues",
create = r#"
diesel::insert_into(issues)
.values((
title.eq(msg.title),
issue_type.eq(msg.issue_type),
issue_status_id.eq(msg.issue_status_id),
priority.eq(msg.priority),
list_position.eq(msg.list_position),
description.eq(msg.description),
description_text.eq(msg.description_text),
estimate.eq(msg.estimate),
time_spent.eq(msg.time_spent),
time_remaining.eq(msg.time_remaining),
reporter_id.eq(msg.reporter_id),
project_id.eq(msg.project_id),
epic_id.eq(msg.epic_id)
))
.on_conflict_do_nothing()
"#
)]
pub struct CreateIssue {
pub title: String,
pub list_position: i32,
pub issue_type: IssueType,
pub issue_status_id: IssueStatusId,
pub priority: IssuePriority,
pub description: Option<String>,
pub description_text: Option<String>,
pub estimate: Option<i32>,
pub time_spent: Option<i32>,
pub time_remaining: Option<i32>,
pub project_id: jirs_data::ProjectId,
pub reporter_id: jirs_data::UserId,
pub epic_id: Option<jirs_data::EpicId>,
}
}
db_create_with_conn! {
CreateIssue,
msg => conn => issues => {
#[derive(Execute)]
#[db_exec(result = "Issue", schema = "issues")]
pub struct CreateIssue {
pub title: String,
pub issue_type: IssueType,
pub issue_status_id: IssueStatusId,
pub priority: IssuePriority,
pub description: Option<String>,
pub description_text: Option<String>,
pub estimate: Option<i32>,
pub time_spent: Option<i32>,
pub time_remaining: Option<i32>,
pub project_id: jirs_data::ProjectId,
pub reporter_id: jirs_data::UserId,
pub user_ids: Vec<jirs_data::UserId>,
pub epic_id: Option<jirs_data::EpicId>,
}
impl CreateIssue {
fn execute(self, conn: &crate::DbPooledConn) -> Result<Issue, crate::DatabaseError> {
use crate::schema::issues::dsl::*;
let msg = self;
let pos = issues
.select(sql("COALESCE(max(list_position), 0) + 1"))
.get_result::<i32>(conn)
.map_err(|e| {
log::error!("resolve new issue position failed {}", e);
crate::DatabaseError::Issue(crate::IssueError::BadListPosition)
})?;
})?;
let i_s_id: IssueStatusId = if msg.issue_status_id == 0 {
crate::issue_statuses::LoadIssueStatuses { project_id: msg.project_id }
.execute(conn)?
.first()
.ok_or_else(|| crate::DatabaseError::Issue(crate::IssueError::NoIssueStatuses))?
.id
crate::issue_statuses::LoadIssueStatuses {
project_id: msg.project_id,
}
.execute(conn)?
.first()
.ok_or_else(|| crate::DatabaseError::Issue(crate::IssueError::NoIssueStatuses))?
.id
} else {
msg.issue_status_id
};
let assign_users = msg.user_ids
.iter()
.cloned()
.filter(|u_id| *u_id != msg.reporter_id)
.collect::<Vec<UserId>>();
let assign_users = msg
.user_ids
.iter()
.cloned()
.filter(|u_id| *u_id != msg.reporter_id)
.collect::<Vec<UserId>>();
let issue = inner::CreateIssue {
title: msg.title,
list_position: pos,
@ -171,25 +221,18 @@ db_create_with_conn! {
project_id: msg.project_id,
reporter_id: msg.reporter_id,
epic_id: msg.epic_id,
}.execute(conn)?;
}
.execute(conn)?;
crate::issue_assignees::AsignMultiple {
issue_id: issue.id,
user_ids: assign_users,
};
issues.find(issue.id)
},
Issue,
title => String,
issue_type => IssueType,
issue_status_id => IssueStatusId,
priority => IssuePriority,
description => Option<String>,
description_text => Option<String>,
estimate => Option<i32>,
time_spent => Option<i32>,
time_remaining => Option<i32>,
project_id => jirs_data::ProjectId,
reporter_id => jirs_data::UserId,
user_ids => Vec<jirs_data::UserId>,
epic_id => Option<jirs_data::EpicId>
issues.find(issue.id).get_result(conn).map_err(|e| {
log::error!("{:?}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::Create,
crate::ResourceKind::Issue,
)
})
}
}

View File

@ -1,6 +1,6 @@
use {
crate::{
db_create_with_conn, db_delete, db_load,
db_create, db_delete, db_load,
users::{FindUser, LookupUser},
},
diesel::prelude::*,
@ -30,7 +30,7 @@ pub enum CreateMessageReceiver {
Lookup { name: String, email: String },
}
db_create_with_conn! {
db_create! {
CreateMessage,
msg => conn => messages => {
let user: User = match msg.receiver {

View File

@ -147,13 +147,9 @@ macro_rules! db_load_field {
#[macro_export]
macro_rules! db_create {
($action: ident, $self: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
$crate::db_create_with_conn! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
}
}
#[macro_export]
macro_rules! db_create_with_conn {
($action: ident, $self: ident => $conn: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
$crate::db_create! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
};
($action: ident, $self: ident => $conn: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
pub struct $action {
$(pub $field : $ty),+
}
@ -191,15 +187,12 @@ macro_rules! db_create_with_conn {
}
};
}
#[macro_export]
macro_rules! db_update {
($action: ident, $self: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
$crate::db_update_with_conn! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
$crate::db_update! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
};
}
#[macro_export]
macro_rules! db_update_with_conn {
($action: ident, $self: ident => $conn: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
pub struct $action {
$(pub $field : $ty),+
@ -240,12 +233,8 @@ macro_rules! db_update_with_conn {
#[macro_export]
macro_rules! db_delete {
($action: ident, $self: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
$crate::db_delete_with_conn! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
$crate::db_delete! { $action, $self => conn => $schema => $q, $resource, $($field => $ty),+ }
};
}
#[macro_export]
macro_rules! db_delete_with_conn {
($action: ident, $self: ident => $conn: ident => $schema: ident => $q: expr, $resource: ident, $($field: ident => $ty: ty),+) => {
pub struct $action {
$(pub $field : $ty),+

View File

@ -1,5 +1,5 @@
use {
crate::{db_create_with_conn, db_find, db_load, db_update},
crate::{db_create, db_find, db_load, db_update},
diesel::prelude::*,
jirs_data::{NameString, Project, ProjectCategory, ProjectId, TimeTracking, UserId},
};
@ -38,7 +38,7 @@ mod inner {
}
}
db_create_with_conn! {
db_create! {
CreateProject,
msg => conn => projects => {
let p = inner::CreateProject {

View File

@ -1,5 +1,5 @@
use {
crate::{db_create, db_find, db_update_with_conn},
crate::{db_create, db_find, db_update},
diesel::prelude::*,
jirs_data::{Token, UserId},
};
@ -18,7 +18,7 @@ db_find! {
token => uuid::Uuid
}
db_update_with_conn! {
db_update! {
UseBindToken,
msg => conn => tokens => {
let token = FindBindToken { token: msg.token }.execute(conn)?;

View File

@ -1,5 +1,5 @@
use {
crate::{db_create, db_delete_with_conn, db_find, db_load, db_update_with_conn},
crate::{db_create, db_delete, db_find, db_load, db_update},
diesel::prelude::*,
jirs_data::{ProjectId, UserId, UserProject, UserProjectId, UserRole},
};
@ -48,7 +48,7 @@ mod inner {
}
}
db_update_with_conn! {
db_update! {
ChangeCurrentUserProject,
msg => conn => user_projects => {
FindUserProject {
@ -91,7 +91,7 @@ db_find! {
role => UserRole
}
db_delete_with_conn! {
db_delete! {
RemoveInvitedUser,
msg => conn => user_projects => {
if msg.invited_id == msg.inviter_id {

View File

@ -1,6 +1,6 @@
use {
crate::{
db_create, db_create_with_conn, db_find, db_load, db_update, projects::CreateProject, q,
db_create, db_find, db_load, db_update, projects::CreateProject, q,
user_projects::CreateUserProject, DbPooledConn,
},
diesel::prelude::*,
@ -64,7 +64,7 @@ db_create! {
email => EmailString
}
db_create_with_conn! {
db_create! {
Register,
msg => conn => users => {
if count_matching_users(msg.name.as_str(), msg.email.as_str(), conn) > 0 {

View File

@ -1,3 +1,4 @@
use jirs_data::{EpicId, IssueStatusId, ListPosition};
use {
crate::{WebSocketActor, WsHandler, WsResult},
database_actor::{
@ -148,7 +149,7 @@ impl WsHandler<UpdateIssueHandler> for WebSocketActor {
return Ok(None);
}
Err(e) => {
error!("{}", e);
error!("{:?}", e);
return Ok(None);
}
};
@ -187,7 +188,7 @@ impl WsHandler<CreateIssuePayload> for WebSocketActor {
return Ok(None);
}
Err(e) => {
error!("{}", e);
error!("{:?}", e);
return Ok(None);
}
};
@ -212,7 +213,7 @@ impl WsHandler<DeleteIssue> for WebSocketActor {
return Ok(None);
}
Err(e) => {
error!("{}", e);
error!("{:?}", e);
return Ok(None);
}
};
@ -255,3 +256,25 @@ impl WsHandler<LoadIssues> for WebSocketActor {
Ok(Some(WsMsg::ProjectIssuesLoaded(issues)))
}
}
pub struct SyncIssueListPosition(pub Vec<(IssueId, ListPosition, IssueStatusId, Option<EpicId>)>);
impl WsHandler<SyncIssueListPosition> for WebSocketActor {
fn handle_msg(&mut self, msg: SyncIssueListPosition, ctx: &mut Self::Context) -> WsResult {
let _project_id = self.require_user_project()?.project_id;
for (issue_id, list_position, status_id, epic_id) in msg.0 {
match block_on(self.db.send(database_actor::issues::UpdateIssue {
issue_id,
list_position: Some(list_position),
issue_status_id: Some(status_id),
epic_id: Some(epic_id),
..Default::default()
})) {
Ok(Ok(_)) => (),
_ => return Ok(None),
};
}
self.handle_msg(LoadIssues, ctx)
}
}

View File

@ -94,6 +94,9 @@ impl WebSocketActor {
)?,
WsMsg::IssueCreate(payload) => self.handle_msg(payload, ctx)?,
WsMsg::IssueDelete(id) => self.handle_msg(DeleteIssue { id }, ctx)?,
WsMsg::IssueSyncListPosition(sync) => {
self.handle_msg(SyncIssueListPosition(sync), ctx)?
}
WsMsg::ProjectIssuesLoad => self.handle_msg(LoadIssues, ctx)?,
// issue statuses

View File

@ -1,5 +1,5 @@
[package]
name = "database-actor-derive"
name = "derive_db_execute"
version = "0.1.0"
authors = ["Adrian Wozniak <adrian.wozniak@ita-prog.pl>"]
edition = "2018"
@ -9,9 +9,8 @@ license = "MPL-2.0"
#license-file = "../LICENSE"
[lib]
name = "database_actor_derive"
name = "derive_db_execute"
path = "./src/lib.rs"
proc-macro = true
[dependencies]

View File

@ -0,0 +1,270 @@
mod parse_attr;
mod utils;
extern crate proc_macro;
use crate::parse_attr::Attributes;
use proc_macro::{token_stream::IntoIter, TokenStream, TokenTree};
use std::iter::Peekable;
fn parse_meta(mut it: Peekable<IntoIter>) -> (Peekable<IntoIter>, Option<Attributes>) {
let mut attrs: Option<Attributes> = None;
while let Some(token) = it.peek() {
match token {
// lookup for attr
TokenTree::Punct(p) if p.as_char() == '#' => {
let res = parse_attr::parse(it);
it = res.0;
attrs = res.1;
}
TokenTree::Ident(_) => {
break;
}
_ => {
eprintln!("skip token {:#?}", token);
it.next();
}
};
}
(it, attrs)
}
#[proc_macro_derive(Execute, attributes(db_exec))]
pub fn derive_enum_iter(item: TokenStream) -> TokenStream {
let mut it = item.into_iter().peekable();
let res = parse_meta(it);
it = res.0;
let attrs = res.1.expect("Result meta attribute is required");
let result = attrs
.result
.expect("Meta attribute `result` is required. Try add db_exec(result = \"foo\")");
let schema = attrs
.schema
.expect("Meta attribute `schema` is required. Try add db_exec(schema = \"foo\")");
it = utils::skip_pub(it);
it = utils::skip_struct(it);
let name = it
.next()
.expect("Expect to struct name but nothing was found")
.to_string();
let action_result = if attrs.load.is_some() {
format!("Vec<{}>", result)
} else if attrs.destroy.is_some() {
"usize".to_string()
} else {
result.clone()
};
let query = if let Some(q) = attrs.find {
build_find_exec(&name, &result, &schema, &q, &action_result)
} else if let Some(q) = attrs.load {
build_load_exec(&name, &result, &schema, &q, &action_result)
} else if let Some(q) = attrs.update {
build_update_exec(&name, &result, &schema, &q, &action_result)
} else if let Some(q) = attrs.destroy {
build_destroy_exec(&name, &result, &schema, &q, &action_result)
} else if let Some(q) = attrs.create {
build_create_exec(&name, &result, &schema, &q, &action_result)
} else {
"".to_string()
};
let code = format!(
r#"
impl actix::Message for {name} {{
type Result = Result<{action_result}, crate::DatabaseError>;
}}
impl actix::Handler<{name}> for crate::DbExecutor {{
type Result = Result<{action_result}, crate::DatabaseError>;
fn handle(&mut self, msg: {name}, _ctx: &mut Self::Context) -> Self::Result {{
let conn = crate::db_pool!(self);
msg.execute(conn)
}}
}}
{query}
"#,
name = name,
query = query,
action_result = action_result
);
code.parse().unwrap()
}
fn build_create_exec(
name: &str,
resource: &str,
schema: &str,
query: &str,
action_result: &str,
) -> String {
format!(
r#"
impl {name} {{
pub fn execute(
self,
conn: &crate::DbPooledConn,
) -> Result<{action_result}, crate::DatabaseError> {{
crate::Guard::new(conn)?.run(|_guard| {{
use crate::schema::{schema}::dsl::*;
let msg = self;
crate::q!({query}).get_result(conn).map_err(|e| {{
log::error!("{{:?}}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::Create,
crate::ResourceKind::{resource},
)
}})
}})
}}
}}
"#,
name = name,
schema = schema,
query = query,
resource = resource,
action_result = action_result
)
}
fn build_find_exec(
name: &str,
resource: &str,
schema: &str,
query: &str,
action_result: &str,
) -> String {
format!(
r#"
impl {name} {{
pub fn execute(
self,
conn: &crate::DbPooledConn,
) -> Result<{action_result}, crate::DatabaseError> {{
use crate::schema::{schema}::dsl::*;
let msg = self;
crate::q!({query}).first(conn).map_err(|e| {{
log::error!("{{:?}}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::LoadSingle,
crate::ResourceKind::{resource},
)
}})
}}
}}
"#,
name = name,
schema = schema,
query = query,
resource = resource,
action_result = action_result
)
}
fn build_load_exec(
name: &str,
resource: &str,
schema: &str,
query: &str,
action_result: &str,
) -> String {
format!(
r#"
impl {name} {{
pub fn execute(
self,
conn: &crate::DbPooledConn,
) -> Result<{action_result}, crate::DatabaseError> {{
use crate::schema::{schema}::dsl::*;
let msg = self;
crate::q!({query}).load(conn).map_err(|e| {{
log::error!("{{:?}}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::LoadCollection,
crate::ResourceKind::{resource},
)
}})
}}
}}
"#,
name = name,
schema = schema,
query = query,
resource = resource,
action_result = action_result
)
}
fn build_update_exec(
name: &str,
resource: &str,
schema: &str,
query: &str,
action_result: &str,
) -> String {
format!(
r#"
impl {name} {{
pub fn execute(
self,
conn: &crate::DbPooledConn,
) -> Result<{action_result}, crate::DatabaseError> {{
use crate::schema::{schema}::dsl::*;
let msg = self;
crate::q!({query}).get_result(conn).map_err(|e| {{
log::error!("{{:?}}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::Update,
crate::ResourceKind::{resource},
)
}})
}}
}}
"#,
name = name,
schema = schema,
query = query,
resource = resource,
action_result = action_result
)
}
fn build_destroy_exec(
name: &str,
resource: &str,
schema: &str,
query: &str,
action_result: &str,
) -> String {
format!(
r#"
impl {name} {{
pub fn execute(
self,
conn: &crate::DbPooledConn,
) -> Result<{action_result}, crate::DatabaseError> {{
use crate::schema::{schema}::dsl::*;
let msg = self;
crate::q!({query}).execute(conn).map_err(|e| {{
log::error!("{{:?}}", e);
crate::DatabaseError::GenericFailure(
crate::OperationError::Delete,
crate::ResourceKind::{resource},
)
}})
}}
}}
"#,
name = name,
schema = schema,
query = query,
resource = resource,
action_result = action_result
)
}

View File

@ -0,0 +1,101 @@
use proc_macro::{token_stream::IntoIter, TokenTree};
use std::iter::Peekable;
#[derive(Default, Debug)]
pub struct Attributes {
pub result: Option<String>,
pub schema: Option<String>,
pub find: Option<String>,
pub load: Option<String>,
pub create: Option<String>,
pub destroy: Option<String>,
pub update: Option<String>,
}
pub fn parse(mut it: Peekable<IntoIter>) -> (Peekable<IntoIter>, Option<Attributes>) {
it.next();
let group = if let Some(TokenTree::Group(group)) = it.next() {
group
} else {
panic!("Expect meta group");
};
let mut git = group.stream().into_iter();
let ident = if let Some(TokenTree::Ident(ident)) = git.next() {
ident
} else {
panic!("Expect attribute name")
};
if ident.to_string().as_str() != "db_exec" {
return (it, None);
}
let group = if let Some(TokenTree::Group(group)) = git.next() {
group
} else {
panic!("Expect attribute name")
};
(it, Some(parse_db_exec(group.stream().into_iter())))
}
fn parse_db_exec(mut it: IntoIter) -> Attributes {
let mut attrs = Attributes::default();
while let Some(token) = it.next() {
let ident = if let TokenTree::Ident(ident) = token {
ident
} else {
continue;
};
match ident.to_string().as_str() {
"result" => {
attrs.result = Some(fetch_name(&mut it));
}
"schema" => {
attrs.schema = Some(fetch_name(&mut it));
}
"find" => {
attrs.find = Some(fetch_name(&mut it));
}
"load" => {
attrs.load = Some(fetch_name(&mut it));
}
"create" => {
attrs.create = Some(fetch_name(&mut it));
}
"destroy" => {
attrs.destroy = Some(fetch_name(&mut it));
}
"update" => {
attrs.update = Some(fetch_name(&mut it));
}
_ => continue,
};
}
attrs
}
fn fetch_name(it: &mut IntoIter) -> String {
if let Some(TokenTree::Punct(_)) = it.next() {
} else {
panic!("Expect equal token");
}
let lit = if let Some(TokenTree::Literal(lit)) = it.next() {
lit
} else {
panic!("Expect type name as string");
};
let mut name = lit.to_string();
if name.starts_with('"') {
name.remove(0);
name.remove(name.len() - 1);
} else if name.starts_with("r#\"") {
name.remove(0);
name.remove(0);
name.remove(0);
name.remove(name.len() - 1);
name.remove(name.len() - 1);
}
let name = name.trim();
name.to_string()
}

View File

@ -1,10 +1,7 @@
extern crate proc_macro;
use proc_macro::{token_stream::IntoIter, TokenTree};
use std::iter::Peekable;
use proc_macro::{TokenStream, TokenTree};
#[proc_macro_derive(DbMsg, attributes(query))]
pub fn db_msg(item: TokenStream) -> TokenStream {
let mut it = item.into_iter();
pub fn skip_pub(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
if let Some(TokenTree::Ident(ident)) = it.next() {
if ident.to_string().as_str() != "pub" {
panic!("Expect to find keyword pub but was found {:?}", ident)
@ -12,6 +9,10 @@ pub fn db_msg(item: TokenStream) -> TokenStream {
} else {
panic!("Expect to find keyword pub but nothing was found")
}
it
}
pub fn skip_struct(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
if let Some(TokenTree::Ident(ident)) = it.next() {
if ident.to_string().as_str() != "struct" {
panic!("Expect to find keyword struct but was found {:?}", ident)
@ -19,9 +20,5 @@ pub fn db_msg(item: TokenStream) -> TokenStream {
} else {
panic!("Expect to find keyword struct but nothing was found")
}
let _name = it
.next()
.expect("Expect to struct name but nothing was found");
"".parse().unwrap()
it
}

View File

@ -0,0 +1,16 @@
[package]
name = "derive_utils"
version = "0.1.0"
authors = ["Adrian Wozniak <adrian.wozniak@ita-prog.pl>"]
edition = "2018"
description = "JIRS (Simplified JIRA in Rust) shared data types"
repository = "https://gitlab.com/adrian.wozniak/jirs"
license = "MPL-2.0"
#license-file = "../LICENSE"
[lib]
name = "derive_utils"
path = "./src/lib.rs"
proc-macro = true
[dependencies]

View File

@ -0,0 +1,35 @@
use proc_macro::{token_stream::IntoIter, TokenTree};
use std::iter::Peekable;
pub fn skip_pub(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
if let Some(TokenTree::Ident(ident)) = it.next() {
if ident.to_string().as_str() != "pub" {
panic!("Expect to find keyword pub but was found {:?}", ident)
}
} else {
panic!("Expect to find keyword pub but nothing was found")
}
it
}
pub fn skip_enum(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
if let Some(TokenTree::Ident(ident)) = it.next() {
if ident.to_string().as_str() != "enum" {
panic!("Expect to find keyword enum but was found {:?}", ident)
}
} else {
panic!("Expect to find keyword enum but nothing was found")
}
it
}
pub fn skip_struct(mut it: Peekable<IntoIter>) -> Peekable<IntoIter> {
if let Some(TokenTree::Ident(ident)) = it.next() {
if ident.to_string().as_str() != "struct" {
panic!("Expect to find keyword struct but was found {:?}", ident)
}
} else {
panic!("Expect to find keyword struct but nothing was found")
}
it
}

View File

@ -24,7 +24,6 @@ services:
- ./jirs-data:/app/jirs-data
- ./jirs-cli:/app/jirs-cli
- ./jirs-client:/app/jirs-client
- ./jirs-css:/app/jirs-css
server:
build:
dockerfile: ./jirs-server/Dockerfile

View File

@ -9,9 +9,6 @@ RUN rustup toolchain install nightly && \
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
ADD ./jirs-data /app/jirs-data
ADD ./jirs-css /app/jirs-css
RUN cd /app/jirs-css && cargo build --bin jirs-css && cp ./target/debug/jirs-css /bin
ADD ./jirs-client /app/jirs-client
@ -19,7 +16,6 @@ RUN cd ./jirs-client && \
rm -Rf build && \
mkdir build && \
wasm-pack build --mode normal --release --out-name jirs --out-dir ./build --target web && \
jirs-css -i ./js/styles.css -O ./build/styles.css && \
cp -r ./static/* ./build && \
cat ./static/index.js \
| sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" \

View File

@ -13,10 +13,3 @@ main {
article.inner-layout {
width: 100%;
}
@media (min-width: 1240px) {
/*article.inner-layout {*/
/* display: flex;*/
/* justify-content: start;*/
/*}*/
}

View File

@ -1,126 +0,0 @@
aside#navbar-left {
z-index: var(--navLeft);
position: fixed;
top: 0;
left: 0;
overflow-x: hidden;
height: 100vh;
width: var(--appNavBarLeftWidth);
background: var(--backgroundDarkPrimary);
transition: all 0.1s;
transform: translateZ(0);
}
aside#navbar-left:hover {
width: 200px;
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.6);
}
aside#navbar-left > .logoLink {
display: block;
position: relative;
left: 0;
margin: 20px 0 10px;
transition: left 0.1s;
}
aside#navbar-left > .logoLink > .styledLogo {
display: inline-block;
margin-left: 0;
padding: 11px;
cursor: pointer;
user-select: none;
background: rgb(246, 246, 246);
}
aside#navbar-left > .logoLink > .styledLogo > img {
width: 42px;
}
aside#navbar-left:hover > .logoLink {
margin: 20px calc((200px - 64px) / 2) 10px;
}
aside#navbar-left:hover > .logoLink > .styledLogo {
border-radius: 34px;
}
aside#navbar-left .item {
position: relative;
width: 100%;
height: 42px;
line-height: 42px;
padding-left: 64px;
color: #deebff;
transition: color 0.1s;
cursor: pointer;
user-select: none;
display: block;
}
aside#navbar-left .item:hover {
background: rgba(255, 255, 255, 0.1);
}
aside#navbar-left .item > .styledIcon {
position: absolute;
left: 18px;
height: 42px;
line-height: 42px;
}
aside#navbar-left .item > .styledIcon > .styledAvatar {
margin-top: 7px;
width: 27px;
height: 27px;
}
aside#navbar-left > .item > i.styledIcon.search {
font-size: 22px;
color: var(--asideIcon);
}
aside#navbar-left > .item > i.styledIcon,
aside#navbar-left > .bottom > .item > i.styledIcon {
font-size: 27px;
color: var(--asideIcon);
}
aside#navbar-left .item > .itemText {
position: relative;
right: 12px;
visibility: hidden;
opacity: 0;
text-transform: uppercase;
transition: all 0.1s;
font-family: var(--font-bold);
font-size: 12px;
transition-property: right, visibility, opacity;
display: block;
}
aside#navbar-left > .bottom {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
}
aside#navbar-left > .bottom > .aboutTooltip > .item > i.styledIcon {
color: var(--asideIcon);
}
aside#navbar-left:hover .item > .itemText {
right: 0;
visibility: visible;
opacity: 1;
}
.styledTooltip.aboutTooltipPopup {
bottom: 48px;
left: 120px;
}
.styledTooltip.aboutTooltipPopup #about-github-button {
margin-left: 10px;
}

View File

@ -0,0 +1,140 @@
aside#navbar-left {
z-index: var(--navLeft);
position: fixed;
top: 0;
left: 0;
overflow-x: hidden;
height: 100vh;
width: var(--appNavBarLeftWidth);
background: var(--backgroundDarkPrimary);
transition: all 0.1s;
transform: translateZ(0);
&:hover {
width: 200px;
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.6);
> .logoLink {
margin: 20px calc((200px - 64px) / 2) 10px;
> .styledLogo {
border-radius: 34px;
}
}
.item {
> .itemText {
right: 0;
visibility: visible;
opacity: 1;
}
}
}
> .logoLink {
display: block;
position: relative;
left: 0;
margin: 20px 0 10px;
transition: left 0.1s;
> .styledLogo {
display: inline-block;
margin-left: 0;
padding: 11px;
cursor: pointer;
user-select: none;
background: rgb(246, 246, 246);
> img {
width: 42px;
}
}
}
.item {
position: relative;
width: 100%;
height: 42px;
line-height: 42px;
padding-left: 64px;
color: #deebff;
transition: color 0.1s;
cursor: pointer;
user-select: none;
display: block;
&:hover {
background: rgba(255, 255, 255, 0.1);
}
> .styledIcon {
position: absolute;
left: 18px;
height: 42px;
line-height: 42px;
> .styledAvatar {
margin-top: 7px;
width: 27px;
height: 27px;
}
}
> .itemText {
position: relative;
right: 12px;
visibility: hidden;
opacity: 0;
text-transform: uppercase;
transition: all 0.1s;
font-family: var(--font-bold);
font-size: 12px;
transition-property: right, visibility, opacity;
display: block;
}
}
> .item {
> i.styledIcon.search {
font-size: 22px;
color: var(--asideIcon);
}
> i.styledIcon {
font-size: 27px;
color: var(--asideIcon);
}
}
> .bottom {
> .item {
> i.styledIcon {
font-size: 27px;
color: var(--asideIcon);
}
}
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
> .aboutTooltip {
> .item {
> i.styledIcon {
color: var(--asideIcon);
}
}
}
}
}
.styledTooltip.aboutTooltipPopup {
bottom: 48px;
left: 120px;
#about-github-button {
margin-left: 10px;
}
}

View File

@ -1,128 +0,0 @@
html, body, #root {
height: 100%;
min-height: 100%;
}
@media (min-width: 1240px) {
html, body, #root {
min-width: 768px;
}
}
body {
font-weight: normal;
color: var(--textDark);
-webkit-tap-highlight-color: transparent;
line-height: 1.2;
font-size: 16px;
font-family: var(--font-regular);
}
#app {
display: flex;
flex-direction: column;
}
button,
input,
optgroup,
select,
textarea {
font-family: var(--font-regular);
font-weight: normal;
}
*, *:after, *:before, input[type="search"] {
box-sizing: border-box;
}
a {
color: inherit;
text-decoration: none;
}
ul {
list-style: none;
}
ul, li, ol, dd, h1, h2, h3, h4, h5, h6, p {
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5, h6, strong {
font-family: var(--font-bold);
}
button {
background: none;
border: none;
}
/* Workaround for IE11 focus highlighting for select elements */
select::-ms-value {
background: none;
color: #42413d;
}
[role="button"], button, input, select, textarea {
outline: none;
}
[role="button"]:disabled, button:disabled, input:disabled, select:disabled, textarea:disabled {
opacity: 1;
}
[role="button"], button, input, textarea {
appearance: none;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
select::-ms-expand {
display: none;
}
select option {
color: var(--textDark)
}
p {
line-height: 1.4285;
}
p a::-webkit-input-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
p a:-moz-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
p a::-moz-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
p a:-ms-input-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
textarea {
line-height: 1.4285;
}
body, select {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html {
touch-action: manipulation;
}

View File

@ -0,0 +1,231 @@
/* Workaround for IE11 focus highlighting for select elements */
html {
height: 100%;
min-height: 100%;
touch-action: manipulation;
}
body {
height: 100%;
min-height: 100%;
font-weight: normal;
color: var(--textDark);
-webkit-tap-highlight-color: transparent;
line-height: 1.2;
font-size: 16px;
font-family: var(--font-regular);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#root {
height: 100%;
min-height: 100%;
}
#app {
display: flex;
flex-direction: column;
}
button {
font-family: var(--font-regular);
font-weight: normal;
background: none;
border: none;
outline: none;
appearance: none;
&:disabled {
opacity: 1;
}
}
input {
font-family: var(--font-regular);
font-weight: normal;
outline: none;
appearance: none;
&:disabled {
opacity: 1;
}
}
optgroup {
font-family: var(--font-regular);
font-weight: normal;
}
select {
font-family: var(--font-regular);
font-weight: normal;
outline: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
&::-ms-value {
background: none;
color: #42413d;
}
&:disabled {
opacity: 1;
}
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
&::-ms-expand {
display: none;
}
option {
color: var(--textDark);
}
}
textarea {
font-family: var(--font-regular);
font-weight: normal;
outline: none;
appearance: none;
line-height: 1.4285;
&:disabled {
opacity: 1;
}
}
* {
box-sizing: border-box;
&:after {
box-sizing: border-box;
}
&:before {
box-sizing: border-box;
}
}
input[type="search"] {
box-sizing: border-box;
}
a {
color: inherit;
text-decoration: none;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
padding: 0;
margin: 0;
}
ol {
padding: 0;
margin: 0;
}
dd {
padding: 0;
margin: 0;
}
h1 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
h2 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
h3 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
h4 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
h5 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
h6 {
padding: 0;
margin: 0;
font-family: var(--font-bold);
}
p {
padding: 0;
margin: 0;
line-height: 1.4285;
a {
&::-webkit-input-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
&:-moz-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
&::-moz-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
&:-ms-input-placeholder {
color: var(--textLight);
opacity: 1 !important;
}
}
}
strong {
font-family: var(--font-bold);
}
[role="button"] {
outline: none;
appearance: none;
&:disabled {
opacity: 1;
}
}
@media (min-width: 1240px) {
html {
min-width: 768px;
}
body {
min-width: 768px;
}
#root {
min-width: 768px;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
#invite > .styledForm {
display: flex;
flex-direction: column;
margin: 0 auto 24px;
width: 400px;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
}
#invite > .styledForm:first-of-type {
margin-top: 124.5px;
margin-bottom: 0;
}
#invite > .styledForm:last-of-type {
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
}
#invite .error {
color: var(--danger);
margin-top: 15px;
}

View File

@ -0,0 +1,27 @@
#invite {
> .styledForm {
display: flex;
flex-direction: column;
margin: 0 auto 24px;
width: 400px;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
&:first-of-type {
margin-top: 124.5px;
margin-bottom: 0;
}
&:last-of-type {
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
}
}
.error {
color: var(--danger);
margin-top: 15px;
}
}

View File

@ -1,157 +0,0 @@
.issueDetails > .content {
display: flex;
padding: 0 30px 60px;
}
/*===================================================*/
/* LEFT */
/*===================================================*/
.issueDetails > .content > .left {
width: 65%;
padding-right: 50px;
}
.issueDetails > .content > .left > .styledInput,
.issueDetails > .content > .left > .styledTextArea {
margin: 18px 0 0 -8px;
height: 44px;
width: 100%;
}
.issueDetails > .content > .left > .styledInput > input,
.issueDetails > .content > .left > .styledTextArea > textarea {
padding: 7px 7px 8px;
line-height: 1.28;
resize: none;
transition: background 0.1s;
font-size: 24px;
font-family: var(--font-medium);
font-weight: normal;
}
.issueDetails > .content > .left > .styledTextArea > textarea:not(:focus),
.issueDetails > .content > .left > .styledInput > input:not(:focus) {
background: #fff;
border: 1px solid transparent;
box-shadow: 0 0 0 1px transparent;
}
.issueDetails > .content > .left > .styledTextArea > textarea:hover:not(:focus) {
background: var(--backgroundLight);
}
.issueDetails > .content > .left > .comments {
padding-top: 40px;
}
.issueDetails > .content > .left > .comments > .title {
font-family: var(--font-medium);
font-weight: normal;
font-size: 15px
}
.issueDetails > .content > .left > .comments > .create {
position: relative;
margin-top: 25px;
font-size: 15px
}
.issueDetails > .content > .left > .comments > .create > .userAvatar {
position: absolute;
top: 0;
left: 0;
}
.issueDetails > .content > .left > .comments > .create > .right {
padding-left: 44px;
}
.issueDetails > .content > .left > .comments > .create > .right > .fakeTextArea {
padding: 12px 16px;
border-radius: 4px;
border: 1px solid var(--borderLightest);
color: var(--textLight);
cursor: pointer;
user-select: none;
}
.issueDetails > .content > .left > .comments > .create > .right > .fakeTextArea:hover {
border: 1px solid var(--borderLight);
}
.issueDetails > .content > .left > .comments > .create > .right > .proTip {
display: flex;
align-items: center;
padding-top: 8px;
color: var(--textMedium);
font-size: 13px;
}
.issueDetails > .content > .left > .comments > .create > .right > .proTip > .strong {
padding-right: 4px;
}
.issueDetails > .content > .left > .comments > .create > .right > .proTip > .tipLetter {
position: relative;
top: 1px;
display: inline-block;
margin: 0 4px;
padding: 0 4px;
border-radius: 2px;
color: var(--textDarkest);
background: var(--backgroundMedium);
font-family: var(--font-bold);
font-weight: normal;
font-size: 12px
}
.issueDetails > .content > .left > .comments > .create > .right > .actions {
display: flex;
padding-top: 10px;
}
.issueDetails > .content > .left > .comments > .create > .right > .actions > .styledButton {
margin-right: 6px;
}
/*===================================================*/
/* RIGHT */
/*===================================================*/
.issueDetails > .content > .right {
width: 35%;
padding-top: 5px;
}
/*===================================================*/
/* TOP ACTIONS */
/*===================================================*/
.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 > .topActions .styledSelect > .valueContainer > .value {
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--textMedium);
font-size: 13px
}
.issueDetails > .sectionTitle {
margin: 24px 0 5px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
font-family: "CircularStdBold", serif;
font-weight: normal
}

View File

@ -0,0 +1,183 @@
.issueDetails {
> .content {
display: flex;
padding: 0 30px 60px;
/*===================================================*/
/* LEFT */
/*===================================================*/
> .left {
width: 65%;
padding-right: 50px;
> .styledInput {
margin: 18px 0 0 -8px;
height: 44px;
width: 100%;
> input {
padding: 7px 7px 8px;
line-height: 1.28;
resize: none;
transition: background 0.1s;
font-size: 24px;
font-family: var(--font-medium);
font-weight: normal;
&:not(:focus) {
background: #fff;
border: 1px solid transparent;
box-shadow: 0 0 0 1px transparent;
}
}
}
> .styledTextArea {
margin: 18px 0 0 -8px;
height: 44px;
width: 100%;
> textarea {
padding: 7px 7px 8px;
line-height: 1.28;
resize: none;
transition: background 0.1s;
font-size: 24px;
font-family: var(--font-medium);
font-weight: normal;
&:not(:focus) {
background: #fff;
border: 1px solid transparent;
box-shadow: 0 0 0 1px transparent;
}
&:hover {
&:not(:focus) {
background: var(--backgroundLight);
}
}
}
}
> .comments {
padding-top: 40px;
> .title {
font-family: var(--font-medium);
font-weight: normal;
font-size: 15px;
}
> .create {
position: relative;
margin-top: 25px;
font-size: 15px;
> .userAvatar {
position: absolute;
top: 0;
left: 0;
}
> .right {
padding-left: 44px;
> .fakeTextArea {
padding: 12px 16px;
border-radius: 4px;
border: 1px solid var(--borderLightest);
color: var(--textLight);
cursor: pointer;
user-select: none;
&:hover {
border: 1px solid var(--borderLight);
}
}
> .proTip {
display: flex;
align-items: center;
padding-top: 8px;
color: var(--textMedium);
font-size: 13px;
> .strong {
padding-right: 4px;
}
> .tipLetter {
position: relative;
top: 1px;
display: inline-block;
margin: 0 4px;
padding: 0 4px;
border-radius: 2px;
color: var(--textDarkest);
background: var(--backgroundMedium);
font-family: var(--font-bold);
font-weight: normal;
font-size: 12px;
}
}
> .actions {
display: flex;
padding-top: 10px;
> .styledButton {
margin-right: 6px;
}
}
}
}
}
}
/*===================================================*/
/* RIGHT */
/*===================================================*/
> .right {
width: 35%;
padding-top: 5px;
}
}
/*===================================================*/
/* TOP ACTIONS */
/*===================================================*/
> .topActions {
display: flex;
justify-content: space-between;
padding: 21px 18px 0;
> .topActionsRight {
display: flex;
align-items: center;
> * {
margin-left: 4px;
}
}
.styledSelect {
> .valueContainer {
> .value {
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--textMedium);
font-size: 13px;
}
}
}
}
> .sectionTitle {
margin: 24px 0 5px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
font-family: "CircularStdBold", serif;
font-weight: normal;
}
}

View File

@ -1,69 +0,0 @@
#login > .styledForm {
margin-top: 5rem;
display: flex;
flex-direction: column;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
}
#login > .styledForm:first-of-type {
margin-bottom: 0;
}
#login > .styledForm:last-of-type {
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
}
#login > .styledForm:first-of-type > .formElement {
padding-bottom: 0;
}
#login > .styledForm:last-of-type > .formElement {
padding-top: 0;
}
#login > .styledForm > .formElement > .formHeading {
color: var(--textMedium);
font-size: 1em;
line-height: 1.1428571428571428;
letter-spacing: -.003em;
font-family: var(--font-bold);
text-align: center;
}
#login > .styledForm > .formElement > .noPasswordSection {
line-height: 32px;
margin-top: 15px;
display: flex;
cursor: pointer;
}
#login > .styledForm > .formElement > .noPasswordSection > .styledIcon {
margin-right: 5px;
line-height: 32px;
}
#login > .styledForm > .formElement > .noPasswordSection > span {
display: block;
line-height: 32px;
}
#login > .styledForm .twoRow {
display: flex;
justify-content: space-between;
}
@media (min-width: 1240px) {
#login > .styledForm {
margin: 0 auto 24px;
width: 400px;
max-width: 400px;
}
#login > .styledForm:first-of-type {
margin-top: 124.5px;
}
}

View File

@ -0,0 +1,75 @@
#login {
> .styledForm {
margin-top: 5rem;
display: flex;
flex-direction: column;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
&:first-of-type {
margin-bottom: 0;
> .formElement {
padding-bottom: 0;
}
}
&:last-of-type {
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 10px;
> .formElement {
padding-top: 0;
}
}
> .formElement {
> .formHeading {
color: var(--textMedium);
font-size: 1em;
line-height: 1.1428571428571428;
letter-spacing: -.003em;
font-family: var(--font-bold);
text-align: center;
}
> .noPasswordSection {
line-height: 32px;
margin-top: 15px;
display: flex;
cursor: pointer;
> .styledIcon {
margin-right: 5px;
line-height: 32px;
}
> span {
display: block;
line-height: 32px;
}
}
}
.twoRow {
display: flex;
justify-content: space-between;
}
}
}
@media (min-width: 1240px) {
#login {
> .styledForm {
margin: 0 auto 24px;
width: 400px;
max-width: 400px;
&:first-of-type {
margin-top: 124.5px;
}
}
}
}

View File

@ -1,255 +0,0 @@
#projectPage {
}
#projectPage > .breadcrumbsContainer {
color: var(--textMedium);
font-size: 15px;
}
#projectPage > .breadcrumbsContainer > .breadcrumbsDivider {
position: relative;
top: 2px;
margin: 0 10px;
font-size: 18px;
}
#projectPage > #projectBoardHeader {
margin-top: 6px;
display: flex;
justify-content: space-between;
}
#projectPage > #projectBoardHeader > #boardName {
font-size: 24px;
font-family: var(--font-medium);
font-weight: normal;
}
#projectPage > #projectBoardFilters {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 24px;
}
#projectPage > #projectBoardFilters > .textFilterBoard {
margin-right: 18px;
width: 160px;
}
#projectPage > #projectBoardFilters > #avatars {
display: flex;
flex-direction: row-reverse;
margin: 0 12px 0 2px;
}
#projectPage > #projectBoardFilters > #avatars > .avatarIsActiveBorder {
display: inline-flex;
margin-left: -2px;
border-radius: 50%;
transition: transform 0.1s;
cursor: pointer;
user-select: none;
background: var(--backgroundMedium);
border-color: var(--backgroundLight)
}
#projectPage > #projectBoardFilters > #avatars > .avatarIsActiveBorder.isActive {
box-shadow: 0 0 0 4px var(--primary);
}
#projectPage > #projectBoardFilters > #avatars > .avatarIsActiveBorder > .letter {
width: 32px;
height: 32px;
font-size: 16px;
font-weight: bolder;
}
#projectPage > #projectBoardFilters > #avatars > .avatarIsActiveBorder:hover {
transform: translateY(-5px);
}
#projectPage > #projectBoardFilters > #avatars > .avatarIsActiveBorder > .styledAvatar {
box-shadow: 0 0 0 2px #fff;
}
#projectPage > #projectBoardFilters .styledButton {
margin-left: 6px;
}
#projectPage > #projectBoardFilters > #clearAllFilters {
height: 32px;
line-height: 32px;
margin-left: 15px;
padding-left: 12px;
border-left: 1px solid var(--borderLightest);
color: var(--textDark);
font-size: 14.5px;
cursor: pointer;
user-select: none;
}
#projectPage > #projectBoardFilters > #clearAllFilters:hover {
color: var(--textMedium);
}
#projectPage > .rows > .row > .epicName {
margin: 18px 0 10px 0;
}
#projectPage > .rows > .row > .projectBoardLists {
display: flex;
margin: 10px -5px 0;
position: relative;
flex-direction: column;
}
#projectPage > .rows > .row > .rowName {
position: relative;
color: var(--textDark);
font-family: var(--font-regular);
margin: 26px -5px 0;
}
#projectPage > .rows > .row > .projectBoardLists > .list {
display: flex;
flex-direction: column;
margin: 0 5px;
min-height: 400px;
border-radius: 3px;
background: var(--backgroundLightest);
}
#projectPage > .rows > .row > .projectBoardLists > .list > .title {
padding: 13px 10px 17px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .title > .issuesCount {
text-transform: lowercase;
font-size: 13px;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues {
height: 100%;
padding: 0 5px;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink {
display: block;
margin-bottom: 5px;
position: relative;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .dragCover {
display: block;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .dragCover:-moz-drag-over {
border: var(--borderInputFocus);
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue {
padding: 10px;
border-radius: 3px;
background: #fff;
box-shadow: 0 1px 2px 0 rgba(9, 30, 66, 0.25);
transition: background 0.1s;
cursor: pointer;
user-select: none;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue.hidden {
display: none;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue.isBeingDragged {
transform: rotate(3deg);
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 90px;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue:hover {
background: var(--backgroundLight);
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .title {
padding-bottom: 11px;
font-size: 15px;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom {
display: flex;
justify-content: space-between;
align-items: center;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > div {
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > div > .issueTypeIcon {
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > div > .issuePriorityIcon {
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > .assignees {
display: flex;
flex-direction: row-reverse;
margin-left: 2px;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > .assignees > .assigneeAvatar,
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue > .bottom > .assignees > .styledAvatar {
margin-left: -2px;
box-shadow: 0 0 0 2px #fff;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue {
display: flex;
justify-content: space-between;
cursor: pointer;
}
#projectPage > #projectBoardFilters > .filterChild {
width: 90%;
margin-bottom: 1rem;
}
@media (min-width: 1240px) {
#projectPage {}
#projectPage > #projectBoardFilters {
flex-direction: row;
}
#projectPage > #projectBoardFilters > .filterChild {
width: auto;
margin-bottom: auto;
}
#projectPage > .rows > .row > .projectBoardLists {
flex-direction: row;
}
#projectPage > .rows > .row > .projectBoardLists > .list {
width: 25%;
}
#projectPage > .rows > .row > .projectBoardLists > .list > .issues > .issueLink > .issue {
display: block;
padding: 10px 8px;
}
}

View File

@ -0,0 +1,281 @@
#projectPage {
> .breadcrumbsContainer {
color: var(--textMedium);
font-size: 15px;
> .breadcrumbsDivider {
position: relative;
top: 2px;
margin: 0 10px;
font-size: 18px;
}
}
> #projectBoardHeader {
margin-top: 6px;
display: flex;
justify-content: space-between;
> #boardName {
font-size: 24px;
font-family: var(--font-medium);
font-weight: normal;
}
}
> #projectBoardFilters {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 24px;
> .textFilterBoard {
margin-right: 18px;
width: 160px;
}
> #avatars {
display: flex;
flex-direction: row-reverse;
margin: 0 12px 0 2px;
> .avatarIsActiveBorder {
display: inline-flex;
margin-left: -2px;
border-radius: 50%;
transition: transform 0.1s;
cursor: pointer;
user-select: none;
background: var(--backgroundMedium);
border-color: var(--backgroundLight);
> .letter {
width: 32px;
height: 32px;
font-size: 16px;
font-weight: bolder;
}
&:hover {
transform: translateY(-5px);
}
> .styledAvatar {
box-shadow: 0 0 0 2px #fff;
}
}
> .avatarIsActiveBorder.isActive {
box-shadow: 0 0 0 4px var(--primary);
}
}
.styledButton {
margin-left: 6px;
}
> #clearAllFilters {
height: 32px;
line-height: 32px;
margin-left: 15px;
padding-left: 12px;
border-left: 1px solid var(--borderLightest);
color: var(--textDark);
font-size: 14.5px;
cursor: pointer;
user-select: none;
&:hover {
color: var(--textMedium);
}
}
> .filterChild {
width: 90%;
margin-bottom: 1rem;
}
}
> .rows {
> .row {
> .epicHeader {
margin: 18px 0 10px 0;
display: flex;
justify-content: space-between;
> .epicName {
}
> .epicActions {
> .styledButton {
> .styledIcon {
color: var(--backgroundLightest);
}
}
}
&:hover {
> .epicActions {
> .styledButton {
> .styledIcon {
color: var(--textDark);
}
}
}
}
}
> .projectBoardLists {
display: flex;
margin: 10px -5px 0;
position: relative;
flex-direction: column;
> .list {
display: flex;
flex-direction: column;
margin: 0 5px;
min-height: 400px;
border-radius: 3px;
background: var(--backgroundLightest);
> .title {
padding: 13px 10px 17px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
> .issuesCount {
text-transform: lowercase;
font-size: 13px;
}
}
> .issues {
height: 100%;
padding: 0 5px;
> .issueLink {
display: block;
margin-bottom: 5px;
position: relative;
> .dragCover {
display: block;
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
&:-moz-drag-over {
border: var(--borderInputFocus);
}
}
> .issue {
padding: 10px;
border-radius: 3px;
background: #fff;
box-shadow: 0 1px 2px 0 rgba(9, 30, 66, 0.25);
transition: background 0.1s;
cursor: pointer;
user-select: none;
display: flex;
justify-content: space-between;
cursor: pointer;
&:hover {
background: var(--backgroundLight);
}
> .title {
padding-bottom: 11px;
font-size: 15px;
}
> .bottom {
display: flex;
justify-content: space-between;
align-items: center;
> .assignees {
display: flex;
flex-direction: row-reverse;
margin-left: 2px;
> .assigneeAvatar {
margin-left: -2px;
box-shadow: 0 0 0 2px #fff;
}
> .styledAvatar {
margin-left: -2px;
box-shadow: 0 0 0 2px #fff;
}
}
}
}
> .issue.hidden {
display: none;
}
> .issue.isBeingDragged {
transform: rotate(3deg);
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 90px;
}
}
}
}
}
> .rowName {
position: relative;
color: var(--textDark);
font-family: var(--font-regular);
margin: 26px -5px 0;
}
}
}
}
@media (min-width: 1240px) {
#projectPage {
> #projectBoardFilters {
flex-direction: row;
> .filterChild {
width: auto;
margin-bottom: auto;
}
}
> .rows {
> .row {
> .projectBoardLists {
flex-direction: row;
> .list {
width: 25%;
> .issues {
> .issueLink {
> .issue {
display: block;
padding: 10px 8px;
}
}
}
}
}
}
}
}
}

View File

@ -1,75 +0,0 @@
#projectSettings > .formContainer {
display: flex;
justify-content: center;
}
#projectSettings > .formContainer .styledForm {
max-width: 1024px;
width: 100%;
}
#projectSettings > .formContainer .styledForm > .formElement > .actionButton {
margin-top: 30px;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField.columnsField > .styledLabel {
font-size: 14px;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns {
display: flex;
flex-direction: row;
justify-content: space-around;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview {
width: auto;
min-height: 60px;
background: var(--backgroundLightest);
margin: 0 5px;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName {
display: block;
margin: 0 5px;
padding: 13px 10px 17px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > span {
cursor: pointer;
user-select: none;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > .styledInput {
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName.addColumn,
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName.addColumn > i {
color: var(--textMedium);
text-align: center;
cursor: pointer;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > .removeColumn {
text-align: center;
display: flex;
justify-content: space-around;
padding-top: 15px;
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:hover > .columnName > .removeColumn,
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:focus > .columnName > .removeColumn,
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview:active > .columnName > .removeColumn {
}
#projectSettings > .formContainer .styledForm > .formElement > .styledField > .columnsSection > .columns > .columnPreview > .columnName > .issueCount {
text-transform: none;
padding-top: 15px;
}

View File

@ -0,0 +1,81 @@
#projectSettings {
> .formContainer {
display: flex;
justify-content: center;
.styledForm {
max-width: 1024px;
width: 100%;
> .formElement {
> .actionButton {
margin-top: 30px;
}
> .styledField.columnsField {
> .styledLabel {
font-size: 14px;
}
}
> .styledField {
> .columnsSection {
> .columns {
display: flex;
flex-direction: row;
justify-content: space-around;
> .columnPreview {
width: auto;
min-height: 60px;
background: var(--backgroundLightest);
margin: 0 5px;
> .columnName {
display: block;
margin: 0 5px;
padding: 13px 10px 17px;
text-transform: uppercase;
color: var(--textMedium);
font-size: 12.5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
> span {
cursor: pointer;
user-select: none;
}
> .removeColumn {
text-align: center;
display: flex;
justify-content: space-around;
padding-top: 15px;
}
> .issueCount {
text-transform: none;
padding-top: 15px;
}
}
> .columnName.addColumn {
color: var(--textMedium);
text-align: center;
cursor: pointer;
> i {
color: var(--textMedium);
text-align: center;
cursor: pointer;
}
}
}
}
}
}
}
}
}
}

View File

@ -1,47 +0,0 @@
#register > .styledForm {
display: flex;
flex-direction: column;
margin: 0 auto 24px;
width: 400px;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
}
#register > .styledForm:first-of-type {
margin-top: 124.5px;
}
#register .twoRow {
display: flex;
justify-content: space-between;
}
#register > .styledForm > .formElement > .noPasswordSection {
line-height: 32px;
margin-top: 15px;
display: flex;
cursor: pointer;
}
#register > .styledForm > .formElement > .noPasswordSection > .styledIcon {
margin-right: 5px;
line-height: 32px;
}
#register > .styledForm > .formElement > .noPasswordSection > span {
display: block;
line-height: 32px;
}
#register .error > p {
line-height: 1.4285;
color: var(--danger);
font-family: var(--font-medium);
text-align: center;
font-size: 14.5px;
border-top: 1px solid var(--danger);
margin-top: 15px;
}

View File

@ -0,0 +1,53 @@
#register {
> .styledForm {
display: flex;
flex-direction: column;
margin: 0 auto 24px;
width: 400px;
background: rgb(255, 255, 255) none repeat scroll 0 0;
border-radius: 3px;
box-shadow: rgba(0, 0, 0, 0.1) 0 0 10px;
box-sizing: border-box;
color: var(--textMedium);
&:first-of-type {
margin-top: 124.5px;
}
> .formElement {
> .noPasswordSection {
line-height: 32px;
margin-top: 15px;
display: flex;
cursor: pointer;
> .styledIcon {
margin-right: 5px;
line-height: 32px;
}
> span {
display: block;
line-height: 32px;
}
}
}
}
.twoRow {
display: flex;
justify-content: space-between;
}
.error {
> p {
line-height: 1.4285;
color: var(--danger);
font-family: var(--font-medium);
text-align: center;
font-size: 14.5px;
border-top: 1px solid var(--danger);
margin-top: 15px;
}
}
}

View File

@ -1,34 +0,0 @@
#reports {
}
#reports > .top > .graph {
margin-top: 15px;
}
#reports > .top > .graph > .graphHeader {
margin-bottom: 15px;
}
#reports > .top > .issueList {
display: block;
margin-top: 15px;
}
#reports > .top > .issueList > .issueListHeader {
margin-bottom: 15px;
}
#reports > .top > .issueList > .issue {
display: grid;
grid-template-columns: 32px 32px 240px auto 120px;
}
#reports > .top > .issueList > .issue.selected {
color: var(--primary);
font-family: var(--font-bold);
}
#reports > .top > .issueList > .issue.nonSelected {
color: var(--textLight);
font-family: var(--font-regular);
}

View File

@ -0,0 +1,35 @@
#reports {
> .top {
> .graph {
margin-top: 15px;
> .graphHeader {
margin-bottom: 15px;
}
}
> .issueList {
display: block;
margin-top: 15px;
> .issueListHeader {
margin-bottom: 15px;
}
> .issue {
display: grid;
grid-template-columns: 32px 32px 240px auto 120px;
}
> .issue.selected {
color: var(--primary);
font-family: var(--font-bold);
}
> .issue.nonSelected {
color: var(--textLight);
font-family: var(--font-regular);
}
}
}
}

View File

@ -1,166 +0,0 @@
nav#sidebar {
position: fixed;
z-index: calc(var(--navLeft) - 1);
top: 0;
left: var(--appNavBarLeftWidth);
height: 100vh;
width: var(--secondarySideBarWidth);
padding: 0 16px 24px;
background: var(--backgroundLightest);
border-right: 1px solid var(--borderLightest);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
nav#sidebar::-webkit-scrollbar {
width: 8px;
}
nav#sidebar::-webkit-scrollbar-track {
background: none;
}
nav#sidebar::-webkit-scrollbar-thumb {
border-radius: 99px;
background: var(--backgroundMedium);
}
@media (max-width: 1100px) {
nav#sidebar {
width: calc(var(--secondarySideBarWidth) - 10px);
}
}
@media (max-width: 999px) {
nav#sidebar {
display: none;
}
}
nav#sidebar #projectInfo {
display: flex;
padding: 24px 4px;
}
nav#sidebar #projectInfo > .projectTexts {
padding: 3px 0 0 10px;
}
nav#sidebar #projectInfo > .projectTexts > .projectName {
color: var(--textDark);
font-size: 15px;
font-family: var(--font-medium);
}
nav#sidebar #projectInfo > .projectTexts > .projectCategory {
color: var(--textMedium);
font-size: 13px;
}
nav#sidebar .linkItem {
position: relative;
display: flex;
padding: 8px 12px;
border-radius: 3px;
cursor: pointer;
user-select: none;
}
nav#sidebar .linkItem > a {
width: 100%;
}
nav#sidebar .linkItem.notAllowed, nav#sidebar .linkItem.notAllowed > a {
cursor: not-allowed;
color: var(--textDark);
}
nav#sidebar .linkItem.notAllowed, nav#sidebar .linkItem.notAllowed > a > .styledIcon {
cursor: not-allowed;
color: var(--textDark);
}
nav#sidebar .linkItem:hover {
background: var(--backgroundLight);
}
nav#sidebar .linkItem.active {
color: var(--primary);
background: var(--backgroundLight);
}
nav#sidebar .linkItem > a {
display: flex;
}
nav#sidebar .linkItem > a > i.styledIcon {
margin-right: 15px;
font-size: 20px;
}
nav#sidebar .linkItem > a > .linkText {
padding-top: 2px;
font-size: 14.7px;
}
.styledTooltip.messages {
min-width: 800px;
}
.styledTooltip.messages > .messagesList {
}
.styledTooltip.messages > .messagesList > .message {
padding: 15px;
/*max-height: 90px;*/
/*overflow: hidden;*/
}
/*.styledTooltip.messages > .messagesList > .message:hover {*/
/* max-height: 100%;*/
/*}*/
.styledTooltip.messages > .messagesList > .message > .top {
display: flex;
justify-content: space-between;
}
.styledTooltip.messages > .messagesList > .message > .top > .summary {
font-family: var(--font-bold);
font-size: 20px;
line-height: 32px;
}
.styledTooltip.messages > .messagesList > .message > .top > .action {
width: 32px;
}
.styledTooltip.messages > .messagesList > .message > .description {
font-family: var(--font-regular);
}
.styledTooltip.messages > .messagesList > .message > .hyperlink {
margin-top: 15px;
}
.styledTooltip.messages > .messagesList > .message > .hyperlink > a {
color: var(--primary);
}
.styledTooltip.messages > .messagesList > .message > .hyperlink > a:hover {
text-decoration: underline;
}
.styledTooltip.messages > .messagesList > .message > .hyperlink > a > .styledIcon {
padding-right: 5px;
font-size: 14.5px;
}
.styledTooltip.messages > .messagesList > .message > .actions {
margin-top: 15px;
}
.styledTooltip.messages > .messagesList > .message > .actions > .styledButton {
margin-right: 15px;
}

View File

@ -0,0 +1,164 @@
/*.styledTooltip.messages > .messagesList > .message:hover {*/
/* max-height: 100%;*/
/*}*/
nav#sidebar {
position: fixed;
z-index: calc(var(--navLeft) - 1);
top: 0;
left: var(--appNavBarLeftWidth);
height: 100vh;
width: var(--secondarySideBarWidth);
padding: 0 16px 24px;
background: var(--backgroundLightest);
border-right: 1px solid var(--borderLightest);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 99px;
background: var(--backgroundMedium);
}
#projectInfo {
display: flex;
padding: 24px 4px;
> .projectTexts {
padding: 3px 0 0 10px;
> .projectName {
color: var(--textDark);
font-size: 15px;
font-family: var(--font-medium);
}
> .projectCategory {
color: var(--textMedium);
font-size: 13px;
}
}
}
.linkItem {
position: relative;
display: flex;
padding: 8px 12px;
border-radius: 3px;
cursor: pointer;
user-select: none;
> a {
width: 100%;
display: flex;
> i.styledIcon {
margin-right: 15px;
font-size: 20px;
}
> .linkText {
padding-top: 2px;
font-size: 14.7px;
}
}
&:hover {
background: var(--backgroundLight);
}
}
.linkItem.notAllowed {
cursor: not-allowed;
color: var(--textDark);
> a {
cursor: not-allowed;
color: var(--textDark);
> .styledIcon {
cursor: not-allowed;
color: var(--textDark);
}
}
}
.linkItem.active {
color: var(--primary);
background: var(--backgroundLight);
}
}
.styledTooltip.messages {
min-width: 800px;
> .messagesList {
> .message {
padding: 15px;
> .top {
display: flex;
justify-content: space-between;
> .summary {
font-family: var(--font-bold);
font-size: 20px;
line-height: 32px;
}
> .action {
width: 32px;
}
}
> .description {
font-family: var(--font-regular);
}
> .hyperlink {
margin-top: 15px;
> a {
color: var(--primary);
&:hover {
text-decoration: underline;
}
> .styledIcon {
padding-right: 5px;
font-size: 14.5px;
}
}
}
> .actions {
margin-top: 15px;
> .styledButton {
margin-right: 15px;
}
}
}
}
}
@media (max-width: 1100px) {
nav#sidebar {
width: calc(var(--secondarySideBarWidth) - 10px);
}
}
@media (max-width: 999px) {
nav#sidebar {
display: none;
}
}

View File

@ -1,61 +0,0 @@
.styledAvatar.image {
display: inline-block;
border-radius: 100%;
/*background-image: url("${imageURL}");*/
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
background-color: var(--backgroundLight);
}
.styledAvatar.letter {
display: inline-block;
/*width: ${props => props.size} px;*/
/*height: ${props => props.size} px;*/
border-radius: 100%;
text-transform: uppercase;
color: #fff;
/*background: ${props => props.color};*/
font-family: var(--font-medium);
font-weight: normal;
/*${props => font.size(Math.round(props.size / 1.7))}*/
}
.styledAvatar.letter > span {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.styledAvatar.avatarColor1, .styledAvatar span.avatarColor1 {
color: var(--avatar-color-1);
}
.styledAvatar.avatarColor2, .styledAvatar span.avatarColor2 {
color: var(--avatar-color-2);
}
.styledAvatar.avatarColor3, .styledAvatar span.avatarColor3 {
color: var(--avatar-color-3);
}
.styledAvatar.avatarColor4, .styledAvatar span.avatarColor4 {
color: var(--avatar-color-4);
}
.styledAvatar.avatarColor5, .styledAvatar span.avatarColor5 {
color: var(--avatar-color-5);
}
.styledAvatar.avatarColor6, .styledAvatar span.avatarColor6 {
color: var(--avatar-color-6);
}
.styledAvatar.avatarColor7, .styledAvatar span.avatarColor7 {
color: var(--avatar-color-7);
}
.styledAvatar.avatarColor8, .styledAvatar span.avatarColor8 {
color: var(--avatar-color-8);
}

View File

@ -0,0 +1,90 @@
.styledAvatar.image {
display: inline-block;
border-radius: 100%;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
background-color: var(--backgroundLight);
}
.styledAvatar.letter {
display: inline-block;
border-radius: 100%;
text-transform: uppercase;
color: #fff;
font-family: var(--font-medium);
font-weight: normal;
> span {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
.styledAvatar.avatarColor1 {
color: var(--avatar-color-1);
}
.styledAvatar {
span.avatarColor1 {
color: var(--avatar-color-1);
}
span.avatarColor2 {
color: var(--avatar-color-2);
}
span.avatarColor3 {
color: var(--avatar-color-3);
}
span.avatarColor4 {
color: var(--avatar-color-4);
}
span.avatarColor5 {
color: var(--avatar-color-5);
}
span.avatarColor6 {
color: var(--avatar-color-6);
}
span.avatarColor7 {
color: var(--avatar-color-7);
}
span.avatarColor8 {
color: var(--avatar-color-8);
}
}
.styledAvatar.avatarColor2 {
color: var(--avatar-color-2);
}
.styledAvatar.avatarColor3 {
color: var(--avatar-color-3);
}
.styledAvatar.avatarColor4 {
color: var(--avatar-color-4);
}
.styledAvatar.avatarColor5 {
color: var(--avatar-color-5);
}
.styledAvatar.avatarColor6 {
color: var(--avatar-color-6);
}
.styledAvatar.avatarColor7 {
color: var(--avatar-color-7);
}
.styledAvatar.avatarColor8 {
color: var(--avatar-color-8);
}

View File

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

View File

@ -1,43 +0,0 @@
.styledCheckbox {
display: flex;
justify-content: space-between;
}
.styledCheckbox > .styledCheckboxChild {
display: block;
border: 1px solid var(--borderLight);
font-family: var(--font-medium);
line-height: 2;
white-space: nowrap;
cursor: pointer;
font-size: 14.5px;
padding: 0 12px;
}
.styledCheckbox > .styledCheckboxChild.selected,
.styledCheckbox > .styledCheckboxChild:focus {
border-color: var(--borderInputFocus);
}
.styledCheckbox > .styledCheckboxChild.selected {
color: var(--borderInputFocus);
}
.styledCheckbox > .styledCheckboxChild > input[type=radio] {
display: none;
}
.styledCheckbox > .styledCheckboxChild.untracking.selected {
color: var(--success);
border-color: var(--success);
}
.styledCheckbox > .styledCheckboxChild.fibonacci.selected {
color: var(--warning);
border-color: var(--warning);
}
.styledCheckbox > .styledCheckboxChild.hourly.selected {
color: var(--danger);
border-color: var(--danger);
}

View File

@ -0,0 +1,43 @@
.styledCheckbox {
display: flex;
justify-content: space-between;
> .styledCheckboxChild {
display: block;
border: 1px solid var(--borderLight);
font-family: var(--font-medium);
line-height: 2;
white-space: nowrap;
cursor: pointer;
font-size: 14.5px;
padding: 0 12px;
&:focus {
border-color: var(--borderInputFocus);
}
> input[type=radio] {
display: none;
}
}
> .styledCheckboxChild.selected {
border-color: var(--borderInputFocus);
color: var(--borderInputFocus);
}
> .styledCheckboxChild.untracking.selected {
color: var(--success);
border-color: var(--success);
}
> .styledCheckboxChild.fibonacci.selected {
color: var(--warning);
border-color: var(--warning);
}
> .styledCheckboxChild.hourly.selected {
color: var(--danger);
border-color: var(--danger);
}
}

View File

@ -1,74 +0,0 @@
.styledComment {
position: relative;
margin-top: 25px;
font-size: 15px
}
.styledComment > .userAvatar {
position: absolute;
top: 0;
left: 0;
}
.styledComment > .content {
padding-left: 44px;
}
.styledComment > .content > .userName {
display: inline-block;
padding-right: 12px;
padding-bottom: 10px;
color: var(--textDark);
font-family: var(--font-medium);
font-weight: normal;
}
.styledComment > .content > .createdAt {
display: inline-block;
padding-bottom: 10px;
color: var(--textDark);
font-size: 14.5px
}
/* as view */
.styledComment > .content > .body {
padding-bottom: 10px;
white-space: pre-wrap;
}
.styledComment > .content > .editButton {
margin-right: 12px;
display: inline-block;
padding: 2px 0;
color: var(--textMedium);
font-size: 14.5px;
cursor: pointer;
user-select: none;
}
.styledComment > .content > .editButton:hover {
text-decoration: underline;
}
.styledComment > .content > .deleteButton {
display: inline-block;
padding: 2px 0;
color: var(--textMedium);
font-size: 14.5px;
cursor: pointer;
user-select: none;
}
.styledComment > .content > .deleteButton:hover {
text-decoration: underline;
}
.styledComment > .content > .deleteButton:before {
position: relative;
right: 6px;
content: '·';
display: inline-block;
}
/* as form */

View File

@ -0,0 +1,71 @@
.styledComment {
position: relative;
margin-top: 25px;
font-size: 15px;
> .userAvatar {
position: absolute;
top: 0;
left: 0;
}
> .content {
padding-left: 44px;
> .userName {
display: inline-block;
padding-right: 12px;
padding-bottom: 10px;
color: var(--textDark);
font-family: var(--font-medium);
font-weight: normal;
}
> .createdAt {
display: inline-block;
padding-bottom: 10px;
color: var(--textDark);
font-size: 14.5px;
}
/* as view */
> .body {
padding-bottom: 10px;
white-space: pre-wrap;
}
> .editButton {
margin-right: 12px;
display: inline-block;
padding: 2px 0;
color: var(--textMedium);
font-size: 14.5px;
cursor: pointer;
user-select: none;
&:hover {
text-decoration: underline;
}
}
> .deleteButton {
display: inline-block;
padding: 2px 0;
color: var(--textMedium);
font-size: 14.5px;
cursor: pointer;
user-select: none;
&:hover {
text-decoration: underline;
}
&:before {
position: relative;
right: 6px;
content: '·';
display: inline-block;
}
}
}
}

View File

@ -1,94 +0,0 @@
.styledDateTimeInput {
position: relative;
}
/* TOOLTIP */
.dateTimeTooltip {
padding: 15px;
position: absolute;
top: -50px;
left: 110px;
width: 610px;
min-width: 610px;
max-width: 610px;
}
.dateTimeTooltip:before {
content: '';
display: block;
width: 20px;
height: 20px;
position: absolute;
left: -10px;
top: 56px;
transform: rotate(45deg);
background: white;
z-index: -1;
border-left: 1px solid rgba(9, 30, 66, 0.25);
border-bottom: 1px solid rgba(9, 30, 66, 0.25);
}
.dateTimeTooltip > h2 {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
font-size: 1.1rem;
}
.dateTimeTooltip > .actions {
display: flex;
justify-content: space-between;
height: 2rem;
line-height: 2rem;
}
.dateTimeTooltip > .calendar {
}
.dateTimeTooltip > .calendar > .week {
display: flex;
}
.dateTimeTooltip > .calendar > .week.weekHeader {
border-bottom: 1px solid var(--textDarkest);
cursor: auto;
}
.dateTimeTooltip > .calendar > .week.weekHeader > .day {
cursor: auto;
}
.dateTimeTooltip > .calendar > .week > .day {
width: calc(100% / 7);
text-align: center;
height: 2rem;
line-height: 2rem;
cursor: pointer;
}
.dateTimeTooltip > .calendar > .week > .day.inCurrentMonth:hover,
.dateTimeTooltip > .calendar > .week > .day.outCurrentMonth:hover {
background: var(--primary);
color: var(--asideIcon);
}
.dateTimeTooltip > .calendar > .week > .day.inCurrentMonth {
color: var(--textDarkest);
}
.dateTimeTooltip > .calendar > .week > .day.outCurrentMonth {
color: var(--textLight);
}
.dateTimeTooltip > .calendar > .week > .day.inCurrentMonth.selected,
.dateTimeTooltip > .calendar > .week > .day.outCurrentMonth.selected {
color: var(--primary);
background: var(--asideIcon);
}
.dateTimeTooltip > .calendar > .week > .day {
font-family: var(--font-medium);
font-size: 1rem;
cursor: pointer;
}

View File

@ -0,0 +1,96 @@
/* TOOLTIP */
.styledDateTimeInput {
position: relative;
}
.dateTimeTooltip {
padding: 15px;
position: absolute;
top: -50px;
left: 110px;
width: 610px;
min-width: 610px;
max-width: 610px;
&:before {
content: '';
display: block;
width: 20px;
height: 20px;
position: absolute;
left: -10px;
top: 56px;
transform: rotate(45deg);
background: white;
z-index: -1;
border-left: 1px solid rgba(9, 30, 66, 0.25);
border-bottom: 1px solid rgba(9, 30, 66, 0.25);
}
> h2 {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
font-size: 1.1rem;
}
> .actions {
display: flex;
justify-content: space-between;
height: 2rem;
line-height: 2rem;
}
> .calendar {
> .week {
display: flex;
> .day {
width: calc(100% / 7);
text-align: center;
height: 2rem;
line-height: 2rem;
cursor: pointer;
font-family: var(--font-medium);
font-size: 1rem;
}
> .day.inCurrentMonth {
&:hover {
background: var(--primary);
color: var(--asideIcon);
}
color: var(--textDarkest);
}
> .day.outCurrentMonth {
&:hover {
background: var(--primary);
color: var(--asideIcon);
}
color: var(--textLight);
}
> .day.inCurrentMonth.selected {
color: var(--primary);
background: var(--asideIcon);
}
> .day.outCurrentMonth.selected {
color: var(--primary);
background: var(--asideIcon);
}
}
> .week.weekHeader {
border-bottom: 1px solid var(--textDarkest);
cursor: auto;
> .day {
cursor: auto;
}
}
}
}

View File

@ -1,76 +0,0 @@
.styledEditor {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
max-width: 100%;
}
.styledEditor > input[type="radio"] {
display: none;
}
.styledEditor > .navbar {
border: 1px solid var(--borderLight);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
font-family: var(--font-medium);
font-weight: normal;
text-align: center;
height: 32px;
vertical-align: middle;
line-height: 2;
white-space: nowrap;
transition: all 0.1s;
appearance: none;
cursor: pointer;
user-select: none;
font-size: 14.5px;
}
.styledEditor > .navbar:not(:hover) {
border-color: var(--backgroundLightest);
background-color: var(--borderLight);
}
.styledEditor > .navbar.activeTab {
background-color: var(--backgroundLightest);
border-color: var(--borderLight);
}
.styledEditor > .navbar:hover {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
.styledEditor > .navbar {
border-color: var(--borderInputFocus);
}
.styledEditor > .navbar.editorTab {
min-width: 50%;
}
.styledEditor > .navbar.viewTab {
min-width: 50%;
}
.styledEditor > .styledTextArea {
grid-area: view;
display: none;
}
.styledEditor > .view {
min-width: 100%;
display: none;
min-height: 40px;
padding-top: 15px;
}
.styledEditor > input.editorRadio:checked ~ .styledTextArea {
display: block;
}
.styledEditor > input.viewRadio:checked ~ .view {
display: block;
}

View File

@ -0,0 +1,85 @@
.styledEditor {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
max-width: 100%;
> input[type="radio"] {
display: none;
}
> .navbar {
border: 1px solid var(--borderLight);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
font-family: var(--font-medium);
font-weight: normal;
text-align: center;
height: 32px;
vertical-align: middle;
line-height: 2;
white-space: nowrap;
transition: all 0.1s;
appearance: none;
cursor: pointer;
user-select: none;
font-size: 14.5px;
border-color: var(--borderInputFocus);
&:not(:hover) {
border-color: var(--backgroundLightest);
background-color: var(--borderLight);
}
&:hover {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
}
> .navbar.activeTab {
background-color: var(--backgroundLightest);
border-color: var(--borderLight);
}
> .navbar.editorTab {
min-width: 50%;
}
> .navbar.viewTab {
min-width: 50%;
}
> .styledTextArea {
grid-area: view;
display: none;
}
> .view {
min-width: 100%;
display: none;
min-height: 40px;
padding-top: 15px;
}
> input.editorRadio {
&:checked {
~ {
.styledTextArea {
display: block;
}
}
}
}
> input.viewRadio {
&:checked {
~ {
.view {
display: block;
}
}
}
}
}

View File

@ -1,66 +0,0 @@
.styledForm {
display: block;
}
.styledForm > .formElement {
padding: 25px 40px 35px;
}
.styledForm > .formElement > .formHeading {
padding-bottom: 15px;
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;
}
.styledField {
display: block;
margin-top: 20px;
}
.styledField > .styledLabel {
display: block;
padding-bottom: 5px;
color: var(--textMedium);
font-family: var(--font-medium);
font-weight: normal;
font-size: 13px;
}
.styledField > .styledTip {
padding-top: 6px;
color: var(--textMedium);
font-size: 12.5px;
}
.styledField > * {
display: block;
}

View File

@ -0,0 +1,67 @@
.styledForm {
display: block;
> .formElement {
padding: 25px 40px 35px;
> .formHeading {
padding-bottom: 15px;
font-size: 21px;
}
.selectItem.withBottomMargin {
margin-bottom: 5px;
}
.selectItem {
> .selectItemLabel {
padding: 0 3px 0 6px;
}
}
.divider {
margin-top: 22px;
border-top: 1px solid var(--borderLightest);
}
> .actions {
display: flex;
justify-content: flex-end;
padding-top: 30px;
> .actionButton {
margin-left: 10px;
}
}
}
}
.selectItem {
display: flex;
align-items: center;
margin-right: 15px;
}
.styledField {
display: block;
margin-top: 20px;
> .styledLabel {
display: block;
padding-bottom: 5px;
color: var(--textMedium);
font-family: var(--font-medium);
font-weight: normal;
font-size: 13px;
}
> .styledTip {
padding-top: 6px;
color: var(--textMedium);
font-size: 12.5px;
}
> * {
display: block;
}
}

View File

@ -1,423 +0,0 @@
i.styledIcon {
color: var(--primary);
display: inline-block;
font-size: 16px;
line-height: 1;
}
i.styledIcon.left {
transform: translate(0, 0);
}
i.styledIcon.top {
transform: translate(0, 0);
}
i.styledIcon:before {
font-family: 'IcoFont';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
i.styledIcon.stopwatch:before {
content: "\ec89";
}
i.styledIcon.bug:before {
content: "\eec7";
}
i.styledIcon.task:before {
content: "\ef27";
}
i.styledIcon.story:before {
content: "\ef2d";
}
i.styledIcon.epic:before {
content: '\ef30';
}
i.styledIcon.arrowDown:before {
content: "\ea92";
}
i.styledIcon.arrowUp:before {
content: "\ea95";
}
i.styledIcon.arrowLeftCircle:before {
font-family: "jira";
content: "\e917";
}
i.styledIcon.chevronDown:before {
font-family: "jira";
content: "\e900";
}
i.styledIcon.chevronLeft:before {
font-family: "jira";
content: "\e901";
}
i.styledIcon.chevronRight:before {
font-family: "jira";
content: "\e902";
}
i.styledIcon.chevronUp:before {
font-family: "jira";
content: "\e903";
}
i.styledIcon.board:before {
content: "\ead0";
}
i.styledIcon.help:before {
content: "\efca";
}
i.styledIcon.link:before {
content: "\ef71";
}
i.styledIcon.menu:before {
font-family: "jira";
content: "\e916";
}
i.styledIcon.more:before {
font-family: "jira";
content: "\e90e";
}
i.styledIcon.attach:before {
font-family: "jira";
content: "\e90d";
}
i.styledIcon.plus:before {
content: "\efc2";
}
i.styledIcon.search:before {
content: "\ec82";
}
i.styledIcon.issues:before {
content: "\ed19";
}
i.styledIcon.settings:before {
content: "\efe2";
}
i.styledIcon.close:before {
content: "\eee4";
}
i.styledIcon.feedback:before {
font-family: "jira";
content: "\e918";
}
i.styledIcon.trash:before {
content: "\eebb";
}
i.styledIcon.github:before {
content: "\ed3e";
}
i.styledIcon.shipping:before {
content: "\efbe";
}
i.styledIcon.component:before {
content: "\eef8";
}
i.styledIcon.reports:before {
content: "\eeaf";
}
i.styledIcon.page:before {
content: "\efb2";
}
i.styledIcon.calendar:before {
content: "\ec45";
}
i.styledIcon.cop:before {
content: "\ebb4";
}
i.styledIcon.arrowLeft:before {
font-family: "jira";
content: "\e91e";
}
i.styledIcon.arrowRight:before {
font-family: "jira";
content: "\e91f";
}
i.styledIcon.user:before {
content: "\ec8e";
}
i.styledIcon.message:before {
content: "\efac";
}
i.styledIcon.check:before {
content: "\ec4b";
}
/********/
/* RTE */
/********/
i.styledIcon.align-center:before {
content: "\eddf";
}
i.styledIcon.align-left:before {
content: "\ede0";
}
i.styledIcon.align-right:before {
content: "\ede1";
}
i.styledIcon.all-caps:before {
content: "\ede2";
}
i.styledIcon.bold:before {
content: "\ede3";
}
i.styledIcon.brush:before {
content: "\ede4";
}
i.styledIcon.clip-board:before {
content: "\ede5";
}
i.styledIcon.code-alt:before {
content: "\ede6";
}
i.styledIcon.color-bucket:before {
content: "\ede7";
}
i.styledIcon.color-picker:before {
content: "\ede8";
}
i.styledIcon.copy-invert:before {
content: "\ede9";
}
i.styledIcon.copy:before {
content: "\edea";
}
i.styledIcon.cut:before {
content: "\edeb";
}
i.styledIcon.delete-alt:before {
content: "\edec";
}
i.styledIcon.edit-alt:before {
content: "\eded";
}
i.styledIcon.eraser-alt:before {
content: "\edee";
}
i.styledIcon.font:before {
content: "\edef";
}
i.styledIcon.heading:before {
content: "\edf0";
}
i.styledIcon.indent:before {
content: "\edf1";
}
i.styledIcon.italic-alt:before {
content: "\edf2";
}
i.styledIcon.italic:before {
content: "\edf3";
}
i.styledIcon.justify-all:before {
content: "\edf4";
}
i.styledIcon.justify-center:before {
content: "\edf5";
}
i.styledIcon.justify-left:before {
content: "\edf6";
}
i.styledIcon.justify-right:before {
content: "\edf7";
}
i.styledIcon.link-broken:before {
content: "\edf8";
}
i.styledIcon.outdent:before {
content: "\edf9";
}
i.styledIcon.paper-clip:before {
content: "\edfa";
}
i.styledIcon.paragraph:before {
content: "\edfb";
}
i.styledIcon.pin:before {
content: "\edfc";
}
i.styledIcon.printer:before {
content: "\edfd";
}
i.styledIcon.redo:before {
content: "\edfe";
}
i.styledIcon.rotation:before {
content: "\edff";
}
i.styledIcon.save:before {
content: "\ee00";
}
i.styledIcon.small-cap:before {
content: "\ee01";
}
i.styledIcon.strike-through:before {
content: "\ee02";
}
i.styledIcon.sub-listing:before {
content: "\ee03";
}
i.styledIcon.subscript:before {
content: "\ee04";
}
i.styledIcon.superscript:before {
content: "\ee05";
}
i.styledIcon.table:before {
content: "\ee06";
}
i.styledIcon.text-height:before {
content: "\ee07";
}
i.styledIcon.text-width:before {
content: "\ee08";
}
i.styledIcon.trash:before {
content: "\ee09";
}
i.styledIcon.underline:before {
content: "\ee0a";
}
i.styledIcon.undo:before {
content: "\ee0b";
}
i.styledIcon.listing-dots:before {
content: "\ef74";
}
i.styledIcon.listing-number:before {
content: "\ef76";
}
i.styledIcon.double-left:before {
content: "\ea7b";
}
i.styledIcon.double-right:before {
content: "\ea7c";
}
i.styledIcon.task {
color: var(--task);
}
i.styledIcon.bug {
color: var(--bug);
}
i.styledIcon.story {
color: var(--story);
}
i.styledIcon.epic {
color: var(--epic);
}
i.styledIcon.highest {
color: var(--highest);
}
i.styledIcon.high {
color: var(--high);
}
i.styledIcon.medium {
color: var(--medium);
}
i.styledIcon.low {
color: var(--low);
}
i.styledIcon.lowest {
color: var(--lowest);
}

View File

@ -0,0 +1,576 @@
i.styledIcon {
color: var(--primary);
display: inline-block;
font-size: 16px;
line-height: 1;
&:before {
font-family: 'IcoFont';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
i.styledIcon.left {
transform: translate(0, 0);
}
i.styledIcon.top {
transform: translate(0, 0);
}
i.styledIcon.stopwatch {
&:before {
content: "\ec89";
}
}
i.styledIcon.bug {
&:before {
content: "\eec7";
}
color: var(--bug);
}
i.styledIcon.task {
&:before {
content: "\ef27";
}
color: var(--task);
}
i.styledIcon.story {
&:before {
content: "\ef2d";
}
color: var(--story);
}
i.styledIcon.epic {
&:before {
content: '\ef30';
}
color: var(--epic);
}
i.styledIcon.arrowDown {
&:before {
content: "\ea92";
}
}
i.styledIcon.arrowUp {
&:before {
content: "\ea95";
}
}
i.styledIcon.arrowLeftCircle {
&:before {
font-family: "jira";
content: "\e917";
}
}
i.styledIcon.chevronDown {
&:before {
font-family: "jira";
content: "\e900";
}
}
i.styledIcon.chevronLeft {
&:before {
font-family: "jira";
content: "\e901";
}
}
i.styledIcon.chevronRight {
&:before {
font-family: "jira";
content: "\e902";
}
}
i.styledIcon.chevronUp {
&:before {
font-family: "jira";
content: "\e903";
}
}
i.styledIcon.board {
&:before {
content: "\ead0";
}
}
i.styledIcon.help {
&:before {
content: "\efca";
}
}
i.styledIcon.link {
&:before {
content: "\ef71";
}
}
i.styledIcon.menu {
&:before {
font-family: "jira";
content: "\e916";
}
}
i.styledIcon.more {
&:before {
font-family: "jira";
content: "\e90e";
}
}
i.styledIcon.attach {
&:before {
font-family: "jira";
content: "\e90d";
}
}
i.styledIcon.plus {
&:before {
content: "\efc2";
}
}
i.styledIcon.search {
&:before {
content: "\ec82";
}
}
i.styledIcon.issues {
&:before {
content: "\ed19";
}
}
i.styledIcon.settings {
&:before {
content: "\efe2";
}
}
i.styledIcon.close {
&:before {
content: "\eee4";
}
}
i.styledIcon.feedback {
&:before {
font-family: "jira";
content: "\e918";
}
}
i.styledIcon.trash {
&:before {
content: "\eebb";
content: "\ee09";
}
}
i.styledIcon.github {
&:before {
content: "\ed3e";
}
}
i.styledIcon.shipping {
&:before {
content: "\efbe";
}
}
i.styledIcon.component {
&:before {
content: "\eef8";
}
}
i.styledIcon.reports {
&:before {
content: "\eeaf";
}
}
i.styledIcon.page {
&:before {
content: "\efb2";
}
}
i.styledIcon.calendar {
&:before {
content: "\ec45";
}
}
i.styledIcon.cop {
&:before {
content: "\ebb4";
}
}
i.styledIcon.arrowLeft {
&:before {
font-family: "jira";
content: "\e91e";
}
}
i.styledIcon.arrowRight {
&:before {
font-family: "jira";
content: "\e91f";
}
}
i.styledIcon.user {
&:before {
content: "\ec8e";
}
}
i.styledIcon.message {
&:before {
content: "\efac";
}
}
i.styledIcon.check {
&:before {
content: "\ec4b";
}
}
i.styledIcon.align-center {
&:before {
content: "\eddf";
}
}
i.styledIcon.align-left {
&:before {
content: "\ede0";
}
}
i.styledIcon.align-right {
&:before {
content: "\ede1";
}
}
i.styledIcon.all-caps {
&:before {
content: "\ede2";
}
}
i.styledIcon.bold {
&:before {
content: "\ede3";
}
}
i.styledIcon.brush {
&:before {
content: "\ede4";
}
}
i.styledIcon.clip-board {
&:before {
content: "\ede5";
}
}
i.styledIcon.code-alt {
&:before {
content: "\ede6";
}
}
i.styledIcon.color-bucket {
&:before {
content: "\ede7";
}
}
i.styledIcon.color-picker {
&:before {
content: "\ede8";
}
}
i.styledIcon.copy-invert {
&:before {
content: "\ede9";
}
}
i.styledIcon.copy {
&:before {
content: "\edea";
}
}
i.styledIcon.cut {
&:before {
content: "\edeb";
}
}
i.styledIcon.delete-alt {
&:before {
content: "\edec";
}
}
i.styledIcon.edit-alt {
&:before {
content: "\eded";
}
}
i.styledIcon.eraser-alt {
&:before {
content: "\edee";
}
}
i.styledIcon.font {
&:before {
content: "\edef";
}
}
i.styledIcon.heading {
&:before {
content: "\edf0";
}
}
i.styledIcon.indent {
&:before {
content: "\edf1";
}
}
i.styledIcon.italic-alt {
&:before {
content: "\edf2";
}
}
i.styledIcon.italic {
&:before {
content: "\edf3";
}
}
i.styledIcon.justify-all {
&:before {
content: "\edf4";
}
}
i.styledIcon.justify-center {
&:before {
content: "\edf5";
}
}
i.styledIcon.justify-left {
&:before {
content: "\edf6";
}
}
i.styledIcon.justify-right {
&:before {
content: "\edf7";
}
}
i.styledIcon.link-broken {
&:before {
content: "\edf8";
}
}
i.styledIcon.outdent {
&:before {
content: "\edf9";
}
}
i.styledIcon.paper-clip {
&:before {
content: "\edfa";
}
}
i.styledIcon.paragraph {
&:before {
content: "\edfb";
}
}
i.styledIcon.pin {
&:before {
content: "\edfc";
}
}
i.styledIcon.printer {
&:before {
content: "\edfd";
}
}
i.styledIcon.redo {
&:before {
content: "\edfe";
}
}
i.styledIcon.rotation {
&:before {
content: "\edff";
}
}
i.styledIcon.save {
&:before {
content: "\ee00";
}
}
i.styledIcon.small-cap {
&:before {
content: "\ee01";
}
}
i.styledIcon.strike-through {
&:before {
content: "\ee02";
}
}
i.styledIcon.sub-listing {
&:before {
content: "\ee03";
}
}
i.styledIcon.subscript {
&:before {
content: "\ee04";
}
}
i.styledIcon.superscript {
&:before {
content: "\ee05";
}
}
i.styledIcon.table {
&:before {
content: "\ee06";
}
}
i.styledIcon.text-height {
&:before {
content: "\ee07";
}
}
i.styledIcon.text-width {
&:before {
content: "\ee08";
}
}
i.styledIcon.underline {
&:before {
content: "\ee0a";
}
}
i.styledIcon.undo {
&:before {
content: "\ee0b";
}
}
i.styledIcon.listing-dots {
&:before {
content: "\ef74";
}
}
i.styledIcon.listing-number {
&:before {
content: "\ef76";
}
}
i.styledIcon.double-left {
&:before {
content: "\ea7b";
}
}
i.styledIcon.double-right {
&:before {
content: "\ea7c";
}
}
i.styledIcon.highest {
color: var(--highest);
}
i.styledIcon.high {
color: var(--high);
}
i.styledIcon.medium {
color: var(--medium);
}
i.styledIcon.low {
color: var(--low);
}
i.styledIcon.lowest {
color: var(--lowest);
}

View File

@ -1,21 +0,0 @@
.styledImageInput {
}
.styledImageInput > .label {
width: 120px;
height: 120px;
margin: 0 auto;
display: block;
}
.styledImageInput > .label > .mask {
display: block;
width: 120px;
height: 120px;
border-radius: 60px;
cursor: pointer;
}
.styledImageInput > .input {
display: none;
}

View File

@ -0,0 +1,18 @@
.styledImageInput {
>.label {
width: 120px;
height: 120px;
margin: 0 auto;
display: block;
>.mask {
display: block;
width: 120px;
height: 120px;
border-radius: 60px;
cursor: pointer;
}
}
>.input {
display: none;
}
}

View File

@ -1,54 +0,0 @@
.styledInput {
position: relative;
display: inline-block;
min-height: 32px;
width: 100%;
}
.styledInput > .inputElement {
min-height: 32px;
height: 100%;
width: 100%;
padding: 0 7px;
border-radius: 3px;
border: 1px solid var(--borderLightest);
color: var(--textDarkest);
background: var(--backgroundLightest);
transition: background 0.1s;
font-family: var(--font-regular);
font-size: 15px;
}
.styledInput > .inputElement.withIcon {
padding-left: 32px;
}
.styledInput > .inputElement.primary {
padding: 8px 12px 9px;
height: 39px;
}
.styledInput > i.styledIcon {
font-size: 15px;
position: absolute;
top: 8px;
left: 8px;
pointer-events: none;
color: #5E6C84;
}
.styledInput > .inputElement:hover {
background: var(--backgroundLight);
}
.styledInput > .inputElement:focus {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
.styledInput.invalid,
.styledInput.invalid:focus {
border: 1px solid var(--danger);
box-shadow: none;
}

View File

@ -0,0 +1,58 @@
.styledInput {
position: relative;
display: inline-block;
min-height: 32px;
width: 100%;
> .inputElement {
min-height: 32px;
height: 100%;
width: 100%;
padding: 0 7px;
border-radius: 3px;
border: 1px solid var(--borderLightest);
color: var(--textDarkest);
background: var(--backgroundLightest);
transition: background 0.1s;
font-family: var(--font-regular);
font-size: 15px;
&:hover {
background: var(--backgroundLight);
}
&:focus {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
}
> .inputElement.withIcon {
padding-left: 32px;
}
> .inputElement.primary {
padding: 8px 12px 9px;
height: 39px;
}
> i.styledIcon {
font-size: 15px;
position: absolute;
top: 8px;
left: 8px;
pointer-events: none;
color: #5E6C84;
}
}
.styledInput.invalid {
border: 1px solid var(--danger);
box-shadow: none;
&:focus {
border: 1px solid var(--danger);
box-shadow: none;
}
}

View File

@ -1,149 +0,0 @@
.modal {
z-index: var(--modal);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.modal > .clickableOverlay {
min-height: 100%;
background: rgba(9, 30, 66, 0.54);
}
.modal > .clickableOverlay.center {
display: flex;
justify-content: center;
align-items: center;
padding: 50px;
}
@media (max-width: 1100px) {
.modal {
z-index: var(--modal);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.modal > .clickableOverlay {
min-height: 100%;
background: rgba(9, 30, 66, 0.54);
}
.modal > .clickableOverlay.center {
display: block;
padding: 0;
}
}
.modal > .clickableOverlay {
min-height: 100%;
background: rgba(9, 30, 66, 0.54);
}
.modal > .clickableOverlay > .styledModal {
display: inline-block;
position: relative;
width: 100%;
background: #fff;
}
.modal > .clickableOverlay > .styledModal.center {
vertical-align: middle;
border-radius: 3px;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
}
.modal > .clickableOverlay > .styledModal.aside {
min-height: 100vh;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.15);
}
.modal > .clickableOverlay > .styledModal.aside > .styledIcon {
position: absolute;
font-size: 25px;
color: var(--textMedium);
transition: all 0.1s;
cursor: pointer;
user-select: none;
}
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantCenter {
top: 10px;
right: 12px;
padding: 3px 5px 0 5px;
border-radius: 4px;
}
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantCenter:hover {
background: var(--backgroundLight);
}
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantAside {
top: 10px;
right: -30px;
width: 50px;
height: 50px;
padding-top: 10px;
border-radius: 3px;
text-align: center;
background: #fff;
border: 1px solid var(--borderLightest);
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
}
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantAside:hover {
color: var(--primary);
}
.modal > .clickableOverlay > .styledModal.addIssue {
max-width: 800px;
}
@media (max-width: 1100px) {
.modal > .clickableOverlay > .styledModal.addIssue {
max-width: none;
width: auto;
}
}
.modal > .clickableOverlay > .styledModal.confirmModal {
padding: 35px 40px 40px;
}
.modal > .clickableOverlay > .styledModal.confirmModal > .title {
padding-bottom: 25px;
font-family: var(--font-medium);
font-weight: normal;
font-size: 22px;
line-height: 1.5;
}
.modal > .clickableOverlay > .styledModal.confirmModal > .message {
padding-bottom: 25px;
white-space: pre-wrap;
font-size: 15px
}
.modal > .clickableOverlay > .styledModal.confirmModal > .actions {
display: flex;
padding-top: 6px;
}
.modal > .clickableOverlay > .styledModal.confirmModal > .actions > .styledButton {
margin-right: 10px;
}
.modal > .clickableOverlay > .styledModal.debugModal {
padding-left: 15px;
}

View File

@ -0,0 +1,142 @@
.modal {
z-index: var(--modal);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
> .clickableOverlay {
min-height: 100%;
background: rgba(9, 30, 66, 0.54);
&.center {
display: flex;
justify-content: center;
align-items: center;
padding: 50px;
}
> .styledModal {
display: inline-block;
position: relative;
width: 100%;
background: #fff;
&.center {
vertical-align: middle;
border-radius: 3px;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
}
&.aside {
min-height: 100vh;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.15);
> .styledIcon {
position: absolute;
font-size: 25px;
color: var(--textMedium);
transition: all 0.1s;
cursor: pointer;
user-select: none;
&.modalVariantCenter {
top: 10px;
right: 12px;
padding: 3px 5px 0 5px;
border-radius: 4px;
&:hover {
background: var(--backgroundLight);
}
}
&.modalVariantAside {
top: 10px;
right: -30px;
width: 50px;
height: 50px;
padding-top: 10px;
border-radius: 3px;
text-align: center;
background: #fff;
border: 1px solid var(--borderLightest);
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.1);
&:hover {
color: var(--primary);
}
}
}
}
&.addIssue {
max-width: 800px;
}
&.confirmModal {
padding: 35px 40px 40px;
> .title {
padding-bottom: 25px;
font-family: var(--font-medium);
font-weight: normal;
font-size: 22px;
line-height: 1.5;
}
> .message {
padding-bottom: 25px;
white-space: pre-wrap;
font-size: 15px
}
> .actions {
display: flex;
padding-top: 6px;
> .styledButton {
margin-right: 10px;
}
}
}
&.debugModal {
padding-left: 15px;
}
}
}
}
@media (max-width: 1100px) {
.modal {
z-index: var(--modal);
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
> .clickableOverlay {
min-height: 100%;
background: rgba(9, 30, 66, 0.54);
&.center {
display: block;
padding: 0;
}
> .styledModal.addIssue {
max-width: none;
width: auto;
}
}
}
}

View File

@ -1,214 +0,0 @@
.styledRte {
display: block;
}
.styledRte > .bar {
display: block;
border-radius: 3px;
border: 1px solid var(--borderLightest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
}
.styledRte > .bar > .row {
padding: 0 0 var(--rte-indent) 0;
display: flex;
margin: 0 var(--rte-indent);
}
.styledRte > .bar > .row:first-child {
padding: var(--rte-indent) 0;
}
.styledRte > .bar > .row > .group {
padding: 0 var(--rte-indent) 0 0;
}
.styledRte > .bar > .row > .group:first-child {
padding-left: 0;
}
.styledRte > .bar > .row > .group:last-child {
padding-right: 0;
}
.styledRte > .bar > .row > .group > .styledRteButton > .styledButton,
.styledRte > .bar > .row > .group > .styledButton,
.styledRte > .bar > .row > .group > span.headingList {
margin-right: var(--rte-indent);
font-size: var(--small-font-size);
}
.styledRte > .bar > .row > .group.font > span.headingList > .headingOption {
margin-right: var(--rte-indent);
}
.styledRte > .bar > .row > .group > .styledRteButton > .styledButton,
.styledRte > .bar > .row > .group > .styledRteButton > .styledButton > .styledIcon,
.styledRte > .bar > .row > .group > span.headingList > .headingOption > .styledButton,
.styledRte > .bar > .row > .group > span.headingList > .headingOption > .styledButton > span {
font-size: var(--small-font-size);
line-height: calc(2 * var(--small-font-size));
height: calc(2 * var(--small-font-size));
}
.styledRte > .bar > .row > .group > .headingList {
height: 32px;
min-width: 40px;
overflow-y: visible;
overflow-x: hidden;
}
.styledRte > .editorWrapper {
display: block;
width: 100%;
border-radius: 3px;
border: 1px solid var(--borderLightest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
color: var(--textDarkest);
font-weight: normal;
font-size: 15px;
resize: vertical;
}
.styledRte > .editorWrapper > .editor {
display: block;
margin: 8px 12px 9px;
min-height: 60px;
}
.styledRte > .editorWrapper > .editor ul,
.styledRte > .editorWrapper > .editor ol {
margin-left: 25px;
}
.styledRte > .editorWrapper > .editor ul > li {
list-style: square;
}
.styledRte > .editorWrapper > .editor ol > li {
list-style: decimal;
}
.styledRte > .editorWrapper > .editor table {
table-layout: fixed;
border-spacing: 0;
text-align: center;
}
.styledRte > .editorWrapper > .editor table > tbody > tr > td {
min-width: 24px;
min-height: 24px;
text-align: center;
padding: var(--rte-indent);
border-bottom: 1px solid var(--borderLight);
border-right: 1px solid var(--borderLight);
border-left: 1px solid var(--borderLight);
border-top: 1px solid var(--borderLight);
}
.styledRte > .editorWrapper > .editor *:focus {
background: var(--secondary);
}
/**********************************************************/
/* Table tooltip */
/**********************************************************/
.tableTooltip {
min-width: 136px;
padding: 15px;
position: absolute;
}
.tableTooltip > h2 {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
.tableTooltip > .tablePreview {
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.tableTooltip > .tablePreview > table {
table-layout: fixed;
border-spacing: 0;
text-align: center;
}
.tableTooltip > .tablePreview > table > tbody > tr > td {
width: 24px;
height: 24px;
text-align: center;
padding: var(--rte-indent);
border-bottom: 1px solid var(--borderLight);
border-right: 1px solid var(--borderLight);
border-left: 1px solid var(--borderLight);
border-top: 1px solid var(--borderLight);
}
.tableTooltip > .tablePreview > input {
height: 32px;
align-self: flex-end;
margin-left: 15px;
}
.tableTooltip > .inputs {
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);
}

View File

@ -0,0 +1,244 @@
.styledRte {
display: block;
> .bar {
display: block;
border-radius: 3px;
border: 1px solid var(--borderLightest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
> .row {
padding: 0 0 var(--rte-indent) 0;
display: flex;
margin: 0 var(--rte-indent);
&:first-child {
padding: var(--rte-indent) 0;
}
> .group {
padding: 0 var(--rte-indent) 0 0;
&:first-child {
padding-left: 0;
}
&:last-child {
padding-right: 0;
}
> .styledRteButton {
> .styledButton {
margin-right: var(--rte-indent);
font-size: var(--small-font-size);
font-size: var(--small-font-size);
line-height: calc(2 * var(--small-font-size));
height: calc(2 * var(--small-font-size));
> .styledIcon {
font-size: var(--small-font-size);
line-height: calc(2 * var(--small-font-size));
height: calc(2 * var(--small-font-size));
}
}
}
> .styledButton {
margin-right: var(--rte-indent);
font-size: var(--small-font-size);
}
> span.headingList {
margin-right: var(--rte-indent);
font-size: var(--small-font-size);
> .headingOption {
> .styledButton {
font-size: var(--small-font-size);
line-height: calc(2 * var(--small-font-size));
height: calc(2 * var(--small-font-size));
> span {
font-size: var(--small-font-size);
line-height: calc(2 * var(--small-font-size));
height: calc(2 * var(--small-font-size));
}
}
}
}
> .headingList {
height: 32px;
min-width: 40px;
overflow-y: visible;
overflow-x: hidden;
}
}
> .group.font {
> span.headingList {
> .headingOption {
margin-right: var(--rte-indent);
}
}
}
}
}
> .editorWrapper {
display: block;
width: 100%;
border-radius: 3px;
border: 1px solid var(--borderLightest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
color: var(--textDarkest);
font-weight: normal;
font-size: 15px;
resize: vertical;
> .editor {
display: block;
margin: 8px 12px 9px;
min-height: 60px;
ul {
margin-left: 25px;
> li {
list-style: square;
}
}
ol {
margin-left: 25px;
> li {
list-style: decimal;
}
}
table {
table-layout: fixed;
border-spacing: 0;
text-align: center;
> tbody {
> tr {
> td {
min-width: 24px;
min-height: 24px;
text-align: center;
padding: var(--rte-indent);
border-bottom: 1px solid var(--borderLight);
border-right: 1px solid var(--borderLight);
border-left: 1px solid var(--borderLight);
border-top: 1px solid var(--borderLight);
}
}
}
}
* {
&:focus {
background: var(--secondary);
}
}
}
}
}
.tableTooltip {
min-width: 136px;
padding: 15px;
position: absolute;
> h2 {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
> .tablePreview {
display: flex;
align-items: flex-end;
justify-content: space-between;
> table {
table-layout: fixed;
border-spacing: 0;
text-align: center;
> tbody {
> tr {
> td {
width: 24px;
height: 24px;
text-align: center;
padding: var(--rte-indent);
border-bottom: 1px solid var(--borderLight);
border-right: 1px solid var(--borderLight);
border-left: 1px solid var(--borderLight);
border-top: 1px solid var(--borderLight);
}
}
}
}
> input {
height: 32px;
align-self: flex-end;
margin-left: 15px;
}
}
> .inputs {
display: flex;
justify-content: space-between;
}
}
.codeTooltip {
min-width: 336px;
padding: 15px;
position: absolute;
> h2 {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
> select {
width: 100%;
border: 1px solid var(--borderLightest);
margin: var(--rte-indent) 0;
min-height: 1rem;
background: var(--backgroundLightest);
}
> textarea {
border: 1px solid var(--borderLightest);
width: 100%;
min-height: 200px;
&:focus {
border: 1px solid var(--borderInputFocus);
}
}
}
@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);
}
}

View File

@ -1,199 +0,0 @@
.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 > .valueContainer {
display: flex;
align-items: center;
width: 100%;
}
.styledSelect > .valueContainer.normal {
min-height: 32px;
padding: 5px 5px 5px 10px;
}
.styledSelect > .valueContainer > .chevronIcon {
margin-left: auto;
font-size: 18px;
color: var(--textMedium);
}
.styledSelect > .valueContainer > .placeholder,
.styledSelect > .valueContainer > .valueMulti > .placeholder {
color: var(--textLight);
}
.styledSelectTip {
padding-top: 6px;
color: var(--textMedium);
font-size: 12.5px;
}
.styledSelect > .dropDown {
z-index: var(--dropdown);
position: absolute;
top: 100%;
left: 0;
border-radius: 0 0 4px 4px;
background: #fff;
box-shadow: rgba(9, 30, 66, 0.25) 0 4px 8px -2px, rgba(9, 30, 66, 0.31) 0 0 1px;
width: 100%;
}
.styledSelect > .dropDown > .dropDownInput {
padding: 10px 14px 8px;
width: 100%;
border: none;
color: var(--textDarkest);
background: none;
}
.styledSelect > .dropDown > .dropDownInput:focus {
outline: none;
}
.styledSelect > .options {
max-height: 200px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.styledSelect > .dropDown > .options::-webkit-scrollbar {
width: 8px;
}
.styledSelect > .dropDown > .options::-webkit-scrollbar-track {
background: none;
}
.styledSelect > .dropDown > .options::-webkit-scrollbar-thumb {
border-radius: 99px;
background: var(--backgroundMedium);
}
.styledSelect > .dropDown > .options > .option {
padding: 8px 14px;
word-break: break-word;
cursor: pointer;
}
.styledSelect > .dropDown > .options > .option:last-of-type {
margin-bottom: 8px;
}
.styledSelect > .dropDown > .options > .option.jira-select-option-is-active {
background: var(--backgroundLightPrimary);
}
.styledSelect > .dropDown > .options > .option:hover {
background: var(--backgroundLightPrimary);
}
.styledSelect > .dropDown > .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;
}
/* multi value */
.styledSelect > .valueContainer > .valueMulti {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.styledSelect > .valueContainer > .valueMulti.normal {
padding-top: 5px;
}
.styledSelect > .valueContainer > .valueMulti > .valueMultiItem {
margin-right: 15px;
display: inline-flex;
align-items: center;
border-radius: 4px;
cursor: pointer;
user-select: none;
color: var(--textDarkest);
height: 24px;
}
.styledSelect > .valueContainer > .valueMulti > .valueMultiItem > .selectItem {
margin-right: 5px;
}
.styledSelect > .valueContainer > .valueMulti > .valueMultiItem > .selectItem > .styledIcon {
margin-left: 4px;
color: var(--textDarkest);
}
.styledSelect > .valueContainer > .valueMulti > .addMore {
display: inline-block;
margin-bottom: 3px;
padding: 3px 0;
font-size: 12.5px;
cursor: pointer;
color: var(--textLink);
font-family: var(--font-medium);
font-weight: normal;
}
.styledSelect > .valueContainer > .valueMulti > .addMore:hover,
.styledSelect > .valueContainer > .valueMulti > .addMore:visited,
.styledSelect > .valueContainer > .valueMulti > .addMore:active {
color: var(--textLink);
}
.styledSelect > .valueContainer > .valueMulti > .addMore:hover {
text-decoration: underline;
}
.styledSelect > .valueContainer > .valueMulti > .addMore > .styledIcon {
margin-right: 3px;
vertical-align: middle;
font-size: 14px;
}

View File

@ -0,0 +1,176 @@
/* multi value */
.styledSelect {
position: relative;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
&:hover {
background: var(--backgroundLight);
}
&:focus {
outline: none;
}
>.valueContainer {
display: flex;
align-items: center;
width: 100%;
>.chevronIcon {
margin-left: auto;
font-size: 18px;
color: var(--textMedium);
}
>.placeholder {
color: var(--textLight);
}
>.valueMulti {
>.placeholder {
color: var(--textLight);
}
display: flex;
align-items: center;
flex-wrap: wrap;
>.valueMultiItem {
margin-right: 15px;
display: inline-flex;
align-items: center;
border-radius: 4px;
cursor: pointer;
user-select: none;
color: var(--textDarkest);
height: 24px;
>.selectItem {
margin-right: 5px;
>.styledIcon {
margin-left: 4px;
color: var(--textDarkest);
}
}
}
>.addMore {
display: inline-block;
margin-bottom: 3px;
padding: 3px 0;
font-size: 12.5px;
cursor: pointer;
color: var(--textLink);
font-family: var(--font-medium);
font-weight: normal;
&:hover {
color: var(--textLink);
text-decoration: underline;
}
&:visited {
color: var(--textLink);
}
&:active {
color: var(--textLink);
}
>.styledIcon {
margin-right: 3px;
vertical-align: middle;
font-size: 14px;
}
}
}
>.valueMulti.normal {
padding-top: 5px;
}
}
>.valueContainer.normal {
min-height: 32px;
padding: 5px 5px 5px 10px;
}
>.dropDown {
z-index: var(--dropdown);
position: absolute;
top: 100%;
left: 0;
border-radius: 0 0 4px 4px;
background: #fff;
box-shadow: rgba(9, 30, 66, 0.25) 0 4px 8px -2px, rgba(9, 30, 66, 0.31) 0 0 1px;
width: 100%;
>.dropDownInput {
padding: 10px 14px 8px;
width: 100%;
border: none;
color: var(--textDarkest);
background: none;
&:focus {
outline: none;
}
}
>.options {
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 99px;
background: var(--backgroundMedium);
}
>.option {
padding: 8px 14px;
word-break: break-word;
cursor: pointer;
&:last-of-type {
margin-bottom: 8px;
}
&:hover {
background: var(--backgroundLightPrimary);
}
}
>.option.jira-select-option-is-active {
background: var(--backgroundLightPrimary);
}
}
>.noOptions {
padding: 5px 15px 15px;
color: var(--textLight);
}
}
>.options {
max-height: 200px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
>.styledIcon {
position: absolute;
top: 4px;
right: 7px;
padding: 5px;
font-size: 16px;
color: var(--textMedium);
cursor: pointer;
user-select: none;
}
}
.styledSelect.normal {
width: 100%;
border: 1px solid var(--borderLightest);
background: var(--backgroundLightest);
transition: background 0.1s;
&:focus {
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
background: #fff;
}
}
.styledSelect.empty {
display: inline-block;
}
.styledSelect.invalid {
border: 1px solid var(--danger);
box-shadow: none;
&:focus {
border: 1px solid var(--danger);
box-shadow: none;
}
}
.styledSelectTip {
padding-top: 6px;
color: var(--textMedium);
font-size: 12.5px;
}

View File

@ -1,72 +0,0 @@
.selectItem {
padding: 4px 8px;
}
.selectItem.capitalize,
.optionItem.capitalize {
text-transform: capitalize;
}
.selectItem.priority.highest > .styledIcon,
.optionItem.priority.highest > .styledIcon {
color: var(--highest);
}
.selectItem.priority.high > .styledIcon,
.optionItem.priority.high > .styledIcon {
color: var(--high);
}
.selectItem.priority.medium > .styledIcon,
.optionItem.priority.medium > .styledIcon {
color: var(--medium);
}
.selectItem.priority.low > .styledIcon,
.optionItem.priority.low > .styledIcon {
color: var(--low);
}
.selectItem.priority.lowest > .styledIcon,
.optionItem.priority.lowest > .styledIcon {
color: var(--lowest);
}
.selectItem.priority > .selectItemLabel {
text-transform: capitalize;
}
.optionItem {
display: flex;
align-items: center;
}
.optionItem > .styledIcon {
font-size: 18px;
}
.selectItem > .selectItemLabel,
.optionItem > .optionLabel {
padding: 0 5px 0 7px;
font-size: 15px;
}
.optionItem.priority > .optionLabel {
padding: 0 5px 0 7px;
font-size: 15px;
text-transform: capitalize;
}
.optionItem.priority > .styledIcon {
font-size: 18px;
}
/* edit issue */
.topActions > .styledSelect > .valueContainer,
.topActions > .styledSelect > .valueContainer > .selectItem {
height: 100%;
}
.topActions .selectItem, .topActions .optionItem {
padding: 0 12px;
}

View File

@ -0,0 +1,106 @@
/* edit issue */
.selectItem {
padding: 4px 8px;
>.selectItemLabel {
padding: 0 5px 0 7px;
font-size: 15px;
}
}
.selectItem.capitalize {
text-transform: capitalize;
}
.optionItem.capitalize {
text-transform: capitalize;
}
.selectItem.priority.highest {
>.styledIcon {
color: var(--highest);
}
}
.optionItem.priority.highest {
>.styledIcon {
color: var(--highest);
}
}
.selectItem.priority.high {
>.styledIcon {
color: var(--high);
}
}
.optionItem.priority.high {
>.styledIcon {
color: var(--high);
}
}
.selectItem.priority.medium {
>.styledIcon {
color: var(--medium);
}
}
.optionItem.priority.medium {
>.styledIcon {
color: var(--medium);
}
}
.selectItem.priority.low {
>.styledIcon {
color: var(--low);
}
}
.optionItem.priority.low {
>.styledIcon {
color: var(--low);
}
}
.selectItem.priority.lowest {
>.styledIcon {
color: var(--lowest);
}
}
.optionItem.priority.lowest {
>.styledIcon {
color: var(--lowest);
}
}
.selectItem.priority {
>.selectItemLabel {
text-transform: capitalize;
}
}
.optionItem {
display: flex;
align-items: center;
>.styledIcon {
font-size: 18px;
}
>.optionLabel {
padding: 0 5px 0 7px;
font-size: 15px;
}
}
.optionItem.priority {
>.optionLabel {
padding: 0 5px 0 7px;
font-size: 15px;
text-transform: capitalize;
}
>.styledIcon {
font-size: 18px;
}
}
.topActions {
>.styledSelect {
>.valueContainer {
height: 100%;
>.selectItem {
height: 100%;
}
}
}
.selectItem {
padding: 0 12px;
}
.optionItem {
padding: 0 12px;
}
}

View File

@ -1,28 +0,0 @@
.styledTextArea {
display: inline-block;
width: 100%;
}
.styledTextArea > textarea {
overflow-y: hidden;
width: 100%;
padding: 8px 12px 9px;
border-radius: 3px;
border: 1px solid var(--borderLightest);
color: var(--textDarkest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
font-weight: normal;
font-size: 15px;
resize: vertical;
}
.styledTextArea > textarea:focus {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
.styledTextArea > textarea.invalid:focus {
border: 1px solid var(--danger);
}

View File

@ -0,0 +1,27 @@
.styledTextArea {
display: inline-block;
width: 100%;
>textarea {
overflow-y: hidden;
width: 100%;
padding: 8px 12px 9px;
border-radius: 3px;
border: 1px solid var(--borderLightest);
color: var(--textDarkest);
background: var(--backgroundLightest);
font-family: var(--font-regular);
font-weight: normal;
font-size: 15px;
resize: vertical;
&:focus {
background: #fff;
border: 1px solid var(--borderInputFocus);
box-shadow: 0 0 0 1px var(--borderInputFocus);
}
}
>textarea.invalid {
&:focus {
border: 1px solid var(--danger);
}
}
}

View File

@ -1,30 +0,0 @@
.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

@ -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;
.feedbackDropdown {
padding: 16px 24px 24px;
}
.feedbackImageCont {
padding: 24px 56px 20px;
}
.feedbackImage {
width: 100%;
}
.feedbackParagraph {
margin-bottom: 12px;
font-size: 15px;
&:last-of-type {
margin-bottom: 22px;
}
}
}

View File

@ -1,76 +0,0 @@
.trackingLink {
padding: 4px 4px 2px 0;
border-radius: 4px;
transition: background 0.1s;
cursor: pointer;
user-select: none;
}
.trackingLink:hover {
background: var(--backgroundLight);
}
.trackingWidget {
display: flex;
justify-content: space-between;
align-items: center;
}
.trackingWidget > .watchIcon {
color: var(--textMedium);
}
.trackingWidget > .right {
width: 90%;
}
.trackingWidget > .right > .barCounter {
height: 5px;
border-radius: 4px;
background: var(--backgroundMedium);
}
.trackingWidget > .right > .barCounter > .bar {
height: 5px;
border-radius: 4px;
background: var(--primary);
transition: all 0.1s;
}
.trackingWidget > .right > .values {
display: flex;
justify-content: space-between;
padding-top: 3px;
font-size: 14.5px;
}
/*MODAL*/
.timeTrackingModal {
padding: 20px 25px 25px;
}
.timeTrackingModal > .modalTitle {
padding-bottom: 14px;
font-family: var(--font-medium);
font-weight: normal;
font-size: 20px;
}
.timeTrackingModal > .trackingWidget {
width: 100%;
}
.timeTrackingModal > .inputs {
display: flex;
margin: 20px -5px 30px;
}
.timeTrackingModal > .inputs > .inputContainer {
margin: 0 5px;
width: 50%;
}
.timeTrackingModal > .actions {
display: flex;
justify-content: flex-end;
}

View File

@ -0,0 +1,75 @@
.trackingLink {
padding: 4px 4px 2px 0;
border-radius: 4px;
transition: background 0.1s;
cursor: pointer;
user-select: none;
&:hover {
background: var(--backgroundLight);
}
}
.trackingWidget {
display: flex;
justify-content: space-between;
align-items: center;
> .watchIcon {
color: var(--textMedium);
}
> .right {
width: 90%;
> .barCounter {
height: 5px;
border-radius: 4px;
background: var(--backgroundMedium);
> .bar {
height: 5px;
border-radius: 4px;
background: var(--primary);
transition: all 0.1s;
}
}
> .values {
display: flex;
justify-content: space-between;
padding-top: 3px;
font-size: 14.5px;
}
}
}
.timeTrackingModal {
padding: 20px 25px 25px;
> .modalTitle {
padding-bottom: 14px;
font-family: var(--font-medium);
font-weight: normal;
font-size: 20px;
}
> .trackingWidget {
width: 100%;
}
> .inputs {
display: flex;
margin: 20px -5px 30px;
> .inputContainer {
margin: 0 5px;
width: 50%;
}
}
> .actions {
display: flex;
justify-content: flex-end;
}
}

View File

@ -1,38 +0,0 @@
#users > .usersSection,
#users > .invitationsSection {
padding: 25px 40px 35px;
}
#users > .usersSection > .usersList,
#users > .invitationsSection > .invitationsList {
list-style: none;
}
#users > .usersSection > .usersList > .user,
#users > .invitationsSection > .invitationsList > .invitation {
list-style: none;
display: flex;
justify-content: space-between;
margin-top: 20px;
}
#users > .usersSection > .usersList > .user > span {
width: 25%;
}
#users > .invitationsSection > .invitationsList > .invitation.revoked {
color: var(--textLight);
}
#users > .invitationsSection > .invitationsList > .invitation > * {
width: 25%;
}
#users .invitationActions {
display: flex;
justify-content: space-between;
}
#users .invitationActions > .error {
color: var(--danger);
}

View File

@ -0,0 +1,52 @@
#users {
> .usersSection {
padding: 25px 40px 35px;
> .usersList {
list-style: none;
> .user {
list-style: none;
display: flex;
justify-content: space-between;
margin-top: 20px;
> span {
width: 25%;
}
}
}
}
> .invitationsSection {
padding: 25px 40px 35px;
> .invitationsList {
list-style: none;
> .invitation {
list-style: none;
display: flex;
justify-content: space-between;
margin-top: 20px;
> * {
width: 25%;
}
}
> .invitation.revoked {
color: var(--textLight);
}
}
}
.invitationActions {
display: flex;
justify-content: space-between;
> .error {
color: var(--danger);
}
}
}

View File

@ -1,36 +1,36 @@
@import "./css/normalize.css";
@import "./css/fonts.css";
@import "./css/iconfonts.css";
@import "./css/variables.css";
@import "./css/global.css";
@import "./css/sidebar.css";
@import "./css/aside.css";
@import "./css/styledIcon.css";
@import "./css/shared.css";
@import "./css/styledTooltip.css";
@import "./css/styledAvatar.css";
@import "./css/styledSelect.css";
@import "./css/styledSelectChild.css";
@import "./css/styledButton.css";
@import "./css/styledInput.css";
@import "./css/styledImageInput.css";
@import "./css/styledModal.css";
@import "./css/styledTextArea.css";
@import "./css/styledForm.css";
@import "./css/styledEditor.css";
@import "./css/styledComment.css";
@import "./css/styledPage.css";
@import "./css/styledLink.css";
@import "./css/styledRte.css";
@import "./css/styledDateTimeInput.css";
@import "./css/app.css";
@import "./css/issue.css";
@import "./css/project.css";
@import "./css/projectSettings.css";
@import "./css/timeTracking.css";
@import "./css/styledCheckbox.css";
@import "./css/login.css";
@import "./css/register.css";
@import "./css/users.css";
@import "./css/invite.css";
@import "./css/reports.css";
@import "css/normalize.scss";
@import "css/fonts.scss";
@import "css/iconfonts.scss";
@import "css/variables.scss";
@import "css/global.scss";
@import "css/sidebar.scss";
@import "css/aside.scss";
@import "css/styledIcon.scss";
@import "css/shared.scss";
@import "css/styledTooltip.scss";
@import "css/styledAvatar.scss";
@import "./css/styledSelect.scss";
@import "./css/styledSelectChild.scss";
@import "css/styledButton.scss";
@import "css/styledInput.scss";
@import "css/styledImageInput.scss";
@import "./css/styledModal.scss";
@import "css/styledTextArea.scss";
@import "css/styledForm.scss";
@import "css/styledEditor.scss";
@import "css/styledComment.scss";
@import "css/styledPage.scss";
@import "./css/styledLink.scss";
@import "css/styledRte.scss";
@import "css/styledDateTimeInput.scss";
@import "css/app.scss";
@import "css/issue.scss";
@import "css/project.scss";
@import "css/projectSettings.scss";
@import "css/timeTracking.scss";
@import "css/styledCheckbox.scss";
@import "css/login.scss";
@import "css/register.scss";
@import "css/users.scss";
@import "css/invite.scss";
@import "css/reports.scss";

View File

@ -1,5 +1,11 @@
#!/usr/bin/env bash
which rsass
if [[ "$status" != "0" ]];
then
cargo install rsass --features=commandline
fi
export PROJECT_ROOT=$(git rev-parse --show-toplevel)
export CLIENT_ROOT=${PROJECT_ROOT}/jirs-client
export HI_ROOT=${PROJECT_ROOT}/highlight/jirs-highlight

View File

@ -1,14 +1,16 @@
#!/usr/bin/env bash
which rsass
if [[ "$status" != "0" ]]; then
cargo install rsass --features=commandline
fi
export PROJECT_ROOT=$(git rev-parse --show-toplevel)
export CLIENT_ROOT=${PROJECT_ROOT}/jirs-client
export HI_ROOT=${PROJECT_ROOT}/highlight/jirs-highlight
export MODE=force
export BUILD_TYPE=--release
cd ${PROJECT_ROOT}
cargo build --bin jirs-css
. .env
rm -Rf build
@ -20,11 +22,13 @@ wasm-pack build --mode normal --release --out-name jirs --out-dir $CLIENT_ROOT/b
cd $HI_ROOT
wasm-pack build --mode normal --release --out-name hi --out-dir $CLIENT_ROOT/build --target web
${PROJECT_ROOT}/target/debug/jirs-css -i ./js/styles.css -o ./build/styles.css
cd $CLIENT_ROOT
rm -Rf ${CLIENT_ROOT}/build/styles.css
rsass -t Compressed ${PROJECT_ROOT}/jirs-client/js/styles.css > ${CLIENT_ROOT}/build/styles.css
cp -r ./static/* ./build
cat ./static/index.js \
| sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" \
| sed -e "s/process.env.JIRS_SERVER_PORT/'$JIRS_SERVER_PORT'/g" &> ./build/index.js
cat ./static/index.js |
sed -e "s/process.env.JIRS_SERVER_BIND/'$JIRS_SERVER_BIND'/g" |
sed -e "s/process.env.JIRS_SERVER_PORT/'$JIRS_SERVER_PORT'/g" &>./build/index.js
cp ./js/template.html ./build/index.html

View File

@ -12,7 +12,8 @@ cd ${CLIENT_ROOT}
wasm-pack --verbose build --mode ${MODE} ${BUILD_TYPE} --out-name jirs --out-dir ${CLIENT_ROOT}/build --target web
cd ${CLIENT_ROOT}
${PROJECT_ROOT}/target/debug/jirs-css -i ${CLIENT_ROOT}/js/styles.css -o ${CLIENT_ROOT}/tmp/styles.css
rm -Rf ${CLIENT_ROOT}/build/styles.css
rsass -t Expanded ${PROJECT_ROOT}/jirs-client/js/styles.css > ${CLIENT_ROOT}/tmp/styles.css
cp -r ${CLIENT_ROOT}/static/* ${CLIENT_ROOT}/tmp

View File

@ -1,4 +1,4 @@
#![feature(or_patterns, type_ascription, trait_alias)]
#![feature(or_patterns, type_ascription, trait_alias, drain_filter)]
use {
crate::{

View File

@ -9,7 +9,7 @@ pub struct StatusIssueIds {
#[derive(Default, Debug)]
pub struct EpicIssuePerStatus {
pub epic_name: EpicName,
pub epic_name: Option<EpicName>,
pub per_status_issues: Vec<StatusIssueIds>,
}
@ -57,7 +57,7 @@ impl ProjectPage {
for epic in epics {
let mut per_epic_map = EpicIssuePerStatus {
epic_name: epic.map(|(_, name)| name).unwrap_or_default().to_string(),
epic_name: epic.map(|(_, name)| name.to_string()),
..Default::default()
};

View File

@ -1,3 +1,4 @@
use crate::shared::styled_button::StyledButton;
use {
crate::{
model::PageContent,
@ -31,11 +32,28 @@ pub fn project_board_lists(model: &Model) -> Node<Msg> {
)
})
.collect();
div![
C!["row"],
div![C!["epicName"], per_epic.epic_name.as_str()],
div![C!["projectBoardLists"], columns]
]
let epic_name = match per_epic.epic_name.as_deref() {
Some(name) => {
let edit_button = StyledButton::build()
.empty()
.icon(Icon::EditAlt)
.build()
.into_node();
let delete_button = StyledButton::build()
.empty()
.icon(Icon::DeleteAlt)
.build()
.into_node();
div![
C!["epicHeader"],
div![C!["epicName"], name],
div![C!["epicActions"], edit_button, delete_button],
]
}
_ => Node::Empty,
};
div![C!["row"], epic_name, div![C!["projectBoardLists"], columns]]
});
div![C!["rows"], rows]
}
@ -108,25 +126,21 @@ fn project_issue(model: &Model, issue: &Issue) -> Node<Msg> {
.build()
.into_node();
let priority_icon = {
let icon = match issue.priority {
IssuePriority::Low | IssuePriority::Lowest => Icon::ArrowDown,
_ => Icon::ArrowUp,
};
StyledIcon::build(icon)
.add_class(issue.priority.to_str())
.with_color(issue.priority.to_str())
.build()
.into_node()
};
let priority_icon = StyledIcon::build(issue.priority.into())
.add_class(issue.priority.to_str())
.with_color(issue.priority.to_str())
.build()
.into_node();
let issue_id = issue.id;
let drag_started = drag_ev(Ev::DragStart, move |_| {
let drag_started = drag_ev(Ev::DragStart, move |ev| {
ev.stop_propagation();
Some(Msg::PageChanged(PageChanged::Board(
BoardPageChange::IssueDragStarted(issue_id),
)))
});
let drag_stopped = drag_ev(Ev::DragEnd, move |_| {
let drag_stopped = drag_ev(Ev::DragEnd, move |ev| {
ev.stop_propagation();
Some(Msg::PageChanged(PageChanged::Board(
BoardPageChange::IssueDragStopped(issue_id),
)))

View File

@ -1,11 +1,12 @@
use seed::prelude::Orders;
use seed::*;
use jirs_data::*;
use crate::model::{Model, PageContent};
use crate::ws::send_ws_msg;
use crate::Msg;
use {
crate::{
model::{Model, PageContent},
ws::send_ws_msg,
Msg,
},
jirs_data::*,
seed::{prelude::Orders, *},
};
pub fn drag_started(issue_id: IssueId, model: &mut Model) {
let project_page = match &mut model.page_content {
@ -27,66 +28,47 @@ pub fn exchange_position(below_id: IssueId, model: &mut Model) {
if below_id == dragged_id {
return;
}
let below_idx = model
let (issue_status_id, epic_id) = model
.issues
.iter()
.find_map(|issue| {
if issue.id == dragged_id {
Some((issue.issue_status_id, issue.epic_id))
} else {
None
}
})
.unwrap_or_default();
let mut issues: Vec<Issue> = model
.issues
.drain_filter(|issue| issue.issue_status_id == issue_status_id && issue.epic_id == epic_id)
.collect();
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
let below_idx = issues
.iter()
.position(|issue| issue.id == below_id)
.unwrap_or(model.issues.len());
let dragged = model
.issues
.unwrap_or_default();
let dragged_idx = issues
.iter()
.position(|issue| issue.id == dragged_id)
.map(|idx| model.issues.remove(idx))
.unwrap();
let epic_id = dragged.epic_id;
model.issues.insert(below_idx, dragged);
let changed: Vec<(IssueId, i32)> = model
.issues
.iter_mut()
.filter(|issue| issue.epic_id == epic_id)
.enumerate()
.map(|(idx, issue)| {
issue.list_position = idx as i32;
(issue.id, issue.list_position)
})
.collect();
for (id, pos) in changed {
if let Some(iss) = model.issues_by_id.get_mut(&id) {
iss.list_position = pos;
.unwrap_or_default();
let dragged = issues.remove(dragged_idx);
issues.insert(below_idx, dragged);
let mut changed = Vec::with_capacity(issues.len());
for (idx, mut issue) in issues.into_iter().enumerate() {
issue.list_position = idx as i32;
if let Some(iss) = model.issues_by_id.get_mut(&issue.id) {
iss.list_position = issue.list_position;
}
changed.push((issue.id, issue.list_position));
model.issues.push(issue);
}
// let dragged_pos = match model.issues_by_id.get(&dragged_id) {
// Some(i) => i.list_position,
// _ => return,
// };
// let below_pos = match model.issues_by_id.get(&below_id) {
// Some(i) => i.list_position,
// _ => return,
// };
// use seed::*;
// log!(format!(
// "exchange dragged {} {} below {} {}",
// dragged_id, dragged_pos, below_id, below_pos
// ));
// for issue in model.issues_by_id.values_mut() {
// if issue.id == below_id {
// issue.list_position = dragged_pos;
// } else if issue.id == dragged_id {
// issue.list_position = below_pos;
// }
// }
//
// for issue in model.issues.iter_mut() {
// if issue.id == below_id {
// issue.list_position = dragged_pos;
// } else if issue.id == dragged_id {
// issue.list_position = below_pos;
// }
// }
// model
// .issues
// .sort_by(|a, b| a.list_position.cmp(&b.list_position));
if let PageContent::Project(project_page) = &mut model.page_content {
project_page.rebuild_visible(
&model.epics,
@ -94,45 +76,40 @@ pub fn exchange_position(below_id: IssueId, model: &mut Model) {
&model.issues,
&model.user,
);
project_page.issue_drag.mark_dirty(dragged_id);
project_page.issue_drag.mark_dirty(below_id);
for (id, _) in changed.iter() {
project_page.issue_drag.mark_dirty(*id);
}
}
}
pub fn sync(model: &mut Model, orders: &mut impl Orders<Msg>) {
// log!("------------------------------------------------------------------");
// log!("| SYNC |");
// log!("------------------------------------------------------------------");
let project_page = match &mut model.page_content {
PageContent::Project(project_page) => project_page,
let dirty = match &mut model.page_content {
PageContent::Project(project_page) => std::mem::take(&mut project_page.issue_drag.dirty),
_ => return,
};
for issue in model.issues.iter() {
if !project_page.issue_drag.dirty.contains(&issue.id) {
continue;
}
let changes: Vec<(IssueId, ListPosition, IssueStatusId, Option<EpicId>)> = dirty
.into_iter()
.filter_map(|id| {
model.issues_by_id.get(&id).map(|issue| {
(
issue.id,
issue.list_position,
issue.issue_status_id,
issue.epic_id,
)
})
})
.collect();
send_ws_msg(
WsMsg::IssueUpdate(
issue.id,
IssueFieldId::IssueStatusId,
PayloadVariant::I32(issue.issue_status_id),
),
model.ws.as_ref(),
orders,
);
send_ws_msg(
WsMsg::IssueUpdate(
issue.id,
IssueFieldId::ListPosition,
PayloadVariant::I32(issue.list_position),
),
model.ws.as_ref(),
orders,
);
}
project_page.issue_drag.clear();
send_ws_msg(
WsMsg::IssueSyncListPosition(changes),
model.ws.as_ref(),
orders,
);
if let PageContent::Project(project_page) = &mut model.page_content {
project_page.issue_drag.clear()
};
}
pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
@ -141,45 +118,46 @@ pub fn change_status(status_id: IssueStatusId, model: &mut Model) {
_ => return,
};
let issue_id = match project_page.issue_drag.dragged_id.as_ref().cloned() {
let dragged_id = match project_page.issue_drag.dragged_id.as_ref().cloned() {
Some(issue_id) => issue_id,
_ => return error!("Nothing is dragged"),
};
let (issue_status_id, epic_id) = model
.issues_by_id
.get(&dragged_id)
.map(|issue| (issue.issue_status_id, issue.epic_id))
.unwrap_or_default();
if status_id == issue_status_id {
return;
}
let mut old: Vec<Issue> = vec![];
let mut pos = 0;
let mut found: Option<Issue> = None;
std::mem::swap(&mut old, &mut model.issues);
old.sort_by(|a, b| a.list_position.cmp(&b.list_position));
for mut issue in old.into_iter() {
if issue.issue_status_id == status_id {
if issue.list_position != pos {
issue.list_position = pos;
project_page.issue_drag.mark_dirty(issue.id);
let mut issues: Vec<Issue> = model
.issues
.drain_filter(|issue| {
if issue.id == dragged_id {
issue.issue_status_id = status_id;
}
issue.issue_status_id == status_id && issue.epic_id == epic_id
})
.collect();
issues.sort_by(|a, b| a.list_position.cmp(&b.list_position));
for mut issue in issues {
if issue.id == dragged_id {
issue.issue_status_id = status_id;
if let Some(iss) = model.issues_by_id.get_mut(&issue.id) {
iss.issue_status_id = status_id;
}
pos += 1;
}
if issue.id != issue_id {
model.issues.push(issue);
} else {
found = Some(issue);
}
project_page.issue_drag.mark_dirty(issue.id);
model.issues.push(issue);
}
let mut issue = match found {
Some(i) => i,
_ => {
return;
}
};
if issue.issue_status_id == status_id {
model.issues.push(issue);
} else {
issue.issue_status_id = status_id;
issue.list_position = pos + 1;
model.issues.push(issue);
project_page.issue_drag.mark_dirty(issue_id);
}
project_page.rebuild_visible(
&model.epics,
&model.issue_statuses,
&model.issues,
&model.user,
);
}

View File

@ -1,12 +0,0 @@
[package]
name = "jirs-css"
version = "0.1.0"
authors = ["Adrian Woźniak <adrian.wozniak.1986@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glob = { version = "*" }
notify = { version = "*" }
gumdrop = { version = "*", default-features = false }

View File

@ -1,342 +0,0 @@
use std::collections::{HashMap, HashSet};
use std::fs::*;
use std::path::Path;
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, RwLock, RwLockWriteGuard};
use std::time::Duration;
use std::time::SystemTime;
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
const INPUT: &str = "./jirs-client/js/styles.css";
type Css = Arc<RwLock<CssFile>>;
// mod prop;
#[derive(Debug)]
enum Partial {
String(String),
File(Css),
}
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
enum FileState {
Clean,
Dirty,
Dead,
}
#[derive(Debug)]
struct CssFile {
pub path: String,
pub lines: Vec<Partial>,
pub last_changed: SystemTime,
pub state: FileState,
}
impl CssFile {
pub fn new(path: String) -> Self {
Self {
path,
lines: vec![],
last_changed: SystemTime::UNIX_EPOCH,
state: FileState::Clean,
}
}
pub fn drop_dead(&mut self) {
let mut old = vec![];
std::mem::swap(&mut self.lines, &mut old);
for child in old {
match child {
Partial::String(_) => {
self.lines.push(child);
}
Partial::File(file) => {
let state = file.read().map(|f| f.state).unwrap();
if state != FileState::Dead {
if let Ok(mut css) = file.write() {
css.drop_dead();
}
self.lines.push(Partial::File(file));
}
}
}
}
}
}
impl std::fmt::Display for CssFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.state == FileState::Dead {
return Ok(());
}
f.write_str(format!("\n/* -- {} --- */\n\n", self.path).as_str())?;
for line in self.lines.iter() {
match line {
Partial::String(line) => {
f.write_str(line.as_str())?;
f.write_str("\n")?;
}
Partial::File(file) => {
if let Ok(css) = file.read() {
f.write_str(format!("{}", css).as_str())?;
f.write_str("\n")?;
}
}
}
}
Ok(())
}
}
#[derive(Debug, Default)]
struct Application<'l> {
input: &'l str,
output: Option<&'l str>,
watch: bool,
prelude_selector: bool,
files_map: HashMap<String, HashSet<String>>,
fm: HashMap<String, Css>,
root_file: Option<Css>,
sender: Option<Sender<DebouncedEvent>>,
}
impl<'l> Application<'l> {
fn read_timestamp(input: &Path) -> Result<SystemTime, String> {
std::fs::File::open(input)
.and_then(|file| file.metadata())
.and_then(|meta| meta.modified())
.map_err(|e| format!("{}", e))
}
fn check_timestamps(
&mut self,
input: &Path,
output_timestamp: SystemTime,
) -> Result<bool, String> {
let input_dir = input
.parent()
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
let path = input_dir.to_str().unwrap();
let paths =
glob::glob(format!("{}/**/*.css", path).as_str()).map_err(|e| format!("{}", e))?;
for path in paths.filter_map(Result::ok) {
if Self::read_timestamp(path.as_path())? > output_timestamp {
return Ok(false);
}
}
Ok(true)
}
fn parse(&mut self) -> Result<(), String> {
let root_path = self.input.to_string();
let root = std::path::Path::new(&root_path);
let root_file = self.parse_file(root)?;
self.root_file = Some(root_file);
Ok(())
}
fn parse_file(&mut self, input: &Path) -> Result<Css, String> {
let file_path = input.display().to_string();
let input_dir = input
.parent()
.ok_or_else(|| format!("Not a valid path {:?}", input))?;
let file = if self.fm.contains_key(&file_path) {
self.fm.get(&file_path).unwrap().clone()
} else {
let css = Arc::new(RwLock::new(CssFile::new(file_path.clone())));
self.fm.insert(file_path.clone(), css.clone());
if let Some(ref tx) = self.sender {
let path = Path::new(&file_path);
tx.send(DebouncedEvent::Create(path.to_path_buf()))
.map_err(|e| format!("{}", e))?;
}
css
};
if let Ok(mut css) = file.write() {
css.last_changed = Self::read_timestamp(input)?;
}
for line in read_to_string(file_path.as_str())
.map_err(|e| format!("{}", e))?
.lines()
{
let l = line.trim();
match l {
"" => continue,
_ if l.starts_with("@import ") => {
let imported = line
.replace("@import ", "")
.trim()
.replace("\"", "")
.replace(";", "")
.to_string();
let child = input_dir
.join(imported.as_str())
.canonicalize()
.map_err(|e| format!("{}", e))?;
let child_file = self.parse_file(&child)?;
if let Ok(mut css) = file.write() {
css.lines.push(Partial::File(child_file));
}
}
_ => {
if let Ok(mut css) = file.write() {
css.lines.push(Partial::String(l.to_string()));
}
}
}
}
Ok(file)
}
pub fn mark_dirty(&mut self, path: &Path) {
if let Ok(mut css) = self.css_at_path(path) {
css.state = FileState::Dirty;
}
}
pub fn mark_dead(&mut self, path: &Path) {
if let Ok(mut css) = self.css_at_path(path) {
css.state = FileState::Dead;
}
}
fn css_at_path(&mut self, path: &Path) -> Result<RwLockWriteGuard<CssFile>, bool> {
self.fm
.get(path.display().to_string().as_str())
.ok_or(false)
.and_then(|css| css.write().or(Err(false)))
}
fn refresh(&mut self) {
if let Ok(mut root) = self
.root_file
.as_mut()
.ok_or_else(|| false)
.and_then(|f| f.write().map_err(|_| false))
{
root.drop_dead();
}
let mut old = HashMap::new();
std::mem::swap(&mut old, &mut self.fm);
for (key, file) in old.into_iter() {
if file
.read()
.map(|f| f.state != FileState::Dead)
.unwrap_or_default()
{
self.fm.insert(key, file);
}
}
}
fn print(&self) {
let css = match self.root_file.as_ref().unwrap().read() {
Ok(css) => css,
_ => return,
};
match self.output.as_ref() {
Some(f) => {
std::fs::create_dir_all(Path::new(f).parent().unwrap()).unwrap();
std::fs::write(f, format!("{}", css)).unwrap();
println!("CSS merge done");
}
_ => println!("{}", css),
}
}
pub fn pipe(&mut self, tx: Sender<DebouncedEvent>) {
self.sender = Some(tx);
}
}
#[derive(gumdrop::Options, Debug)]
struct Opts {
#[options(help = "Root input file")]
input: Option<String>,
#[options(help = "Optional output file. If not given result will be printed to stdout")]
output: Option<String>,
#[options(help = "Watch file changes")]
watch: bool,
#[options(help = "Add reset css prelude")]
prelude: bool,
#[options(help = "Print help message")]
help: bool,
}
fn main() -> Result<(), String> {
use gumdrop::Options;
let opts: Opts = Opts::parse_args_default_or_exit();
let mut app = Application {
input: opts.input.as_deref().unwrap_or_else(|| INPUT),
output: opts.output.as_deref(),
watch: opts.watch,
prelude_selector: opts.prelude,
files_map: Default::default(),
fm: Default::default(),
root_file: None,
sender: None,
};
let root_path = app.input.to_string();
let root = std::path::Path::new(&root_path);
let output_timestamp = opts
.output
.as_deref()
.ok_or_else(|| std::io::Error::from_raw_os_error(0))
.and_then(File::open)
.and_then(|file| file.metadata())
.and_then(|meta| meta.modified())
.unwrap_or_else(|_| SystemTime::UNIX_EPOCH);
if app.check_timestamps(root, output_timestamp)? && !opts.watch {
return Ok(());
}
let (tx, rx) = channel();
app.pipe(tx.clone());
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
app.parse()?;
app.print();
if !opts.watch {
return Ok(());
}
loop {
match rx.recv() {
Ok(DebouncedEvent::NoticeWrite(path)) => {
app.mark_dirty(path.as_path());
if let Err(s) = app.parse_file(&path) {
eprintln!("{}", s);
}
app.print();
}
Ok(DebouncedEvent::NoticeRemove(path)) => {
app.mark_dead(path.as_path());
watcher.unwatch(path).unwrap();
app.refresh();
app.print();
}
Ok(DebouncedEvent::Create(path)) => {
if let Err(e) = watcher.watch(path, RecursiveMode::NonRecursive) {
eprintln!("{}", e);
}
}
Ok(_event) => (),
Err(e) => eprintln!("watch error: {:?}", e),
}
}
}

View File

@ -1,496 +0,0 @@
use crate::prop::{CssParser, ParseToken, Parser, PropertyValue, TimeProperty, Token, ValueResult};
#[derive(Debug, PartialEq)]
pub enum AnimationDirectionProperty {
Normal,
Reverse,
Alternate,
AlternateReverse,
Initial,
Inherit,
}
impl Token for AnimationDirectionProperty {}
impl ParseToken<AnimationDirectionProperty> for CssParser {
fn parse_token(&mut self) -> Result<PropertyValue<AnimationDirectionProperty>, String> {
let p = match self.expect_consume()?.as_str() {
"normal" => AnimationDirectionProperty::Normal,
"reverse" => AnimationDirectionProperty::Reverse,
"alternate" => AnimationDirectionProperty::Alternate,
"alternate-reverse" => AnimationDirectionProperty::AlternateReverse,
"initial" => AnimationDirectionProperty::Initial,
"inherit" => AnimationDirectionProperty::Inherit,
_ => return Err(format!("invalid animation direction {:?}", self.current)),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationFillModeProperty {
None,
Forwards,
Backwards,
Both,
Initial,
Inherit,
}
impl Token for AnimationFillModeProperty {}
impl ParseToken<AnimationFillModeProperty> for CssParser {
fn parse_token(&mut self) -> ValueResult<AnimationFillModeProperty> {
let p = match self.expect_consume()?.as_str() {
"none" => AnimationFillModeProperty::None,
"forwards" => AnimationFillModeProperty::Forwards,
"backwards" => AnimationFillModeProperty::Backwards,
"both" => AnimationFillModeProperty::Both,
"initial" => AnimationFillModeProperty::Initial,
"inherit" => AnimationFillModeProperty::Inherit,
_ => return Err(format!("invalid animation fill mode {:?}", self.current)),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationPlayStateProperty {
Paused,
Running,
Initial,
Inherit,
}
impl Token for AnimationPlayStateProperty {}
impl ParseToken<AnimationPlayStateProperty> for CssParser {
fn parse_token(&mut self) -> ValueResult<AnimationPlayStateProperty> {
self.skip_white();
let name = self.expect_consume()?;
let p = match name.as_str() {
"paused" => AnimationPlayStateProperty::Paused,
"running" => AnimationPlayStateProperty::Running,
"initial" => AnimationPlayStateProperty::Initial,
"inherit" => AnimationPlayStateProperty::Inherit,
_ => return Err(format!("invalid animation play state {:?}", name)),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationTimingFunctionStepsProperty {
Start,
End,
}
impl Token for AnimationTimingFunctionStepsProperty {}
impl ParseToken<AnimationTimingFunctionStepsProperty> for CssParser {
fn parse_token(&mut self) -> ValueResult<AnimationTimingFunctionStepsProperty> {
let s = self.expect_consume()?;
let p = match s.to_lowercase().as_str() {
"start" => AnimationTimingFunctionStepsProperty::Start,
"end" => AnimationTimingFunctionStepsProperty::End,
_ => return Err(format!("invalid animation timing function step {:?}", s)),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationTimingFunctionProperty {
Linear,
Ease,
EaseIn,
EaseOut,
EaseInOut,
StepStart,
StepEnd,
Steps(
PropertyValue<u32>,
PropertyValue<AnimationTimingFunctionStepsProperty>,
),
CubicBezier(
PropertyValue<f64>,
PropertyValue<f64>,
PropertyValue<f64>,
PropertyValue<f64>,
),
Initial,
Inherit,
}
impl Token for AnimationTimingFunctionProperty {}
impl ParseToken<AnimationTimingFunctionProperty> for CssParser {
fn parse_token(&mut self) -> ValueResult<AnimationTimingFunctionProperty> {
let current = self.expect_consume()?;
let p = match current.as_str() {
"linear" => AnimationTimingFunctionProperty::Linear,
"ease" => AnimationTimingFunctionProperty::Ease,
"ease-in" => AnimationTimingFunctionProperty::EaseIn,
"ease-out" => AnimationTimingFunctionProperty::EaseOut,
"ease-in-out" => AnimationTimingFunctionProperty::EaseInOut,
"step-start" => AnimationTimingFunctionProperty::StepStart,
"step-end" => AnimationTimingFunctionProperty::StepEnd,
"initial" => AnimationTimingFunctionProperty::Initial,
"inherit" => AnimationTimingFunctionProperty::Inherit,
"steps" => {
self.consume_expected("(")?;
self.skip_white();
let b = self.parse_token()?;
match b {
PropertyValue::Other(n) if n <= 0 => {
return Err(format!("invalid animation timing function, number of iterations must be greater than 0"));
}
_ => (),
}
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let c = self.parse_token()?;
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
self.consume_semicolon()?;
AnimationTimingFunctionProperty::Steps(b, c)
}
"cubic-bezier" => {
self.consume_expected("(")?;
self.skip_white();
let a = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let b = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let c = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let d = self.parse_token()?;
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
self.consume_semicolon()?;
AnimationTimingFunctionProperty::CubicBezier(a, b, c, d)
}
_ => return Err(format!("invalid animation timing function {:?}", current)),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationDelayProperty {
Time(PropertyValue<TimeProperty>),
Initial,
Inherit,
}
impl Token for AnimationDelayProperty {}
impl ParseToken<AnimationDelayProperty> for CssParser {
fn parse_token(&mut self) -> Result<PropertyValue<AnimationDelayProperty>, String> {
let p = match self.peek().cloned().unwrap_or_default().as_str() {
"initial" => {
self.expect_consume()?;
AnimationDelayProperty::Initial
}
"inherit" => {
self.expect_consume()?;
AnimationDelayProperty::Inherit
}
_ => AnimationDelayProperty::Time(self.parse_token()?),
};
Ok(PropertyValue::Other(p))
}
}
#[derive(Debug, PartialEq)]
pub enum AnimationProperty {
Initial,
Inherit,
Custom(
String,
PropertyValue<TimeProperty>,
PropertyValue<AnimationTimingFunctionProperty>,
PropertyValue<AnimationDelayProperty>,
PropertyValue<usize>,
PropertyValue<AnimationDirectionProperty>,
PropertyValue<AnimationFillModeProperty>,
PropertyValue<AnimationPlayStateProperty>,
),
}
impl Token for AnimationProperty {}
impl ParseToken<AnimationProperty> for CssParser {
fn parse_token(&mut self) -> Result<PropertyValue<AnimationProperty>, String> {
eprintln!("only full animation is supported!");
if let Some(v) = self.try_parse_variable() {
return Ok(PropertyValue::Variable(v));
}
let def = self
.peek()
.cloned()
.ok_or_else(|| "expect to find token but EOF".to_string())?;
let p = match def.as_str() {
"initial" => {
self.expect_consume()?;
PropertyValue::Other(AnimationProperty::Initial)
}
"inherit" => {
self.expect_consume()?;
PropertyValue::Other(AnimationProperty::Inherit)
}
_ => {
let duration = if self.next_is_semicolon() {
PropertyValue::Other(TimeProperty::Seconds(0))
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let timing = if self.next_is_semicolon() {
PropertyValue::Other(AnimationTimingFunctionProperty::Ease)
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let delay = if self.next_is_semicolon() {
PropertyValue::Other(AnimationDelayProperty::Time(PropertyValue::Other(
TimeProperty::Seconds(0),
)))
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let iteration_count = if self.next_is_semicolon() {
PropertyValue::Other(1)
} else {
let v = self.expect_consume()?.parse::<usize>().map_err(|_| {
format!(
"invalid animation iteration count, expect number got {:?}",
self.current
)
})?;
self.skip_white();
PropertyValue::Other(v)
};
let direction = if self.next_is_semicolon() {
PropertyValue::Other(AnimationDirectionProperty::Normal)
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let fill_mode = if self.next_is_semicolon() {
PropertyValue::Other(AnimationFillModeProperty::None)
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let play_state = if self.next_is_semicolon() {
PropertyValue::Other(AnimationPlayStateProperty::Running)
} else {
let v = self.parse_token()?;
self.skip_white();
v
};
let name = self.expect_consume()?;
PropertyValue::Other(AnimationProperty::Custom(
name,
duration,
timing,
delay,
iteration_count,
direction,
fill_mode,
play_state,
))
}
};
Ok(p)
}
}
#[cfg(test)]
mod tests {
use crate::prop::CssTokenizer;
use super::*;
/// we assume currently we hit property name
/// display : block;
/// ^
/// so we need to add `:`
///
/// But we also we adding spaces around because they are allowed in css and needs to be skipped
fn parse_prop_value(s: &str) -> CssParser {
let full = format!(" : {} {}", s, if s.contains(";") { "" } else { ";" });
let tokens = CssTokenizer::new(full.as_str()).tokenize();
CssParser::new("", tokens)
}
#[test]
fn parse_animation_timing_function() {
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("linear").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::Linear,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("ease").parse_token();
let expected = Ok(PropertyValue::Other(AnimationTimingFunctionProperty::Ease));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("ease-in").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::EaseIn,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("ease-out").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::EaseOut,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("ease-in-out").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::EaseInOut,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("step-start").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::StepStart,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("step-end").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::StepEnd,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(1,start)").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::Steps(
PropertyValue::Other(1),
PropertyValue::Other(AnimationTimingFunctionStepsProperty::Start),
),
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(3,end)").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::Steps(
PropertyValue::Other(3),
PropertyValue::Other(AnimationTimingFunctionStepsProperty::End),
),
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(0,start)").parse_token();
let expected = Err(
"invalid animation timing function, number of iterations must be greater than 0"
.to_string(),
);
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(-2,start)").parse_token();
let expected =
Err("invalid token, expect number greater or equal 0 got \"-2\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(0,end)").parse_token();
let expected = Err(
"invalid animation timing function, number of iterations must be greater than 0"
.to_string(),
);
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(-1,end)").parse_token();
let expected =
Err("invalid token, expect number greater or equal 0 got \"-1\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(end)").parse_token();
let expected =
Err("invalid token, expect number greater or equal 0 got \"end\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(start)").parse_token();
let expected =
Err("invalid token, expect number greater or equal 0 got \"start\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(0)").parse_token();
let expected = Err(
"invalid animation timing function, number of iterations must be greater than 0"
.to_string(),
);
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("steps(1)").parse_token();
let expected = Err("expect to find token \",\" but found \")\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("cubic-bezier(0.1,0.2,0.3,0.4)").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::CubicBezier(
PropertyValue::Other(0.1),
PropertyValue::Other(0.2),
PropertyValue::Other(0.3),
PropertyValue::Other(0.4),
),
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("cubic-bezier(0.1, 0.2, 0.3, 0.4)").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::CubicBezier(
PropertyValue::Other(0.1),
PropertyValue::Other(0.2),
PropertyValue::Other(0.3),
PropertyValue::Other(0.4),
),
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("cubic-bezier(0.1,0.2,0.3)").parse_token();
let expected = Err("expect to find token \",\" but found \")\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("cubic-bezier(0.1,0.2)").parse_token();
let expected = Err("expect to find token \",\" but found \")\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("cubic-bezier(0.1)").parse_token();
let expected = Err("expect to find token \",\" but found \")\"".to_string());
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("initial").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::Initial,
));
assert_eq!(res, expected);
let res: ValueResult<AnimationTimingFunctionProperty> =
parse_prop_value("inherit").parse_token();
let expected = Ok(PropertyValue::Other(
AnimationTimingFunctionProperty::Inherit,
));
assert_eq!(res, expected);
}
}

View File

@ -1,776 +0,0 @@
use std::str::FromStr;
use crate::prop::{CssParser, ParseToken, Parser, PropertyValue, Token, ValueResult};
#[derive(Debug, PartialEq)]
pub enum ColorProperty {
Rgba(
PropertyValue<u8>,
PropertyValue<u8>,
PropertyValue<u8>,
PropertyValue<u8>,
),
Hsla(
PropertyValue<u16>,
PropertyValue<u8>,
PropertyValue<u8>,
PropertyValue<f64>,
),
Current,
}
impl Token for ColorProperty {}
impl ParseToken<ColorProperty> for CssParser {
fn parse_token(&mut self) -> ValueResult<ColorProperty> {
self.skip_white();
let current = self.expect_consume()?;
let s = current.trim();
let p = match s {
"currentColor" => ColorProperty::Current,
_ if s.len() == 7 && s.starts_with('#') => {
let r = u8::from_str_radix(&s[1..=2], 16)
.map_err(|_| format!("invalid color {:?}", s))?;
let g = u8::from_str_radix(&s[3..=4], 16)
.map_err(|_| format!("invalid color {:?}", s))?;
let b = u8::from_str_radix(&s[5..=6], 16)
.map_err(|_| format!("invalid color {:?}", s))?;
ColorProperty::Rgba(
PropertyValue::Other(r),
PropertyValue::Other(g),
PropertyValue::Other(b),
PropertyValue::Other(255),
)
}
_ if s.len() == 4 && s.starts_with('#') => {
let _x = &s[1..=1];
let r = u8::from_str_radix(&s[1..=1].repeat(2), 16)
.map_err(|_| format!("invalid color {:?}", s))?;
let g = u8::from_str_radix(&s[2..=2].repeat(2), 16)
.map_err(|_| format!("invalid color {:?}", s))?;
let b = u8::from_str_radix(&s[3..=3].repeat(2), 16)
.map_err(|_| format!("invalid color {:?}", s))?;
ColorProperty::Rgba(
PropertyValue::Other(r),
PropertyValue::Other(g),
PropertyValue::Other(b),
PropertyValue::Other(255),
)
}
_ if s.len() == 9 && s.starts_with('#') => {
let (r, g, b, a) = (
u8::from_str_radix(&s[1..=2], 16)
.map_err(|_| format!("invalid color {:?}", s))?,
u8::from_str_radix(&s[3..=4], 16)
.map_err(|_| format!("invalid color {:?}", s))?,
u8::from_str_radix(&s[5..=6], 16)
.map_err(|_| format!("invalid color {:?}", s))?,
u8::from_str_radix(&s[7..=8], 16)
.map_err(|_| format!("invalid color {:?}", s))?,
);
ColorProperty::Rgba(
PropertyValue::Other(r),
PropertyValue::Other(g),
PropertyValue::Other(b),
PropertyValue::Other(a),
)
}
"rgba" => {
self.skip_white();
self.consume_expected("(")?;
self.skip_white();
let r = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let g = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let b = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let a = self.parse_token()?.into_color_alpha();
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
ColorProperty::Rgba(r, g, b, a)
}
"rgb" => {
self.skip_white();
self.consume_expected("(")?;
self.skip_white();
let r = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let g = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let b = self.parse_token()?;
self.skip_white();
let a = PropertyValue::Other(255);
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
ColorProperty::Rgba(r, g, b, a)
}
"hsla" => {
self.skip_white();
self.consume_expected("(")?;
self.skip_white();
let h = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let s = self.parse_token()?;
self.consume_expected("%")?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let l = self.parse_token()?;
self.consume_expected("%")?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let a = self.parse_token()?;
match a {
PropertyValue::Other(f) if -0.001f64 > f || f > 1.001f64 => {
return Err(format!("out of range hsl alpha value {:?}", a));
}
_ => (),
};
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
ColorProperty::Hsla(h, s, l, a)
}
"hsl" => {
self.skip_white();
self.consume_expected("(")?;
self.skip_white();
let h = self.parse_token()?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let s = self.parse_token()?;
self.consume_expected("%")?;
self.skip_white();
self.consume_expected(",")?;
self.skip_white();
let l = self.parse_token()?;
self.consume_expected("%")?;
let a = PropertyValue::Other(1f64);
self.skip_white();
self.consume_expected(")")?;
self.skip_white();
ColorProperty::Hsla(h, s, l, a)
}
_ => s
.parse::<Color>()
.map(|c| c.to_values())
.and_then(|(r, g, b)| {
Ok(ColorProperty::Rgba(
PropertyValue::Other(r),
PropertyValue::Other(g),
PropertyValue::Other(b),
PropertyValue::Other(255),
))
})?,
};
Ok(PropertyValue::Other(p))
}
}
pub enum Color {
Pink,
LightPink,
HotPink,
DeepPink,
PaleVioletRed,
MediumVioletRed,
LightSalmon,
Salmon,
DarkSalmon,
LightCoral,
IndianRed,
Crimson,
Firebrick,
DarkRed,
Red,
OrangeRed,
Tomato,
Coral,
DarkOrange,
Orange,
Yellow,
LightYellow,
LemonChiffon,
LightGoldenrodYellow,
PapayaWhip,
Moccasin,
PeachPuff,
PaleGoldenrod,
Khaki,
DarkKhaki,
Gold,
Cornsilk,
BlanchedAlmond,
Bisque,
NavajoWhite,
Wheat,
Burlywood,
Tan,
RosyBrown,
SandyBrown,
Goldenrod,
DarkGoldenrod,
Peru,
Chocolate,
SaddleBrown,
Sienna,
Brown,
Maroon,
DarkOliveGreen,
Olive,
OliveDrab,
YellowGreen,
LimeGreen,
Lime,
LawnGreen,
Chartreuse,
GreenYellow,
SpringGreen,
MediumSpringGreen,
LightGreen,
PaleGreen,
DarkSeaGreen,
MediumAquamarine,
MediumSeaGreen,
SeaGreen,
ForestGreen,
Green,
DarkGreen,
Aqua,
Cyan,
LightCyan,
PaleTurquoise,
Aquamarine,
Turquoise,
MediumTurquoise,
DarkTurquoise,
LightSeaGreen,
CadetBlue,
DarkCyan,
Teal,
LightSteelBlue,
PowderBlue,
LightBlue,
SkyBlue,
LightSkyBlue,
DeepSkyBlue,
DodgerBlue,
CornflowerBlue,
SteelBlue,
RoyalBlue,
Blue,
MediumBlue,
DarkBlue,
Navy,
MidnightBlue,
Lavender,
Thistle,
Plum,
Violet,
Orchid,
Fuchsia,
Magenta,
MediumOrchid,
MediumPurple,
BlueViolet,
DarkViolet,
DarkOrchid,
DarkMagenta,
Purple,
Indigo,
DarkSlateBlue,
SlateBlue,
MediumSlateBlue,
White,
Snow,
Honeydew,
MintCream,
Azure,
AliceBlue,
GhostWhite,
WhiteSmoke,
Seashell,
Beige,
OldLace,
FloralWhite,
Ivory,
AntiqueWhite,
Linen,
LavenderBlush,
MistyRose,
Gainsboro,
LightGray,
Silver,
DarkGray,
Gray,
DimGray,
LightSlateGray,
SlateGray,
DarkSlateGray,
Black,
}
impl ToString for Color {
fn to_string(&self) -> String {
match self {
Color::Pink => "Pink",
Color::LightPink => "LightPink",
Color::HotPink => "HotPink",
Color::DeepPink => "DeepPink",
Color::PaleVioletRed => "PaleVioletRed",
Color::MediumVioletRed => "MediumVioletRed",
Color::LightSalmon => "LightSalmon",
Color::Salmon => "Salmon",
Color::DarkSalmon => "DarkSalmon",
Color::LightCoral => "LightCoral",
Color::IndianRed => "IndianRed",
Color::Crimson => "Crimson",
Color::Firebrick => "Firebrick",
Color::DarkRed => "DarkRed",
Color::Red => "Red",
Color::OrangeRed => "OrangeRed",
Color::Tomato => "Tomato",
Color::Coral => "Coral",
Color::DarkOrange => "DarkOrange",
Color::Orange => "Orange",
Color::Yellow => "Yellow",
Color::LightYellow => "LightYellow",
Color::LemonChiffon => "LemonChiffon",
Color::LightGoldenrodYellow => "LightGoldenrodYellow",
Color::PapayaWhip => "PapayaWhip",
Color::Moccasin => "Moccasin",
Color::PeachPuff => "PeachPuff",
Color::PaleGoldenrod => "PaleGoldenrod",
Color::Khaki => "Khaki",
Color::DarkKhaki => "DarkKhaki",
Color::Gold => "Gold",
Color::Cornsilk => "Cornsilk",
Color::BlanchedAlmond => "BlanchedAlmond",
Color::Bisque => "Bisque",
Color::NavajoWhite => "NavajoWhite",
Color::Wheat => "Wheat",
Color::Burlywood => "Burlywood",
Color::Tan => "Tan",
Color::RosyBrown => "RosyBrown",
Color::SandyBrown => "SandyBrown",
Color::Goldenrod => "Goldenrod",
Color::DarkGoldenrod => "DarkGoldenrod",
Color::Peru => "Peru",
Color::Chocolate => "Chocolate",
Color::SaddleBrown => "SaddleBrown",
Color::Sienna => "Sienna",
Color::Brown => "Brown",
Color::Maroon => "Maroon",
Color::DarkOliveGreen => "DarkOliveGreen",
Color::Olive => "Olive",
Color::OliveDrab => "OliveDrab",
Color::YellowGreen => "YellowGreen",
Color::LimeGreen => "LimeGreen",
Color::Lime => "Lime",
Color::LawnGreen => "LawnGreen",
Color::Chartreuse => "Chartreuse",
Color::GreenYellow => "GreenYellow",
Color::SpringGreen => "SpringGreen",
Color::MediumSpringGreen => "MediumSpringGreen",
Color::LightGreen => "LightGreen",
Color::PaleGreen => "PaleGreen",
Color::DarkSeaGreen => "DarkSeaGreen",
Color::MediumAquamarine => "MediumAquamarine",
Color::MediumSeaGreen => "MediumSeaGreen",
Color::SeaGreen => "SeaGreen",
Color::ForestGreen => "ForestGreen",
Color::Green => "Green",
Color::DarkGreen => "DarkGreen",
Color::Aqua => "Aqua",
Color::Cyan => "Cyan",
Color::LightCyan => "LightCyan",
Color::PaleTurquoise => "PaleTurquoise",
Color::Aquamarine => "Aquamarine",
Color::Turquoise => "Turquoise",
Color::MediumTurquoise => "MediumTurquoise",
Color::DarkTurquoise => "DarkTurquoise",
Color::LightSeaGreen => "LightSeaGreen",
Color::CadetBlue => "CadetBlue",
Color::DarkCyan => "DarkCyan",
Color::Teal => "Teal",
Color::LightSteelBlue => "LightSteelBlue",
Color::PowderBlue => "PowderBlue",
Color::LightBlue => "LightBlue",
Color::SkyBlue => "SkyBlue",
Color::LightSkyBlue => "LightSkyBlue",
Color::DeepSkyBlue => "DeepSkyBlue",
Color::DodgerBlue => "DodgerBlue",
Color::CornflowerBlue => "CornflowerBlue",
Color::SteelBlue => "SteelBlue",
Color::RoyalBlue => "RoyalBlue",
Color::Blue => "Blue",
Color::MediumBlue => "MediumBlue",
Color::DarkBlue => "DarkBlue",
Color::Navy => "Navy",
Color::MidnightBlue => "MidnightBlue",
Color::Lavender => "Lavender",
Color::Thistle => "Thistle",
Color::Plum => "Plum",
Color::Violet => "Violet",
Color::Orchid => "Orchid",
Color::Fuchsia => "Fuchsia",
Color::Magenta => "Magenta",
Color::MediumOrchid => "MediumOrchid",
Color::MediumPurple => "MediumPurple",
Color::BlueViolet => "BlueViolet",
Color::DarkViolet => "DarkViolet",
Color::DarkOrchid => "DarkOrchid",
Color::DarkMagenta => "DarkMagenta",
Color::Purple => "Purple",
Color::Indigo => "Indigo",
Color::DarkSlateBlue => "DarkSlateBlue",
Color::SlateBlue => "SlateBlue",
Color::MediumSlateBlue => "MediumSlateBlue",
Color::White => "White",
Color::Snow => "Snow",
Color::Honeydew => "Honeydew",
Color::MintCream => "MintCream",
Color::Azure => "Azure",
Color::AliceBlue => "AliceBlue",
Color::GhostWhite => "GhostWhite",
Color::WhiteSmoke => "WhiteSmoke",
Color::Seashell => "Seashell",
Color::Beige => "Beige",
Color::OldLace => "OldLace",
Color::FloralWhite => "FloralWhite",
Color::Ivory => "Ivory",
Color::AntiqueWhite => "AntiqueWhite",
Color::Linen => "Linen",
Color::LavenderBlush => "LavenderBlush",
Color::MistyRose => "MistyRose",
Color::Gainsboro => "Gainsboro",
Color::LightGray => "LightGray",
Color::Silver => "Silver",
Color::DarkGray => "DarkGray",
Color::Gray => "Gray",
Color::DimGray => "DimGray",
Color::LightSlateGray => "LightSlateGray",
Color::SlateGray => "SlateGray",
Color::DarkSlateGray => "DarkSlateGray",
Color::Black => "Black",
}
.to_string()
}
}
impl Color {
pub fn to_values(&self) -> (u8, u8, u8) {
match self {
Color::Pink => (255, 192, 203),
Color::LightPink => (255, 182, 193),
Color::HotPink => (255, 105, 180),
Color::DeepPink => (255, 20, 147),
Color::PaleVioletRed => (219, 112, 147),
Color::MediumVioletRed => (199, 21, 133),
Color::LightSalmon => (255, 160, 122),
Color::Salmon => (250, 128, 114),
Color::DarkSalmon => (233, 150, 122),
Color::LightCoral => (240, 128, 128),
Color::IndianRed => (205, 92, 92),
Color::Crimson => (220, 20, 60),
Color::Firebrick => (178, 34, 34),
Color::DarkRed => (139, 0, 0),
Color::Red => (255, 0, 0),
Color::OrangeRed => (255, 69, 0),
Color::Tomato => (255, 99, 71),
Color::Coral => (255, 127, 80),
Color::DarkOrange => (255, 140, 0),
Color::Orange => (255, 165, 0),
Color::Yellow => (255, 255, 0),
Color::LightYellow => (255, 255, 224),
Color::LemonChiffon => (255, 250, 205),
Color::LightGoldenrodYellow => (250, 250, 210),
Color::PapayaWhip => (255, 239, 213),
Color::Moccasin => (255, 228, 181),
Color::PeachPuff => (255, 218, 185),
Color::PaleGoldenrod => (238, 232, 170),
Color::Khaki => (240, 230, 140),
Color::DarkKhaki => (189, 183, 107),
Color::Gold => (255, 215, 0),
Color::Cornsilk => (255, 248, 220),
Color::BlanchedAlmond => (255, 235, 205),
Color::Bisque => (255, 228, 196),
Color::NavajoWhite => (255, 222, 173),
Color::Wheat => (245, 222, 179),
Color::Burlywood => (222, 184, 135),
Color::Tan => (210, 180, 140),
Color::RosyBrown => (188, 143, 143),
Color::SandyBrown => (244, 164, 96),
Color::Goldenrod => (218, 165, 32),
Color::DarkGoldenrod => (184, 134, 11),
Color::Peru => (205, 133, 63),
Color::Chocolate => (210, 105, 30),
Color::SaddleBrown => (139, 69, 19),
Color::Sienna => (160, 82, 45),
Color::Brown => (165, 42, 42),
Color::Maroon => (128, 0, 0),
Color::DarkOliveGreen => (85, 107, 47),
Color::Olive => (128, 128, 0),
Color::OliveDrab => (107, 142, 35),
Color::YellowGreen => (154, 205, 50),
Color::LimeGreen => (50, 205, 50),
Color::Lime => (0, 255, 0),
Color::LawnGreen => (124, 252, 0),
Color::Chartreuse => (127, 255, 0),
Color::GreenYellow => (173, 255, 47),
Color::SpringGreen => (0, 255, 127),
Color::MediumSpringGreen => (0, 250, 154),
Color::LightGreen => (144, 238, 144),
Color::PaleGreen => (152, 251, 152),
Color::DarkSeaGreen => (143, 188, 143),
Color::MediumAquamarine => (102, 205, 170),
Color::MediumSeaGreen => (60, 179, 113),
Color::SeaGreen => (46, 139, 87),
Color::ForestGreen => (34, 139, 34),
Color::Green => (0, 128, 0),
Color::DarkGreen => (0, 100, 0),
Color::Aqua => (0, 255, 255),
Color::Cyan => (0, 255, 255),
Color::LightCyan => (224, 255, 255),
Color::PaleTurquoise => (175, 238, 238),
Color::Aquamarine => (127, 255, 212),
Color::Turquoise => (64, 224, 208),
Color::MediumTurquoise => (72, 209, 204),
Color::DarkTurquoise => (0, 206, 209),
Color::LightSeaGreen => (32, 178, 170),
Color::CadetBlue => (95, 158, 160),
Color::DarkCyan => (0, 139, 139),
Color::Teal => (0, 128, 128),
Color::LightSteelBlue => (176, 196, 222),
Color::PowderBlue => (176, 224, 230),
Color::LightBlue => (173, 216, 230),
Color::SkyBlue => (135, 206, 235),
Color::LightSkyBlue => (135, 206, 250),
Color::DeepSkyBlue => (0, 191, 255),
Color::DodgerBlue => (30, 144, 255),
Color::CornflowerBlue => (100, 149, 237),
Color::SteelBlue => (70, 130, 180),
Color::RoyalBlue => (65, 105, 225),
Color::Blue => (0, 0, 255),
Color::MediumBlue => (0, 0, 205),
Color::DarkBlue => (0, 0, 139),
Color::Navy => (0, 0, 128),
Color::MidnightBlue => (25, 25, 112),
Color::Lavender => (230, 230, 250),
Color::Thistle => (216, 191, 216),
Color::Plum => (221, 160, 221),
Color::Violet => (238, 130, 238),
Color::Orchid => (218, 112, 214),
Color::Fuchsia => (255, 0, 255),
Color::Magenta => (255, 0, 255),
Color::MediumOrchid => (186, 85, 211),
Color::MediumPurple => (147, 112, 219),
Color::BlueViolet => (138, 43, 226),
Color::DarkViolet => (148, 0, 211),
Color::DarkOrchid => (153, 50, 204),
Color::DarkMagenta => (139, 0, 139),
Color::Purple => (128, 0, 128),
Color::Indigo => (75, 0, 130),
Color::DarkSlateBlue => (72, 61, 139),
Color::SlateBlue => (106, 90, 205),
Color::MediumSlateBlue => (123, 104, 238),
Color::White => (255, 255, 255),
Color::Snow => (255, 250, 250),
Color::Honeydew => (240, 255, 240),
Color::MintCream => (245, 255, 250),
Color::Azure => (240, 255, 255),
Color::AliceBlue => (240, 248, 255),
Color::GhostWhite => (248, 248, 255),
Color::WhiteSmoke => (245, 245, 245),
Color::Seashell => (255, 245, 238),
Color::Beige => (245, 245, 220),
Color::OldLace => (253, 245, 230),
Color::FloralWhite => (255, 250, 240),
Color::Ivory => (255, 255, 240),
Color::AntiqueWhite => (250, 235, 215),
Color::Linen => (250, 240, 230),
Color::LavenderBlush => (255, 240, 245),
Color::MistyRose => (255, 228, 225),
Color::Gainsboro => (220, 220, 220),
Color::LightGray => (211, 211, 211),
Color::Silver => (192, 192, 192),
Color::DarkGray => (169, 169, 169),
Color::Gray => (128, 128, 128),
Color::DimGray => (105, 105, 105),
Color::LightSlateGray => (119, 136, 153),
Color::SlateGray => (112, 128, 144),
Color::DarkSlateGray => (47, 79, 79),
Color::Black => (0, 0, 0),
}
}
}
impl FromStr for Color {
type Err = String;
fn from_str(s: &str) -> Result<Color, Self::Err> {
let p = match s.to_lowercase().trim() {
"pink" | "Pink" => Color::Pink,
"lightpink" | "LightPink" => Color::LightPink,
"hotpink" | "HotPink" => Color::HotPink,
"deeppink" | "DeepPink" => Color::DeepPink,
"palevioletred" | "PaleVioletRed" => Color::PaleVioletRed,
"mediumvioletred" | "MediumVioletRed" => Color::MediumVioletRed,
"lightsalmon" | "LightSalmon" => Color::LightSalmon,
"salmon" | "Salmon" => Color::Salmon,
"darksalmon" | "DarkSalmon" => Color::DarkSalmon,
"lightcoral" | "LightCoral" => Color::LightCoral,
"indianred" | "IndianRed" => Color::IndianRed,
"crimson" | "Crimson" => Color::Crimson,
"firebrick" | "Firebrick" => Color::Firebrick,
"darkred" | "DarkRed" => Color::DarkRed,
"red" | "Red" => Color::Red,
"orangered" | "OrangeRed" => Color::OrangeRed,
"tomato" | "Tomato" => Color::Tomato,
"coral" | "Coral" => Color::Coral,
"darkorange" | "DarkOrange" => Color::DarkOrange,
"orange" | "Orange" => Color::Orange,
"yellow" | "Yellow" => Color::Yellow,
"lightyellow" | "LightYellow" => Color::LightYellow,
"lemonchiffon" | "LemonChiffon" => Color::LemonChiffon,
"lightgoldenrodyellow" | "LightGoldenrodYellow" => Color::LightGoldenrodYellow,
"papayawhip" | "PapayaWhip" => Color::PapayaWhip,
"moccasin" | "Moccasin" => Color::Moccasin,
"peachpuff" | "PeachPuff" => Color::PeachPuff,
"palegoldenrod" | "PaleGoldenrod" => Color::PaleGoldenrod,
"khaki" | "Khaki" => Color::Khaki,
"darkkhaki" | "DarkKhaki" => Color::DarkKhaki,
"gold" | "Gold" => Color::Gold,
"cornsilk" | "Cornsilk" => Color::Cornsilk,
"blanchedalmond" | "BlanchedAlmond" => Color::BlanchedAlmond,
"bisque" | "Bisque" => Color::Bisque,
"navajowhite" | "NavajoWhite" => Color::NavajoWhite,
"wheat" | "Wheat" => Color::Wheat,
"burlywood" | "Burlywood" => Color::Burlywood,
"tan" | "Tan" => Color::Tan,
"rosybrown" | "RosyBrown" => Color::RosyBrown,
"sandybrown" | "SandyBrown" => Color::SandyBrown,
"goldenrod" | "Goldenrod" => Color::Goldenrod,
"darkgoldenrod" | "DarkGoldenrod" => Color::DarkGoldenrod,
"peru" | "Peru" => Color::Peru,
"chocolate" | "Chocolate" => Color::Chocolate,
"saddlebrown" | "SaddleBrown" => Color::SaddleBrown,
"sienna" | "Sienna" => Color::Sienna,
"brown" | "Brown" => Color::Brown,
"maroon" | "Maroon" => Color::Maroon,
"darkolivegreen" | "DarkOliveGreen" => Color::DarkOliveGreen,
"olive" | "Olive" => Color::Olive,
"olivedrab" | "OliveDrab" => Color::OliveDrab,
"yellowgreen" | "YellowGreen" => Color::YellowGreen,
"limegreen" | "LimeGreen" => Color::LimeGreen,
"lime" | "Lime" => Color::Lime,
"lawngreen" | "LawnGreen" => Color::LawnGreen,
"chartreuse" | "Chartreuse" => Color::Chartreuse,
"greenyellow" | "GreenYellow" => Color::GreenYellow,
"springgreen" | "SpringGreen" => Color::SpringGreen,
"mediumspringgreen" | "MediumSpringGreen" => Color::MediumSpringGreen,
"lightgreen" | "LightGreen" => Color::LightGreen,
"palegreen" | "PaleGreen" => Color::PaleGreen,
"darkseagreen" | "DarkSeaGreen" => Color::DarkSeaGreen,
"mediumaquamarine" | "MediumAquamarine" => Color::MediumAquamarine,
"mediumseagreen" | "MediumSeaGreen" => Color::MediumSeaGreen,
"seagreen" | "SeaGreen" => Color::SeaGreen,
"forestgreen" | "ForestGreen" => Color::ForestGreen,
"green" | "Green" => Color::Green,
"darkgreen" | "DarkGreen" => Color::DarkGreen,
"aqua" | "Aqua" => Color::Aqua,
"cyan" | "Cyan" => Color::Cyan,
"lightcyan" | "LightCyan" => Color::LightCyan,
"paleturquoise" | "PaleTurquoise" => Color::PaleTurquoise,
"aquamarine" | "Aquamarine" => Color::Aquamarine,
"turquoise" | "Turquoise" => Color::Turquoise,
"mediumturquoise" | "MediumTurquoise" => Color::MediumTurquoise,
"darkturquoise" | "DarkTurquoise" => Color::DarkTurquoise,
"lightseagreen" | "LightSeaGreen" => Color::LightSeaGreen,
"cadetblue" | "CadetBlue" => Color::CadetBlue,
"darkcyan" | "DarkCyan" => Color::DarkCyan,
"teal" | "Teal" => Color::Teal,
"lightsteelblue" | "LightSteelBlue" => Color::LightSteelBlue,
"powderblue" | "PowderBlue" => Color::PowderBlue,
"lightblue" | "LightBlue" => Color::LightBlue,
"skyblue" | "SkyBlue" => Color::SkyBlue,
"lightskyblue" | "LightSkyBlue" => Color::LightSkyBlue,
"deepskyblue" | "DeepSkyBlue" => Color::DeepSkyBlue,
"dodgerblue" | "DodgerBlue" => Color::DodgerBlue,
"cornflowerblue" | "CornflowerBlue" => Color::CornflowerBlue,
"steelblue" | "SteelBlue" => Color::SteelBlue,
"royalblue" | "RoyalBlue" => Color::RoyalBlue,
"blue" | "Blue" => Color::Blue,
"mediumblue" | "MediumBlue" => Color::MediumBlue,
"darkblue" | "DarkBlue" => Color::DarkBlue,
"navy" | "Navy" => Color::Navy,
"midnightblue" | "MidnightBlue" => Color::MidnightBlue,
"lavender" | "Lavender" => Color::Lavender,
"thistle" | "Thistle" => Color::Thistle,
"plum" | "Plum" => Color::Plum,
"violet" | "Violet" => Color::Violet,
"orchid" | "Orchid" => Color::Orchid,
"fuchsia" | "Fuchsia" => Color::Fuchsia,
"magenta" | "Magenta" => Color::Magenta,
"mediumorchid" | "MediumOrchid" => Color::MediumOrchid,
"mediumpurple" | "MediumPurple" => Color::MediumPurple,
"blueviolet" | "BlueViolet" => Color::BlueViolet,
"darkviolet" | "DarkViolet" => Color::DarkViolet,
"darkorchid" | "DarkOrchid" => Color::DarkOrchid,
"darkmagenta" | "DarkMagenta" => Color::DarkMagenta,
"purple" | "Purple" => Color::Purple,
"indigo" | "Indigo" => Color::Indigo,
"darkslateblue" | "DarkSlateBlue" => Color::DarkSlateBlue,
"slateblue" | "SlateBlue" => Color::SlateBlue,
"mediumslateblue" | "MediumSlateBlue" => Color::MediumSlateBlue,
"white" | "White" => Color::White,
"snow" | "Snow" => Color::Snow,
"honeydew" | "Honeydew" => Color::Honeydew,
"mintcream" | "MintCream" => Color::MintCream,
"azure" | "Azure" => Color::Azure,
"aliceblue" | "AliceBlue" => Color::AliceBlue,
"ghostwhite" | "GhostWhite" => Color::GhostWhite,
"whitesmoke" | "WhiteSmoke" => Color::WhiteSmoke,
"seashell" | "Seashell" => Color::Seashell,
"beige" | "Beige" => Color::Beige,
"oldlace" | "OldLace" => Color::OldLace,
"floralwhite" | "FloralWhite" => Color::FloralWhite,
"ivory" | "Ivory" => Color::Ivory,
"antiquewhite" | "AntiqueWhite" => Color::AntiqueWhite,
"linen" | "Linen" => Color::Linen,
"lavenderblush" | "LavenderBlush" => Color::LavenderBlush,
"mistyrose" | "MistyRose" => Color::MistyRose,
"gainsboro" | "Gainsboro" => Color::Gainsboro,
"lightgray" | "LightGray" => Color::LightGray,
"silver" | "Silver" => Color::Silver,
"darkgray" | "DarkGray" => Color::DarkGray,
"gray" | "Gray" => Color::Gray,
"dimgray" | "DimGray" => Color::DimGray,
"lightslategray" | "LightSlateGray" => Color::LightSlateGray,
"slategray" | "SlateGray" => Color::SlateGray,
"darkslategray" | "DarkSlateGray" => Color::DarkSlateGray,
"black" | "Black" => Color::Black,
_ => return Err(format!("{:?} is not a predefined color", s)),
};
Ok(p)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
.foo {
}
.bar {
}
.foz {
}
.baz {
display: block;
justify-content: space-between;
color: red;
background-color: #42413d;
}

Some files were not shown because too many files have changed in this diff Show More