From 07b4cb31ce54e8cc8648e3ca9878c3c999ca89ee Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 6 Oct 2023 20:36:27 -0400 Subject: [PATCH] Add reasonable desktop styling for the gallery --- file-service/src/html.rs | 84 ++++++++++++++++++++++++-------- file-service/src/pages.rs | 49 +++++++++++-------- file-service/templates/style.css | 39 ++++++++++----- 3 files changed, 118 insertions(+), 54 deletions(-) diff --git a/file-service/src/html.rs b/file-service/src/html.rs index c545e6a..9d293f4 100644 --- a/file-service/src/html.rs +++ b/file-service/src/html.rs @@ -1,8 +1,40 @@ use build_html::{self, Html, HtmlContainer}; +#[derive(Clone, Debug, Default)] +pub struct Attributes(Vec<(String, String)>); + +/* +impl FromIterator<(String, String)> for Attributes { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + Attributes(iter.collect::>()) + } +} + +impl FromIterator<(&str, &str)> for Attributes { + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + unimplemented!() + } +} +*/ + +impl ToString for Attributes { + fn to_string(&self) -> String { + self.0 + .iter() + .map(|(key, value)| format!("{}=\"{}\"", key, value)) + .collect::>() + .join(" ") + } +} + #[derive(Clone, Debug)] pub struct Form { - classes: Option, path: String, method: String, encoding: Option, @@ -12,7 +44,6 @@ pub struct Form { impl Form { pub fn new() -> Self { Self { - classes: None, path: "/".to_owned(), method: "get".to_owned(), encoding: None, @@ -38,21 +69,16 @@ impl Form { impl Html for Form { fn to_html_string(&self) -> String { - let classes = match self.classes { - Some(ref classes) => format!("class=\"{}\"", classes), - None => "".to_owned(), - }; let encoding = match self.encoding { Some(ref encoding) => format!("enctype=\"{encoding}\"", encoding = encoding), None => "".to_owned(), }; format!( - "
\n{elements}\n
\n", + "
\n", path = self.path, method = self.method, encoding = encoding, elements = self.elements.to_html_string(), - classes = classes, ) } } @@ -69,7 +95,7 @@ pub struct Input { name: String, id: Option, value: Option, - attributes: Vec<(String, String)>, + attributes: Attributes, } impl Html for Input { @@ -82,12 +108,7 @@ impl Html for Input { Some(ref value) => format!("value=\"{}\"", value), None => "".to_owned(), }; - let attrs = self - .attributes - .iter() - .map(|(key, value)| format!("{}=\"{}\"", key, value)) - .collect::>() - .join(" "); + let attrs = self.attributes.to_string(); format!( "\n", @@ -107,7 +128,7 @@ impl Input { name: name.to_owned(), id: None, value: None, - attributes: vec![], + attributes: Attributes::default(), } } @@ -125,10 +146,12 @@ impl Input { mut self, values: impl IntoIterator, ) -> Self { - self.attributes = values - .into_iter() - .map(|(a, b)| (a.to_owned(), b.to_owned())) - .collect::>(); + self.attributes = Attributes( + values + .into_iter() + .map(|(a, b)| (a.to_owned(), b.to_owned())) + .collect::>(), + ); self } } @@ -201,18 +224,37 @@ impl Html for Button { #[derive(Clone, Debug)] pub struct Image { path: String, + attributes: Attributes, } impl Image { pub fn new(path: &str) -> Self { Self { path: path.to_owned(), + attributes: Attributes::default(), } } + + pub fn with_attributes<'a>( + mut self, + values: impl IntoIterator, + ) -> Self { + self.attributes = Attributes( + values + .into_iter() + .map(|(a, b)| (a.to_owned(), b.to_owned())) + .collect::>(), + ); + self + } } impl Html for Image { fn to_html_string(&self) -> String { - format!("", path = self.path,) + format!( + "", + path = self.path, + attrs = self.attributes.to_string() + ) } } diff --git a/file-service/src/pages.rs b/file-service/src/pages.rs index a5cceb5..45a6fc5 100644 --- a/file-service/src/pages.rs +++ b/file-service/src/pages.rs @@ -37,22 +37,27 @@ pub fn auth(_message: Option) -> build_html::HtmlPage { pub fn gallery(handles: Vec>) -> build_html::HtmlPage { let mut page = build_html::HtmlPage::new() - .with_title("Admin list of files") + .with_title("Gallery") .with_stylesheet("/css") - .with_header(1, "Admin list of files") - .with_html( - Form::new() - .with_path("/upload") - .with_method("post") - .with_encoding("multipart/form-data") - .with_container( - Container::new(ContainerType::Div) - .with_html(Input::new("file", "file").with_id("for-selector-input")) - .with_html(Label::new("for-selector-input", "Select a file")), - ) - .with_html(Button::new("Upload file").with_type("submit")), + .with_container( + Container::new(ContainerType::Div) + .with_attributes([("class", "gallery-page")]) + .with_header(1, "Gallery") + .with_html( + Form::new() + .with_path("/upload") + .with_method("post") + .with_encoding("multipart/form-data") + .with_container( + Container::new(ContainerType::Div) + .with_html(Input::new("file", "file").with_id("for-selector-input")) + .with_html(Label::new("for-selector-input", "Select a file")), + ) + .with_html(Button::new("Upload file").with_type("submit")), + ), ); + let mut gallery = Container::new(ContainerType::Div).with_attributes([("class", "gallery")]); for handle in handles { let container = match handle { Ok(ref handle) => thumbnail(&handle.id).with_html( @@ -67,19 +72,21 @@ pub fn gallery(handles: Vec>) -> build_html::H .with_attributes(vec![("class", "file")]) .with_paragraph(format!("{:?}", err)), }; - page.add_container(container) + gallery.add_container(container); } + page.add_container(gallery); page } pub fn thumbnail(id: &FileId) -> Container { - let mut container = Container::new(ContainerType::Div).with_attributes(vec![("class", "file")]); - let tn = Container::new(ContainerType::Div) - .with_attributes(vec![("class", "thumbnail")]) - .with_link( - format!("/{}", **id), - Image::new(&format!("{}/tn", **id)).to_html_string(), - ); + let mut container = + Container::new(ContainerType::Div).with_attributes(vec![("class", "thumbnail")]); + let tn = Container::new(ContainerType::Div).with_link( + format!("/{}", **id), + Image::new(&format!("{}/tn", **id)) + .with_attributes([("class", "thumbnail__image")]) + .to_html_string(), + ); container.add_html(tn); container } diff --git a/file-service/templates/style.css b/file-service/templates/style.css index 5e7265b..a7345c5 100644 --- a/file-service/templates/style.css +++ b/file-service/templates/style.css @@ -2,9 +2,9 @@ --main-bg-color: #e5f0fc; --fg-color: #449dfc; - --px-4: 4px; - --px-8: 8px; - --px-12: 12px; + --space-small: 4px; + --space-medium: 8px; + --space-large: 12px; --hover-low: 4px 4px 4px gray; } @@ -16,6 +16,7 @@ body { .authentication-page { display: flex; + flex-direction: column; justify-content: center; align-items: center; height: 200px; @@ -29,35 +30,46 @@ body { } .authentication-form__label { - margin: var(--px-4); + margin: var(--space-small); } .authentication-form__input { - margin: var(--px-4); + margin: var(--space-small); } -.files { +.gallery-page { display: flex; flex-wrap: wrap; } -.file { +.gallery { display: flex; - margin: 1em; - border: 1px solid #449dfc; - border-radius: 5px; - padding: 1em; } +.thumbnail { + border: 1px solid black; + box-shadow: var(--hover-low); + max-width: 300px; + margin: var(--space-large); + padding: var(--space-medium); + display: flex; + flex-direction: column; + justify-content: space-between; +} + +/* .thumbnail { max-width: 320px; margin: 1em; } +*/ -img { +.thumbnail__image { max-width: 100%; + border: none; } +/* [type="submit"] { border-radius: 1em; margin: 1em; @@ -69,8 +81,10 @@ img { margin: 1em; padding: 1em; } +*/ /* https://benmarshall.me/styling-file-inputs/ */ + /* [type="file"] { border: 0; clip: rect(0, 0, 0, 0); @@ -102,6 +116,7 @@ img { outline: 1px dotted #000; outline: -webkit-focus-ring-color auto 5px; } +*/ /* @media screen and (max-width: 980px) { /* This is the screen width of a OnePlus 5t