|
|
@@ -0,0 +1,631 @@
|
|
|
+# Компонент FilterSort - Полная документация
|
|
|
+
|
|
|
+## Назначение компонента
|
|
|
+
|
|
|
+`FilterSort` - универсальный компонент для фильтрации и сортировки данных в приложении. Предоставляет интуитивно понятный интерфейс для применения множественных фильтров, сортировки результатов и управления состоянием данных. Компонент полностью настраиваемый и поддерживает различные типы фильтров.
|
|
|
+
|
|
|
+## Импорт и регистрация
|
|
|
+
|
|
|
+```coffee
|
|
|
+# В основном файле приложения (app/temp.coffee)
|
|
|
+components:
|
|
|
+ 'filter-sort': require 'app/shared/FilterSort'
|
|
|
+```
|
|
|
+
|
|
|
+## Базовое использование
|
|
|
+
|
|
|
+### 1. Простая фильтрация
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="filtersConfig"
|
|
|
+ :sort-options="sortOptions"
|
|
|
+ @filter-change="handleFilterChange"
|
|
|
+ @sort-change="handleSortChange"
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 2. С предустановленными значениями
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="filtersConfig"
|
|
|
+ :sort-options="sortOptions"
|
|
|
+ :initial-filters="{ category: 'music', status: 'active' }"
|
|
|
+ :initial-sort="'date_desc'"
|
|
|
+ @change="handleFilterSortChange"
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+## Полный список props
|
|
|
+
|
|
|
+### `filters` (обязательный)
|
|
|
+- **Тип:** `Array<Object>`
|
|
|
+- **Описание:** Конфигурация фильтров
|
|
|
+- **Структура фильтра:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ key: 'category', # Уникальный ключ фильтра
|
|
|
+ type: 'select', # Тип фильтра: select, multiselect, range, date, search
|
|
|
+ label: 'Категория', # Отображаемое название
|
|
|
+ options: [ # Опции для select/multiselect
|
|
|
+ { value: 'music', label: 'Музыка' },
|
|
|
+ { value: 'art', label: 'Искусство' }
|
|
|
+ ],
|
|
|
+ placeholder: 'Выберите категорию', # Плейсхолдер
|
|
|
+ multiple: false, # Множественный выбор
|
|
|
+ searchable: true # Поиск в опциях
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+### `sortOptions` (обязательный)
|
|
|
+- **Тип:** `Array<Object>`
|
|
|
+- **Описание:** Опции сортировки
|
|
|
+- **Структура:**
|
|
|
+ ```coffee
|
|
|
+ [
|
|
|
+ { value: 'date_asc', label: 'Дата (сначала старые)' },
|
|
|
+ { value: 'date_desc', label: 'Дата (сначала новые)' },
|
|
|
+ { value: 'title_asc', label: 'Название (А-Я)' },
|
|
|
+ { value: 'title_desc', label: 'Название (Я-А)' }
|
|
|
+ ]
|
|
|
+ ```
|
|
|
+
|
|
|
+### `initialFilters` (опциональный)
|
|
|
+- **Тип:** `Object`
|
|
|
+- **По умолчанию:** `{}`
|
|
|
+- **Описание:** Начальные значения фильтров
|
|
|
+- **Пример:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ category: 'music',
|
|
|
+ price: { min: 100, max: 500 },
|
|
|
+ status: ['active', 'upcoming']
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+### `initialSort` (опциональный)
|
|
|
+- **Тип:** `String`
|
|
|
+- **По умолчанию:** Первая опция из `sortOptions`
|
|
|
+- **Описание:** Начальная сортировка
|
|
|
+
|
|
|
+### `debounceDelay` (опциональный)
|
|
|
+- **Тип:** `Number`
|
|
|
+- **По умолчанию:** `300`
|
|
|
+- **Описание:** Задержка debounce для событий изменения (мс)
|
|
|
+
|
|
|
+### `showReset` (опциональный)
|
|
|
+- **Тип:** `Boolean`
|
|
|
+- **По умолчанию:** `true`
|
|
|
+- **Описание:** Показывать кнопку сброса фильтров
|
|
|
+
|
|
|
+### `compact` (опциональный)
|
|
|
+- **Тип:** `Boolean`
|
|
|
+- **По умолчанию:** `false`
|
|
|
+- **Описание:** Компактный режим отображения
|
|
|
+
|
|
|
+### `mobileFriendly` (опциональный)
|
|
|
+- **Тип:** `Boolean`
|
|
|
+- **По умолчанию:** `true`
|
|
|
+- **Описание:** Адаптивность для мобильных устройств
|
|
|
+
|
|
|
+## События (Events)
|
|
|
+
|
|
|
+### `@filter-change`
|
|
|
+- **Описание:** Изменение значений фильтров
|
|
|
+- **Payload:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ filters: Object, # Текущие значения всех фильтров
|
|
|
+ changedFilter: String # Ключ измененного фильтра
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+### `@sort-change`
|
|
|
+- **Описание:** Изменение сортировки
|
|
|
+- **Payload:** `String` - значение выбранной сортировки
|
|
|
+
|
|
|
+### `@change`
|
|
|
+- **Описание:** Общее событие изменения (фильтры + сортировка)
|
|
|
+- **Payload:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ filters: Object, # Текущие значения фильтров
|
|
|
+ sort: String, # Текущая сортировка
|
|
|
+ hasActiveFilters: Boolean # Есть ли активные фильтры
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+### `@reset`
|
|
|
+- **Описание:** Сброс всех фильтров и сортировки
|
|
|
+- **Payload:** `null`
|
|
|
+
|
|
|
+## Слоты (Slots)
|
|
|
+
|
|
|
+### `[filter-{key}]` (кастомные фильтры)
|
|
|
+- **Описание:** Слот для кастомной реализации фильтра
|
|
|
+- **Props:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ filter: Object, # Конфигурация фильтра
|
|
|
+ value: Any, # Текущее значение
|
|
|
+ update: Function # Функция обновления значения
|
|
|
+ }
|
|
|
+ ```
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ template([filter-custom]="{ filter, value, update }")
|
|
|
+ div(class="custom-filter")
|
|
|
+ input(
|
|
|
+ :value="value"
|
|
|
+ @input="update($event.target.value)"
|
|
|
+ :placeholder="filter.placeholder"
|
|
|
+ )
|
|
|
+ ```
|
|
|
+
|
|
|
+### `[before-filters]`
|
|
|
+- **Описание:** Контент перед блоком фильтров
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ template([before-filters])
|
|
|
+ div(class="text-sm text-gray-500 mb-4") Используйте фильтры для уточнения результатов
|
|
|
+ ```
|
|
|
+
|
|
|
+### `[after-filters]`
|
|
|
+- **Описание:** Контент после блока фильтров
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ template([after-filters])
|
|
|
+ div(class="text-xs text-gray-400 mt-2") Найдено результатов: {{ totalCount }}
|
|
|
+ ```
|
|
|
+
|
|
|
+### `[sort-label]`
|
|
|
+- **Описание:** Кастомная метка для селектора сортировки
|
|
|
+- **Props:** `{ currentSort: Object }`
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ template([sort-label]="{ currentSort }")
|
|
|
+ span Сортировка: {{ currentSort.label }}
|
|
|
+ ```
|
|
|
+
|
|
|
+## Типы фильтров
|
|
|
+
|
|
|
+### 1. Select (одиночный выбор)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'category',
|
|
|
+ type: 'select',
|
|
|
+ label: 'Категория',
|
|
|
+ options: [
|
|
|
+ { value: 'music', label: 'Музыка' },
|
|
|
+ { value: 'art', label: 'Искусство' }
|
|
|
+ ],
|
|
|
+ placeholder: 'Выберите категорию'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Multiselect (множественный выбор)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'tags',
|
|
|
+ type: 'multiselect',
|
|
|
+ label: 'Теги',
|
|
|
+ multiple: true,
|
|
|
+ options: [
|
|
|
+ { value: 'classical', label: 'Классика' },
|
|
|
+ { value: 'jazz', label: 'Джаз' }
|
|
|
+ ]
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Range (диапазон значений)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'price',
|
|
|
+ type: 'range',
|
|
|
+ label: 'Цена',
|
|
|
+ min: 0,
|
|
|
+ max: 1000,
|
|
|
+ step: 50,
|
|
|
+ unit: 'TJS'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Date (дата)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'event_date',
|
|
|
+ type: 'date',
|
|
|
+ label: 'Дата мероприятия',
|
|
|
+ format: 'YYYY-MM-DD'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 5. Search (поиск)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'search',
|
|
|
+ type: 'search',
|
|
|
+ label: 'Поиск',
|
|
|
+ placeholder: 'Введите запрос...'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 6. Boolean (логический)
|
|
|
+```coffee
|
|
|
+{
|
|
|
+ key: 'featured',
|
|
|
+ type: 'boolean',
|
|
|
+ label: 'Только избранные',
|
|
|
+ trueLabel: 'Да',
|
|
|
+ falseLabel: 'Нет'
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Примеры использования
|
|
|
+
|
|
|
+### 1. Фильтрация мероприятий
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="eventFilters"
|
|
|
+ :sort-options="eventSortOptions"
|
|
|
+ :initial-filters="initialEventFilters"
|
|
|
+ @change="handleEventsFilterChange"
|
|
|
+ class="mb-6"
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+```coffee
|
|
|
+data: ->
|
|
|
+ eventFilters: [
|
|
|
+ {
|
|
|
+ key: 'category'
|
|
|
+ type: 'select'
|
|
|
+ label: 'Категория'
|
|
|
+ options: [
|
|
|
+ { value: 'music', label: 'Музыка' },
|
|
|
+ { value: 'art', label: 'Искусство' },
|
|
|
+ { value: 'theater', label: 'Театр' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'price_range'
|
|
|
+ type: 'range'
|
|
|
+ label: 'Цена'
|
|
|
+ min: 0
|
|
|
+ max: 1000
|
|
|
+ step: 100
|
|
|
+ unit: 'TJS'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'date'
|
|
|
+ type: 'date'
|
|
|
+ label: 'Дата'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'status'
|
|
|
+ type: 'multiselect'
|
|
|
+ label: 'Статус'
|
|
|
+ options: [
|
|
|
+ { value: 'upcoming', label: 'Предстоящие' },
|
|
|
+ { value: 'ongoing', label: 'Текущие' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ eventSortOptions: [
|
|
|
+ { value: 'date_asc', label: 'Дата (сначала старые)' },
|
|
|
+ { value: 'date_desc', label: 'Дата (сначала новые)' },
|
|
|
+ { value: 'price_asc', label: 'Цена (по возрастанию)' },
|
|
|
+ { value: 'price_desc', label: 'Цена (по убыванию)' },
|
|
|
+ { value: 'title_asc', label: 'Название (А-Я)' }
|
|
|
+ ]
|
|
|
+
|
|
|
+ initialEventFilters:
|
|
|
+ status: ['upcoming']
|
|
|
+
|
|
|
+methods:
|
|
|
+ handleEventsFilterChange: ({ filters, sort, hasActiveFilters }) ->
|
|
|
+ # Загрузка отфильтрованных данных
|
|
|
+ @loadEvents(filters, sort)
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Фильтрация товаров
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="productFilters"
|
|
|
+ :sort-options="productSortOptions"
|
|
|
+ @filter-change="handleProductFilterChange"
|
|
|
+ compact
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+```coffee
|
|
|
+data: ->
|
|
|
+ productFilters: [
|
|
|
+ {
|
|
|
+ key: 'category'
|
|
|
+ type: 'select'
|
|
|
+ label: 'Категория'
|
|
|
+ options: [
|
|
|
+ { value: 'clothing', label: 'Одежда' },
|
|
|
+ { value: 'souvenirs', label: 'Сувениры' },
|
|
|
+ { value: 'music', label: 'Музыка' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'size'
|
|
|
+ type: 'multiselect'
|
|
|
+ label: 'Размер'
|
|
|
+ options: [
|
|
|
+ { value: 's', label: 'S' },
|
|
|
+ { value: 'm', label: 'M' },
|
|
|
+ { value: 'l', label: 'L' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'price'
|
|
|
+ type: 'range'
|
|
|
+ label: 'Цена'
|
|
|
+ min: 0
|
|
|
+ max: 500
|
|
|
+ step: 50
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'in_stock'
|
|
|
+ type: 'boolean'
|
|
|
+ label: 'В наличии'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Поиск в блоге
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="blogFilters"
|
|
|
+ :sort-options="blogSortOptions"
|
|
|
+ @change="handleBlogFilterChange"
|
|
|
+ mobile-friendly
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+```coffee
|
|
|
+data: ->
|
|
|
+ blogFilters: [
|
|
|
+ {
|
|
|
+ key: 'search'
|
|
|
+ type: 'search'
|
|
|
+ label: 'Поиск'
|
|
|
+ placeholder: 'Поиск по статьям...'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'author'
|
|
|
+ type: 'select'
|
|
|
+ label: 'Автор'
|
|
|
+ options: [
|
|
|
+ { value: 'admin', label: 'Администрация' },
|
|
|
+ { value: 'editor', label: 'Редактор' }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'tags'
|
|
|
+ type: 'multiselect'
|
|
|
+ label: 'Теги'
|
|
|
+ options: [
|
|
|
+ { value: 'news', label: 'Новости' },
|
|
|
+ { value: 'events', label: 'События' },
|
|
|
+ { value: 'culture', label: 'Культура' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Кастомный фильтр через слот
|
|
|
+```pug
|
|
|
+filter-sort(:filters="customFilters" @change="handleChange")
|
|
|
+ template([filter-custom]="{ filter, value, update }")
|
|
|
+ div(class="custom-rating-filter")
|
|
|
+ h4 {{ filter.label }}
|
|
|
+ div(class="rating-stars")
|
|
|
+ span(
|
|
|
+ v-for="star in [1,2,3,4,5]"
|
|
|
+ :key="star"
|
|
|
+ @click="update(star)"
|
|
|
+ :class="{ 'text-yellow-400': star <= value }"
|
|
|
+ class="cursor-pointer"
|
|
|
+ ) ★
|
|
|
+
|
|
|
+ template([before-filters])
|
|
|
+ div(class="bg-blue-50 p-4 rounded-lg mb-4")
|
|
|
+ h3(class="text-lg font-semibold") Уточните поиск
|
|
|
+ p Используйте фильтры для точного поиска
|
|
|
+```
|
|
|
+
|
|
|
+## Методы компонента (refs)
|
|
|
+
|
|
|
+### `reset()`
|
|
|
+- **Описание:** Сброс всех фильтров и сортировки
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ filter-sort(ref="filterComponent" :filters="filters")
|
|
|
+ button(@click="$refs.filterComponent.reset()") Сбросить
|
|
|
+ ```
|
|
|
+
|
|
|
+### `getCurrentState()`
|
|
|
+- **Описание:** Получение текущего состояния фильтров и сортировки
|
|
|
+- **Возвращает:**
|
|
|
+ ```coffee
|
|
|
+ {
|
|
|
+ filters: Object,
|
|
|
+ sort: String,
|
|
|
+ hasActiveFilters: Boolean
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+### `setFilter(key, value)`
|
|
|
+- **Описание:** Установка значения конкретного фильтра
|
|
|
+- **Параметры:** `key` (String), `value` (Any)
|
|
|
+- **Пример:**
|
|
|
+ ```coffee
|
|
|
+ @$refs.filterComponent.setFilter('category', 'music')
|
|
|
+ ```
|
|
|
+
|
|
|
+### `clearFilter(key)`
|
|
|
+- **Описание:** Очистка конкретного фильтра
|
|
|
+- **Параметр:** `key` (String)
|
|
|
+
|
|
|
+## Интеграция с загрузкой данных
|
|
|
+
|
|
|
+### 1. Использование с AppDB
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ loadFilteredEvents: (filters, sort) ->
|
|
|
+ try
|
|
|
+ options = {}
|
|
|
+
|
|
|
+ # Преобразование фильтров для базы данных
|
|
|
+ if filters.category
|
|
|
+ options.category = filters.category
|
|
|
+
|
|
|
+ if filters.price_range
|
|
|
+ options.minPrice = filters.price_range.min
|
|
|
+ options.maxPrice = filters.price_range.max
|
|
|
+
|
|
|
+ if filters.search
|
|
|
+ options.searchQuery = filters.search
|
|
|
+
|
|
|
+ # Добавление сортировки
|
|
|
+ options.sort = sort
|
|
|
+
|
|
|
+ @events = await AppDB.getEvents(options)
|
|
|
+
|
|
|
+ catch error
|
|
|
+ debug.log "Ошибка загрузки мероприятий: "+error
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Дебаунс для производительности
|
|
|
+```coffee
|
|
|
+data: ->
|
|
|
+ filterTimeout: null
|
|
|
+
|
|
|
+methods:
|
|
|
+ handleFilterChange: ({ filters, sort }) ->
|
|
|
+ # Отмена предыдущего таймера
|
|
|
+ if @filterTimeout
|
|
|
+ clearTimeout(@filterTimeout)
|
|
|
+
|
|
|
+ # Установка нового таймера
|
|
|
+ @filterTimeout = setTimeout(=>
|
|
|
+ @loadData(filters, sort)
|
|
|
+ , 500)
|
|
|
+```
|
|
|
+
|
|
|
+## Стилизация и кастомизация
|
|
|
+
|
|
|
+### CSS классы для кастомизации
|
|
|
+```styl
|
|
|
+.filter-sort
|
|
|
+ // Основной контейнер
|
|
|
+ &__container
|
|
|
+ @apply bg-white rounded-lg shadow-sm border
|
|
|
+
|
|
|
+ // Группа фильтров
|
|
|
+ &__filters
|
|
|
+ @apply p-4 space-y-4
|
|
|
+
|
|
|
+ // Отдельный фильтр
|
|
|
+ &__filter
|
|
|
+ @apply space-y-2
|
|
|
+
|
|
|
+ // Метка фильтра
|
|
|
+ &__label
|
|
|
+ @apply block text-sm font-medium text-gray-700
|
|
|
+
|
|
|
+ // Контролы фильтра
|
|
|
+ &__control
|
|
|
+ @apply w-full rounded-md border-gray-300 shadow-sm
|
|
|
+
|
|
|
+ // Блок сортировки
|
|
|
+ &__sort
|
|
|
+ @apply border-t border-gray-200 px-4 py-3 bg-gray-50
|
|
|
+
|
|
|
+ // Кнопка сброса
|
|
|
+ &__reset
|
|
|
+ @apply text-sm text-blue-600 hover:text-blue-800
|
|
|
+```
|
|
|
+
|
|
|
+### Темная тема
|
|
|
+```styl
|
|
|
+@media (prefers-color-scheme: dark)
|
|
|
+ .filter-sort
|
|
|
+ &__container
|
|
|
+ @apply bg-gray-800 border-gray-700
|
|
|
+
|
|
|
+ &__label
|
|
|
+ @apply text-gray-300
|
|
|
+
|
|
|
+ &__control
|
|
|
+ @apply bg-gray-700 border-gray-600 text-white
|
|
|
+
|
|
|
+ &__sort
|
|
|
+ @apply border-gray-700 bg-gray-900
|
|
|
+```
|
|
|
+
|
|
|
+## Best Practices
|
|
|
+
|
|
|
+### 1. Оптимизация производительности
|
|
|
+```coffee
|
|
|
+# Используйте debounce для частых изменений
|
|
|
+filter-sort(
|
|
|
+ :filters="filters"
|
|
|
+ :debounce-delay="500"
|
|
|
+ @change="handleFilterChange"
|
|
|
+)
|
|
|
+
|
|
|
+# Ленивая загрузка опций
|
|
|
+{
|
|
|
+ key: 'category',
|
|
|
+ type: 'select',
|
|
|
+ label: 'Категория',
|
|
|
+ options: async () => await loadCategories() # Функция загрузки
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Валидация фильтров
|
|
|
+```coffee
|
|
|
+# Проверка перед применением
|
|
|
+handleFilterChange: ({ filters }) ->
|
|
|
+ if filters.price_range?.min > filters.price_range?.max
|
|
|
+ @showError('Минимальная цена не может быть больше максимальной')
|
|
|
+ return
|
|
|
+
|
|
|
+ @applyFilters(filters)
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Сохранение состояния
|
|
|
+```coffee
|
|
|
+# Сохранение в localStorage
|
|
|
+handleFilterChange: ({ filters, sort }) ->
|
|
|
+ localStorage.setItem('eventFilters', JSON.stringify(filters))
|
|
|
+ localStorage.setItem('eventSort', sort)
|
|
|
+
|
|
|
+# Восстановление при загрузке
|
|
|
+created: ->
|
|
|
+ savedFilters = localStorage.getItem('eventFilters')
|
|
|
+ if savedFilters
|
|
|
+ @initialFilters = JSON.parse(savedFilters)
|
|
|
+
|
|
|
+ savedSort = localStorage.getItem('eventSort')
|
|
|
+ if savedSort
|
|
|
+ @initialSort = savedSort
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Доступность (a11y)
|
|
|
+```pug
|
|
|
+filter-sort(
|
|
|
+ :filters="filters"
|
|
|
+ aria-label="Фильтрация и сортировка контента"
|
|
|
+)
|
|
|
+ template([before-filters])
|
|
|
+ h2(id="filter-heading" class="sr-only") Фильтрация и сортировка
|
|
|
+```
|
|
|
+
|
|
|
+Компонент FilterSort предоставляет мощный и гибкий инструмент для управления фильтрацией и сортировкой данных в приложении. Благодаря модульной архитектуре и богатым возможностям кастомизации, он может быть адаптирован для любых сценариев использования - от простых списков до сложных систем поиска.
|