Merge pull request #1726 from joachimesque/options-panels

Front-end: New look and behavior for Filters and Result panels
This commit is contained in:
Mouse Reeve 2022-01-03 09:23:12 -08:00 committed by GitHub
commit cccd28cb74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 255 additions and 109 deletions

View file

@ -135,6 +135,18 @@ button::-moz-focus-inner {
border-bottom: 1px solid #ededed; border-bottom: 1px solid #ededed;
border-right: 0; border-right: 0;
} }
.is-flex-direction-row-mobile {
flex-direction: row !important;
}
.is-flex-direction-column-mobile {
flex-direction: column !important;
}
}
.tag.is-small {
height: auto;
} }
.button.is-transparent { .button.is-transparent {
@ -215,7 +227,7 @@ input[type=file]::file-selector-button:hover {
/** General `details` element styles /** General `details` element styles
******************************************************************************/ ******************************************************************************/
summary { details summary {
cursor: pointer; cursor: pointer;
} }
@ -223,22 +235,22 @@ summary::-webkit-details-marker {
display: none; display: none;
} }
summary::marker { details summary::marker {
content: none; content: none;
} }
.detail-pinned-button summary { details.detail-pinned-button summary {
position: absolute; position: absolute;
right: 0; right: 0;
} }
.detail-pinned-button form { details.detail-pinned-button form {
float: left; float: left;
width: -webkit-fill-available; width: 100%;
margin-top: 1em; margin-top: 1em;
} }
/** Details dropdown /** Dropdown w/ Details element
******************************************************************************/ ******************************************************************************/
details.dropdown[open] summary.dropdown-trigger::before { details.dropdown[open] summary.dropdown-trigger::before {
@ -250,11 +262,11 @@ details.dropdown[open] summary.dropdown-trigger::before {
right: 0; right: 0;
} }
details .dropdown-menu { details.dropdown .dropdown-menu {
display: block !important; display: block !important;
} }
details .dropdown-menu button { details.dropdown .dropdown-menu button {
/* Fix weird Safari defaults */ /* Fix weird Safari defaults */
box-sizing: border-box; box-sizing: border-box;
} }
@ -289,6 +301,46 @@ details.dropdown .dropdown-menu a:focus-visible {
} }
} }
/** Details panel
******************************************************************************/
details.details-panel {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s ease;
padding: 0.75rem;
}
details[open].details-panel,
details.details-panel:hover {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}
details.details-panel summary {
position: relative;
}
details.details-panel summary .details-close {
position: absolute;
right: 0;
top: 0;
transform: rotate(45deg);
transition: transform 0.2s ease;
}
details[open].details-panel summary .details-close {
transform: rotate(0deg);
}
@media only screen and (min-width: 769px) {
.details-panel .filters-field:not(:last-child) {
border-right: 1px solid rgba(0, 0, 0, 0.1);
margin-top: 0.75rem;
margin-bottom: 0.75rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
}
/** Shelving /** Shelving
******************************************************************************/ ******************************************************************************/
@ -1149,3 +1201,93 @@ ol.ordered-list li::before {
margin-bottom: 0.75rem !important; margin-bottom: 0.75rem !important;
} }
} }
/* Gaps (for Flexbox and Grid)
*
* Those are supplementary rules to Bulmas. They follow the same conventions.
* Add those youll need.
******************************************************************************/
.is-gap-0 {
gap: 0;
}
.is-gap-1 {
gap: 0.25rem;
}
.is-gap-2 {
gap: 0.5rem;
}
.is-gap-3 {
gap: 0.75rem;
}
.is-gap-4 {
gap: 1rem;
}
.is-gap-5 {
gap: 1.5rem;
}
.is-gap-6 {
gap: 3rem;
}
.is-row-gap-0 {
row-gap: 0;
}
.is-row-gap-1 {
row-gap: 0.25rem;
}
.is-row-gap-2 {
row-gap: 0.5rem;
}
.is-row-gap-3 {
row-gap: 0.75rem;
}
.is-row-gap-4 {
row-gap: 1rem;
}
.is-row-gap-5 {
row-gap: 1.5rem;
}
.is-row-gap-6 {
row-gap: 3rem;
}
.is-column-gap-0 {
column-gap: 0;
}
.is-column-gap-1 {
column-gap: 0.25rem;
}
.is-column-gap-2 {
column-gap: 0.5rem;
}
.is-column-gap-3 {
column-gap: 0.75rem;
}
.is-column-gap-4 {
column-gap: 1rem;
}
.is-column-gap-5 {
column-gap: 1.5rem;
}
.is-column-gap-6 {
column-gap: 3rem;
}

View file

@ -25,6 +25,10 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon.is-small {
font-size: small;
}
.icon-book:before { .icon-book:before {
content: "\e901"; content: "\e901";
} }

View file

@ -6,8 +6,7 @@
<h1 class="title"> <h1 class="title">
{{ tab.name }} {{ tab.name }}
</h1> </h1>
<div class="block is-clipped">
<div class="is-pulled-left">
<div class="tabs"> <div class="tabs">
<ul> <ul>
{% for stream in streams %} {% for stream in streams %}
@ -17,45 +16,11 @@
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
</div>
{# feed settings #} {# feed settings #}
<details class="detail-pinned-button" {% if settings_saved %}open{% endif %}> {% with "/"|add:tab.key|add:"#feed" as action %}
<summary class="control"> {% include 'feed/feed_filters.html' with size="small" method="post" action=action %}
<span class="button"> {% endwith %}
<span class="icon icon-dots-three m-0-mobile" aria-hidden="true"></span>
<span class="is-sr-only-mobile">{{ _("Feed settings") }}</span>
</span>
</summary>
<form class="notification level is-align-items-flex-end" method="post" action="/{{ tab.key }}#feed">
{% csrf_token %}
<div class="level-left">
<div class="field">
<div class="control">
<span class="is-flex is-align-items-baseline">
<label class="label mt-2 mb-1">Status types</label>
{% if settings_saved %}
<span class="tag is-success is-light ml-2">{{ _("Saved!") }}</span>
{% endif %}
</span>
{% for name, value in feed_status_types_options %}
<label class="mr-2">
<input type="checkbox" name="feed_status_types" value="{{ name }}" {% if name in user.feed_status_types %}checked=""{% endif %}/>
{{ value }}
</label>
{% endfor %}
</div>
</div>
</div>
<div class="level-right control">
<button class="button is-small is-primary is-outlined" type="submit">
{{ _("Save settings") }}
</button>
</div>
</form>
</details>
</div>
{# announcements and system messages #} {# announcements and system messages #}
{% if not activities.number > 1 %} {% if not activities.number > 1 %}

View file

@ -0,0 +1,5 @@
{% extends 'snippets/filters_panel/filters_panel.html' %}
{% block filter_fields %}
{% include 'feed/status_types_filter.html' %}
{% endblock %}

View file

@ -0,0 +1,16 @@
{% extends 'snippets/filters_panel/filter_field.html' %}
{% load i18n %}
{% block filter %}
<label class="label mt-2 mb-1">Status types</label>
<div class="is-flex is-flex-direction-row is-flex-direction-column-mobile">
{% for name, value in feed_status_types_options %}
<label class="mr-2">
<input type="checkbox" name="feed_status_types" value="{{ name }}" {% if name in user.feed_status_types %}checked=""{% endif %}/>
{{ value }}
</label>
{% endfor %}
</div>
{% endblock %}

View file

@ -8,7 +8,7 @@
<ul class="block"> <ul class="block">
{% for result in local_results.results %} {% for result in local_results.results %}
<li class="pd-4 mb-5"> <li class="pd-4 mb-5">
<div class="columns is-mobile is-gapless"> <div class="columns is-mobile is-gapless mb-0">
<div class="column is-cover"> <div class="column is-cover">
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' %} {% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' %}
</div> </div>
@ -34,34 +34,28 @@
<div class="block"> <div class="block">
{% for result_set in results|slice:"1:" %} {% for result_set in results|slice:"1:" %}
{% if result_set.results %} {% if result_set.results %}
<section class="box has-background-white-bis"> <section class="mb-5">
{% if not result_set.connector.local %} {% if not result_set.connector.local %}
<header class="columns is-mobile"> <details class="details-panel box" {% if forloop.first %}open{% endif %}>
<div class="column"> {% endif %}
<h3 class="title is-5"> {% if not result_set.connector.local %}
<summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2">
<span class="mb-0 title is-5">
{% trans 'Results from' %} {% trans 'Results from' %}
<a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a> <a href="{{ result_set.connector.base_url }}" target="_blank">{{ result_set.connector.name|default:result_set.connector.identifier }}</a>
</h3> </span>
</div>
<div class="column is-narrow"> <span class="details-close icon icon-x" aria-hidden></span>
{% trans "Open" as button_text %} </summary>
{% include 'snippets/toggle/open_button.html' with text=button_text controls_text="more_results_panel" controls_uid=result_set.connector.identifier class="is-small" icon_with_text="arrow-down" pressed=forloop.first %}
{% trans "Close" as button_text %}
{% include 'snippets/toggle/close_button.html' with text=button_text controls_text="more_results_panel" controls_uid=result_set.connector.identifier class="is-small" icon_with_text="arrow-up" pressed=forloop.first %}
</div>
</header>
{% endif %} {% endif %}
<div class="box has-background-white is-shadowless{% if not forloop.first %} is-hidden{% endif %}" id="more_results_panel_{{ result_set.connector.identifier }}"> <div class="mt-5">
<div class="is-flex is-flex-direction-row-reverse"> <div class="is-flex is-flex-direction-row-reverse">
<div>
</div>
<ul class="is-flex-grow-1"> <ul class="is-flex-grow-1">
{% for result in result_set.results %} {% for result in result_set.results %}
<li class="mb-5"> <li class="{% if not forloop.last %}mb-5{% endif %}">
<div class="columns is-mobile is-gapless">
<div class="columns is-mobile is-gapless"> <div class="columns is-mobile is-gapless">
<div class="column is-1 is-cover">
{% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' external_path=True %} {% include 'snippets/book_cover.html' with book=result cover_class='is-w-xs is-h-xs' external_path=True %}
</div> </div>
<div class="column is-10 ml-3"> <div class="column is-10 ml-3">
@ -92,6 +86,9 @@
</ul> </ul>
</div> </div>
</div> </div>
{% if not result_set.connector.local %}
</details>
{% endif %}
</section> </section>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

View file

@ -1,6 +1,4 @@
<div class="column is-flex"> <div class="filters-field column">
<div class="box is-flex-grow-1">
{% block filter %} {% block filter %}
{% endblock %} {% endblock %}
</div> </div>
</div>

View file

@ -1,28 +1,47 @@
{% load i18n %} {% load i18n %}
<div class="notification content"> <details class="details-panel box is-size-{{ size|default:'normal' }}" {% if filters_applied %}open{% endif %}>
<h2 class="columns is-mobile mb-0"> <summary class="is-flex is-align-items-center is-flex-wrap-wrap is-gap-2">
<span class="column pb-0">Filters</span> <span class="mb-0 title {% if size == 'small' %}is-6{% else %}is-5{% endif %} is-flex-shrink-0">
{% trans "Filters" %}
<span class="column is-narrow pb-0">
{% trans "Show filters" as text %}
{% include 'snippets/toggle/open_button.html' with text=text controls_text="filters" icon_with_text="arrow-down" class="is-small" focus="filters" %}
{% trans "Hide filters" as text %}
{% include 'snippets/toggle/close_button.html' with text=text controls_text="filters" icon_with_text="arrow-up" class="is-small" %}
</span> </span>
</h2>
<form class="is-hidden mt-3" id="filters" method="get" action="{{ request.path }}" tabindex="0"> {% if filters_applied %}
<span class="tag is-success is-light ml-2 mb-0 is-{{ size|default:'normal' }}">
{{ _("Filters are applied") }}
</span>
{% endif %}
{% if request.GET %}
<span class="mb-0 tags has-addons">
<span class="mb-0 tag is-success is-light is-{{ size|default:'normal' }}">
{% trans "Filters are applied" %}
</span>
<a class="mb-0 tag is-success is-{{ size|default:'normal' }}" href="{{ request.path }}">
{% trans "Clear filters" %}
</a>
</span>
{% endif %}
<span class="details-close icon icon-x is-{{ size|default:'normal' }}" aria-hidden></span>
</summary>
<div class="mt-3">
<form id="filters" method="{{ method|default:'get' }}" action="{{ action|default:request.path }}">
{% if method == 'post' %}
{% csrf_token %}
{% endif %}
{% if sort %} {% if sort %}
<input type="hidden" name="sort" value="{{ sort }}"> <input type="hidden" name="sort" value="{{ sort }}">
{% endif %} {% endif %}
<div class="columns"> <div class="mt-3 columns filters-fields is-align-items-stretch">
{% block filter_fields %} {% block filter_fields %}
{% endblock %} {% endblock %}
</div> </div>
<button class="button is-primary">{% trans "Apply filters" %}</button> <button type="submit" class="button is-primary is-small">
{% trans "Apply filters" %}
</button>
</form> </form>
{% if request.GET %}
<a class="help" href="{{ request.path }}">{% trans "Clear filters" %}</a>
{% endif %}
</div> </div>
</details>

View file

@ -26,15 +26,15 @@ class Feed(View):
def post(self, request, tab): def post(self, request, tab):
"""save feed settings form, with a silent validation fail""" """save feed settings form, with a silent validation fail"""
settings_saved = False filters_applied = False
form = forms.FeedStatusTypesForm(request.POST, instance=request.user) form = forms.FeedStatusTypesForm(request.POST, instance=request.user)
if form.is_valid(): if form.is_valid():
form.save() form.save()
settings_saved = True filters_applied = True
return self.get(request, tab, settings_saved) return self.get(request, tab, filters_applied)
def get(self, request, tab, settings_saved=False): def get(self, request, tab, filters_applied=False):
"""user's homepage with activity feed""" """user's homepage with activity feed"""
tab = [s for s in STREAMS if s["key"] == tab] tab = [s for s in STREAMS if s["key"] == tab]
tab = tab[0] if tab else STREAMS[0] tab = tab[0] if tab else STREAMS[0]
@ -61,7 +61,7 @@ class Feed(View):
"goal_form": forms.GoalForm(), "goal_form": forms.GoalForm(),
"feed_status_types_options": FeedFilterChoices, "feed_status_types_options": FeedFilterChoices,
"allowed_status_types": request.user.feed_status_types, "allowed_status_types": request.user.feed_status_types,
"settings_saved": settings_saved, "filters_applied": filters_applied,
"path": f"/{tab['key']}", "path": f"/{tab['key']}",
"annual_summary_year": get_annual_summary_year(), "annual_summary_year": get_annual_summary_year(),
}, },