diff --git a/pgml-dashboard/.editorconfig b/pgml-dashboard/.editorconfig index 8b67e0f71..88b69042e 100644 --- a/pgml-dashboard/.editorconfig +++ b/pgml-dashboard/.editorconfig @@ -13,4 +13,4 @@ indent_size = 4 [*.html] ident_style = space -indent_size = 4 +indent_size = 2 diff --git a/pgml-dashboard/build.rs b/pgml-dashboard/build.rs index d76aa7923..0c9604dee 100644 --- a/pgml-dashboard/build.rs +++ b/pgml-dashboard/build.rs @@ -3,7 +3,6 @@ use std::process::Command; fn main() { println!("cargo:rerun-if-changed=migrations"); - println!("cargo:rerun-if-changed=src"); let output = Command::new("git") .args(&["rev-parse", "HEAD"]) diff --git a/pgml-dashboard/src/components/mod.rs b/pgml-dashboard/src/components/mod.rs index fd6a206da..40205f3c2 100644 --- a/pgml-dashboard/src/components/mod.rs +++ b/pgml-dashboard/src/components/mod.rs @@ -4,7 +4,6 @@ mod component; pub(crate) use component::{component, Component}; - // src/components/breadcrumbs pub mod breadcrumbs; pub use breadcrumbs::Breadcrumbs; @@ -49,6 +48,9 @@ pub use navbar::Navbar; pub mod navbar_web_app; pub use navbar_web_app::NavbarWebApp; +// src/components/navigation +pub mod navigation; + // src/components/postgres_logo pub mod postgres_logo; pub use postgres_logo::PostgresLogo; @@ -65,6 +67,9 @@ pub use static_nav::StaticNav; pub mod static_nav_link; pub use static_nav_link::StaticNavLink; +// src/components/tables +pub mod tables; + // src/components/test_component pub mod test_component; pub use test_component::TestComponent; diff --git a/pgml-dashboard/src/components/navigation/mod.rs b/pgml-dashboard/src/components/navigation/mod.rs new file mode 100644 index 000000000..e615f8406 --- /dev/null +++ b/pgml-dashboard/src/components/navigation/mod.rs @@ -0,0 +1,5 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/navigation/tabs +pub mod tabs; diff --git a/pgml-dashboard/src/components/navigation/tabs/mod.rs b/pgml-dashboard/src/components/navigation/tabs/mod.rs new file mode 100644 index 000000000..122d9b659 --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/mod.rs @@ -0,0 +1,10 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/navigation/tabs/tab +pub mod tab; +pub use tab::Tab; + +// src/components/navigation/tabs/tabs +pub mod tabs; +pub use tabs::Tabs; diff --git a/pgml-dashboard/src/components/navigation/tabs/tab/mod.rs b/pgml-dashboard/src/components/navigation/tabs/tab/mod.rs new file mode 100644 index 000000000..b1e33cc00 --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/tab/mod.rs @@ -0,0 +1,70 @@ +#![allow(unused_variables)] +use crate::components::component; +use crate::components::component::Component; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default, Clone)] +#[template(path = "navigation/tabs/tab/template.html")] +pub struct Tab { + content: Component, + active: bool, + name: String, +} + +impl Tab { + pub fn new(name: impl ToString, content: Component) -> Tab { + Tab { + content, + active: false, + name: name.to_string(), + } + } + + pub fn button_classes(&self) -> String { + if self.active { + "nav-link active btn btn-tertiary rounded-0".to_string() + } else { + "nav-link btn btn-tertiary rounded-0".to_string() + } + } + + pub fn content_classes(&self) -> String { + if self.active { + "tab-pane my-4 show active".to_string() + } else { + "tab-pane my-4".to_string() + } + } + + pub fn id(&self) -> String { + format!("tab-{}", self.name.to_lowercase().replace(" ", "-")) + } + + pub fn selected(&self) -> String { + if self.active { + "selected".to_string() + } else { + "".to_string() + } + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn active(mut self) -> Self { + self.active = true; + self + } + + pub fn inactive(mut self) -> Self { + self.active = false; + self + } + + pub fn is_active(&self) -> bool { + self.active + } +} + +component!(Tab); diff --git a/pgml-dashboard/src/components/navigation/tabs/tab/tab.scss b/pgml-dashboard/src/components/navigation/tabs/tab/tab.scss new file mode 100644 index 000000000..e69de29bb diff --git a/pgml-dashboard/src/components/navigation/tabs/tab/template.html b/pgml-dashboard/src/components/navigation/tabs/tab/template.html new file mode 100644 index 000000000..3033744fa --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/tab/template.html @@ -0,0 +1,3 @@ +
+ <%+ content %> +
diff --git a/pgml-dashboard/src/components/navigation/tabs/tabs/mod.rs b/pgml-dashboard/src/components/navigation/tabs/tabs/mod.rs new file mode 100644 index 000000000..21b176837 --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/tabs/mod.rs @@ -0,0 +1,44 @@ +use crate::components::component; +use crate::components::navigation::tabs::Tab; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default)] +#[template(path = "navigation/tabs/tabs/template.html")] +pub struct Tabs { + tabs: Vec, +} + +impl Tabs { + pub fn new(tabs: &[Tab]) -> Tabs { + // Set the first tab to active if none are. + let mut tabs = tabs.to_vec(); + if tabs.iter().all(|t| !t.is_active()) { + tabs = tabs + .into_iter() + .enumerate() + .map(|(i, tab)| if i == 0 { tab.active() } else { tab }) + .collect(); + } + + Tabs { tabs } + } + + pub fn active_tab(mut self, name: impl ToString) -> Self { + let tabs = self + .tabs + .into_iter() + .map(|tab| { + if tab.name() == name.to_string() { + tab.active() + } else { + tab.inactive() + } + }) + .collect(); + + self.tabs = tabs; + self + } +} + +component!(Tabs); diff --git a/pgml-dashboard/src/components/navigation/tabs/tabs/tabs.scss b/pgml-dashboard/src/components/navigation/tabs/tabs/tabs.scss new file mode 100644 index 000000000..2da2868c6 --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/tabs/tabs.scss @@ -0,0 +1,21 @@ +.nav-tabs { + // These tabs are used in docs as well, where they are + // generated using Bootstrap. It wasn't obvious to me + // how to replace those with this component yet, so I'm just + // enforcing the font-family here so they look the same. Docs use Roboto by default. + font-family: 'silka', 'Roboto', 'sans-serif'; + + --bs-nav-tabs-border-width: 4px; + + .nav-link { + border: none; + + &.active, &:focus, &:active { + border-bottom: 4px solid #{$slate-tint-700}; + color: #{$slate-tint-700}; + text-shadow: none; + } + + color: #{$slate-tint-100}; + } +} diff --git a/pgml-dashboard/src/components/navigation/tabs/tabs/template.html b/pgml-dashboard/src/components/navigation/tabs/tabs/template.html new file mode 100644 index 000000000..c43ca94ec --- /dev/null +++ b/pgml-dashboard/src/components/navigation/tabs/tabs/template.html @@ -0,0 +1,30 @@ + +
+ <% for (i, tab) in tabs.into_iter().enumerate() { %> +
+ <%+ tab %> +
+ <% } %> +
diff --git a/pgml-dashboard/src/components/tables/large/mod.rs b/pgml-dashboard/src/components/tables/large/mod.rs new file mode 100644 index 000000000..17fdf1b6f --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/mod.rs @@ -0,0 +1,10 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/tables/large/row +pub mod row; +pub use row::Row; + +// src/components/tables/large/table +pub mod table; +pub use table::Table; diff --git a/pgml-dashboard/src/components/tables/large/row/mod.rs b/pgml-dashboard/src/components/tables/large/row/mod.rs new file mode 100644 index 000000000..eac8a2e65 --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/row/mod.rs @@ -0,0 +1,19 @@ +use crate::components::component; +use crate::components::component::Component; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default, Clone)] +#[template(path = "tables/large/row/template.html")] +pub struct Row { + columns: Vec, +} + +impl Row { + pub fn new(columns: &[Component]) -> Row { + Row { + columns: columns.to_vec(), + } + } +} + +component!(Row); diff --git a/pgml-dashboard/src/components/tables/large/row/row.scss b/pgml-dashboard/src/components/tables/large/row/row.scss new file mode 100644 index 000000000..5a65b7722 --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/row/row.scss @@ -0,0 +1,37 @@ +table.table.table-lg { + tr { + &:first-of-type { + td { + padding-top: 16px; + } + } + + &:hover { + div.table-cell-content { + background: #{$gray-800}; + } + } + + td { + vertical-align: middle; + padding: 8px 0; + + div.table-cell-content { + background: #{$gray-600}; + padding: 20px 0; + } + + &:first-of-type { + div.table-cell-content { + padding-left: 67px; + } + } + + &:last-of-type { + div.table-cell-content { + padding-right: 67px; + } + } + } + } +} diff --git a/pgml-dashboard/src/components/tables/large/row/template.html b/pgml-dashboard/src/components/tables/large/row/template.html new file mode 100644 index 000000000..32b9c8714 --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/row/template.html @@ -0,0 +1,9 @@ + + <% for column in columns { %> + +
+ <%+ column %> +
+ + <% } %> + diff --git a/pgml-dashboard/src/components/tables/large/table/mod.rs b/pgml-dashboard/src/components/tables/large/table/mod.rs new file mode 100644 index 000000000..42da7d9fd --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/table/mod.rs @@ -0,0 +1,21 @@ +use crate::components::component; +use crate::components::tables::large::Row; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default)] +#[template(path = "tables/large/table/template.html")] +pub struct Table { + rows: Vec, + headers: Vec, +} + +impl Table { + pub fn new(headers: &[impl ToString], rows: &[Row]) -> Table { + Table { + headers: headers.iter().map(|h| h.to_string()).collect(), + rows: rows.to_vec(), + } + } +} + +component!(Table); diff --git a/pgml-dashboard/src/components/tables/large/table/table.scss b/pgml-dashboard/src/components/tables/large/table/table.scss new file mode 100644 index 000000000..3befa6e55 --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/table/table.scss @@ -0,0 +1,19 @@ +table.table.table-lg { + * { + border-width: 0; + } + + thead { + th { + color: #{$slate-shade-100}; + background: #{$gray-800}; + text-transform: uppercase; + font-size: 0.75rem; + padding: 16px 0; + + &:first-of-type { + padding-left: 67px; + } + } + } +} diff --git a/pgml-dashboard/src/components/tables/large/table/template.html b/pgml-dashboard/src/components/tables/large/table/template.html new file mode 100644 index 000000000..1aa2a831b --- /dev/null +++ b/pgml-dashboard/src/components/tables/large/table/template.html @@ -0,0 +1,14 @@ + + + + <% for header in headers { %> + + <% } %> + + + + <% for row in rows { %> + <%+ row %> + <% } %> + + diff --git a/pgml-dashboard/src/components/tables/mod.rs b/pgml-dashboard/src/components/tables/mod.rs new file mode 100644 index 000000000..48a76b04c --- /dev/null +++ b/pgml-dashboard/src/components/tables/mod.rs @@ -0,0 +1,5 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/tables/large +pub mod large; diff --git a/pgml-dashboard/src/utils/markdown.rs b/pgml-dashboard/src/utils/markdown.rs index 0957e5e39..b13636c6a 100644 --- a/pgml-dashboard/src/utils/markdown.rs +++ b/pgml-dashboard/src/utils/markdown.rs @@ -669,7 +669,7 @@ impl<'a> Tab<'a> { "
  • <%= header %>