diff --git a/assets/css/search.css b/assets/css/search.css new file mode 100644 index 00000000..ad2b0b16 --- /dev/null +++ b/assets/css/search.css @@ -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; + } +} diff --git a/locales/en-US.json b/locales/en-US.json index a78d8062..03df88b6 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -404,37 +404,44 @@ "Videos": "Videos", "Playlists": "Playlists", "Community": "Community", - "relevance": "Relevance", - "rating": "Rating", - "date": "Upload date", - "views": "View count", - "content_type": "Type", - "duration": "Duration", - "features": "Features", - "sort": "Sort By", - "hour": "Last Hour", - "today": "Today", - "week": "This week", - "month": "This month", - "year": "This year", - "video": "Video", - "channel": "Channel", - "playlist": "Playlist", - "movie": "Movie", - "show": "Show", - "short": "Short (< 4 minutes)", - "long": "Long (> 20 minutes)", - "hd": "HD", - "subtitles": "Subtitles/CC", - "creative_commons": "Creative Commons", - "3d": "3D", - "live": "Live", - "4k": "4K", - "location": "Location", - "hdr": "HDR", - "purchased": "Purchased", - "360": "360°", - "filter": "Filter", + "search_filters_title": "Filters", + "search_filters_date_label": "Upload date", + "search_filters_date_option_none": "Any date", + "search_filters_date_option_hour": "Last Hour", + "search_filters_date_option_today": "Today", + "search_filters_date_option_week": "This week", + "search_filters_date_option_month": "This month", + "search_filters_date_option_year": "This year", + "search_filters_type_label": "Type", + "search_filters_type_option_all": "Any type", + "search_filters_type_option_video": "Video", + "search_filters_type_option_channel": "Channel", + "search_filters_type_option_playlist": "Playlist", + "search_filters_type_option_movie": "Movie", + "search_filters_type_option_show": "Show", + "search_filters_duration_label": "Duration", + "search_filters_duration_option_none": "Any duration", + "search_filters_duration_option_short": "Short (< 4 minutes)", + "search_filters_duration_option_medium": "Medium (4 - 20 minutes)", + "search_filters_duration_option_long": "Long (> 20 minutes)", + "search_filters_features_label": "Features", + "search_filters_features_option_live": "Live", + "search_filters_features_option_four_k": "4K", + "search_filters_features_option_hd": "HD", + "search_filters_features_option_subtitles": "Subtitles/CC", + "search_filters_features_option_c_commons": "Creative Commons", + "search_filters_features_option_three_sixty": "360°", + "search_filters_features_option_vr180": "VR180", + "search_filters_features_option_three_d": "3D", + "search_filters_features_option_hdr": "HDR", + "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: ", "next_steps_error_message": "After which you should try to: ", "next_steps_error_message_refresh": "Refresh", diff --git a/src/invidious/frontend/search_filters.cr b/src/invidious/frontend/search_filters.cr new file mode 100644 index 00000000..68f27b4f --- /dev/null +++ b/src/invidious/frontend/search_filters.cr @@ -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 << "
\n" + str << "\t
" + str << "\t\t" << translate(locale, "search_filters_title") << "\n" + + str << "\t\t
\n" + + str << "\t\t\t\n" + str << "\t\t\t\n" + + str << "\t\t\t
" + + filter_wrapper(date) + filter_wrapper(type) + filter_wrapper(duration) + filter_wrapper(features) + filter_wrapper(sort) + + str << "\t\t\t
\n" + + str << "\t\t\t
" + str << "
\n" + + str << "\t\t
\n" + + str << "\t
\n" + str << "
\n" + end + end + + # Generate wrapper HTML (`
`, filter name, etc...) around the + # `` elements of a search filter + macro filter_wrapper(name) + str << "\t\t\t\t
\n" + + str << "\t\t\t\t\t
" + str << translate(locale, "search_filters_{{name}}_label") + str << "
\n" + + str << "\t\t\t\t\t
\n" + make_{{name}}_filter_options(str, filters.{{name}}, locale) + str << "\t\t\t\t\t
" + + str << "\t\t\t\t
\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
" + str << "' + + str << "
\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
" + str << "' + + str << "
\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
" + str << "' + + str << "
\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
" + str << "' + + str << "
\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
" + str << "' + + str << "
\n" + {% end %} + end +end