diff --git a/opentech/apply/funds/templates/funds/base_submissions_table.html b/opentech/apply/funds/templates/funds/base_submissions_table.html index 43917fa7f2757879bd9163bc8f1a95aabc281c8a..1aa5a97edb9dc3195a8e6305aea9e43cb6baea83 100644 --- a/opentech/apply/funds/templates/funds/base_submissions_table.html +++ b/opentech/apply/funds/templates/funds/base_submissions_table.html @@ -8,25 +8,29 @@ {% block content %} {% block table %} + <div class="wrapper wrapper--table-actions"> {% if table.data or active_filters %} - <div class="button button--filters button--contains-icons js-open-filters">Filter By</div> + <button class="button button--filters button--contains-icons js-toggle-filters">Filters</button> + {% endif %} + </div> - <div class="filters js-filter-wrapper"> - <div class="filters__header"> - <div class="js-clear-filters">Clear</div> - <div>Filter by</div> - <div class="js-close-filters">Close</div> - </div> + {% if table.data or active_filters %} + <div class="filters"> + <div class="filters__header"> + <button class="filters__button js-clear-filters">Clear</button> + <div>Filter by</div> + <button class="filters__button js-close-filters">Close</button> + </div> - <form action="" method="get" class="form form--filters"> - <ul class="form__filters select2 js-filter-list"> - {{ filter.form.as_ul }} - <li> - <button class="button button--primary" type="submit" value="Filter">Filter</button> - </li> - </ul> - </form> - </div> + <form action="" method="get" class="form form--filters"> + <ul class="form__filters select2"> + {{ filter.form.as_ul }} + <li> + <button class="button button--primary" type="submit" value="Filter">Filter</button> + </li> + </ul> + </form> + </div> {% endif %} {% render_table table %} diff --git a/opentech/static_src/src/javascript/apply/submission-filters.js b/opentech/static_src/src/javascript/apply/submission-filters.js index fe4f36b9c2e7368c7675f98eec0918c616c3dda9..1049f923a48df39bf916fbdcafb1a79265802c40 100644 --- a/opentech/static_src/src/javascript/apply/submission-filters.js +++ b/opentech/static_src/src/javascript/apply/submission-filters.js @@ -2,15 +2,23 @@ 'use strict'; - const $openButton = $('.js-open-filters'); + // Variables + const $body = $('body'); + const $toggleButton = $('.js-toggle-filters'); const $closeButton = $('.js-close-filters'); const $clearButton = $('.js-clear-filters'); - const $filterList = $('.js-filter-list'); - const $filterWrapper = $('.js-filter-wrapper'); + const filterOpenClass = 'filters-open'; + const filterActiveClass = 'is-active'; + + // check if the page has a query string and keep filters open if so on desktop + if (window.location.href.indexOf('?') > -1 && $(window).width() > 1024) { + $body.addClass(filterOpenClass); + updateButtonText(); + } // Add active class to filters - dropdowns are dynamically appended to the dom, // so we have to listen for the event higher up - $('body').on('click', '.select2-dropdown', (e) => { + $body.on('click', '.select2-dropdown', (e) => { // get the id of the dropdown let selectId = e.target.parentElement.parentElement.id; @@ -19,44 +27,121 @@ // if the dropdown contains a clear class, the filters are active if ($(match[0]).find('span.select2-selection__clear').length !== 0) { - match[0].classList.add('is-active'); + match[0].classList.add(filterActiveClass); } else { - match[0].classList.remove('is-active'); + match[0].classList.remove(filterActiveClass); } }); // remove active class on clearing select2 $('.select2').on('select2:unselecting', (e) => { const dropdown = e.target.nextElementSibling.firstChild.firstChild; - if (dropdown.classList.contains('is-active')) { - dropdown.classList.remove('is-active'); + if (dropdown.classList.contains(filterActiveClass)) { + dropdown.classList.remove(filterActiveClass); } }); - // open mobile filters - $openButton.on('click', (e) => { - $('body').addClass('no-scroll'); - $(e.target).next($filterWrapper).addClass('is-open'); - $filterList.addClass('form__filters--mobile'); + // toggle filters + $toggleButton.on('click', () => { + if ($body.hasClass(filterOpenClass)) { + handleClearFilters(); + } + else { + $body.addClass(filterOpenClass); + updateButtonText(); + } }); - // close mobile filters + // close filters on mobile $closeButton.on('click', (e) => { - $('body').removeClass('no-scroll'); - $(e.target).closest($filterWrapper).removeClass('is-open'); - $filterList.removeClass('form__filters--mobile'); + $body.removeClass(filterOpenClass); + updateButtonText(); }); + // redirect to submissions home to clear filters + function handleClearFilters() { + window.location.href = window.location.href.split('?')[0]; + } + + // toggle filters button wording + function updateButtonText() { + if ($body.hasClass(filterOpenClass)) { + $toggleButton.text('Clear filters'); + } + else { + $toggleButton.text('Filters'); + } + } + + // corrects spacing of dropdowns when toggled on mobile + function mobileFilterPadding(element) { + const expanded = 'expanded-filter-element'; + const dropdown = $(element).closest('.select2'); + const openDropdown = $('.select2 .' + expanded); + let dropdownMargin = 0; + + if (openDropdown.length > 0 && !openDropdown.hasClass('select2-container--open')) { + // reset the margin of the select we previously worked + openDropdown.removeClass(expanded); + // store the offset to adjust the new select box (elements above the old dropdown unaffected) + if (dropdown.position().top > openDropdown.position().top) { + dropdownMargin = parseInt(openDropdown.css('marginBottom')); + } + openDropdown.css('margin-bottom', '0px'); + } + + if (dropdown.hasClass('select2-container--open')) { + dropdown.addClass(expanded); + const dropdownID = $(element).closest('.select2-selection').attr('aria-owns'); + // Element which has the height of the select dropdown + const match = $(`ul#${dropdownID}`); + const dropdownHeight = match.outerHeight(true); + + // Element which has the position of the dropdown + const positionalMatch = match.closest('.select2-container'); + + // Pad the bottom of the select box + dropdown.css('margin-bottom', `${dropdownHeight}px`); + + // bump up the dropdown options by height of closed elements + positionalMatch.css('top', positionalMatch.position().top - dropdownMargin); + } + } + // clear all filters $clearButton.on('click', () => { - const dropdowns = document.querySelectorAll('.form__filters--mobile select'); + const dropdowns = document.querySelectorAll('.form__filters select'); dropdowns.forEach(dropdown => { $(dropdown).val(null).trigger('change'); - $('.select2-selection.is-active').removeClass('is-active'); + $('.select2-selection.is-active').removeClass(filterActiveClass); mobileFilterPadding(dropdown); // eslint-disable-line no-undef }); }); + $(function () { + // Add active class to select2 checkboxes after page has been filtered + const clearButtons = document.querySelectorAll('.select2-selection__clear'); + clearButtons.forEach(clearButton => { + clearButton.parentElement.parentElement.classList.add(filterActiveClass); + }); + }); + + // reset mobile filters if they're open past the tablet breakpoint + $(window).resize(function resize() { + if ($(window).width() < 1024) { + // close the filters if open when reducing the window size + $('body').removeClass('filters-open'); + + // update filter button text + $('.js-toggle-filters').text('Filters'); + + // Correct spacing of dropdowns when toggled + $('.select2').on('click', (e) => { + mobileFilterPadding(e.target); + }); + } + }).trigger('resize'); + })(jQuery); diff --git a/opentech/static_src/src/javascript/main.js b/opentech/static_src/src/javascript/main.js index 8356fe85e95e43e5fcd325c6c5619b00a7019f0b..64620fb592e12b6465e532a75c5247aa82cfac4d 100644 --- a/opentech/static_src/src/javascript/main.js +++ b/opentech/static_src/src/javascript/main.js @@ -126,67 +126,15 @@ message.classList.add('messages__text--hide'); }); - // Add active class to select2 checkboxes after page has been filtered - document.addEventListener('DOMContentLoaded', () => { - // If there are clear buttons in the dom, it means the filters have been applied - const clearButtons = document.querySelectorAll('.select2-selection__clear'); - clearButtons.forEach(clearButton => { - clearButton.parentElement.parentElement.classList.add('is-active'); - }); - }); - // reset mobile filters if they're open past the tablet breakpoint $(window).resize(function resize() { - if ($(window).width() < 768) { - $('.select2').on('click', (e) => { - mobileFilterPadding(e.target); - }); - } - else { - $('body').removeClass('no-scroll'); - $('.js-filter-wrapper').removeClass('is-open'); - $('.js-filter-list').removeClass('form__filters--mobile'); + if ($(window).width() > 1024) { $('.js-actions-toggle').removeClass('is-active'); $('.js-actions-sidebar').removeClass('is-visible'); $('.tr--parent.is-expanded').removeClass('is-expanded'); } }).trigger('resize'); - function mobileFilterPadding(element) { - const expanded = 'expanded-filter-element'; - const dropdown = $(element).closest('.select2'); - const openDropdown = $('.select2 .' + expanded); - let dropdownMargin = 0; - - if (openDropdown.length > 0 && !openDropdown.hasClass('select2-container--open')) { - // reset the margin of the select we previously worked - openDropdown.removeClass(expanded); - // store the offset to adjust the new select box (elements above the old dropdown unaffected) - if (dropdown.position().top > openDropdown.position().top) { - dropdownMargin = parseInt(openDropdown.css('marginBottom')); - } - openDropdown.css('margin-bottom', '0px'); - } - - if (dropdown.hasClass('select2-container--open')) { - dropdown.addClass(expanded); - const dropdownID = $(element).closest('.select2-selection').attr('aria-owns'); - // Element which has the height of the select dropdown - const match = $(`ul#${dropdownID}`); - const dropdownHeight = match.outerHeight(true); - - // Element which has the position of the dropdown - const positionalMatch = match.closest('.select2-container'); - - // Pad the bottom of the select box - dropdown.css('margin-bottom', `${dropdownHeight}px`); - - // bump up the dropdown options by height of closed elements - positionalMatch.css('top', positionalMatch.position().top - dropdownMargin); - } - } - - $('form').filter('.form__comments').submit(function (e) { var $form = $(this); var formValues = $form.serialize(); diff --git a/opentech/static_src/src/sass/apply/abstracts/_variables.scss b/opentech/static_src/src/sass/apply/abstracts/_variables.scss index 8f6c7113797a6ed556b4e59ef7446e7f88156958..f49d745e8984da9dd0ccaff6c02c294279b91ab0 100644 --- a/opentech/static_src/src/sass/apply/abstracts/_variables.scss +++ b/opentech/static_src/src/sass/apply/abstracts/_variables.scss @@ -98,6 +98,13 @@ $breakpoints: ( // Transition $transition: .25s ease-out; $quick-transition: .15s ease; +$medium-transition: .5s ease; + +// Delays +$base-delay: 30ms; // Spacing $mobile-gutter: 20px; + +// Filters +$filter-dropdown: '.select2 .select2-selection.select2-selection--single'; diff --git a/opentech/static_src/src/sass/apply/components/_button.scss b/opentech/static_src/src/sass/apply/components/_button.scss index 32efcfb5da86bc49602c897f429e5cad93fc46ab..280373f0980ba5567d19d04fb97bf7670da99f54 100644 --- a/opentech/static_src/src/sass/apply/components/_button.scss +++ b/opentech/static_src/src/sass/apply/components/_button.scss @@ -11,8 +11,8 @@ .form--filters & { width: 100%; - height: 100%; text-align: center; + height: 45px; } } @@ -78,15 +78,29 @@ background: url('./../../images/filters.svg') $color--white no-repeat 93% center; border: 1px solid $color--light-mid-grey; transition: none; - - &:focus, - &:active, - &:hover { - background: url('./../../images/filters.svg') $color--white no-repeat 93% center; - } + width: 100%; @include media-query(tablet-landscape) { - display: none; + background: none; + padding: 0; + border: 0; + align-items: center; + justify-content: flex-start; + max-width: initial; + width: auto; + + &::before { + content: ''; + background-image: url('./../../images/filters.svg'); + background-color: transparent; + background-position: left center; + transform: rotate(90deg); + background-size: 20px; + width: 20px; + height: 20px; + display: inline-block; + margin-right: 10px; + } } } diff --git a/opentech/static_src/src/sass/apply/components/_filters.scss b/opentech/static_src/src/sass/apply/components/_filters.scss index 812e3758fdda9463815598c4d655fe4ae916d40f..8a0dbb5deae0dfda50b3f16c7b2bac3e286fc4f7 100644 --- a/opentech/static_src/src/sass/apply/components/_filters.scss +++ b/opentech/static_src/src/sass/apply/components/_filters.scss @@ -1,11 +1,7 @@ .filters { display: none; - @include media-query(tablet-landscape) { - display: block; - } - - &.is-open { + .filters-open & { position: fixed; top: 0; right: 0; @@ -18,6 +14,24 @@ background: $color--white; } + @include media-query(tablet-landscape) { + display: block; + max-height: 0; + transition: max-height $medium-transition; + transition-delay: $base-delay; + + .filters-open & { + position: relative; + top: auto; + left: auto; + right: auto; + bottom: auto; + height: auto; + background: transparent; + max-height: 85px; + } + } + &__header { display: flex; align-items: center; @@ -36,4 +50,12 @@ } } } + + &__button { + appearance: none; + -webkit-appearance: none; // sass-lint:disable-line no-vendor-prefixes + border: 0; + color: $color--primary; + background: transparent; + } } diff --git a/opentech/static_src/src/sass/apply/components/_form.scss b/opentech/static_src/src/sass/apply/components/_form.scss index 44c94c9aea1d60515fde8b2ce023234235c57936..ec3401f029c51565d0ea9080fa14a2e1abe29316 100644 --- a/opentech/static_src/src/sass/apply/components/_form.scss +++ b/opentech/static_src/src/sass/apply/components/_form.scss @@ -107,8 +107,33 @@ } &__filters { - display: flex; - padding: 10px 0 30px; + #{$filter-dropdown} { + border: 0; + border-top: 1px solid $color--mid-grey; + + &.is-active { + font-weight: $weight--normal; + background-color: transparentize($color--primary, .9); + border-color: $color--mid-grey; + } + + @include media-query(tablet-landscape) { + border: 1px solid $color--mid-grey; + } + } + + @include media-query(tablet-landscape) { + display: flex; + align-items: flex-start; + padding: 10px 0 30px; + opacity: 0; + transition: opacity $transition; + + .filters-open & { + opacity: 1; + transition-delay: $base-delay * 10; + } + } label { display: none; @@ -116,25 +141,16 @@ // so the form can be output in any tag > * { - flex-basis: 225px; - margin-right: 10px; + @include media-query(tablet-landscape) { + flex-basis: 225px; + margin-right: 10px; + } } &--mobile { flex-direction: column; padding: 0; - .select2 .select2-selection.select2-selection--single { // sass-lint:disable-line force-element-nesting - border: 0; - border-top: 1px solid $color--mid-grey; - - &.is-active { - font-weight: $weight--normal; - background-color: transparentize($color--primary, .9); - border-color: $color--mid-grey; - } - } - // so the form can be output in any tag > * { flex-basis: auto; @@ -179,6 +195,7 @@ } .form__filters & { + background: $color--white; max-width: 100%; } diff --git a/opentech/static_src/src/sass/apply/components/_select2.scss b/opentech/static_src/src/sass/apply/components/_select2.scss index 54147955936b9ec77cc96fd6bab97db832395664..dff4cdb83d084a81fd0bb1c4f5c18685167da01c 100644 --- a/opentech/static_src/src/sass/apply/components/_select2.scss +++ b/opentech/static_src/src/sass/apply/components/_select2.scss @@ -1,3 +1,5 @@ +$dropdown-height: 45px; + .select2 { &-container { z-index: 99995; // to override any modals @@ -8,7 +10,7 @@ width: 100% !important; // sass-lint:disable-line no-important .select2-selection--single { - height: 55px; + height: $dropdown-height; border: 1px solid $color--mid-grey; border-radius: 0; @@ -18,27 +20,22 @@ } .select2-selection__clear { - position: absolute; - right: 35px; display: none; - float: none; - - @include media-query(small-tablet) { - display: block; - } } .select2-selection__rendered { padding-left: 15px; - line-height: 55px; + padding-right: 30px; + line-height: $dropdown-height; } .select2-selection__arrow { right: 15px; - height: 53px; + height: $dropdown-height; pointer-events: none; background: url('./../../images/dropdown.svg') transparent no-repeat 95% center; background-size: 8px; + width: 8px; b[role='presentation'] { display: none; @@ -90,7 +87,7 @@ padding: 6px; &::before { - width: 20px; + min-width: 20px; height: 20px; margin-right: 10px; background: $color--white; diff --git a/opentech/static_src/src/sass/apply/components/_wrapper.scss b/opentech/static_src/src/sass/apply/components/_wrapper.scss index cf14c2fe9e3c99c34137f3c664e107db9a940445..27dbe6aebaa95adb0b41a2e31e2ebea49e260a73 100644 --- a/opentech/static_src/src/sass/apply/components/_wrapper.scss +++ b/opentech/static_src/src/sass/apply/components/_wrapper.scss @@ -265,4 +265,11 @@ &--reviews-table { overflow-x: scroll; } + + &--table-actions { + @include media-query(tablet-landscape) { + display: flex; + justify-content: flex-end; + } + } } diff --git a/opentech/static_src/src/sass/public/abstracts/_variables.scss b/opentech/static_src/src/sass/public/abstracts/_variables.scss index 8f6c7113797a6ed556b4e59ef7446e7f88156958..8545b9184473fddf3b1265333fe22b842cc6d0cd 100644 --- a/opentech/static_src/src/sass/public/abstracts/_variables.scss +++ b/opentech/static_src/src/sass/public/abstracts/_variables.scss @@ -99,5 +99,8 @@ $breakpoints: ( $transition: .25s ease-out; $quick-transition: .15s ease; +// Delays +$base-delay: 30ms; + // Spacing $mobile-gutter: 20px;