<div class="item-filter " data-query="">
<div class="content-section content-section--sticky item-filter__font-col">
<div class="content-section__title text-button">
<div class="item-filter__font-title">
<div class="item-filter__font-title-text">
recent fonts
</div>
<button type="button" class="button item-filter__burger-open ">
<span class="button__inner" data-text="filter and sort">
<span class="button__text">
filter and sort
</span>
</span>
</button>
</div>
</div>
<div class="content-section__content">
<div class="item-list item-filter__item-list ">
<div class="item-cell item-list__cell" data-font="" data-id="">
<a class="item-cell__content" href="#">
<div class="item-cell__main-text">
Väike mölder hüppas rongile.
</div>
<span class="item-cell__sub-text text-small">
Flex 0.1
</span>
</a>
</div>
<div class="item-cell item-list__cell" data-font="" data-id="">
<a class="item-cell__content" href="#">
<div class="item-cell__main-text">
Eriti väike mölder hüppas rongile.
</div>
<span class="item-cell__sub-text text-small">
Flex 0.1
</span>
</a>
</div>
<div class="item-cell item-list__cell" data-font="" data-id="">
<a class="item-cell__content" href="#">
<div class="item-cell__main-text">
Kõige väiksem mölder hüppas rongile.
</div>
<span class="item-cell__sub-text text-small">
Flex 0.1
</span>
</a>
</div>
<div class="item-cell item-list__cell" data-font="" data-id="">
<a class="item-cell__content" href="#">
<div class="item-cell__main-text">
Väike mölder hüppas eriti suurele rongile.
</div>
<span class="item-cell__sub-text text-small">
Flex 0.1
</span>
</a>
</div>
</div>
</div>
</div>
<div class="content-section content-section--sticky item-filter__filter-col">
<div class="content-section__title text-button">
<div class="textfield select ">
<div class="textfield__inner">
<select name="sort" id="sort" class="textfield__input select__input" data-text="Sort by">
<option value="1" selected>added (newest first)</option>
<option value="1">added (oldest first)</option>
<option value="1">name (a-z)</option>
<option value="1">name (z-a)</option>
<option value="1">year (newest first)</option>
<option value="1">year (oldest first)</option>
</select>
<svg class="icon select__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#chevron-bottom"></use>
</svg>
<label class="textfield__label text-button select__label" for="sort">
Sort by
</label>
</div>
</div>
</div>
<div class="content-section__content">
<div class="content-section item-filter__sort-content">
<div class="content-section__title text-button">filter by</div>
<div class="content-section__content">
<fieldset class="choice-group item-filter__filter-list">
<legend class="choice-group__label"></legend>
<div class="choice-group__inner">
<div class="check choice-group__item">
<input type="checkbox" id="latin" name="filter" value="" class="check__input">
<label for="latin" class="check__label">
<span class="check__indicator">
<svg class="icon check__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#checkbox-crossed-big"></use>
</svg>
</span>
<span class="check__text">Latin</span>
</label>
</div>
<div class="check choice-group__item">
<input type="checkbox" id="sans" name="filter" value="" class="check__input">
<label for="sans" class="check__label">
<span class="check__indicator">
<svg class="icon check__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#checkbox-crossed-big"></use>
</svg>
</span>
<span class="check__text">Sans Serif</span>
</label>
</div>
<div class="check choice-group__item">
<input type="checkbox" id="display" name="filter" value="" class="check__input">
<label for="display" class="check__label">
<span class="check__indicator">
<svg class="icon check__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#checkbox-crossed-big"></use>
</svg>
</span>
<span class="check__text">Display</span>
</label>
</div>
<div class="check choice-group__item">
<input type="checkbox" id="serif" name="filter" value="" class="check__input">
<label for="serif" class="check__label">
<span class="check__indicator">
<svg class="icon check__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#checkbox-crossed-big"></use>
</svg>
</span>
<span class="check__text">Serif</span>
</label>
</div>
</div>
</fieldset>
</div>
</div>
<div class="item-filter__burger-heading">switch colors</div>
<div class="theme-switcher item-filter__theme-switcher">
<button data-theme="theme-beige" class="theme-switcher__button theme-switcher__button--beige">
<span class="theme-switcher__icon-cross"></span>
</button>
<button data-theme="theme-gray" class="theme-switcher__button theme-switcher__button--gray">
<span class="theme-switcher__icon-cross"></span>
</button>
<button data-theme="theme-yellow" class="theme-switcher__button theme-switcher__button--yellow">
<span class="theme-switcher__icon-cross"></span>
</button>
<button data-theme="theme-green" class="theme-switcher__button theme-switcher__button--green">
<span class="theme-switcher__icon-cross"></span>
</button>
<button data-theme="theme-dark" class="theme-switcher__button theme-switcher__button--dark">
<span class="theme-switcher__icon-cross"></span>
</button>
</div>
</div>
</div>
<div class="item-filter__burger-header">
<button type="button" class="button button--icon item-filter__burger-close ">
<span class="button__pseudo-icon">
<svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#exit"></use>
</svg>
</span>
<span class="button__inner" data-text="">
<span class="button__text">
<svg class="icon button__icon">
<use xlink:href="../../inc/svg/global.49d3f64de1a3f5ccf16c763316702027.svg#exit"></use>
</svg>
</span>
</span>
</button>
</div>
</div>
{% set itemListTitle %}
<div class="item-filter__font-title">
{% if data.listTitle %}
<div class="item-filter__font-title-text">
{{ data.listTitle }}
</div>
{% endif %}
{% if data.burgerButton %}
{% include '@button' with {class: "item-filter__burger-open", data: data.burgerButton} %}
{% endif %}
</div>
{% endset %}
{% set itemListContent %}
{% if data.itemList %}
{% include '@item-list' with {class: "item-filter__item-list", data: data.itemList} %}
{% endif %}
{% endset %}
{% set sortTitle %}
{% if data.sort %}
{% include '@select' with {data: data.sort} %}
{% endif %}
{% if data.filters and not data.sort %}
<div class="item-filter__filter-text">{{ data.filterTitle }}</div>
{% endif %}
{% endset %}
{% set filterContent %}
{% if data.filters %}
{% include '@choice-group' with {class: "item-filter__filter-list", data: data.filters} %}
{% endif %}
{% endset %}
{% set sortContent %}
{% if data.sort %}
{% include '@content-section' with {class: "item-filter__sort-content", data: {title: data.filterTitle, content: filterContent}} %}
{% else %}
{% include '@content-section' with {class: "item-filter__sort-content item-filter__sort-content--small-top", data: {content: filterContent}} %}
{% endif %}
{% if data.themeSwitcherTitle %}
<div class="item-filter__burger-heading">{{ data.themeSwitcherTitle }}</div>
{% endif %}
{% if data.themeSwitcher %}
{% include '@theme-switcher' with {class: "item-filter__theme-switcher", data: data.themeSwitcher} %}
{% endif %}
{% endset %}
<div class="item-filter {{ class }} {{ modifier }}" data-query="{{ data.initialQuery }}">
{% include '@content-section' with {class: "item-filter__font-col", modifier: "content-section--sticky", data: {title: itemListTitle, content: itemListContent}} %}
{% include '@content-section' with {class: "item-filter__filter-col", modifier: "content-section--sticky", data: {title: sortTitle, content: sortContent}} %}
<div class="item-filter__burger-header">
{% include '@button' with {class: "item-filter__burger-close", modifier: "button--icon", data: {icon: "exit"}} %}
</div>
</div>
{
"language": "en-US",
"data": {
"listTitle": "recent fonts",
"itemList": {
"items": [
{
"mainText": "Väike mölder hüppas rongile.",
"subText": "Flex 0.1",
"link": "#"
},
{
"mainText": "Eriti väike mölder hüppas rongile.",
"subText": "Flex 0.1",
"link": "#"
},
{
"mainText": "Kõige väiksem mölder hüppas rongile.",
"subText": "Flex 0.1",
"link": "#"
},
{
"mainText": "Väike mölder hüppas eriti suurele rongile.",
"subText": "Flex 0.1",
"link": "#"
}
]
},
"filterTitle": "filter by",
"sort": {
"label": "Sort by",
"id": "sort",
"name": "sort",
"options": [
{
"name": "added (newest first)",
"value": "1",
"isSelected": true
},
{
"name": "added (oldest first)",
"value": "1"
},
{
"name": "name (a-z)",
"value": "1"
},
{
"name": "name (z-a)",
"value": "1"
},
{
"name": "year (newest first)",
"value": "1"
},
{
"name": "year (oldest first)",
"value": "1"
}
]
},
"themeSwitcherTitle": "switch colors",
"themeSwitcher": {
"palettes": [
{
"class": "beige"
},
{
"class": "gray"
},
{
"class": "yellow"
},
{
"class": "green"
},
{
"class": "dark"
}
]
},
"burgerButton": {
"text": "filter and sort"
},
"burgerCloseButton": {
"text": null,
"icon": "exit"
},
"filters": {
"type": "check",
"choices": [
{
"id": "latin",
"label": "Latin",
"name": "filter"
},
{
"id": "sans",
"label": "Sans Serif",
"name": "filter"
},
{
"id": "display",
"label": "Display",
"name": "filter"
},
{
"id": "serif",
"label": "Serif",
"name": "filter"
}
]
}
}
}
.item-filter {
display: flex;
flex: 1 1 auto;
}
.item-filter__font-col {
max-width: 100%;
@include bp(md-min) {
@include gridcol(max-width, 17, $grid-cols-md);
@include gridcol(min-width, 17, $grid-cols-md);
@include gridcol(flex-basis, 17, $grid-cols-md);
}
@include bp(lg-min) {
@include gridcol(max-width, 18, $grid-cols-md);
@include gridcol(min-width, 18, $grid-cols-md);
@include gridcol(flex-basis, 18, $grid-cols-md);
}
}
.item-filter__font-title {
display: flex;
overflow: hidden;
min-height: 38px;
@include bp(md-min) {
min-height: 58px;
}
}
.item-filter__font-title-text {
padding: 8px 16px;
flex-grow: 1;
border-right: 1px solid var(--color-brand);
transition: border-right-color $transition-duration $transition-easing;
@include bp(sm-min) {
padding: 15px 32px;
}
@include bp(md-min) {
padding: 14px 32px;
border: none;
}
}
.item-filter__burger-open {
@include bp(md-min) {
display: none;
}
}
.item-filter__sort-content {
box-shadow: none;
border: none;
top: 120px;
display: flex;
flex-direction: column;
flex-grow: 1;
@include bp(md-min) {
position: sticky; /* stylelint-disable-line plugin/no-unsupported-browser-features */
display: block;
flex-grow: 0;
}
&.item-filter__sort-content--small-top {
margin: 0;
}
}
.item-filter__filter-list {
text-transform: uppercase;
padding: 26px 18px;
@include bp(md-min) {
padding: 35px 26px;
}
}
.item-filter__burger-heading {
border-top: 1px solid var(--color-brand);
border-bottom: 1px solid var(--color-brand);
text-transform: uppercase;
padding: 6px 16px;
margin-top: 40px;
transition: $transition-duration $transition-easing;
transition-property: border-bottom-color, border-top-color;
@include bp(md-min) {
display: none;
}
}
.item-filter__burger-header {
display: flex;
position: fixed;
top: 0;
left: 0;
justify-content: flex-end;
width: 100%;
height: 40px;
background: var(--color-background);
border-bottom: 1px solid var(--color-brand);
transform: translateX(-101%);
z-index: map_get($zindex, header);
transition: $transition-duration $transition-easing;
transition-property: background-color, border-bottom-color, transform;
@include bp(sm-min) {
height: 60px;
}
@include bp(md-min) {
display: none;
}
.item-filter__filter-col.is-burger-visible + & {
transform: translateX(0);
}
}
.item-filter__burger-close {
margin-right: -1px;
box-shadow: none;
border-left: 1px solid var(--color-brand);
border-right: 1px solid var(--color-brand);
}
.item-filter__theme-switcher {
padding: 27px 17px 44px;
justify-content: flex-start;
@include bp(md-min) {
display: none;
}
}
.item-filter__filter-col {
display: flex;
position: fixed;
flex-direction: column;
top: 0;
left: 0;
margin: 0;
padding-top: 39px;
width: 100%;
height: var(--app-height);
z-index: map_get($zindex, itemListBurgerMenu);
border-bottom: 1px solid var(--color-brand);
background: var(--color-background);
transition: $transition-duration $transition-easing;
transition-property: background-color, border, transform, box-shadow;
transform: translateY(-101%);
overflow: hidden;
overflow-y: auto;
@include bp(sm-min) {
padding-top: 59px;
}
@include bp(md-min) {
position: relative;
overflow: visible;
height: auto;
z-index: map_get($zindex, default);
border-left: 1px solid var(--color-brand);
transform: translateY(0);
margin: 0;
padding-top: 0;
}
&.is-burger-visible {
transform: translateY(0);
}
}
.item-filter__filter-text {
border: none;
padding: 8px 16px;
@include bp(sm-min) {
padding: 14px 26px;
}
}
.theme-switcher {
.item-filter__burger & {
justify-content: flex-start;
margin: 24px 16px 33px;
}
}
.content-section__title {
.item-filter__font-col > &,
.item-filter__filter-col > & {
min-height: 40px;
padding: 0;
@include bp(sm-min) {
min-height: 60px;
}
}
.item-filter__filter-col > & {
position: static;
@include bp(md-min) {
position: sticky; /* stylelint-disable-line plugin/no-unsupported-browser-features */
}
}
.item-filter__sort-content & {
z-index: map_get($zindex, default);
margin-top: 40px;
@include bp(sm-min) {
margin-top: 60px;
}
@include bp(md-min) {
margin: 0;
}
}
}
.content-section__content {
.item-filter__filter-col > & {
display: flex;
flex-direction: column;
flex: 1 1;
}
.item-filter__sort-content > & {
box-shadow: none;
}
}
.select__inner {
.item-filter__filter-col & {
border: none;
@include bp(md-min) {
padding-left: 26px;
}
}
}
.choice-group__item {
.item-filter__filter-list & {
padding-bottom: 20px;
margin: 0;
@include bp(md-min) {
padding-bottom: 30px;
}
}
}
import './item-filter.scss';
import Component from '@component';
import Helpers from '@helpers';
import Ajax, {IAjaxPostParams} from '@ajax';
interface IItemFilterSettings {
isVisibleClass: string;
burgerOpenClass: string;
burgerCloseClass: string;
burgerMenuClass: string;
fontItemClass: string;
fontListClass: string;
filtersClass: string;
}
interface ISearchParams {
filters?: string[];
authors?: string[];
names?: string[];
}
export default class ItemFilter extends Component {
static initSelector: string = '.item-filter';
settings: IItemFilterSettings;
burgerMenu: JQuery;
openButton: JQuery;
closeButton: JQuery;
fontList: JQuery;
filters: JQuery;
waitingResponse: boolean;
loadedPages: number;
activeFilters: string[];
defaultQuery: string;
constructor(element: HTMLElement) {
super(element);
this.settings = {
burgerCloseClass: 'item-filter__burger-close',
burgerMenuClass: 'item-filter__filter-col',
burgerOpenClass: 'item-filter__burger-open',
filtersClass: 'item-filter__filter-list',
fontItemClass: 'item-list__cell',
fontListClass: 'item-filter__item-list',
isVisibleClass: 'is-burger-visible',
};
this.burgerMenu = this.element.find('.' + this.settings.burgerMenuClass);
this.openButton = this.element.find('.' + this.settings.burgerOpenClass);
this.closeButton = this.element.find('.' + this.settings.burgerCloseClass);
this.fontList = this.element.find('.' + this.settings.fontListClass);
this.filters = this.element.find('.' + this.settings.filtersClass);
this.waitingResponse = false;
this.loadedPages = 1;
this.activeFilters = [];
this.defaultQuery = this.element.data('query');
this.init();
}
init(): void {
this.openButton.on('click', this.openBurger.bind(this));
this.closeButton.on('click', this.closeBurger.bind(this));
this.filters.on('change', this.filterHandler.bind(this));
document.addEventListener('scroll', this.scrollHandler.bind(this));
window.addEventListener('resize', this.resizeHandler.bind(this));
const hashes : string[] = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
if (window.location.href.indexOf('?') === -1) {
this.loadFonts();
} else {
const search: string = hashes[0].split('=')[1].replace('+', '-');
this.loadFonts(search);
}
}
filterHandler(): void {
const checkedFilters: string[] = [];
this.filters.find('input').each((index: number, element: HTMLInputElement): void => {
if (element.checked) {
checkedFilters.push(element.value);
}
});
this.activeFilters = checkedFilters;
this.clearFonts();
this.loadFonts();
}
scrollHandler(): void {
const fontItems: JQuery = this.element.find('.' + this.settings.fontItemClass).last();
if (fontItems.length > 0) {
const percent: number = fontItems[0].getBoundingClientRect().bottom / window.innerHeight;
if (percent < 0.7 && !this.waitingResponse) {
this.loadFonts();
}
} else {
if (!this.waitingResponse) {
this.loadFonts();
}
}
}
clearFonts(): void {
if (!this.waitingResponse) {
Helpers.clearFontFaces();
this.fontList.children().remove();
this.loadedPages = 1;
}
}
loadFonts(search: string = ''): void {
const searchParams: Record<string, string[]> = {};
if (this.defaultQuery !== '') {
const params: string[] = this.defaultQuery.split(';');
const keyValuePairs: { key: string, value: string[] }[] = params.map((param: string): { key: string, value: string[] } => {
const split: string[] = param.split('=');
const key: string = split[0];
const values: string[] = split[1].split(',');
return {
key,
value: values,
};
});
keyValuePairs.forEach((pair: { key: string, value: string[] }): void => {
searchParams[pair.key] = pair.value;
});
}
searchParams.filters = this.activeFilters;
const formData: IAjaxPostParams = {
action: 'loadList',
page: this.loadedPages,
searchParams,
};
if (search !== '') {
formData.search = search;
}
this.waitingResponse = true;
Ajax.post(formData).then((response: {items: string[]}) => {
response.items.forEach((font: string): void => {
const item: JQuery = this.fontList.append(font).children().last();
const fontFamily: string = Helpers.addFontFace(item.data('font'), item.data('id'));
item.children().children().first().css('font-family', fontFamily);
});
this.loadedPages++;
this.waitingResponse = false;
});
}
resizeHandler(): void {
if (window.matchMedia('only screen and (min-width: 1024px)').matches) {
this.closeBurger();
}
}
openBurger(): void {
this.burgerMenu.addClass(this.settings.isVisibleClass);
Helpers.disableScroll();
}
closeBurger(): void {
this.burgerMenu.removeClass(this.settings.isVisibleClass);
Helpers.enableScroll();
}
}