Add filters UI HTML generator

This commit is contained in:
Samantaz Fox 2022-03-09 22:21:53 +01:00
parent 6991d0851f
commit 1e3425fdee
No known key found for this signature in database
GPG key ID: F42821059186176E
3 changed files with 264 additions and 31 deletions

91
assets/css/search.css Normal file
View file

@ -0,0 +1,91 @@
summary {
/* This should hide the marker */
display: block;
font-size: 1.17em;
font-weight: bold;
margin: 0 auto 10px auto;
}
summary::-webkit-details-marker,
summary::marker { display: none; }
summary:before {
border-radius: 5px;
content: "[ + ]";
margin: -2px 10px 0 10px;
padding: 1px 0 3px 0;
text-align: center;
width: 40px;
}
details[open] > summary:before { content: "[ ]"; }
#filters-box {
background: #373737;
padding: 10px 20px 20px 10px;
margin: 10px 15px;
}
#filters-flex {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: flex-start;
align-content: flex-start;
justify-content: flex-start;
}
fieldset, legend {
display: contents !important;
border: none !important;
margin: 0 !important;
padding: 0 !important;
}
.filter-column {
display: inline-block;
display: inline-flex;
width: max-content;
min-width: max-content;
max-width: 16em;
margin: 15px;
flex-grow: 2;
flex-basis: auto;
flex-direction: column;
}
.filter-name, .filter-options {
display: block;
padding: 5px 10px;
margin: 0;
text-align: start;
}
/* TODO: move that to the main file */
.underlined {
border-bottom: 1px solid;
margin-bottom: 20px;
}
.filter-options div { margin: 6px 0; }
.filter-options div * { vertical-align: middle; }
.filter-options label { margin: 0 10px; }
#filters-apply { text-align: end; }
@media only screen and (max-width: 800px) {
summary { font-size: 1.30em; }
#filters-box {
margin: 10px 0 0 0;
padding: 0;
}
#filters-apply {
text-align: center;
padding: 15px;
}
}

View file

@ -404,37 +404,44 @@
"Videos": "Videos", "Videos": "Videos",
"Playlists": "Playlists", "Playlists": "Playlists",
"Community": "Community", "Community": "Community",
"relevance": "Relevance", "search_filters_title": "Filters",
"rating": "Rating", "search_filters_date_label": "Upload date",
"date": "Upload date", "search_filters_date_option_none": "Any date",
"views": "View count", "search_filters_date_option_hour": "Last Hour",
"content_type": "Type", "search_filters_date_option_today": "Today",
"duration": "Duration", "search_filters_date_option_week": "This week",
"features": "Features", "search_filters_date_option_month": "This month",
"sort": "Sort By", "search_filters_date_option_year": "This year",
"hour": "Last Hour", "search_filters_type_label": "Type",
"today": "Today", "search_filters_type_option_all": "Any type",
"week": "This week", "search_filters_type_option_video": "Video",
"month": "This month", "search_filters_type_option_channel": "Channel",
"year": "This year", "search_filters_type_option_playlist": "Playlist",
"video": "Video", "search_filters_type_option_movie": "Movie",
"channel": "Channel", "search_filters_type_option_show": "Show",
"playlist": "Playlist", "search_filters_duration_label": "Duration",
"movie": "Movie", "search_filters_duration_option_none": "Any duration",
"show": "Show", "search_filters_duration_option_short": "Short (< 4 minutes)",
"short": "Short (< 4 minutes)", "search_filters_duration_option_medium": "Medium (4 - 20 minutes)",
"long": "Long (> 20 minutes)", "search_filters_duration_option_long": "Long (> 20 minutes)",
"hd": "HD", "search_filters_features_label": "Features",
"subtitles": "Subtitles/CC", "search_filters_features_option_live": "Live",
"creative_commons": "Creative Commons", "search_filters_features_option_four_k": "4K",
"3d": "3D", "search_filters_features_option_hd": "HD",
"live": "Live", "search_filters_features_option_subtitles": "Subtitles/CC",
"4k": "4K", "search_filters_features_option_c_commons": "Creative Commons",
"location": "Location", "search_filters_features_option_three_sixty": "360°",
"hdr": "HDR", "search_filters_features_option_vr180": "VR180",
"purchased": "Purchased", "search_filters_features_option_three_d": "3D",
"360": "360°", "search_filters_features_option_hdr": "HDR",
"filter": "Filter", "search_filters_features_option_location": "Location",
"search_filters_features_option_purchased": "Purchased",
"search_filters_sort_label": "Sort By",
"search_filters_sort_option_relevance": "Relevance",
"search_filters_sort_option_rating": "Rating",
"search_filters_sort_option_date": "Upload Date",
"search_filters_sort_option_views": "View count",
"search_filters_apply_button": "Apply selected filters",
"Current version: ": "Current version: ", "Current version: ": "Current version: ",
"next_steps_error_message": "After which you should try to: ", "next_steps_error_message": "After which you should try to: ",
"next_steps_error_message_refresh": "Refresh", "next_steps_error_message_refresh": "Refresh",

View file

@ -0,0 +1,135 @@
module Invidious::Frontend::SearchFilters
extend self
# Generate the search filters collapsable widget.
def generate(filters : Search::Filters, query : String, page : Int, locale : String) : String
return String.build(8000) do |str|
str << "<div id='filters'>\n"
str << "\t<details id='filters-collapse'>"
str << "\t\t<summary>" << translate(locale, "search_filters_title") << "</summary>\n"
str << "\t\t<div id='filters-box'><form action='/search' method='get'>\n"
str << "\t\t\t<input type='hidden' name='q' value='" << HTML.escape(query) << "'>\n"
str << "\t\t\t<input type='hidden' name='page' value='" << page << "'>\n"
str << "\t\t\t<div id='filters-flex'>"
filter_wrapper(date)
filter_wrapper(type)
filter_wrapper(duration)
filter_wrapper(features)
filter_wrapper(sort)
str << "\t\t\t</div>\n"
str << "\t\t\t<div id='filters-apply'>"
str << "<button type='submit' class=\"pure-button pure-button-primary\">"
str << translate(locale, "search_filters_apply_button")
str << "</button></div>\n"
str << "\t\t</form></div>\n"
str << "\t</details>\n"
str << "</div>\n"
end
end
# Generate wrapper HTML (`<div>`, filter name, etc...) around the
# `<input>` elements of a search filter
macro filter_wrapper(name)
str << "\t\t\t\t<div class=\"filter-column\"><fieldset>\n"
str << "\t\t\t\t\t<legend><div class=\"filter-name underlined\">"
str << translate(locale, "search_filters_{{name}}_label")
str << "</div></legend>\n"
str << "\t\t\t\t\t<div class=\"filter-options\">\n"
make_{{name}}_filter_options(str, filters.{{name}}, locale)
str << "\t\t\t\t\t</div>"
str << "\t\t\t\t</fieldset></div>\n"
end
# Generates the HTML for the list of radio buttons of the "date" search filter
def make_date_filter_options(str : String::Builder, value : Search::Filters::Date, locale : String)
{% for value in Invidious::Search::Filters::Date.constants %}
{% date = value.underscore %}
str << "\t\t\t\t\t\t<div>"
str << "<input type='radio' name='date' id='filter-date-{{date}}' value='{{date}}'"
str << " checked" if value.{{date}}?
str << '>'
str << "<label for='filter-date-{{date}}'>"
str << translate(locale, "search_filters_date_option_{{date}}")
str << "</label></div>\n"
{% end %}
end
# Generates the HTML for the list of radio buttons of the "type" search filter
def make_type_filter_options(str : String::Builder, value : Search::Filters::Type, locale : String)
{% for value in Invidious::Search::Filters::Type.constants %}
{% type = value.underscore %}
str << "\t\t\t\t\t\t<div>"
str << "<input type='radio' name='type' id='filter-type-{{type}}' value='{{type}}'"
str << " checked" if value.{{type}}?
str << '>'
str << "<label for='filter-type-{{type}}'>"
str << translate(locale, "search_filters_type_option_{{type}}")
str << "</label></div>\n"
{% end %}
end
# Generates the HTML for the list of radio buttons of the "duration" search filter
def make_duration_filter_options(str : String::Builder, value : Search::Filters::Duration, locale : String)
{% for value in Invidious::Search::Filters::Duration.constants %}
{% duration = value.underscore %}
str << "\t\t\t\t\t\t<div>"
str << "<input type='radio' name='duration' id='filter-duration-{{duration}}' value='{{duration}}'"
str << " checked" if value.{{duration}}?
str << '>'
str << "<label for='filter-duration-{{duration}}'>"
str << translate(locale, "search_filters_duration_option_{{duration}}")
str << "</label></div>\n"
{% end %}
end
# Generates the HTML for the list of checkboxes of the "features" search filter
def make_features_filter_options(str : String::Builder, value : Search::Filters::Features, locale : String)
{% for value in Invidious::Search::Filters::Features.constants %}
{% if value.stringify != "All" && value.stringify != "None" %}
{% feature = value.underscore %}
str << "\t\t\t\t\t\t<div>"
str << "<input type='checkbox' name='features' id='filter-features-{{feature}}' value='{{feature}}'"
str << " checked" if value.{{feature}}?
str << '>'
str << "<label for='filter-feature-{{feature}}'>"
str << translate(locale, "search_filters_features_option_{{feature}}")
str << "</label></div>\n"
{% end %}
{% end %}
end
# Generates the HTML for the list of radio buttons of the "sort" search filter
def make_sort_filter_options(str : String::Builder, value : Search::Filters::Sort, locale : String)
{% for value in Invidious::Search::Filters::Sort.constants %}
{% sort = value.underscore %}
str << "\t\t\t\t\t\t<div>"
str << "<input type='radio' name='sort' id='filter-sort-{{sort}}' value='{{sort}}'"
str << " checked" if value.{{sort}}?
str << '>'
str << "<label for='filter-sort-{{sort}}'>"
str << translate(locale, "search_filters_sort_option_{{sort}}")
str << "</label></div>\n"
{% end %}
end
end