GrTooltip
Берут, когда подпись к иконочной кнопке.
Обзор
Когда брать
- подпись к иконочной кнопке — единственный способ дать имя контролу, у которого нет видимого текста;
- расшифровка сокращения или статуса — короткий текст, который иначе занял бы место в раскладке;
- подсказка появляется и по фокусу — не только по наведению, поэтому она достижима с клавиатуры;
- текста немного — одна-две строки: длинный текст в подсказке нельзя ни выделить, ни прокрутить.
Когда взять другое
| Нужно | Берите |
|---|---|
| Внутри нужен интерактив: ссылка, кнопка, поле | GrPopover |
| Список действий | GrDropdownMenu |
| Сообщение, которое пользователь обязан заметить | GrAlert |
| Сообщение о результате действия | GrToaster |
| Постоянное пояснение под полем | GrFormField |
Единственным носителем информации подсказка быть не может. На тач-устройстве наведения нет, а текст, доступный только по нему, для части пользователей не существует вовсе. То, без чего задачу не выполнить, живёт в самой раскладке — подписью, хинтом поля или строкой рядом.
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrTooltip open text="Saves without closing the form"><GrButton>Save draft</GrButton></GrTooltip>Примеры 3 у каждого — живое демо и его код
Кнопки-иконки, которые называют себя
Слот по умолчанию принимает триггер, и кнопка внутри — единственная остановка Tab. Имя у неё своё —
aria-label, сочетание —aria-keyshortcuts, поэтому подсказка — те же слова с клавишами для глаз, через слотcontent, — второй раз не читается:describe-triggerвыключен.open-delayне даёт подсказкам мигать, пока курсор бежит по ряду.Note on the invoice Thank you for your business. Payment within 30 days to IE29 AIBK 9311 5212 3456 78.
Термины в сравнении тарифов
Без своего триггера подсказка рисует иконку info и сама становится остановкой Tab;
text— всё пояснение.placement="top-start"поднимает её над строкой, и цены, которые она поясняет, остаются на виду,size="sm"подгоняет под таблицу, аicon-color— под приглушённый текст рядом. Цифры остаются в таблице: подсказка не может быть единственным носителем — на сенсорном экране наведения нет.Plan Team · €49 Business · €149 Seats 10 50 Overage €0.20 each €0.10 each SSO — Included Длинные имена в узкой колонке
Каждая строка — триггер, поэтому
blockрастягивает обёртку на строку. Подсказку получает только имя, обрезанное многоточием, у остальных онаdisabled: повтор видимого текста — шум. «Where is the export?» открывает подсказку кнопки выгрузки на несколько секунд черезv-model:open, аoffset-pxдержит её на расстоянии от кнопки.Customers- Northwind Traders
- Fabrikam Industrielle Fertigung und Vertrieb GmbH & Co. KG
- Contoso Ltd
- Adventure Works Cycles Manufacturing and Distribution, S.L.
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
placement | Placement | undefined | "top" | Предпочтительная сторона панели; `flip` может её изменить. |
disabled | boolean | undefined | false | Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. |
openv-model | boolean | undefined | undefined | Управляемая видимость. Без неё компонент ведёт видимость сам. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Размер панели и дефолтной триггер-иконки. |
block | boolean | undefined | false | Обёртка занимает ширину родителя вместо ширины содержимого. Нужно там, где триггер обязан тянуться: пункт списка, строка меню, ячейка. `inline-flex` по умолчанию схлопнул бы такой триггер по содержимому, и подсветка с кольцом фокуса сжались бы вместе с ним. |
offsetPx | number | undefined | undefined | Зазор между триггером и панелью, px. |
openDelay | number | undefined | 0 | Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. |
closeDelay | number | undefined | 0 | Задержка перед скрытием, мс. |
text | string | undefined | undefined | Текст подсказки. Разметка вместо текста — слот `#content`. |
iconColor | string | undefined | "var(--gr-muted-fg)" | Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. |
describeTrigger | boolean | undefined | true | Вешать ли `aria-describedby` на триггер. По умолчанию да. `false` — когда подсказка **дословно повторяет** доступное имя триггера: тогда это не описание, а шум, и диктор произносит подпись дважды. Так устроен свёрнутый `GrSidebarItem`: имя у него уже в `aria-label`. |
Слоты
| Slot | Type | Описание |
|---|---|---|
default | any | Триггер. По умолчанию — иконка info. |
content | any | Содержимое подсказки; заменяет проп `text`. |
События
| Event | Сигнатура | Описание |
|---|---|---|
update:open | (event: "update:open", value: boolean): void | — |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrTooltipPlacement, GrTooltipProps, GrTooltipEmits } from '@feugene/granularity/components/GrTooltip'Файлdist/types/components/GrTooltip/GrTooltip.vue.d.ts
type GrTooltipPlacement = UseFloatingPlacement;interface GrTooltipProps {
/** Текст подсказки. Разметка вместо текста — слот `#content`. */
text?: string;
/** Цвет триггер-иконки (CSS color). По умолчанию — `var(--gr-muted-fg)`. */
iconColor?: string;
/** Размер панели и дефолтной триггер-иконки. */
size?: GrTooltipSize;
/** Предпочтительная сторона панели; `flip` может её изменить. */
placement?: GrTooltipPlacement;
/** Зазор между триггером и панелью, px. */
offsetPx?: number;
/** Задержка перед показом, мс. Спасает от мигания при проведении курсором по панели кнопок. */
openDelay?: number;
/** Задержка перед скрытием, мс. */
closeDelay?: number;
/** Подсказка не показывается ничем — ни курсором, ни фокусом, ни `v-model:open`. */
disabled?: boolean;
/** Управляемая видимость. Без неё компонент ведёт видимость сам. */
open?: boolean;
/**
* Обёртка занимает ширину родителя вместо ширины содержимого.
*
* Нужно там, где триггер обязан тянуться: пункт списка, строка меню, ячейка.
* `inline-flex` по умолчанию схлопнул бы такой триггер по содержимому, и
* подсветка с кольцом фокуса сжались бы вместе с ним.
*/
block?: boolean;
/**
* Вешать ли `aria-describedby` на триггер. По умолчанию да.
*
* `false` — когда подсказка **дословно повторяет** доступное имя триггера:
* тогда это не описание, а шум, и диктор произносит подпись дважды. Так
* устроен свёрнутый `GrSidebarItem`: имя у него уже в `aria-label`.
*/
describeTrigger?: boolean;
}interface GrTooltipEmits {
/** Подсказка показана или скрыта (`v-model:open`): курсором, фокусом, тапом или `Esc`. */
(e: 'update:open', value: boolean): void;
}Файлdist/types/components/GrTooltip/config.d.ts
const grTooltipConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrTooltip">;Файлdist/types/components/GrTooltip/defaults.d.ts
Пропы GrTooltip, настраиваемые глобально через componentDefaults.
interface GrTooltipConfigurableProps {
size: GrComponentSize;
}Файлdist/types/components/GrTooltip/grTooltipStyles.d.ts
type GrTooltipSize = GrComponentSize;Файлdist/types/components/GrTooltip/safelist.d.ts
const grTooltipSafelist: string[];Доступность
Клавиши
Esc
| Клавиша | Действие |
|---|---|
| Esc | скрыть (мгновенно, минуя closeDelay); показывается по фокусу триггера, не только по наведению. Со слотом остановка Tab одна — сам контрол, а описание уезжает на него |
Полный контракт пакета: docs/keyboard.md ↗
Использование из прозы библиотеки
Триггер и таб-порядок
Без слота триггер — иконка info, и остановкой Tab становится обёртка. Со
слотом обёртка отходит в сторону: если внутри есть фокусируемый элемент
(кнопка, ссылка, поле), aria-describedby уезжает на него, а обёртка теряет
tabindex. Иначе на один визуальный контрол приходилось бы два таб-стопа,
причём внешний — безролевой <span>.
Если фокусироваться в слоте нечему (текст, картинка), обёртка остаётся
остановкой Tab — иначе подсказка была бы недоступна с клавиатуры вовсе. Но
только пока подсказку есть что показать: с disabled (или без текста)
обёртка снимает и tabindex, и aria-describedby. Типичный случай — подсказка
с полным текстом, включённая только у обрезанной строки списка: остальные
строки лишних остановок не дают.
describe-trigger="false" отменяет aria-describedby. Нужно там, где подсказка
дословно повторяет доступное имя триггера: описание тем же текстом диктор
прочитает вторым заходом, и получится «Billing, Billing». Так подключён
свёрнутый GrSidebarItem — имя ему даёт aria-label, а подсказка нужна
глазам.
<!-- Обёртка прозрачна: остановка Tab одна — сама кнопка. -->
<GrTooltip text="Удалить безвозвратно">
<GrButton variant="ghost" square><IconTrash /></GrButton>
</GrTooltip>Содержимое
text — короткая строка; разметка вместо неё — слот #content. Без того и
другого подсказка не открывается: пустая панель хуже отсутствующей.
Ширина триггера
Обе обёртки — внешняя и триггерная — по умолчанию inline-flex, то есть
подсказка сжимается по содержимому. block переводит их в flex w-full:
нужно, когда триггер обязан занять ширину родителя — пункт списка, строка меню,
ячейка. Проп меняет обе обёртки: снаружи до внутренней класс не доходит, и
одной внешней мало — триггер всё равно схлопнулся бы вместе с подсветкой и
кольцом фокуса. Содержимое такого триггера прижато к началу, как строка списка,
а не к центру.
Размещение
placement — любая сторона из шкалы @floating-ui (top, right-start, …),
offsetPx — зазор до триггера. Итоговая сторона может отличаться от заданной:
flip переворачивает подсказку, когда места не хватает, shift не даёт ей
вылезти за край экрана.
Задержки
openDelay и closeDelay (мс, по умолчанию оба 0). Задержка показа нужна
там, где подсказки стоят плотно — на панели кнопок без неё подсказка мигает при
каждом проведении курсором. Escape и клик вне закрывают мгновенно, минуя
closeDelay: задержка там читалась бы как залипание.
Управление снаружи
disabled запрещает показ полностью. v-model:open переводит видимость под
контроль владельца — компонент перестаёт открывать себя сам и только сообщает о
намерении через update:open.
Тач
На тач-устройствах hover не существует, а фокус по тапу не гарантирован, поэтому тап по триггеру переключает подсказку, а тап вне — закрывает.
Пропы этого примера