<div class="relative" :class="{ 'overflow-hidden': noResults }">
<!-- Filters column -->
- <div
+ <div ref="filters"
class="products-filters-wrapper sticky sm:static top-60 mr-1v sm:mr-0 pt-4 whitespace-no-wrap float-left sm:float-none">
<!-- Filters panel -->
<div class="bg-white p-4">
- <div class="products-filters" v-for="(filter, index) in filterData" :key="index">
+ <div class="products-filters" :class="{ 'folded': currentFilterIndex !== index }" v-for="(filter, index) in filterData" :key="index">
<hr class="h-px bg-grey-250 my-4" v-if="index !== 0"/>
- <h3 class="text-base mb-2 whitespace-normal">{{ filter.label }}</h3>
+ <h3 class="flex items-center justify-between text-base whitespace-normal mb-0" @click="expandFilters(index)">
+ <span class="pr-4">{{ filter.label }}</span>
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 8" width="13" height="8" xml:space="preserve"
+ :class="{ 'rotate-180': currentFilterIndex !== index }"
+ class="transition transform origin-center duration-300">
+ <path d="m.8 7.2 5.7-5.7 5.7 5.7" fill="none" stroke="#6b7287" stroke-width="2"/>
+ </svg>
+ </h3>
- <ul v-if="filter.type === 'list' || filter.type === 'mlist'">
- <li v-for="(option, option_index) in filter.options" :key="option_index"
- class="flex justify-between py-1 text-sm">
+ <!-- To allow accordion transition to be animated, this element will have
+ its max-height set based on the child elements - see mounted() function -->
+ <div class="filters-wrapper">
- <label>
- <input type="checkbox" v-model="filters[filter.id]" :value="option.value"
- @change="updateFilters">
- {{ option.label }}
- </label>
+ <ul v-if="filter.type === 'list' || filter.type === 'mlist'" class="pt-3">
+ <li v-for="(option, option_index) in filter.options" :key="option_index"
+ class="flex justify-between py-1 text-sm">
- <div class="products-filters-count pl-8">({{ option.nb_products }})</div>
+ <label>
+ <input type="checkbox" v-model="filters[filter.id]" :value="option.value"
+ @change="updateFilters">
+ {{ option.label }}
+ </label>
- </li>
- </ul>
+ <div class="products-filters-count pl-8">({{ option.nb_products }})</div>
- <div v-if="filter.type === 'range'">
- <vue-slider class="mt-3"
- v-model="filters[filter.id]"
- :min="parseFloat(filter.minscale)"
- :max="parseFloat(filter.maxscale)"
- :enable-cross="false"
- :contained="true"
- :lazy="true"
- :interval="(filter.maxscale - filter.minscale) / 100"
- :tooltip-formatter="filter.scale === 'log' ? log : linear"
- @change="updateFilters">
- </vue-slider>
-
- <div class="flex justify-between mt-2">
- <span class="text-xs">{{ filter.prefix }} {{ filter.min }} {{ filter.unit }}</span>
- <span class="text-xs">{{ filter.prefix }} {{ filter.max }} {{ filter.unit }}</span>
+ </li>
+ </ul>
+
+ <div v-if="filter.type === 'range'" class="pt-3">
+ <vue-slider v-model="filters[filter.id]"
+ :min="parseFloat(filter.minscale)"
+ :max="parseFloat(filter.maxscale)"
+ :enable-cross="false"
+ :contained="true"
+ :lazy="true"
+ :interval="(filter.maxscale - filter.minscale) / 100"
+ :tooltip-formatter="filter.scale === 'log' ? log : linear"
+ @change="updateFilters">
+ </vue-slider>
+
+ <div class="flex justify-between mt-2">
+ <span class="text-xs">{{ filter.prefix }} {{ filter.min }} {{ filter.unit }}</span>
+ <span class="text-xs">{{ filter.prefix }} {{ filter.max }} {{ filter.unit }}</span>
+ </div>
</div>
</div>
productType: {
required: true,
},
+ baseFilters: {
+ type: String,
+ default: '',
+ },
filterData: {
required: true,
},
data: () => ({
viewStyle: 'grid',
filters: {},
+ filterWrappers: [], // All the sets of filters in the accordion
+ currentFilterIndex: 0, // Index of the filter group that is open
matches: {},
products: [],
log: function (v) {
this.viewStyle = localStorage.viewStyle;
}
+ // Gather all the filter sets for the accordion
+ this.filterWrappers = this.$refs.filters.querySelectorAll('.filters-wrapper');
+
+ // Calculate the max-height of each filter set (required so accordion height animation can work)
+ this.filterWrappers.forEach(function(el) {
+
+ let totalHeight = 0;
+ for (let i = 0; i < el.children.length; i++) {
+ totalHeight += el.children[i].clientHeight;
+ }
+
+ el.style.maxHeight = totalHeight + 'px';
+ });
+
+ // Find the first filter set that has pre-selected filters and open it
+ let initialFilterIndex = 0;
+ for (let fi = 0; fi < this.filterData.length; fi++) {
+ let id = this.filterData[fi].id;
+ if (this.filters[id].length > 0 && this.filterData[fi].type !== 'range') {
+ initialFilterIndex = fi;
+ break;
+ }
+ }
+ this.expandFilters(initialFilterIndex);
+
},
methods: {
parseQuerystring() {
const $this = this;
- const querystring = window.baseFilters + '&' + location.search.substring(1); // Get querystring minus first character (?)
+ const querystring = this.baseFilters + '&' + location.search.substring(1); // Get querystring minus first character (?)
if (querystring.length > 0) {
querystring.split('&').forEach(function (pair) {
}
this.viewStyle = this.viewStyle === 'list' ? 'grid' : 'list';
},
+
+ expandFilters(index) {
+ // For the accordion animation to work, overflow:hidden must be set on the wrapping element.
+ // However, some filter elements like sliders don't display properly if the overflow is hidden
+ // (due to tooltips etc), so we need to set the overflow to visible on the open set of filters
+ // immediately *after* the CSS transition (300ms) ends. When a new filter set is opened, the
+ // overflow:visible override needs to be removed immediately, before the transition accordion
+ // animation begins...
+ let $this = this;
+ let previousIndex = this.currentFilterIndex;
+ this.filterWrappers[previousIndex].style.removeProperty('overflow');
+ this.currentFilterIndex = index;
+ setTimeout(function() {
+ $this.filterWrappers[index].style.overflow = 'visible';
+ }, 300); // Keep this value in sync with the CSS transition duration defined below!
+ },
},
}
</script>
+
+<style lang="stylus" scoped>
+ .products-filters
+ &.folded
+ h3
+ cursor: pointer
+ .filters-wrapper
+ max-height: 0 !important
+
+ .filters-wrapper
+ overflow: hidden
+ transition: max-height 300ms ease-out
+</style>