Выбор временного диапазона

Построение выбора временного диапазона в интерфейсах на базе Choices.js требует понимания того, как библиотека управляет списками опций, как динамически изменять содержимое селекта и каким образом интегрировать пользовательские сценарии, связанные с фильтрацией данных по времени.

Choices.js представляет собой надстройку над стандартными <select> и <input> элементами, позволяющую создавать расширенные интерфейсы выбора с поиском, тегами, кастомными шаблонами и управляемым состоянием. При работе с временными диапазонами библиотека используется не для хранения дат как таковых, а для предоставления пользователю предопределённых или динамически формируемых интервалов.

Временной диапазон в пользовательском интерфейсе обычно представлен набором предустановленных значений:

  • последние 24 часа
  • последние 7 дней
  • последние 30 дней
  • текущий месяц
  • предыдущий месяц
  • произвольный диапазон

Choices.js позволяет моделировать такой список через массив объектов choices, где каждый элемент содержит значение и отображаемый текст.

const timeRanges = [
  { value: 'last_24h', label: 'Последние 24 часа' },
  { value: 'last_7d', label: 'Последние 7 дней' },
  { value: 'last_30d', label: 'Последние 30 дней' },
  { value: 'this_month', label: 'Текущий месяц' },
  { value: 'prev_month', label: 'Предыдущий месяц' },
  { value: 'custom', label: 'Произвольный диапазон' }
];

Инициализация Choices.js на основе этого массива выполняется через стандартный конструктор.

const select = document.querySelector('#timeRange');

const choices = new Choices(select, {
  searchEnabled: false,
  shouldSort: false,
  itemSelectText: '',
  choices: timeRanges
});

Управление предустановленными значениями

Временные диапазоны часто используются как фильтр данных в аналитических панелях. В таких сценариях важно фиксировать значения и предотвращать их сортировку или изменение порядка.

Параметр shouldSort: false критичен, поскольку сортировка по алфавиту нарушает логическую последовательность временных интервалов.

Также часто отключается поиск:

searchEnabled: false

Это связано с тем, что количество вариантов обычно ограничено и не требует фильтрации.

Реакция на выбор диапазона

Ключевой аспект — обработка изменения значения. Choices.js предоставляет событие change, которое позволяет реагировать на выбор пользователя.

select.addEventListener('change', (event) => {
  const selected = event.detail.value;

  applyTimeFilter(selected);
});

Функция фильтрации может интерпретировать значение и преобразовывать его в реальные даты.

function applyTimeFilter(range) {
  const now = new Date();
  let startDate, endDate = now;

  switch (range) {
    case 'last_24h':
      startDate = new Date(now.getTime() - 24 * 60 * 60 * 1000);
      break;

    case 'last_7d':
      startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
      break;

    case 'last_30d':
      startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
      break;

    case 'this_month':
      startDate = new Date(now.getFullYear(), now.getMonth(), 1);
      break;

    case 'prev_month':
      startDate = new Date(now.getFullYear(), now.getMonth() - 1, 1);
      endDate = new Date(now.getFullYear(), now.getMonth(), 0);
      break;

    default:
      return;
  }

  fetchData({ startDate, endDate });
}

Реализация пользовательского диапазона

Вариант custom требует дополнительного интерфейса ввода дат. Choices.js не предназначен для выбора дат напрямую, поэтому используется комбинированный подход: селект + отдельные input type=“date”.

<select id="timeRange"></select>

<div id="customRange" style="display:none;">
  <input type="date" id="startDate">
  <input type="date" id="endDate">
</div>

Логика отображения:

const customRangeBlock = document.querySelector('#customRange');

select.addEventListener('change', (event) => {
  const value = event.detail.value;

  if (value === 'custom') {
    customRangeBlock.style.display = 'block';
  } else {
    customRangeBlock.style.display = 'none';
    applyTimeFilter(value);
  }
});

Дополнительная обработка применяется при изменении дат вручную:

document.querySelector('#endDate').addEventListener('change', () => {
  const start = document.querySelector('#startDate').value;
  const end = document.querySelector('#endDate').value;

  if (start && end) {
    fetchData({
      startDate: new Date(start),
      endDate: new Date(end)
    });
  }
});

Динамическое обновление диапазонов

Choices.js позволяет изменять список опций после инициализации. Это важно при сценариях, где временные диапазоны зависят от контекста пользователя или типа данных.

choices.setChoices([
  { value: 'today', label: 'Сегодня' },
  { value: 'yesterday', label: 'Вчера' },
  { value: 'last_7d', label: 'Последние 7 дней' }
], 'value', 'label', true);

Флаг true очищает предыдущие значения, заменяя их новым набором.

Интеграция с серверной фильтрацией

При работе с большими объёмами данных временной диапазон почти всегда связан с серверным API. Значение из Choices.js передаётся как параметр запроса.

async function fetchData({ startDate, endDate }) {
  const response = await fetch('/api/data', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ startDate, endDate })
  });

  const data = await response.json();
  renderChart(data);
}

Такой подход разгружает клиентскую сторону и позволяет обрабатывать данные на сервере с использованием индексов по датам.

Стилизация временных диапазонов

Choices.js позволяет изменять внешний вид выпадающего списка через CSS-классы. Для временных диапазонов часто выделяют логические группы, например быстрые пресеты и пользовательский режим.

.choices__item--selectable[data-value="custom"] {
  font-weight: 600;
  color: #2b6cb0;
}

Это помогает визуально отделить стандартные интервалы от расширенных режимов.

Группировка диапазонов

При увеличении количества вариантов используется группировка через optgroup-подобную структуру.

choices.setChoices([
  {
    label: 'Короткие интервалы',
    choices: [
      { value: 'today', label: 'Сегодня' },
      { value: 'yesterday', label: 'Вчера' }
    ]
  },
  {
    label: 'Долгие интервалы',
    choices: [
      { value: 'last_7d', label: 'Последние 7 дней' },
      { value: 'last_30d', label: 'Последние 30 дней' }
    ]
  }
], 'value', 'label', false);

Такое разделение улучшает восприятие при большом количестве временных фильтров.

Управление состоянием выбора

Choices.js хранит текущее значение внутри компонента, но для синхронизации с приложением часто требуется принудительная установка значения.

choices.setChoiceByValue('last_7d');

Это полезно при восстановлении состояния фильтров после перезагрузки страницы или при переключении вкладок интерфейса.

Обработка крайних случаев

При работе с временными диапазонами возникают ситуации, требующие дополнительной логики:

  • отсутствие данных за выбранный период
  • некорректные пользовательские даты
  • пересечение диапазонов при сравнении
  • ограничения по максимальному периоду

Choices.js не решает эти задачи напрямую, но обеспечивает стабильный слой выбора значения, который затем интерпретируется бизнес-логикой приложения.

function validateRange(start, end) {
  const maxRange = 365 * 24 * 60 * 60 * 1000;

  if (end - start > maxRange) {
    throw new Error('Превышен максимально допустимый диапазон');
  }
}

Связь с визуализацией данных

Временной диапазон, выбранный через Choices.js, часто напрямую связан с графиками и аналитическими компонентами. После изменения значения триггерится пересчёт данных и обновление визуализации.

function onRangeChange(range) {
  applyTimeFilter(range)
    .then(data => updateDashboard(data));
}

Такой механизм делает Choices.js частью реактивного интерфейса, где селект выступает управляющим элементом фильтрации временных данных.