Сортировка внутри групп

Группировка в Tom Select строится вокруг сущности optgroup, которая задаёт логическое объединение элементов списка. Каждый элемент может принадлежать одной группе через поле optgroup, а сами группы описываются отдельно через optgroups.

Базовая структура данных:

const select = new TomSelect('#select', {
  options: [
    { value: 'js', text: 'JavaScript', optgroup: 'frontend' },
    { value: 'ts', text: 'TypeScript', optgroup: 'frontend' },
    { value: 'node', text: 'Node.js', optgroup: 'backend' },
    { value: 'go', text: 'Go', optgroup: 'backend' }
  ],
  optgroups: [
    { value: 'frontend', label: 'Frontend' },
    { value: 'backend', label: 'Backend' }
  ]
});

В такой модели существует два независимых уровня порядка:

  • порядок самих групп
  • порядок элементов внутри каждой группы

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


Базовая сортировка элементов внутри групп

Основной механизм сортировки элементов задаётся через sortField. Это свойство определяет, по какому полю и в каком порядке сравниваются элементы внутри одной группы.

Простейший вариант:

const select = new TomSelect('#select', {
  sortField: 'text'
});

В этом случае элементы внутри каждой группы сортируются по текстовому значению text.

Поддерживается также направление сортировки:

sortField: {
  field: 'text',
  direction: 'asc'
}

или обратный порядок:

sortField: {
  field: 'text',
  direction: 'desc'
}

Стабильность сортировки

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


Многоуровневая сортировка внутри групп

sortField поддерживает массив правил, что позволяет задавать приоритеты сортировки.

sortField: [
  { field: 'priority', direction: 'desc' },
  { field: 'text', direction: 'asc' }
]

Такой подход формирует каскадное сравнение:

  1. сначала сортировка по priority
  2. при равенстве — сортировка по text

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


Сортировка элементов с учётом optgroup

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

Расширенная форма sortField:

sortField: [
  { field: 'optgroup', direction: 'asc' },
  { field: 'text', direction: 'asc' }
]

В этом случае происходит предварительное упорядочивание по имени группы, а затем внутри неё — по тексту.

Однако в большинстве случаев более корректно разделять:

  • сортировку групп
  • сортировку элементов внутри групп

Управление порядком групп (optgroups)

Порядок групп не всегда зависит от сортировки элементов. Он определяется отдельно через массив optgroups.

optgroups: [
  { value: 'backend', label: 'Backend' },
  { value: 'frontend', label: 'Frontend' }
]

Здесь группы выводятся строго в заданном порядке.

Если требуется сортировка групп по полю, применяется кастомная логика:

optgroups: [
  { value: 'frontend', label: 'Frontend', order: 2 },
  { value: 'backend', label: 'Backend', order: 1 }
]

И затем:

optgroupOrder: 'order'

Кастомная сортировка групп

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

optgroups: [
  { value: 'frontend', label: 'Frontend', weight: 20 },
  { value: 'backend', label: 'Backend', weight: 10 }
],

optgroupOrder: (a, b) => a.weight - b.weight

Функция сравнения получает два объекта группы и должна возвращать:

  • отрицательное число — первый элемент выше
  • положительное число — второй элемент выше
  • 0 — равенство

Взаимодействие сортировки групп и элементов

Комбинированная модель поведения выглядит следующим образом:

  1. определяется порядок optgroups
  2. внутри каждой группы сортируются options
  3. отображение строится по итоговому порядку

Пример комбинированной конфигурации:

new TomSelect('#select', {
  optgroups: [
    { value: 'backend', label: 'Backend', weight: 1 },
    { value: 'frontend', label: 'Frontend', weight: 2 }
  ],

  optgroupOrder: (a, b) => a.weight - b.weight,

  sortField: [
    { field: 'priority', direction: 'desc' },
    { field: 'text', direction: 'asc' }
  ]
});

Поведение при поиске и фильтрации

Сортировка внутри групп сохраняется даже после применения поиска, однако изменяется набор элементов, попадающих в каждую группу.

Важно учитывать:

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

Если используется кастомный score, он влияет на релевантность, но не заменяет sortField.


Динамическая загрузка и сортировка

При использовании load данные могут поступать частями. В таком случае сортировка выполняется после добавления новых элементов.

load: function(query, callback) {
  fetch('/api/items?q=' + query)
    .then(res => res.json())
    .then(data => callback(data));
}

После вызова callback Tom Select:

  • добавляет элементы в соответствующие optgroup
  • применяет sortField
  • обновляет отображение списка

Если порядок важен на уровне сервера, можно частично разгрузить клиент, передавая уже отсортированные данные.


Кастомная логика сортировки элементов

Для сложных сценариев используется функция сравнения вместо декларативного sortField.

sortField: (a, b) => {
  if (a.priority !== b.priority) {
    return b.priority - a.priority;
  }
  return a.text.localeCompare(b.text);
}

Такой подход позволяет учитывать:

  • веса элементов
  • локализацию строк
  • внешние флаги состояния
  • принадлежность к состояниям UI

Сортировка с учётом визуальных группировок

При наличии визуальных разделителей или кастомного render.optgroup_header порядок групп остаётся логическим, а не визуальным.

render: {
  optgroup_header: (data) => {
    return `<div class="group-header">${data.label}</div>`;
  }
}

Сортировка групп при этом не зависит от рендера и определяется исключительно optgroupOrder.


Особенности повторной инициализации порядка

При изменении options или optgroups через API:

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

Пример обновления:

select.addOption({ value: 'rust', text: 'Rust', optgroup: 'backend' });
select.refreshOptions(false);

После этого все правила сортировки применяются повторно ко всей структуре.


Практика комбинирования нескольких уровней сортировки

Распространённая модель для сложных интерфейсов:

  • группы сортируются по бизнес-важности
  • элементы внутри групп — по пользовательской активности
  • дополнительный уровень — алфавитный порядок
optgroupOrder: (a, b) => a.rank - b.rank,

sortField: [
  { field: 'usage', direction: 'desc' },
  { field: 'text', direction: 'asc' }
]

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