npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@brandup/ui-dropdown

v1.2.11

Published

DropDown control type and styles.

Downloads

1,679

Readme

@brandup/ui-dropdown

Build Status

Компонент выпадающего списка, заменяющий стандартный <select>. Поддерживает поиск с транслитерацией (EN ↔ RU), адаптивный режим для планшетов и валидацию через форму.

Установка

npm i @brandup/ui-dropdown

Использование

<select id="city" data-placeholder="Выберите город" data-search-on="5">
    <option value="">— не выбрано —</option>
    <option value="msk">Москва</option>
    <option value="spb">Санкт-Петербург</option>
</select>
import DropDown, { DROPDOWN, ChangeEventData } from "@brandup/ui-dropdown";

const selectElem = document.getElementById("city") as HTMLSelectElement;
const dropdown = new DropDown(selectElem);

dropdown.on(DROPDOWN.EVENT.CHANGE, (data: ChangeEventData) => {
    console.log(data.value, data.title, data.index);
});

Data-атрибуты

| Атрибут | По умолчанию | Описание | | --- | --- | --- | | data-placeholder | "Select" | Текст при отсутствии выбранного значения | | data-emptytext | "Empty list" | Текст при пустом списке опций | | data-search-placeholder | "Search" | Плейсхолдер строки поиска | | data-search-empty | "Not found" | Текст при отсутствии результатов поиска | | data-cancel | "Cancel" | Текст кнопки закрытия (адаптивный режим) | | data-search-on | 15 | Порог отображения поиска: число (минимальное кол-во опций), "true" — всегда, "false" — никогда | | data-autofocus | — | Автофокус при инициализации; то же делает нативный autofocus. Условия отмены — см. @brandup/ui-input | | data-readonly | — | Значение показывается, но сменить его нельзя: список не открывается. У <select> нативного readonly нет, поэтому режим объявляется этим атрибутом |

API

Собственные методы

| Метод | Описание | | --- | --- | | getValue(): string \| null | Значение выбранного <option>; null, если не выбрано ничего | | getSelectedIndex(): number | Индекс выбранного <option>; -1, если не выбрано ничего | | getSelectedTitle(): string \| null | Текст выбранного пункта списка | | setValue(value: string \| null): void | Выбирает пункт с таким значением и показывает его. Значения без своего пункта в списке (в том числе пустое) сбрасывают выбор в плейсхолдер. ui:dropdown:change поднимается только при действительной смене показанного пункта |

Методы (унаследованы от InputControl)

| Метод | Описание | | --- | --- | | validate(): boolean | Проверяет значение через нативный checkValidity() | | focus(): void | Ведёт фокус в кнопку показа списка и прокручивает контрол в видимую область | | destroy(): void | Восстанавливает исходный <select> и освобождает ресурсы |

Свойства

| Свойство | Тип | Описание | | --- | --- | --- | | placeholder | string | Текст-заглушка | | emptyText | string | Текст при пустом списке | | searchOn | number \| boolean | Настройка отображения строки поиска |

Событие ui:dropdown:change

Генерируется при выборе значения из списка.

import { DROPDOWN, ChangeEventData } from "@brandup/ui-dropdown";

dropdown.on(DROPDOWN.EVENT.CHANGE, (data: ChangeEventData) => {
    console.log(data.value);  // значение выбранного <option>
    console.log(data.title);  // текст выбранного <option>
    console.log(data.index);  // индекс выбранного элемента в списке
});

Поиск и транслитерация

Когда количество опций превышает порог data-search-on, в попапе отображается строка поиска. Поиск выполняется по тексту опций и поддерживает автоматическую транслитерацию:

  • Ввод на русской раскладке → поиск по английскому тексту
  • Ввод на английской раскладке → поиск по русскому тексту
import { detectLanguage, transcriptText } from "@brandup/ui-dropdown";

const lang = detectLanguage("hello"); // "english"
const variants = transcriptText("руку"); // { english: "reue" }

Клавиатура и доступность

Список — слой поверх страницы: он встаёт в стек слоёв кита, поэтому Escape закрывает именно его (список внутри модального окна закроется один, окно останется), а придержанную им прокрутку страницы стек считает вместе с остальными слоями. Выход по Escape из списка возвращает фокус на кнопку показа — на узком экране список занимает весь экран, и уйти с него больше не на что. Раскрытый список на странице один: открытие следующего закрывает предыдущий.

Разметка объявляет то же самое для озвучки:

| Элемент | Что объявлено | | --- | --- | | Кнопка показа | aria-haspopup="listbox", aria-controls с идентификатором самого списка, aria-expanded (true/false — и в закрытом виде тоже) | | ul списка | role="listbox" и aria-label с текстом-заглушкой (data-placeholder) | | li пункта | role="presentation" — носитель команды и значения, между списком и пунктом он пустой | | span пункта | role="option" и aria-selected; он же принимает фокус, по нему ходят стрелки |

Вид

Кнопка показа оформлена как поле ввода кита и отвечает на состояния так же: рамка, заливка и цвет текста берутся из тех же токенов (--input-*, --hover--input-*, --focus--input-*, --readonly--input-*, --disabled--input-*, --invalid--input-*). Своих цветов у дропдауна нет — перекрасив поля ввода темой, вы перекрасите и его.

Состояния объявляет базовый InputControl классами на корне (readonly, disabled, invalid), плюс собственные expanded и hasvalue.

Вид в фокусе висит на всём контроле (:focus-within), а не на самой кнопке: открытие списка уводит фокус на выпадающую панель, и правило на кнопке гасло бы ровно тогда, когда контролом пользуются.

Одно отличие от поля намеренное: у закрытого дропдауна (readonly) стрелка приглушена цветом выключенного поля. Текст при этом обычный, как и у [readonly] у поля ввода, — читается в полную силу; а стрелка обещает раскрыть список, который не раскроется.

Сторона раскрытия выбирается сама, по месту вокруг кнопки: вниз, а вверх — только если снизу список не помещается и сверху места больше. Место считается по коробке, в которой список видно, — её даёт clippingRect кита: обёртка с overflow: hidden режет всё, что раскрывается ниже последнего элемента страницы, а окно при этом сообщает, что место есть. Пересчитывается на прокрутке и изменении размера, поэтому сторона меняется на ходу, когда кнопка уезжает к краю. Ниже 1030px списка нет вовсе — там он показывается листом по центру экрана, и место не считается.

Список — это элемент с классом ui-dropdown-popup (раньше popup; имя читается из DROPDOWN.CLASS.ELEMENT.POPUP). Если проект оформлял его своим правилом, класс надо переименовать.

Совпадение с полем ввода не на словах: test/dropdown-css.test.ts компилирует оба стиля, разрешает каскад и сверяет, что в каждом состоянии кнопка и поле красятся одним и тем же токеном. Там же проверяется, что классы разворота достают до самого списка, а не до коробки внутри него, и что отступ списка от кнопки в стилях совпадает с числом, по которому считается место.

Локализация

Подписи по умолчанию английские. Русские возит сам пакет, приложение объявляет их один раз при старте:

import { setTexts } from "@brandup/ui-kit/i18n";
import ru from "@brandup/ui-dropdown/locale/ru.json";

setTexts(ru);

Подпись отдельного контрола задаётся на нём самом и сильнее текстов приложения. Для списка это data-* из таблицы выше. Подробнее — @brandup/ui-kit.

Имена

Классы, команды, события, подписи по умолчанию и числа, на которые опирается поведение, пакет отдаёт одним объектом DROPDOWN (модуль names.ts) — россыпи ROOT_CLASS / CHANGE_EVENT / *_COMMAND больше нет. Модуль имён ничего не импортирует: их можно взять, не притаскивая стили и код контрола. Так же устроены остальные пакеты кита.

// коротким путём — без стилей и кода контрола
import { DROPDOWN } from "@brandup/ui-dropdown/names";
// или из общего входа, если пакет и так подключён
import { DROPDOWN } from "@brandup/ui-dropdown";

DROPDOWN.CLASS.ROOT;              // "ui-dropdown" — корень контрола
DROPDOWN.CLASS.INPUT;             // "ui-dropdown-input" — поле-носитель
DROPDOWN.CLASS.MINIATURE;         // "ui-dropdown-miniature"
DROPDOWN.CLASS.BODY;              // "body-dropdown-opened" — на <body>, пока список раскрыт
DROPDOWN.CLASS.ELEMENT.POPUP;     // части разметки: POPUP, CONTENT, HEADER, SEARCH, VIEW, CANCEL, EMPTY
DROPDOWN.CLASS.STATE.EXPANDED;    // состояния: EXPANDED, HAS_VALUE, EMPTY, SEARCHABLE, INVALID,
                                  // RESULT, NOT_FOUND, MATCH, TOP, RIGHT
DROPDOWN.COMMAND.TOGGLE;          // команды разметки (см. ниже)
DROPDOWN.EVENT.CHANGE;            // "ui:dropdown:change"
DROPDOWN.TEXT.CANCEL;             // подписи: PLACEHOLDER, EMPTY, SEARCH, SEARCH_EMPTY, CANCEL
DROPDOWN.VALUE.SEARCH_ON;         // 15 — порог показа строки поиска
DROPDOWN.VALUE.SEARCH_MAX_LENGTH; // 50
DROPDOWN.VALUE.TABLET_WIDTH;      // 1030 — ниже список раскрывается во весь экран
DROPDOWN.LIST_ID;                 // префикс идентификатора списка для aria-controls

Команды

Разметку контрола собирает он сам, но команды в ней объявлены с префиксом кита — свою кнопку закрытия списка достаточно объявить тем же data-command:

| Команда | Имя | Действие | | --- | --- | --- | | ui-dropdown-toggle | DROPDOWN.COMMAND.TOGGLE | Раскрыть или закрыть список | | ui-dropdown-close | DROPDOWN.COMMAND.CLOSE | Закрыть список (работает и на выключенном контроле) | | ui-dropdown-select | DROPDOWN.COMMAND.SELECT | Выбрать пункт (data-value, data-index) |

Адаптивный режим

На экранах шириной менее 1030px попап открывается в полноэкранном режиме с кнопкой «Отмена».

Оформление

Список опций прокручивается сам и оформлен общим классом кита ui-scrollable — полоса выглядит одинаково во всех компонентах, а её размер и цвет переопределяются переменными --scrollbar-* (см. @brandup/ui-kit).