aboutsummaryrefslogtreecommitdiffstats
path: root/src/renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer.rs')
-rw-r--r--src/renderer.rs668
1 files changed, 498 insertions, 170 deletions
diff --git a/src/renderer.rs b/src/renderer.rs
index c166bc6..bd7b272 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,51 +15,120 @@ pub fn page(
page_parent: Option<String>,
sort_method: Option<listing::SortingMethod>,
sort_order: Option<listing::SortingOrder>,
+ color_scheme: themes::ColorScheme,
file_upload: bool,
upload_route: &str,
current_dir: &str,
) -> Markup {
html! {
- (page_header(page_title))
- body id="dropContainer" {
- span #top { }
- h1 { (page_title) }
- @if file_upload {
- form id="file_submit" action={(upload_route) "?path=" (current_dir)} method="POST" enctype="multipart/form-data" {
- p { "Select file to upload or drag it into the window" }
- input type="file" name="file_to_upload" id="fileInput" {}
- input type="submit" value="Upload file" {}
+ (page_header(page_title, &color_scheme))
+ body#drop-container {
+ div.drag-form {
+ div.drag-title {
+ h1 { "Drop your file here to upload it" }
}
}
- 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))
+ }
+ }
+ @if file_upload {
+ div.upload {
+ form id="file_submit" action={(upload_route) "?path=" (current_dir)} method="POST" enctype="multipart/form-data" {
+ p { "Select a file to upload or drag it anywhere into the window" }
+ div {
+ input#file-input type="file" name="file_to_upload" {}
+ button type="submit" { "Upload file" }
+ }
+ }
+ }
}
- 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)"
}
}
}
@@ -76,12 +146,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);
@@ -104,31 +197,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=(&entry.link) {
(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())
}
@@ -136,8 +238,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) {
@@ -156,14 +259,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)
}
}
}
@@ -172,165 +274,374 @@ 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};
+ position: relative;
+ }}
+ .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;
- }
- .drag_hover {
- box-shadow: inset 0 25px 40px #aae;
- }
- @media (max-width: 600px) {
- h1 {
- font-size: 1.375em;
- }
- td:not(:nth-child(1)), th:not(:nth-child(1)){
+ }}
+ .upload {{
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 1rem;
+ }}
+ .upload p {{
+ font-size: 0.8rem;
+ margin-bottom: 1rem;
+ color: {upload_text_color};
+ }}
+ .upload form {{
+ padding: 1rem;
+ border: 1px solid {upload_form_border_color};
+ background: {upload_form_background};
+ }}
+ .upload button {{
+ background: {upload_button_background};
+ padding: 0.5rem;
+ border-radius: 0.2rem;
+ color: {upload_button_text_color};
+ border: none;
+ }}
+ .upload div {{
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ }}
+ .drag-form {{
+ display: none;
+ background: {drag_background};
+ position: absolute;
+ border: 0.5rem dashed {drag_border_color};
+ width: calc(100% - 1rem);
+ height: calc(100% - 1rem);
+ text-align: center;
+ z-index: 2;
+ }}
+ .drag-title {{
+ position: fixed;
+ color: {drag_text_color};
+ top: 50%;
+ width: 100%;
+ text-align: center;
+ }}
+ @media (max-width: 760px) {{
+ nav {{
+ padding: 0 2.5rem;
+ }}
+ .container {{
+ padding: 1.5rem 2.5rem;
+ }}
+ h1 {{
+ font-size: 1.4em;
+ }}
+ 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 {{
+ display: initial;
+ }}
+ .upload {{
+ margin-top: 2rem;
+ }}
+ .upload form {{
+ width: 100%;
+ }}
+ .back {{
+ right: 1.5rem;
+ }}
+ }}
+ @media (max-width: 600px) {{
+ h1 {{
font-size: 1.375em;
- }
- }"#.to_string()))
+ }}
+ }}
+ @media (max-width: 400px) {{
+ nav {{
+ padding: 0 0.5rem;
+ }}
+ .container {{
+ padding: 0.5rem;
+ }}
+ h1 {{
+ font-size: 1.375em;
+ }}
+ .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,
+ upload_text_color = theme.upload_text_color,
+ upload_form_border_color = theme.upload_form_border_color,
+ upload_form_background = theme.upload_form_background,
+ upload_button_background = theme.upload_button_background,
+ upload_button_text_color = theme.upload_button_text_color,
+ drag_background = theme.drag_background,
+ drag_border_color = theme.drag_border_color,
+ drag_text_color = theme.drag_text_color);
+ (PreEscaped(css))
}
/// Partial: up arrow
@@ -359,7 +670,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 {
@@ -367,24 +678,41 @@ 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)) }
(PreEscaped(r#"
<script>
window.onload = function() {
- dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
- dropContainer.className = "drag_hover";
- evt.preventDefault();
+ const dropContainer = document.querySelector('#drop-container');
+ const dragForm = document.querySelector('.drag-form');
+ const fileInput = document.querySelector('#file-input');
+ const collection = [];
+
+ dropContainer.ondragover = function(e) {
+ e.preventDefault();
+ }
+
+ dropContainer.ondragenter = function(e) {
+ e.preventDefault();
+ if (collection.length === 0) {
+ dragForm.style.display = 'initial';
+ }
+ collection.push(e.target);
};
- dropContainer.ondrop = function(evt) {
- fileInput.files = evt.dataTransfer.files;
- evt.preventDefault();
- file_submit.submit();
+ dropContainer.ondragleave = function(e) {
+ e.preventDefault();
+ collection.splice(collection.indexOf(e.target), 1);
+ if (collection.length === 0) {
+ dragForm.style.display = 'none';
+ }
};
- dropContainer.ondragleave = function() {
- dropContainer.className = "";
- }
+ dropContainer.ondrop = function(e) {
+ e.preventDefault();
+ fileInput.files = e.dataTransfer.files;
+ file_submit.submit();
+ dragForm.style.display = 'none';
+ };
}
</script>
"#))
@@ -398,7 +726,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(),
)
})