PathLogs UI
English

Виджеты

FilterBar

Панель фильтров, собираемая из описания полей.

Панель фильтров, собираемая из описания полей. Что фильтровать, панель не знает — знает массив fields. Поэтому новое условие добавляется одной записью, а не правкой пяти файлов.

Установка

terminal
npx @toimetdev/pathlogs-ui add filter-bar

Пример

Сохранённые:
  • UI-12БагИмпорт досок из Trello падает на больших проектахМТ
  • UI-14ФичаЖивые обновления доски по SSEАСДК
  • UI-15РефакторингВынести разбор Markdown в отдельный модульДК
  • UI-16ФичаWIP-лимиты у колонокМТ
  • UI-17ФичаКритический путь на диаграмме ГантаАС
  • UI-9БагТултипы обрезались в колонках доскиДК

Описание полей

Поле — это подпись, тип контрола и функция «подходит ли элемент». Готовые матчеры покрывают почти всё:

tsx
import {
  textMatcher, equalsMatcher, includesMatcher, type FilterField,
} from "@/components/ui/filter-bar/filterModel";

export const taskFilterFields: FilterField<Task>[] = [
  {
    key: "q", label: "Поиск", kind: "text", placeholder: "название или номер",
    matches: textMatcher((t) => [t.title, t.number]),
  },
  {
    key: "status", label: "Статус", kind: "select", anyLabel: "Любой",
    options: statusOptions,
    matches: equalsMatcher((t) => t.status),
  },
  {
    key: "assignee", label: "Исполнитель", kind: "select", anyLabel: "Любой",
    options: members.map((m) => ({ value: m.id, label: m.name })),
    matches: includesMatcher((t) => t.assignees),
  },
];
textMatcher(pick: (item) => (string | number | null)[]) => Matcher

Подстрока без учёта регистра сразу по нескольким свойствам: «12» находит задачу №12, «оплат» — «Страница оплаты».

equalsMatcher(pick: (item) => string | null) => Matcher

Точное совпадение одного свойства.

includesMatcher(pick: (item) => { id: string }[]) => Matcher

Значение есть среди связанных сущностей: исполнители, метки.

Свой матчер — обычная функция (item, value) => boolean: диапазон дат, «просрочено», «без исполнителя».

Строка запроса

Состояние сериализуется в обычный query string. В этом же виде оно лежит в сохранённых пресетах и в адресной строке:

tsx
serializeFilter(fields, filter);  // "status=TODO&assignee=u1"
parseFilter(fields, query);       // обратно в состояние

Поэтому ссылка на отфильтрованный список открывается ровно тем же, чем была, а сохранённый фильтр — это просто строка в базе.

Пропсы

fields*FilterField<T>[]

Описание полей.

value*FilterState

Текущее состояние.

onChange*(next: FilterState) => void

Изменение.

savedFilters{ id, name, query }[]

Пресеты. Без обработчиков они только применяются.

onSaveFilter(name, query) => void | Promise

Сохранение пресета. Без него нет кнопки «сохранить фильтр».

onDeleteFilter(id: string) => void | Promise

Удаление пресета.

matchedCount / totalCountnumber

Сколько прошло отбор и сколько всего — подпись под панелью.

compactboolean

по умолчанию: false

Плотная раскладка: панель встраивается в шапку, а не стоит отдельно.

labelsFilterBarLabels

Подписи. По умолчанию английские.