diff options
author | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-04-01 18:36:35 +0000 |
---|---|---|
committer | boasting-squirrel <boasting.squirrel@gmail.com> | 2019-04-01 18:36:35 +0000 |
commit | 8ed18a551696ba4f89d7dfbdeaa879b21e079d33 (patch) | |
tree | 11af08394bb2a07b02f1238607f2ce7eda3e026f /src/renderer.rs | |
parent | Try rustup update to fix random failure (diff) | |
download | miniserve-8ed18a551696ba4f89d7dfbdeaa879b21e079d33.tar.gz miniserve-8ed18a551696ba4f89d7dfbdeaa879b21e079d33.zip |
Color schemes
Diffstat (limited to 'src/renderer.rs')
-rw-r--r-- | src/renderer.rs | 541 |
1 files changed, 391 insertions, 150 deletions
diff --git a/src/renderer.rs b/src/renderer.rs index 66fc714..56935d8 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -5,6 +5,7 @@ use std::time::SystemTime; use crate::archive; use crate::listing; +use crate::themes; /// Renders the file listing pub fn page( @@ -14,41 +15,101 @@ pub fn page( page_parent: Option<String>, sort_method: Option<listing::SortingMethod>, sort_order: Option<listing::SortingOrder>, + color_scheme: themes::ColorScheme, ) -> Markup { html! { - (page_header(page_title)) + (page_header(page_title, &color_scheme)) body { - span #top { } - h1.title { (page_title) } - div.download { - (archive_button(archive::CompressionMethod::TarGz)) - } - table { - thead { - th { (build_link("name", "Name", &sort_method, &sort_order)) } - th { (build_link("size", "Size", &sort_method, &sort_order)) } - th { (build_link("date", "Last modification", &sort_method, &sort_order)) } + (color_scheme_selector(&sort_method, &sort_order, &color_scheme)) + div.container { + span #top { } + h1.title { (page_title) } + div.download { + @for compression_method in archive::CompressionMethod::get_compression_methods() { + (archive_button(compression_method)) + } } - tbody { - @if !is_root { - @if let Some(parent) = page_parent { - tr { - td colspan="3" { - span.chevron { (chevron_left()) } - a.root href=(parent) { - "Parent directory" + table { + thead { + th { (build_link("name", "Name", &sort_method, &sort_order, &color_scheme)) } + th { (build_link("size", "Size", &sort_method, &sort_order, &color_scheme)) } + th { (build_link("date", "Last modification", &sort_method, &sort_order, &color_scheme)) } + } + tbody { + @if !is_root { + @if let Some(parent) = page_parent { + tr { + td colspan="3" { + span.root-chevron { (chevron_left()) } + a.root href=(parametrized_link(&parent, &sort_method, &sort_order, &color_scheme)) { + "Parent directory" + } } } } } + @for entry in entries { + (entry_row(entry, &sort_method, &sort_order, &color_scheme)) + } + } + } + a.back href="#top" { + (arrow_up()) + } + } + } + } +} + +/// Partial: color scheme selector +fn color_scheme_selector( + sort_method: &Option<listing::SortingMethod>, + sort_order: &Option<listing::SortingOrder>, + active_color_scheme: &themes::ColorScheme, +) -> Markup { + html! { + nav { + ul { + li { + a.change-theme href="#" title="Change theme" { + "Change theme..." } - @for entry in entries { - (entry_row(entry)) + ul { + @for color_scheme in themes::ColorScheme::get_color_schemes() { + @if active_color_scheme.get_name() == color_scheme.get_name() { + li.active { + (color_scheme_link(&sort_method, &sort_order, &color_scheme)) + } + } @else { + li { + (color_scheme_link(&sort_method, &sort_order, &color_scheme)) + } + } + } } } } - a.back href="#top" { - (arrow_up()) + } + } +} + +/// Partial: color scheme link +fn color_scheme_link( + sort_method: &Option<listing::SortingMethod>, + sort_order: &Option<listing::SortingOrder>, + color_scheme: &themes::ColorScheme, +) -> Markup { + let link = parametrized_link("", &sort_method, &sort_order, &color_scheme); + let title = format!("Switch to {} theme", color_scheme.get_name()); + + html! { + a href=(link) title=(title) { + (color_scheme.get_name()) + " " + @if color_scheme.is_dark() { + "(dark)" + } @ else { + "(light)" } } } @@ -66,12 +127,35 @@ fn archive_button(compress_method: archive::CompressionMethod) -> Markup { } } +/// If they are set, adds query parameters to links to keep them across pages +fn parametrized_link( + link: &str, + sort_method: &Option<listing::SortingMethod>, + sort_order: &Option<listing::SortingOrder>, + color_scheme: &themes::ColorScheme, +) -> String { + if let Some(method) = sort_method { + if let Some(order) = sort_order { + return format!( + "{}?sort={}&order={}&theme={}", + link, + method.to_string(), + order.to_string(), + color_scheme.to_string() + ); + } + } + + format!("{}?theme={}", link.to_string(), color_scheme.to_string()) +} + /// Partial: table header link fn build_link( name: &str, title: &str, sort_method: &Option<listing::SortingMethod>, sort_order: &Option<listing::SortingOrder>, + color_scheme: &themes::ColorScheme, ) -> Markup { let mut link = format!("?sort={}&order=asc", name); let mut help = format!("Sort by {} in ascending order", name); @@ -94,31 +178,40 @@ fn build_link( html! { span class=(class) { span.chevron { (chevron) } - a href=(link) title=(help) { (title) } + a href=(format!("{}&theme={}", &link, color_scheme.to_string())) title=(help) { (title) } } } } /// Partial: row for an entry -fn entry_row(entry: listing::Entry) -> Markup { +fn entry_row( + entry: listing::Entry, + sort_method: &Option<listing::SortingMethod>, + sort_order: &Option<listing::SortingOrder>, + color_scheme: &themes::ColorScheme, +) -> Markup { html! { tr { td { p { @if entry.is_dir() { - a.directory href=(entry.link) { + a.directory href=(parametrized_link(&entry.link, &sort_method, &sort_order, &color_scheme)) { (entry.name) "/" } - } @else { - a.file href=(entry.link) { + } @else if entry.is_file() { + a.file href=(parametrized_link(&entry.link, &sort_method, &sort_order, &color_scheme)) { (entry.name) } + } @ else if entry.is_symlink() { + a.symlink href=(parametrized_link(&entry.link, &sort_method, &sort_order, &color_scheme)) { + (entry.name) span.symlink-symbol { "⇢" } + } } } @if !entry.is_dir() { @if let Some(size) = entry.size { span .mobile-info { - strong { "Size: " } + strong.field { "Size: " } (size) (br()) } @@ -126,8 +219,9 @@ fn entry_row(entry: listing::Entry) -> Markup { } span .mobile-info { @if let Some(modification_date) = convert_to_utc(entry.last_modification_date) { - strong { "Last modification: " } + strong.field { "Last modification: " } (modification_date.0) " " + span.at { " at " } (modification_date.1) " " } @if let Some(modification_timer) = humanize_systemtime(entry.last_modification_date) { @@ -146,14 +240,13 @@ fn entry_row(entry: listing::Entry) -> Markup { @if let Some(modification_date) = convert_to_utc(entry.last_modification_date) { span { (modification_date.0) " " - } - span { + span.at { " at " } (modification_date.1) " " } } @if let Some(modification_timer) = humanize_systemtime(entry.last_modification_date) { - span { - "(" (modification_timer) ")" + span.history { + (modification_timer) } } } @@ -162,162 +255,310 @@ fn entry_row(entry: listing::Entry) -> Markup { } /// Partial: CSS -fn css() -> Markup { - (PreEscaped(r#" - html { +fn css(color_scheme: &themes::ColorScheme) -> Markup { + let theme = color_scheme.clone().get_theme(); + + let css = format!(" + html {{ font-smoothing: antialiased; text-rendering: optimizeLegibility; - } - body { + }} + body {{ margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,"Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\"Helvetica Neue\", Helvetica, Arial, sans-serif; font-weight: 300; - color: #444444; - padding: 0.125rem; - } - strong { + color: {text_color}; + background: {background}; + }} + .container {{ + padding: 1.5rem 5rem; + }} + a {{ + text-decoration: none; + }} + a.root, a.root:visited, .root-chevron {{ font-weight: bold; - } - p { + color: {root_link_color}; + }} + a:hover {{ + text-decoration: underline; + }} + a.directory, a.directory:visited {{ + font-weight: bold; + color: {directory_link_color}; + }} + a.file, a.file:visited {{ + color: {file_link_color}; + }} + a.symlink, a.symlink:visited {{ + color: {symlink_link_color}; + }} + a.directory:hover {{ + color: {directory_link_color}; + }} + a.file:hover {{ + color: {file_link_color}; + }} + a.symlink:hover {{ + color: {symlink_link_color}; + }} + .symlink-symbol {{ + display: inline-block; + border: 1px solid {symlink_link_color}; + margin-left: 0.5rem; + border-radius: .2rem; + padding: 0 0.1rem; + }} + nav {{ + padding: 0 5rem; + }} + nav ul {{ + text-align: right; + list-style: none; margin: 0; padding: 0; - } - h1 { + }} + nav ul li {{ + display: block; + transition-duration: 0.5s; + float: right; + position: relative; + padding: 0.5rem 1rem; + background: {switch_theme_background}; + width: 8rem; + text-align: center; + }} + nav ul li:hover {{ + cursor: pointer; + text-decoration: none; + color: {change_theme_link_color} + }} + nav ul li a:hover {{ + text-decoration: none; + color: {change_theme_link_color_hover}; + }} + nav ul li ul {{ + visibility: hidden; + opacity: 0; + position: absolute; + transition: all 0.5s ease; + margin-top: 0.5rem; + left: 0; + display: none; + text-align: center; + }} + nav ul li:hover > ul, + nav ul li ul:hover {{ + visibility: visible; + opacity: 1; + display: block; + }} + nav ul li ul li:first-of-type {{ + border-top: 1px solid {switch_theme_border}; + }} + nav ul li ul li {{ + clear: both; + width: 8rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + }} + nav ul li ul li a:hover {{ + text-decoration: underline; + }} + nav ul li a, nav ul li ul li a, nav ul li a:visited, nav ul li ul li a:visited {{ + color: {switch_theme_link_color} + }} + nav ul li ul li.active a {{ + font-weight: bold; + color: {switch_theme_active}; + }} + strong {{ + font-weight: bold; + }} + .field {{ + color: {field_color} + }} + p {{ + margin: 0; + padding: 0; + }} + h1 {{ + margin-top: 0; font-size: 1.5rem; - } - table { + }} + table {{ margin-top: 2rem; width: 100%; - background: white; border: 0; table-layout: auto; - } - table thead { - background: #efefef; - } - table tr th, - table tr td { + background: {table_background}; + }} + table thead tr th, + table tbody tr td {{ padding: 0.5625rem 0.625rem; font-size: 0.875rem; - color: #777c82; + color: {table_text_color}; text-align: left; line-height: 1.125rem; width: 33.333%; - } - table tr th { + }} + table thead tr th {{ padding: 0.5rem 0.625rem 0.625rem; font-weight: bold; - color: #444444; - } - table tr:nth-child(even) { - background: #f6f6f6; - } - table tr:hover { - background: #deeef7a6; - } - a { - text-decoration: none; - color: #3498db; - } - a.root, a.root:visited { - font-weight: bold; - color: #777c82; - } - a.directory { - font-weight: bold; - } - a:hover { - text-decoration: underline; - } - a:visited { - color: #8e44ad; - } - td.date-cell { + }} + table tbody tr:nth-child(odd) {{ + background: {odd_row_background}; + }} + table tbody tr:nth-child(even) {{ + background: {even_row_background}; + }} + table thead {{ + background: {table_header_background}; + }} + table tbody tr:hover {{ + background: {active_row_color}; + }} + td.date-cell {{ display: flex; width: calc(100% - 1.25rem); - } - td.date-cell span:first-of-type, - td.date-cell span:nth-of-type(2) { - flex-basis:4.5rem; - } - td.date-cell span:nth-of-type(3), .history { - color: #c5c5c5; - } - .file, .directory { + justify-content: space-between; + }} + .at {{ + color: {at_color}; + }} + .history {{ + color: {date_text_color}; + }} + .file, .directory, .symlink {{ display: block; - } - .mobile-info { + }} + .mobile-info {{ display: none; - } - th a, th a:visited, .chevron { - color: #777c82; - } - .chevron { + }} + th a, th a:visited, .chevron {{ + color: {table_header_text_color}; + }} + .chevron, .root-chevron {{ margin-right: .5rem; font-size: 1.2em; font-weight: bold; - } - th span.active a, th span.active span { - color: #444444; - } - .back { + }} + th span.active a, th span.active span {{ + color: {table_header_active_color}; + }} + .back {{ position: fixed; - bottom: 1.1rem; - right: 0.625rem; - background: #e0e0e0; + bottom: 3rem; + right: 3.75rem; + background: {back_button_background}; border-radius: 100%; box-shadow: 0 0 8px -4px #888888; - opacity: 0.8; - padding: 1rem 1.1rem; - color: #444444; - } - .back:visited { - color: #444444; - } - .back:hover { - color: #3498db; + padding: 1.4rem 1.5rem; + color: {back_button_link_color}; + display: none; + }} + .back:visited {{ + color: {back_button_link_color}; + }} + .back:hover {{ + color: {back_button_link_color_hover}; + font-weight: bold; text-decoration: none; - } - .download { + background: {back_button_background_hover}; + }} + .download {{ display: flex; flex-wrap: wrap; margin-top: .5rem; padding: 0.125rem; - } - .download a, .download a:visited { - color: #3498db; - } - .download a { - background: #efefef; + }} + .download a, .download a:visited {{ + color: {download_button_link_color}; + }} + .download a {{ + background: {download_button_background}; padding: 0.5rem; border-radius: 0.2rem; margin-top: 1rem; - } - .download a:hover { - background: #deeef7a6; - } - .download a:not(:last-of-type) { + }} + .download a:hover {{ + background: {download_button_background_hover}; + color: {download_button_link_color_hover}; + }} + .download a:not(:last-of-type) {{ margin-right: 1rem; - } - @media (max-width: 600px) { - h1 { + }} + @media (max-width: 760px) {{ + nav {{ + padding: 0 2.5rem; + }} + .container {{ + padding: 1.5rem 2.5rem; + }} + h1 {{ font-size: 1.375em; - } - td:not(:nth-child(1)), th:not(:nth-child(1)){ + }} + td:not(:nth-child(1)), th:not(:nth-child(1)){{ display: none; - } - .mobile-info { + }} + .mobile-info {{ display: block; - } - .file, .directory{ + }} + .file, .directory, .symlink{{ padding-bottom: 1rem; - } - } - @media (max-width: 400px) { - h1 { + }} + .back {{ + right: 1.5rem; + }} + .back {{ + display: initial; + }} + }} + @media (max-width: 400px) {{ + nav {{ + padding: 0 0.5rem; + }} + .container {{ + padding: 0.5rem; + }} + h1 {{ font-size: 1.375em; - } - }"#.to_string())) + }} + .back {{ + right: 1.5rem; + }} + }}", background = theme.background, + text_color = theme.text_color, + directory_link_color = theme.directory_link_color, + file_link_color = theme.file_link_color, + symlink_link_color = theme.symlink_link_color, + table_background = theme.table_background, + table_text_color = theme.table_text_color, + table_header_background = theme.table_header_background, + table_header_text_color = theme.table_header_text_color, + table_header_active_color = theme.table_header_active_color, + active_row_color = theme.active_row_color, + odd_row_background = theme.odd_row_background, + even_row_background = theme.even_row_background, + root_link_color = theme.root_link_color, + download_button_background = theme.download_button_background, + download_button_background_hover = theme.download_button_background_hover, + download_button_link_color = theme.download_button_link_color, + download_button_link_color_hover = theme.download_button_link_color_hover, + back_button_background = theme.back_button_background, + back_button_background_hover = theme.back_button_background_hover, + back_button_link_color = theme.back_button_link_color, + back_button_link_color_hover = theme.back_button_link_color_hover, + date_text_color = theme.date_text_color, + at_color = theme.at_color, + switch_theme_background = theme.switch_theme_background, + switch_theme_link_color = theme.switch_theme_link_color, + switch_theme_active = theme.switch_theme_active, + switch_theme_border = theme.switch_theme_border, + change_theme_link_color = theme.change_theme_link_color, + change_theme_link_color_hover = theme.change_theme_link_color_hover, + field_color = theme.field_color); + (PreEscaped(css)) } /// Partial: up arrow @@ -346,7 +587,7 @@ fn chevron_down() -> Markup { } /// Partial: page header -fn page_header(page_title: &str) -> Markup { +fn page_header(page_title: &str, color_scheme: &themes::ColorScheme) -> Markup { html! { (DOCTYPE) html { @@ -354,7 +595,7 @@ fn page_header(page_title: &str) -> Markup { meta http-equiv="X-UA-Compatible" content="IE=edge"; meta name="viewport" content="width=device-width, initial-scale=1"; title { (page_title) } - style { (css()) } + style { (css(&color_scheme)) } } } } @@ -365,7 +606,7 @@ fn page_header(page_title: &str) -> Markup { fn convert_to_utc(src_time: Option<SystemTime>) -> Option<(String, String)> { src_time.map(DateTime::<Utc>::from).map(|date_time| { ( - date_time.format("%e %b").to_string(), + date_time.format("%b %e").to_string(), date_time.format("%R").to_string(), ) }) |