diff --git a/pgml-dashboard/src/components/inputs/select/mod.rs b/pgml-dashboard/src/components/inputs/select/mod.rs index 43e671f5c..30f2e5805 100644 --- a/pgml-dashboard/src/components/inputs/select/mod.rs +++ b/pgml-dashboard/src/components/inputs/select/mod.rs @@ -1,7 +1,9 @@ use crate::components::stimulus::stimulus_action::{StimulusAction, StimulusEvents}; +use crate::components::stimulus::stimulus_target::StimulusTarget; use pgml_components::component; use pgml_components::Component; use sailfish::TemplateOnce; +use crate::types::CustomOption; #[derive(TemplateOnce, Default)] #[template(path = "inputs/select/template.html")] @@ -14,6 +16,8 @@ pub struct Select { menu_position: String, expandable: bool, name: String, + value_target: StimulusTarget, + action: CustomOption, } impl Select { @@ -34,13 +38,13 @@ impl Select { ]) } - pub fn options(mut self, values: Vec) -> Self { + pub fn options(mut self, values: Vec) -> Self { let mut options = Vec::new(); - self.value = values.first().unwrap().to_owned(); + self.value = values.first().unwrap().to_string(); for value in values { let item = Option::new( - value, + value.to_string(), StimulusAction::new() .controller("inputs-select") .method("choose") @@ -92,6 +96,16 @@ impl Select { self.expandable = true; self } + + pub fn value_target(mut self, value_target: StimulusTarget) -> Self { + self.value_target = value_target; + self + } + + pub fn action(mut self, action: StimulusAction) -> Self { + self.action = action.into(); + self + } } #[derive(TemplateOnce)] diff --git a/pgml-dashboard/src/components/inputs/select/select_controller.js b/pgml-dashboard/src/components/inputs/select/select_controller.js index 15a34269b..e7c712dba 100644 --- a/pgml-dashboard/src/components/inputs/select/select_controller.js +++ b/pgml-dashboard/src/components/inputs/select/select_controller.js @@ -6,6 +6,6 @@ export default class extends Controller { choose(e) { this.inputTarget.value = e.target.innerHTML this.valueTarget.innerHTML = e.target.innerHTML + this.inputTarget.dispatchEvent(new Event('change')) } - } diff --git a/pgml-dashboard/src/components/inputs/select/template.html b/pgml-dashboard/src/components/inputs/select/template.html index 39193e10e..b158717bc 100644 --- a/pgml-dashboard/src/components/inputs/select/template.html +++ b/pgml-dashboard/src/components/inputs/select/template.html @@ -29,5 +29,5 @@ <%+ dropdown %> - + data-action="<%- action %>" /> diff --git a/pgml-dashboard/src/lib.rs b/pgml-dashboard/src/lib.rs index d7fc4c620..e28af9206 100644 --- a/pgml-dashboard/src/lib.rs +++ b/pgml-dashboard/src/lib.rs @@ -13,6 +13,7 @@ use sailfish::TemplateOnce; use sqlx::PgPool; use std::collections::HashMap; +pub mod types; pub mod api; pub mod components; pub mod fairings; diff --git a/pgml-dashboard/src/types.rs b/pgml-dashboard/src/types.rs new file mode 100644 index 000000000..e523fd48e --- /dev/null +++ b/pgml-dashboard/src/types.rs @@ -0,0 +1,46 @@ +use sailfish::runtime::{Buffer, Render}; +use std::ops::{Deref, DerefMut}; + +// This is a custom wrapper around Option so we can implement render for it +pub struct CustomOption(Option); + +impl Default for CustomOption { + fn default() -> Self { + Self(None) + } +} + +impl CustomOption { + pub fn new(value: T) -> Self { + Self(Some(value)) + } +} + +impl From for CustomOption { + fn from(value: T) -> Self { + Self(Some(value)) + } +} + +impl Deref for CustomOption { + type Target = Option; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CustomOption { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Render for CustomOption { + fn render(&self, b: &mut Buffer) -> Result<(), sailfish::RenderError> { + match &self.0 { + Some(value) => value.render(b), + None => Ok(()), + } + } +} diff --git a/pgml-dashboard/static/css/scss/base/_typography.scss b/pgml-dashboard/static/css/scss/base/_typography.scss index e837ff600..460d5452a 100644 --- a/pgml-dashboard/static/css/scss/base/_typography.scss +++ b/pgml-dashboard/static/css/scss/base/_typography.scss @@ -1,7 +1,9 @@ - - // all other displays are default bootstrap styling -.display-2 {font-weight: 700; font-size: 4rem; line-height: 80px;} +.display-2 { + font-weight: 700; + font-size: 4rem; + line-height: 80px; +} // TODO: Go through html and ensure headers use appropriate header class, header and class do not need to match. // .h1 {font-weight: 700; font-size: 64px; line-height: 72px;} @@ -11,10 +13,48 @@ // .h5 {font-weight: 700; font-size: 28px; line-height: 34px;} // .h6 {font-weight: 700; font-size: 24px; line-height: 30px;} -.subcopy-text {font-size: 18px; line-height: 22px} -.body-text {font-size: 16px; line-height: 20px} -.legal-text {font-size: 12px; line-height: 16px;} +.subcopy-text { + font-size: 18px; + line-height: 22px; +} +.body-text { + font-size: 16px; + line-height: 20px; +} +.legal-text { + font-size: 12px; + line-height: 16px; +} + +.text-black { + color: #{$gray-900} !important; +} +.text-white { + color: #{$gray-100} !important; +} +.text-soft-white { + color: #{$gray-200} !important; +} -.text-black {color: #{$gray-900} !important;} -.text-white {color: #{$gray-100} !important;} -.text-soft-white { color: #{$gray-200} !important; } +@mixin text-gradient($gradient) { + background: #{$gradient}; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-fill-color: transparent; +} +.text-gradient-blue { + @include text-gradient($gradient-blue); +} +.text-gradient-green { + @include text-gradient($gradient-green); +} +.text-gradient-orange { + @include text-gradient($gradient-orange); +} +.text-gradient-pink { + @include text-gradient($gradient-pink); +} +.text-gradient-purple { + @include text-gradient($gradient-purple); +} diff --git a/pgml-dashboard/static/css/scss/components/_buttons.scss b/pgml-dashboard/static/css/scss/components/_buttons.scss index 04670a96c..f04148619 100644 --- a/pgml-dashboard/static/css/scss/components/_buttons.scss +++ b/pgml-dashboard/static/css/scss/components/_buttons.scss @@ -7,7 +7,8 @@ text-align: center; gap: 8px; - &[disabled], &:disabled { + &[disabled], + &:disabled { cursor: not-allowed; } } @@ -52,20 +53,20 @@ --bs-btn-font-size: 18px; // the attached canvas for border in secondary-btn.js - &> .secondary-btn-canvas { + & > .secondary-btn-canvas { position: absolute; border: None; display: None; } - + &:hover { - &> .secondary-btn-canvas { + & > .secondary-btn-canvas { display: block; } } &:active { - &> .secondary-btn-canvas { + & > .secondary-btn-canvas { filter: brightness(65%); display: block; } @@ -80,7 +81,7 @@ --bs-btn-hover-bg: transparent; --bs-btn-hover-color: #{$gray-100}; --bs-btn-hover-border-color: transparent; - + --bs-btn-active-bg: transparent; --bs-btn-active-color: #{$gray-100}; --bs-btn-active-border-color: transparent; @@ -115,18 +116,22 @@ border: 0px; - &:disabled, &.disabled { + &:disabled, + &.disabled { color: #{$neon-shade-400}; background-color: #{$neon-shade-700}; } - &:hover, &:active { + &:hover, + &:active { @include bold_by_shadow(var(--bs-btn-hover-color)); } &:active { @include bold_by_shadow(var(--bs-btn-active-color)); } - &:focus, &:focus-visible, &:focus-within { + &:focus, + &:focus-visible, + &:focus-within { @include bold_by_shadow(var(--bs-btn-focus-color)); } } @@ -153,7 +158,9 @@ &:active { @include bold_by_shadow(var(--bs-btn-active-color)); } - &:focus, &:focus-visible, &:focus-within { + &:focus, + &:focus-visible, + &:focus-within { @include bold_by_shadow(var(--bs-btn-focus-color)); } } @@ -182,14 +189,14 @@ &.with-icon { &::after { - content: ">" + content: ">"; } } } .btn-code-toolbar { - @extend .btn; - @extend .btn-tertiary; + @extend .btn; + @extend .btn-tertiary; @extend .noselect; @extend .z-1; @@ -202,14 +209,15 @@ text-shadow: none; } - &:disabled, &[disabled] { + &:disabled, + &[disabled] { color: #{$slate-shade-700} !important; } } .btn-copy { @extend .btn-code-toolbar; - position: absolute; + position: absolute; top: 4px; right: 4px; } @@ -221,7 +229,7 @@ border-bottom: 2px solid #{$gray-300}; color: #{$gray-100}; padding: 0 0 calc($spacer / 2) 0; - + &:hover { border-bottom-color: #{$neon-shade-100}; } @@ -231,7 +239,7 @@ border-bottom: #{$gray-500}; } - &> span { + & > span { color: #{$neon-shade-100}; } } @@ -258,7 +266,7 @@ border-left: 0px; border-right: 0px; border-bottom: 1px solid #{$gray-100} !important; - + --bs-btn-active-color: #{$gray-100}; --bs-btn-hover-color: #{$gray-100}; } @@ -266,7 +274,7 @@ .left-nav-toggle-icon { margin-left: calc($left-nav-w - 9rem); margin-right: 0px; - + &.expanding { animation-name: expand-margin; animation-duration: $animation-timer; @@ -279,7 +287,7 @@ } &.expanded { - margin-left: calc($left-nav-w - 9rem); + margin-left: calc($left-nav-w - 9rem); margin-right: 0px; } @@ -296,21 +304,21 @@ @keyframes collapse-margin { from { - margin-left: calc($left-nav-w - 9rem); + margin-left: calc($left-nav-w - 9rem); margin-right: 0px; } to { - margin-left: calc($left-nav-w-collapsed/2 - 32px); - margin-right: calc($left-nav-w-collapsed/2 - 32px); + margin-left: calc($left-nav-w-collapsed/2 - 32px); + margin-right: calc($left-nav-w-collapsed/2 - 32px); } } @keyframes expand-margin { from { - margin-left: calc($left-nav-w-collapsed/2 - 32px); + margin-left: calc($left-nav-w-collapsed/2 - 32px); margin-right: calc($left-nav-w-collapsed/2 - 32px); } to { - margin-left: calc($left-nav-w - 9rem); + margin-left: calc($left-nav-w - 9rem); margin-right: 0px; } }