diff --git a/packages/pgml-components/src/lib.rs b/packages/pgml-components/src/lib.rs index d6d7d1ddb..0bc42b623 100644 --- a/packages/pgml-components/src/lib.rs +++ b/packages/pgml-components/src/lib.rs @@ -3,7 +3,7 @@ use sailfish::TemplateOnce; -#[derive(Default, Clone, TemplateOnce)] +#[derive(Default, Clone, TemplateOnce, Debug)] #[template(path = "components/component.html")] pub struct Component { pub value: String, diff --git a/pgml-dashboard/Cargo.lock b/pgml-dashboard/Cargo.lock index 6d9483caf..2f8580a39 100644 --- a/pgml-dashboard/Cargo.lock +++ b/pgml-dashboard/Cargo.lock @@ -2354,9 +2354,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oneshot" @@ -2559,6 +2559,7 @@ dependencies = [ "itertools", "lopdf", "md5", + "once_cell", "parking_lot", "regex", "reqwest", @@ -2874,9 +2875,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -2937,9 +2938,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] diff --git a/pgml-dashboard/src/api/cms.rs b/pgml-dashboard/src/api/cms.rs index b5e906ce5..e5580e025 100644 --- a/pgml-dashboard/src/api/cms.rs +++ b/pgml-dashboard/src/api/cms.rs @@ -14,7 +14,7 @@ use yaml_rust::YamlLoader; use crate::{ components::{cms::index_link::IndexLink, layouts::marketing::base::Theme, layouts::marketing::Base}, guards::Cluster, - responses::{Response, ResponseOk, Template}, + responses::{Error, Response, ResponseOk, Template}, templates::docs::*, utils::{config, markdown::SearchResult}, }; @@ -882,6 +882,37 @@ async fn careers_landing_page(cluster: &Cluster) -> Result")] +async fn demo(search: Option) -> Result { + #[cfg(not(debug_assertions))] + { + let _search = search; + return Ok(Response::not_found()); + } + + #[cfg(debug_assertions)] + { + use crate::components::dropdown::{DropdownFrame, DropdownItems}; + use crate::components::inputs::text::search::SearchOption; + if let Some(search) = search { + let candidates = vec!["hello", "world", "foo", "bar"] + .into_iter() + .filter(|c| c.starts_with(&search)) + .map(|c| SearchOption::new(c.into()).into()) + .collect::>(); + + Ok(Response::ok( + DropdownFrame::rendered("model-search", DropdownItems::new(candidates).into()).render_once()?, + )) + } else { + let layout = Base::new("Demos", None).theme(Theme::Marketing); + + let page = crate::components::pages::demo::Demo::new(); + Ok(Response::ok(layout.render(page))) + } + } +} + pub fn routes() -> Vec { routes![ blog_landing_page, @@ -896,7 +927,8 @@ pub fn routes() -> Vec { get_docs_asset, get_user_guides, search, - search_blog + search_blog, + demo, ] } diff --git a/pgml-dashboard/src/components/badges/large/label/label.scss b/pgml-dashboard/src/components/badges/large/label/label.scss new file mode 100644 index 000000000..05683b38b --- /dev/null +++ b/pgml-dashboard/src/components/badges/large/label/label.scss @@ -0,0 +1,11 @@ +span[data-controller="badges-large-label"] { + padding: 8px; + background: #{$gray-500}; + font-weight: #{$font-weight-medium}; + border: 1px solid #{$neon-tint-100}; + + &.active { + background: #{$neon-tint-100}; + border: 1px solid #{$neon-tint-600}; + } +} diff --git a/pgml-dashboard/src/components/badges/large/label/mod.rs b/pgml-dashboard/src/components/badges/large/label/mod.rs new file mode 100644 index 000000000..56b534774 --- /dev/null +++ b/pgml-dashboard/src/components/badges/large/label/mod.rs @@ -0,0 +1,39 @@ +use crate::components::stimulus::StimulusAction; +use pgml_components::component; +use sailfish::TemplateOnce; + +#[derive(Clone, Debug)] +pub struct LabelCloseOptions { + pub action: StimulusAction, + pub url: String, +} + +#[derive(TemplateOnce, Default)] +#[template(path = "badges/large/label/template.html")] +pub struct Label { + value: String, + close_options: Option, + active: String, +} + +impl Label { + pub fn new(value: &str) -> Label { + Label { + value: value.into(), + close_options: None, + active: "".into(), + } + } + + pub fn close_options(mut self, options: LabelCloseOptions) -> Label { + self.close_options = Some(options); + self + } + + pub fn active(mut self) -> Label { + self.active = "active".into(); + self + } +} + +component!(Label); diff --git a/pgml-dashboard/src/components/badges/large/label/template.html b/pgml-dashboard/src/components/badges/large/label/template.html new file mode 100644 index 000000000..7125c42cc --- /dev/null +++ b/pgml-dashboard/src/components/badges/large/label/template.html @@ -0,0 +1,12 @@ +<% use crate::components::badges::large::label::LabelCloseOptions; %> + + + <%= value %> + <% if let Some(LabelCloseOptions { action, url }) = close_options { %> + + + close + + + <% } %> + diff --git a/pgml-dashboard/src/components/badges/large/mod.rs b/pgml-dashboard/src/components/badges/large/mod.rs new file mode 100644 index 000000000..11645838e --- /dev/null +++ b/pgml-dashboard/src/components/badges/large/mod.rs @@ -0,0 +1,6 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/badges/large/label +pub mod label; +pub use label::Label; diff --git a/pgml-dashboard/src/components/badges/mod.rs b/pgml-dashboard/src/components/badges/mod.rs new file mode 100644 index 000000000..f93091b93 --- /dev/null +++ b/pgml-dashboard/src/components/badges/mod.rs @@ -0,0 +1,8 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/badges/large +pub mod large; + +// src/components/badges/small +pub mod small; diff --git a/pgml-dashboard/src/components/badges/small/label/label.scss b/pgml-dashboard/src/components/badges/small/label/label.scss new file mode 100644 index 000000000..8e59a8719 --- /dev/null +++ b/pgml-dashboard/src/components/badges/small/label/label.scss @@ -0,0 +1,12 @@ +span[data-controller="badges-small-label"] { + span { + font-size: 12px; + font-weight: #{$font-weight-normal}; + } + + background: #{$gray-800}; + padding: 4px 8px; + border-radius: 4px; + + text-transform: uppercase; +} diff --git a/pgml-dashboard/src/components/badges/small/label/mod.rs b/pgml-dashboard/src/components/badges/small/label/mod.rs new file mode 100644 index 000000000..5c0880a47 --- /dev/null +++ b/pgml-dashboard/src/components/badges/small/label/mod.rs @@ -0,0 +1,48 @@ +use pgml_components::component; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce, Default)] +#[template(path = "badges/small/label/template.html")] +pub struct Label { + value: String, + image_url: String, +} + +impl Label { + pub fn check_circle(value: &str) -> Label { + Label { + value: value.into(), + image_url: "/dashboard/static/images/icons/check_circle.svg".to_string(), + } + } + + pub fn cancel(value: &str) -> Label { + Label { + value: value.into(), + image_url: "/dashboard/static/images/icons/cancel.svg".to_string(), + } + } + + pub fn outbound(value: &str) -> Label { + Label { + value: value.into(), + image_url: "/dashboard/static/images/icons/outbound.svg".to_string(), + } + } + + pub fn download_for_offline(value: &str) -> Label { + Label { + value: value.into(), + image_url: "/dashboard/static/images/icons/download_for_offline.svg".to_string(), + } + } + + pub fn forward_circle(value: &str) -> Label { + Label { + value: value.into(), + image_url: "/dashboard/static/images/icons/forward_circle.svg".to_string(), + } + } +} + +component!(Label); diff --git a/pgml-dashboard/src/components/badges/small/label/template.html b/pgml-dashboard/src/components/badges/small/label/template.html new file mode 100644 index 000000000..467ed4c0a --- /dev/null +++ b/pgml-dashboard/src/components/badges/small/label/template.html @@ -0,0 +1,4 @@ + + + <%= value %> + diff --git a/pgml-dashboard/src/components/badges/small/mod.rs b/pgml-dashboard/src/components/badges/small/mod.rs new file mode 100644 index 000000000..45ce0cbce --- /dev/null +++ b/pgml-dashboard/src/components/badges/small/mod.rs @@ -0,0 +1,6 @@ +// This file is automatically generated. +// You shouldn't modify it manually. + +// src/components/badges/small/label +pub mod label; +pub use label::Label; diff --git a/pgml-dashboard/src/components/cards/mod.rs b/pgml-dashboard/src/components/cards/mod.rs index 00dfd1a7c..2c6661725 100644 --- a/pgml-dashboard/src/components/cards/mod.rs +++ b/pgml-dashboard/src/components/cards/mod.rs @@ -7,3 +7,7 @@ pub mod blog; // src/components/cards/newsletter_subscribe pub mod newsletter_subscribe; pub use newsletter_subscribe::NewsletterSubscribe; + +// src/components/cards/rgb +pub mod rgb; +pub use rgb::Rgb; diff --git a/pgml-dashboard/src/components/cards/rgb/mod.rs b/pgml-dashboard/src/components/cards/rgb/mod.rs new file mode 100644 index 000000000..e4abfc3f3 --- /dev/null +++ b/pgml-dashboard/src/components/cards/rgb/mod.rs @@ -0,0 +1,38 @@ +use pgml_components::{component, Component}; +use sailfish::TemplateOnce; + +#[derive(TemplateOnce)] +#[template(path = "cards/rgb/template.html")] +pub struct Rgb { + value: Component, + active: bool, + link: Option, +} + +impl Default for Rgb { + fn default() -> Self { + Rgb::new("RGB card".into()) + } +} + +impl Rgb { + pub fn new(value: Component) -> Rgb { + Rgb { + value, + active: false, + link: None, + } + } + + pub fn active(mut self) -> Self { + self.active = true; + self + } + + pub fn link(mut self, link: &str) -> Self { + self.link = Some(link.to_string()); + self + } +} + +component!(Rgb); diff --git a/pgml-dashboard/src/components/cards/rgb/rgb.scss b/pgml-dashboard/src/components/cards/rgb/rgb.scss new file mode 100644 index 000000000..46b8b1a04 --- /dev/null +++ b/pgml-dashboard/src/components/cards/rgb/rgb.scss @@ -0,0 +1,6 @@ +div[data-controller="cards-rgb"] { + .card { + --bs-card-bg: transparent; + --bs-card-border-color: #{$gray-700}; + } +} diff --git a/pgml-dashboard/src/components/cards/rgb/rgb_controller.js b/pgml-dashboard/src/components/cards/rgb/rgb_controller.js new file mode 100644 index 000000000..e7c876fda --- /dev/null +++ b/pgml-dashboard/src/components/cards/rgb/rgb_controller.js @@ -0,0 +1,17 @@ +import { Controller } from "@hotwired/stimulus"; + +export default class extends Controller { + // Activate this card (add RGB). + active() { + this.element + .querySelector(".card") + .classList.add("main-gradient-border-card-1"); + } + + // Deactivate this card (remove RGB). + inactive() { + this.element + .querySelector(".card") + .classList.remove("main-gradient-border-card-1"); + } +} diff --git a/pgml-dashboard/src/components/cards/rgb/template.html b/pgml-dashboard/src/components/cards/rgb/template.html new file mode 100644 index 000000000..1fffe1d0a --- /dev/null +++ b/pgml-dashboard/src/components/cards/rgb/template.html @@ -0,0 +1,11 @@ +<% let active = if active { "main-gradient-border-card-1 active" } else { "" }; %> +
+
+
+ <%+ value %> + <% if let Some(link) = link { %> + + <% } %> +
+
+
diff --git a/pgml-dashboard/src/components/dropdown/dropdown.scss b/pgml-dashboard/src/components/dropdown/dropdown.scss index 938595b94..8a04d4d76 100644 --- a/pgml-dashboard/src/components/dropdown/dropdown.scss +++ b/pgml-dashboard/src/components/dropdown/dropdown.scss @@ -95,7 +95,7 @@ } @mixin dropdown-menu($primary-color: null) { - padding: 20px 0px 40px 0px; + padding: 20px 0px 20px 0px; overflow-y: auto; @if ($primary-color) { diff --git a/pgml-dashboard/src/components/dropdown/dropdown_frame.html b/pgml-dashboard/src/components/dropdown/dropdown_frame.html new file mode 100644 index 000000000..3c4d724ad --- /dev/null +++ b/pgml-dashboard/src/components/dropdown/dropdown_frame.html @@ -0,0 +1,8 @@ +<% if let Some(src) = src { %> + + +<% } else { %> + + <%+ content %> + +<% } %> diff --git a/pgml-dashboard/src/components/dropdown/dropdown_items.html b/pgml-dashboard/src/components/dropdown/dropdown_items.html new file mode 100644 index 000000000..06627fc9e --- /dev/null +++ b/pgml-dashboard/src/components/dropdown/dropdown_items.html @@ -0,0 +1,3 @@ +<% for item in items { %> + <%+ item %> +<% } %> diff --git a/pgml-dashboard/src/components/dropdown/mod.rs b/pgml-dashboard/src/components/dropdown/mod.rs index 734b2eb8a..847719ca4 100644 --- a/pgml-dashboard/src/components/dropdown/mod.rs +++ b/pgml-dashboard/src/components/dropdown/mod.rs @@ -9,6 +9,7 @@ use crate::components::StaticNavLink; pub enum DropdownValue { Icon(Component), Text(Component), + None, } impl Default for DropdownValue { @@ -17,6 +18,48 @@ impl Default for DropdownValue { } } +#[derive(TemplateOnce, Default)] +#[template(path = "dropdown/dropdown_items.html")] +pub struct DropdownItems { + items: Vec, +} + +impl DropdownItems { + pub fn new(items: Vec) -> Self { + DropdownItems { items } + } +} + +component!(DropdownItems); + +#[derive(TemplateOnce, Default)] +#[template(path = "dropdown/dropdown_frame.html")] +pub struct DropdownFrame { + src: Option, + id: String, + content: Component, +} + +impl DropdownFrame { + pub fn rendered(id: impl ToString, content: Component) -> Self { + DropdownFrame { + src: None, + id: id.to_string(), + content, + } + } + + pub fn new(id: impl ToString, src: impl ToString) -> Self { + DropdownFrame { + src: Some(src.to_string()), + id: id.to_string(), + content: "".into(), + } + } +} + +component!(DropdownFrame); + #[derive(TemplateOnce, Default)] #[template(path = "dropdown/template.html")] pub struct Dropdown { @@ -24,7 +67,7 @@ pub struct Dropdown { value: DropdownValue, /// The list of dropdown items to render. - items: Vec, + items: Component, /// Position of the dropdown menu. offset: String, @@ -39,12 +82,15 @@ pub struct Dropdown { /// target to control value value_target: StimulusTarget, + + /// If the dropdown should be shown + show: String, } impl Dropdown { pub fn new() -> Self { Dropdown { - items: Vec::new(), + items: DropdownItems::default().into(), value: DropdownValue::Text("Dropdown".to_owned().into()), offset: "0, 10".to_owned(), offset_collapsed: "68, -44".to_owned(), @@ -53,6 +99,13 @@ impl Dropdown { } } + pub fn new_no_button() -> Self { + Dropdown { + value: DropdownValue::None, + ..Self::new() + } + } + pub fn nav(links: Vec) -> Self { let binding = links.iter().filter(|link| link.active).collect::>(); @@ -70,7 +123,7 @@ impl Dropdown { } Dropdown { - items, + items: DropdownItems::new(items).into(), value: DropdownValue::Text(value.into()), offset: "0, 10".to_owned(), offset_collapsed: "68, -44".to_owned(), @@ -80,7 +133,13 @@ impl Dropdown { } pub fn items(mut self, items: Vec) -> Self { - self.items = items; + self.items = DropdownItems::new(items).into(); + self + } + + pub fn frame(mut self, id: impl ToString, src: impl ToString) -> Self { + self.items = DropdownFrame::new(id, src).into(); + self } @@ -128,6 +187,11 @@ impl Dropdown { self.value_target = value_target; self } + + pub fn show(mut self) -> Self { + self.show = "show".into(); + self + } } component!(Dropdown); diff --git a/pgml-dashboard/src/components/dropdown/template.html b/pgml-dashboard/src/components/dropdown/template.html index 697b834db..ae273980a 100644 --- a/pgml-dashboard/src/components/dropdown/template.html +++ b/pgml-dashboard/src/components/dropdown/template.html @@ -41,10 +41,8 @@ <% } %> -