@olddevs/ui 4.2.2 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/components/navigation/MegaMenu.d.ts +43 -23
- package/dist/components/navigation/MegaMenu.d.ts.map +1 -1
- package/dist/components/navigation/MegaMenu.js +110 -47
- package/dist/components/navigation/MegaMenu.js.map +1 -1
- package/dist/components/navigation/useMenuAim.d.ts +8 -8
- package/dist/components/navigation/useMenuAim.d.ts.map +1 -1
- package/dist/components/overlays/Menu.d.ts +4 -2
- package/dist/components/overlays/Menu.d.ts.map +1 -1
- package/dist/components/overlays/Menu.js.map +1 -1
- package/dist/editor/RichTextEditor.d.ts.map +1 -1
- package/dist/editor/RichTextEditor.js +38 -4
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/editor/core/handle.d.ts +1 -5
- package/dist/editor/core/handle.d.ts.map +1 -1
- package/dist/editor/core/handle.js +1 -13
- package/dist/editor/core/handle.js.map +1 -1
- package/dist/editor/core/handleWatchers.d.ts +6 -0
- package/dist/editor/core/handleWatchers.d.ts.map +1 -0
- package/dist/editor/core/handleWatchers.js +14 -0
- package/dist/editor/core/handleWatchers.js.map +1 -0
- package/dist/editor/core/tools.d.ts +7 -2
- package/dist/editor/core/tools.d.ts.map +1 -1
- package/dist/editor/core/tools.js +2 -1
- package/dist/editor/core/tools.js.map +1 -1
- package/dist/editor/features/toc/RichTextToc.d.ts +63 -0
- package/dist/editor/features/toc/RichTextToc.d.ts.map +1 -0
- package/dist/editor/features/toc/RichTextToc.js +372 -0
- package/dist/editor/features/toc/RichTextToc.js.map +1 -0
- package/dist/editor/features/toc/headings.d.ts +31 -0
- package/dist/editor/features/toc/headings.d.ts.map +1 -0
- package/dist/editor/features/toc/headings.js +25 -0
- package/dist/editor/features/toc/headings.js.map +1 -0
- package/dist/editor/features/toc/stickyOffset.d.ts +3 -0
- package/dist/editor/features/toc/stickyOffset.d.ts.map +1 -0
- package/dist/editor/features/toc/stickyOffset.js +14 -0
- package/dist/editor/features/toc/stickyOffset.js.map +1 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js +1 -0
- package/dist/editor/index.js.map +1 -1
- package/dist/i18n/defaults.d.ts.map +1 -1
- package/dist/i18n/defaults.js +1 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/messages.d.ts +5 -0
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/locales/ru.d.ts.map +1 -1
- package/dist/locales/ru.js +1 -0
- package/dist/locales/ru.js.map +1 -1
- package/docs/api/messages.en.md +743 -0
- package/docs/api/messages.md +1 -0
- package/docs/api/utilities.en.md +233 -0
- package/docs/components/content/rich-text-toc.en.md +130 -0
- package/docs/components/content/rich-text-toc.md +129 -0
- package/docs/components/index.en.md +3 -2
- package/docs/components/index.md +2 -1
- package/docs/components/inputs/rich-text-editor.en.md +52 -34
- package/docs/components/inputs/rich-text-editor.md +20 -2
- package/docs/components/navigation/mega-menu.en.md +86 -61
- package/docs/components/navigation/mega-menu.md +81 -56
- package/docs/components/overlays/menu.en.md +8 -4
- package/docs/components/overlays/menu.md +6 -3
- package/docs/conventions.en.md +318 -0
- package/docs/getting-started.en.md +5 -5
- package/docs/getting-started.md +4 -4
- package/docs/glossary.en.md +24 -23
- package/docs/glossary.md +23 -22
- package/docs/index.en.md +53 -0
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/src/components/navigation/MegaMenu.tsx +300 -140
- package/src/components/navigation/useMenuAim.ts +19 -19
- package/src/components/overlays/Menu.tsx +12 -2
- package/src/editor/RichTextEditor.tsx +107 -3
- package/src/editor/core/handle.ts +2 -23
- package/src/editor/core/handleWatchers.ts +27 -0
- package/src/editor/core/tools.ts +9 -2
- package/src/editor/features/toc/RichTextToc.tsx +712 -0
- package/src/editor/features/toc/headings.ts +70 -0
- package/src/editor/features/toc/stickyOffset.ts +21 -0
- package/src/editor/index.ts +2 -0
- package/src/i18n/defaults.ts +1 -0
- package/src/i18n/messages.ts +5 -0
- package/src/locales/ru.ts +1 -0
|
@@ -12,9 +12,10 @@ story: src/components/navigation/MegaMenu.stories.tsx
|
|
|
12
12
|
## Кратко для AI
|
|
13
13
|
|
|
14
14
|
`MegaMenu` — каталог магазина в духе Amazon: слева Столбец категорий, справа от наведённой Категории — её
|
|
15
|
-
|
|
15
|
+
Полка с колонками ссылок и любым своим содержимым. Это навигация по ссылкам, а не меню команд: разметка по
|
|
16
16
|
шаблону WAI-ARIA Disclosure Navigation (`nav`, кнопки с `aria-expanded`, настоящие `a`). Категории —
|
|
17
|
-
`MegaMenuCategory`
|
|
17
|
+
`MegaMenuCategory` (прямо или внутри своих компонентов, в том числе серверных), колонки — `MegaMenuGroup`,
|
|
18
|
+
ссылки — `MegaMenuLink`. Текущий раздел и текущая страница — проп `active`, как у `NavItem`. Тот же каталог
|
|
18
19
|
открывается выпадающим оверлеем от кнопки: `MegaMenuTrigger` с `MegaMenuButton` и неизменным `MegaMenu`.
|
|
19
20
|
|
|
20
21
|
## Когда использовать
|
|
@@ -22,7 +23,7 @@ story: src/components/navigation/MegaMenu.stories.tsx
|
|
|
22
23
|
- Каталог с десятком и больше разделов, у каждого — подразделы колонками: магазин, маркетплейс, справочный
|
|
23
24
|
портал.
|
|
24
25
|
- Разделы надо показать сразу, а подразделы — по наведению, не уводя пользователя со страницы.
|
|
25
|
-
- В
|
|
26
|
+
- В Полке рядом со ссылками нужно своё содержимое: промо-карточка, картинка, подборка.
|
|
26
27
|
|
|
27
28
|
## Когда не использовать
|
|
28
29
|
|
|
@@ -37,10 +38,10 @@ story: src/components/navigation/MegaMenu.stories.tsx
|
|
|
37
38
|
import { MegaMenu, MegaMenuCategory, MegaMenuGroup, MegaMenuLink } from "@olddevs/ui";
|
|
38
39
|
|
|
39
40
|
<MegaMenu aria-label="Каталог">
|
|
40
|
-
<MegaMenuCategory id="computers" label="Компьютеры" href="/computers">
|
|
41
|
+
<MegaMenuCategory id="computers" label="Компьютеры" href="/computers" active>
|
|
41
42
|
<MegaMenuGroup title="Ноутбуки" href="/computers/laptops">
|
|
42
43
|
<MegaMenuLink href="/computers/laptops/gaming">Игровые</MegaMenuLink>
|
|
43
|
-
<MegaMenuLink href="/computers/laptops/ultrabooks"
|
|
44
|
+
<MegaMenuLink href="/computers/laptops/ultrabooks" active>
|
|
44
45
|
Ультрабуки
|
|
45
46
|
</MegaMenuLink>
|
|
46
47
|
</MegaMenuGroup>
|
|
@@ -57,62 +58,75 @@ import { MegaMenu, MegaMenuCategory, MegaMenuGroup, MegaMenuLink } from "@olddev
|
|
|
57
58
|
| ------------------------------------------ | ------------ | ------------------------------------------------------------------------------------------ |
|
|
58
59
|
| `aria-label` | — | Доступное имя навигации, обязательно: на странице с несколькими `nav` их различают по нему |
|
|
59
60
|
| `value` / `defaultValue` / `onValueChange` | `null` | Открытая Категория (её `id`) или `null`, управляемо или нет |
|
|
60
|
-
| `showcaseWidth` | `"40rem"` | Ширина
|
|
61
|
+
| `showcaseWidth` | `"40rem"` | Ширина Полки, одна на все Категории: каталог не прыгает при переходе между ними |
|
|
61
62
|
| `className` | — | Классы на `nav`; ширина Столбца по умолчанию — `w-64` |
|
|
62
63
|
|
|
63
|
-
`MegaMenuCategory`: `id` — ключ для `value`; `label` — подпись в Столбце и имя
|
|
64
|
-
`link` — адрес раздела целиком; `disabled`;
|
|
65
|
-
|
|
64
|
+
`MegaMenuCategory`: `id` — ключ для `value`; `label` — подпись в Столбце и имя Полки; `icon`; `href` или
|
|
65
|
+
`link` — адрес раздела целиком; `disabled`; `active` — текущая страница лежит в этом разделе; дети —
|
|
66
|
+
содержимое Полки. У Категории с Полкой адрес становится
|
|
67
|
+
заголовком-ссылкой в начале Полки, у Категории без детей сама строка Столбца — ссылка. `link` — ссылка роутера
|
|
66
68
|
пустым элементом (`link={<Link href="/x" />}`), подпись уезжает внутрь; это отдельный проп, а не `asChild`,
|
|
67
|
-
потому что дети Категории — её
|
|
69
|
+
потому что дети Категории — её Полка.
|
|
68
70
|
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
+
`active` у Категории ставит `aria-current="true"` и красит подпись акцентным цветом — тем же, что у текущей
|
|
72
|
+
ссылки. Открыта Полка или нет, отметка остаётся: текущий раздел задаёт маршрут, а не наведение. Открытая
|
|
73
|
+
Категория по-прежнему выделена фоном, так что у одной строки бывают оба признака. Пока ни одна Полка не открыта,
|
|
74
|
+
Tab приходит в Столбец на текущий раздел. Какой раздел текущий, решает потребитель, как с `active` у `NavItem`.
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
Категории не обязаны быть прямыми детьми `MegaMenu`: их можно разложить по своим компонентам и рендерить из
|
|
77
|
+
серверного компонента Next.js. Каждая Категория сама сообщает Столбцу о себе, а Столбец не разбирает детей по
|
|
78
|
+
типу — у серверного компонента тип элемента на клиенте другой. До гидратации, пока Категории о себе не сообщили,
|
|
79
|
+
остановка Tab стоит у каждой доступной Категории; после гидратации Столбец снова одна остановка.
|
|
80
|
+
|
|
81
|
+
`MegaMenuGroup`: `title` — заголовок колонки, он же подпись списка; `href` или `link` делают заголовок ссылкой
|
|
82
|
+
(`link` — ссылка роутера пустым элементом, как у Категории); дети — `MegaMenuLink`, каждая уезжает в свой пункт
|
|
83
|
+
списка, в том числе пришедшие фрагментом.
|
|
84
|
+
|
|
85
|
+
`MegaMenuLink`: обычные пропы `a`, `icon`, `active` и `asChild`. `active` ставит `aria-current="page"` и вид
|
|
73
86
|
текущей: текущую страницу кит не вычисляет, правила сравнения адресов у каждого роутера свои. `asChild` выводит
|
|
74
|
-
ссылку элементом роутера: `<MegaMenuLink asChild><Link href="/x">Подпись</Link></MegaMenuLink>`.
|
|
87
|
+
ссылку элементом роутера: `<MegaMenuLink asChild><Link href="/x">Подпись</Link></MegaMenuLink>`. Прежнее имя
|
|
88
|
+
`current` работает до 5.0 и помечено `@deprecated`: текущий пункт навигации в ките называется `active`.
|
|
75
89
|
|
|
76
|
-
## Наведение и
|
|
90
|
+
## Наведение и Полка
|
|
77
91
|
|
|
78
|
-
Наведение мышью на Категорию открывает её
|
|
79
|
-
|
|
92
|
+
Наведение мышью на Категорию открывает её Полку сразу, без задержки. Подсвечена ровно та Категория, чья
|
|
93
|
+
Полка открыта: подсветка идёт по `aria-expanded`, а не по `:hover`. Наведение на Категорию без Полки
|
|
80
94
|
закрывает открытую — с той же оговоркой про Прицел, что ниже.
|
|
81
95
|
|
|
82
|
-
|
|
96
|
+
Полка закрывается, когда курсор ушёл и со Столбца, и с Полки, через 150 мс: короткий выход за край не
|
|
83
97
|
сбрасывает каталог. Закрывают её и нажатие указателем вне каталога, и уход фокуса со страницы каталога.
|
|
84
98
|
|
|
85
|
-
Содержимое
|
|
99
|
+
Содержимое Полки монтируется только у открытой Категории: каталог из сотни ссылок и картинок не грузит всё
|
|
86
100
|
разом. Сам регион есть всегда, скрытым: на него указывает `aria-controls` кнопки.
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
по странице», и
|
|
102
|
+
Полка — потомок своей Категории в DOM, а не портал. Поэтому порядок Tab идёт «Категория → её ссылки → дальше
|
|
103
|
+
по странице», и Полка встаёт вплотную к Столбцу без расчёта позиции. Предок с `overflow: hidden` её обрежет:
|
|
90
104
|
ставьте каталог туда, где рядом есть место.
|
|
91
105
|
|
|
92
|
-
|
|
106
|
+
Полка стоит со стороны конца строки — справа, а при письме справа налево слева. Если там не хватает места, а с
|
|
93
107
|
другой стороны хватает, она переходит на другую сторону; сторону кит меряет заранее и при смене размера окна,
|
|
94
|
-
поэтому стрелка «к
|
|
108
|
+
поэтому стрелка «к Полке» сразу смотрит туда, где Полка окажется.
|
|
95
109
|
|
|
96
110
|
## Касание и узкий экран
|
|
97
111
|
|
|
98
112
|
Касание наведения не знает, поэтому Категорию открывает само касание (`click`), без Прицела. Указатель с
|
|
99
|
-
`pointerType` «touch» или «pen» не открывает Категорию наведением и не закрывает
|
|
113
|
+
`pointerType` «touch» или «pen» не открывает Категорию наведением и не закрывает Полку уходом: перо
|
|
100
114
|
ведёт себя как палец. Повторное касание открытой Категории её не закрывает и никуда не ведёт: закрывает
|
|
101
|
-
|
|
115
|
+
Полку касание вне каталога или Escape.
|
|
102
116
|
|
|
103
117
|
Мышь ведёт себя, как раньше: наведение, Прицел и закрытие через 150 мс остаются, пока экран широкий.
|
|
104
118
|
|
|
105
|
-
На узком экране — меньше 640px, граница та же, что у подменю `Menu` и подменю Редактора, —
|
|
119
|
+
На узком экране — меньше 640px, граница та же, что у подменю `Menu` и подменю Редактора, — Полке некуда
|
|
106
120
|
встать сбоку: справа от Столбца её обрезал бы край окна. Поэтому каталог идёт «спуском»:
|
|
107
121
|
|
|
108
122
|
- Столбец занимает всю ширину, подписи Категорий переносятся, а не обрезаются.
|
|
109
|
-
- Открытая Категория (касанием, щелчком, Enter или Пробелом) заменяет Столбец своей
|
|
123
|
+
- Открытая Категория (касанием, щелчком, Enter или Пробелом) заменяет Столбец своей Полкой. Полка
|
|
110
124
|
начинается строкой «Назад» с названием Категории; если у Категории есть своя ссылка, название показывает она,
|
|
111
125
|
строкой ниже, а не строка «Назад». Ниже — колонки ссылок, одна под другой.
|
|
112
126
|
- «Назад» возвращает Столбец и ставит фокус на Категорию, из которой человек пришёл. Escape внутри
|
|
113
|
-
|
|
127
|
+
Полки работает как «Назад».
|
|
114
128
|
- Фокус при спуске переходит на «Назад»: кнопка Категории скрыта вместе со Столбцом, и фокус на ней
|
|
115
|
-
пропал бы. Стрелка в сторону Столбца в
|
|
129
|
+
пропал бы. Стрелка в сторону Столбца в Полке на узком экране ничего не делает: Столбца рядом нет.
|
|
116
130
|
- Прицела на узком экране нет, наведение мышью Категорию не открывает, а уход мыши не отменяет спуск:
|
|
117
131
|
заменять Столбец под курсором значило бы вырывать его из-под руки.
|
|
118
132
|
- Разметка остаётся по шаблону disclosure: кнопка Категории сохраняет `aria-expanded` и `aria-controls`
|
|
@@ -123,25 +137,25 @@ import { MegaMenu, MegaMenuCategory, MegaMenuGroup, MegaMenuLink } from "@olddev
|
|
|
123
137
|
|
|
124
138
|
Ширину кит читает через `matchMedia`, а не через CSS: от неё зависит состав разметки (прячется Столбец), а
|
|
125
139
|
не только вид. До гидратации и на сервере хук возвращает «широкий» (ADR-0018): сервер ширины не знает, и
|
|
126
|
-
разметка с него всегда обычная. На телефоне после гидратации
|
|
140
|
+
разметка с него всегда обычная. На телефоне после гидратации Полка, открытая `defaultValue`, один раз
|
|
127
141
|
перерисовывается спуском. На телефоне `nav` занимает всю ширину классом `max-sm:w-full`; `w-*` из `className` действует на
|
|
128
142
|
широком экране.
|
|
129
143
|
|
|
130
144
|
## Прицел
|
|
131
145
|
|
|
132
|
-
Курсор идёт от Категории к её
|
|
133
|
-
открывало их сразу,
|
|
134
|
-
куда движется курсор: пока он идёт в сторону открытой
|
|
146
|
+
Курсор идёт от Категории к её Полке наискосок и по пути задевает соседние Категории. Если бы наведение
|
|
147
|
+
открывало их сразу, Полка менялась бы у человека под рукой, и до ссылки было бы не дотянуться. Прицел смотрит,
|
|
148
|
+
куда движется курсор: пока он идёт в сторону открытой Полки, соседняя Категория наведение не перехватывает.
|
|
135
149
|
|
|
136
150
|
- Направление последнего шага курсора сравнивается с сектором от его прежней точки к двум углам ближнего края
|
|
137
|
-
|
|
151
|
+
Полки — это и есть треугольник Каменса из раздела «Откуда это». Шаг в сектор — курсор целится.
|
|
138
152
|
- Одно движение мимо прощается, два подряд — нет: тогда открывается Категория под курсором.
|
|
139
153
|
- Курсор остановился на 300 мс — открывается Категория под ним, даже если он целился. Меню не «залипает».
|
|
140
|
-
- Курсор дошёл до
|
|
154
|
+
- Курсор дошёл до Полки — отложенная смена отменяется.
|
|
141
155
|
- Щелчок по Категории открывает её сразу, мимо Прицела. Клавиатура Прицела не знает вовсе.
|
|
142
156
|
- Только мышь, и только на широком экране: касание и перо открывают Категорию сразу, а на узком экране
|
|
143
157
|
Прицела нет вовсе.
|
|
144
|
-
- Первое наведение, пока
|
|
158
|
+
- Первое наведение, пока Полки нет, открывает Категорию без всякой задержки: целиться ещё не во что.
|
|
145
159
|
|
|
146
160
|
Модель та же, что у подменю react-aria (`Menu` с `SubmenuTrigger`), чтобы два меню кита вели себя одинаково.
|
|
147
161
|
Отличий два. Смена Категории откладывается, а не гасится `pointer-events: none` на Столбце: иначе пропала бы
|
|
@@ -194,15 +208,20 @@ _Анимация из [поста Бена Каменса](https://bjk5.com/pos
|
|
|
194
208
|
## Клавиатура и имена
|
|
195
209
|
|
|
196
210
|
- Столбец — одна остановка Tab. Стрелки вверх и вниз, Home и End ходят по Категориям; выключенные
|
|
197
|
-
пропускаются. Вернувшись Tab'ом, пользователь попадает на ту Категорию, где
|
|
198
|
-
|
|
211
|
+
пропускаются. Вернувшись Tab'ом, пользователь попадает на ту Категорию, где был; в первый раз — на открытую,
|
|
212
|
+
иначе на текущий раздел (`active`), иначе на первую.
|
|
213
|
+
- Буква на Категории переводит фокус на следующую Категорию, чья подпись с неё начинается, по кругу; та же буква
|
|
214
|
+
ещё раз — на следующую. Буквы, набранные подряд, ищут начало подписи, пауза в полсекунды сбрасывает набранное.
|
|
215
|
+
Пробел посреди набора — часть подписи («Home and garden»), а не открытие Категории.
|
|
216
|
+
Буква с Ctrl, Cmd или Alt — сочетание клавиш, каталог её не трогает.
|
|
217
|
+
- Enter, Пробел и стрелка в сторону Полки открывают её и ставят фокус на первую ссылку. У Категории-ссылки
|
|
199
218
|
Enter — переход.
|
|
200
|
-
- Внутри
|
|
201
|
-
и закрывает
|
|
202
|
-
- Пока
|
|
203
|
-
- Стрелка «к
|
|
219
|
+
- Внутри Полки работает обычный Tab. Стрелка в сторону Столбца возвращает фокус на Категорию, Escape — тоже,
|
|
220
|
+
и закрывает Полку. Tab за последней ссылкой уходит дальше по странице и закрывает Полку.
|
|
221
|
+
- Пока Полка открыта, она следует за фокусом стрелок и букв, как следует за наведением.
|
|
222
|
+
- Стрелка «к Полке» — та, что смотрит туда, где Полка стоит: при письме справа налево и у края экрана
|
|
204
223
|
стрелки меняются местами.
|
|
205
|
-
- Скринридер слышит навигацию с именем из `aria-label`, Категории — кнопками «развёрнуто / свёрнуто»,
|
|
224
|
+
- Скринридер слышит навигацию с именем из `aria-label`, Категории — кнопками «развёрнуто / свёрнуто», Полку
|
|
206
225
|
— регионом с именем Категории, колонку — списком с именем заголовка.
|
|
207
226
|
|
|
208
227
|
## Выпадающий вид
|
|
@@ -242,10 +261,10 @@ import { ListIcon } from "@olddevs/ui/icons";
|
|
|
242
261
|
- **Открытие.** Щелчок, Enter, Пробел и стрелка вниз на кнопке. Наведение каталог не открывает: он не должен
|
|
243
262
|
выпрыгивать от случайного движения мыши.
|
|
244
263
|
- **Фокус.** При открытии фокус встаёт на первую доступную Категорию (если одна из них уже открыта через `value` или
|
|
245
|
-
`defaultValue` — на неё),
|
|
264
|
+
`defaultValue` — на неё), Полка закрыта до выбора. Дальше Столбец и Полка работают как во встроенном виде.
|
|
246
265
|
- **Закрытие.** Escape на Категории, нажатие вне каталога, повторный щелчок по кнопке, переход по любой ссылке
|
|
247
266
|
(Категория, заголовок колонки, `MegaMenuLink`, в том числе ссылки роутера) и Tab за последним элементом.
|
|
248
|
-
Первый Escape внутри
|
|
267
|
+
Первый Escape внутри Полки закрывает только Полку и возвращает фокус на её Категорию; оверлей закрывает
|
|
249
268
|
следующий.
|
|
250
269
|
- **Возврат фокуса.** На кнопку фокус возвращается при Escape и при повторном щелчке. После Tab за последним
|
|
251
270
|
элементом фокус идёт дальше по странице, за кнопкой; Shift+Tab перед первым элементом возвращает на кнопку. После
|
|
@@ -253,10 +272,14 @@ import { ListIcon } from "@olddevs/ui/icons";
|
|
|
253
272
|
- **Скринридер.** Кнопка — раскрыватель с `aria-expanded` и `aria-controls`, без `aria-haspopup`: оверлей — не
|
|
254
273
|
меню и не диалог, а та же навигация с ссылками. Роли `dialog` у оверлея нет.
|
|
255
274
|
- **Слои.** Оверлей лежит на слое `popover`: выше затемнения открытого `Dialog`, ниже подтверждения. Внутри окна
|
|
256
|
-
первый Escape закрывает каталог, следующий — окно. Если фокус в открытой
|
|
275
|
+
первый Escape закрывает каталог, следующий — окно. Если фокус в открытой Полке, самый первый Escape закрывает
|
|
257
276
|
только её и возвращает фокус на Категорию.
|
|
258
|
-
-
|
|
259
|
-
|
|
277
|
+
- **Узкий экран.** Уже 640px оверлей занимает всю ширину окна за вычетом отступов по 8px с краёв, а не сжимается по
|
|
278
|
+
самой длинной подписи: подписи переносятся, длинная не выталкивает каталог за край, и ширина не меняется при
|
|
279
|
+
спуске в Категорию. Спуск, «Назад» и Escape работают внутри оверлея так же, как во встроенном виде: первый
|
|
280
|
+
Escape в Полке возвращает Столбец, следующий закрывает каталог.
|
|
281
|
+
- **Высота.** Оверлей не выше видимой области от кнопки до края окна. Столбец и Полка прокручиваются каждый
|
|
282
|
+
внутри себя (`overscroll-contain`), а не оверлей целиком: иначе рамка оверлея обрезала бы Полку, которая
|
|
260
283
|
стоит рядом со Столбцом. На iOS высота ограничена `min(24rem, 60dvh)`, как у панели `Menu`.
|
|
261
284
|
- **Появление.** Плавное, по шкале движения кита; при `prefers-reduced-motion` без анимации.
|
|
262
285
|
- **Сервер.** На сервере рисуется закрытая кнопка; оверлей монтируется в портале после открытия.
|
|
@@ -265,14 +288,16 @@ import { ListIcon } from "@olddevs/ui/icons";
|
|
|
265
288
|
|
|
266
289
|
- Реализация: `src/components/navigation/MegaMenu.tsx`, Прицел — `src/components/navigation/useMenuAim.ts`.
|
|
267
290
|
- Тесты: `tests/mega-menu.test.tsx` — роли и `aria-controls`, наведение и монтирование только открытой
|
|
268
|
-
|
|
269
|
-
Категория-ссылка, выключенная Категория, `current`, `asChild` и `link
|
|
270
|
-
|
|
291
|
+
Полки, закрытие с задержкой, roving tabindex, Enter, Escape и стрелки, Tab за последней ссылкой,
|
|
292
|
+
Категория-ссылка, выключенная Категория, `active` и алиас `current`, `asChild` и `link` (у Категории и у
|
|
293
|
+
колонки), фрагменты в колонке, текущий раздел и остановка Tab, поиск по первой букве, Категории в своих
|
|
294
|
+
компонентах, управляемое значение, нажатие вне каталога, рендер на сервере; `tests/mega-menu-aim.test.tsx` — Прицел на подменённой геометрии и поддельном
|
|
271
295
|
времени, переворот у края; `tests/mega-menu-touch.test.tsx` — касание и перо, подмена `matchMedia`, спуск
|
|
272
|
-
вместо Столбца, «Назад» и фокус, Escape, подписи из каталога сообщений,
|
|
273
|
-
|
|
296
|
+
вместо Столбца, «Назад» и фокус, Escape, подписи из каталога сообщений, спуск у Категорий в своих
|
|
297
|
+
компонентах, рендер на сервере; браузерный тест `tests-visual/mega-menu.spec.ts` — настоящая мышь наискосок и
|
|
298
|
+
вниз по Столбцу, ширина выпадающего вида на телефоне.
|
|
274
299
|
- Выпадающий вид: `tests/mega-menu-dropdown.test.tsx` — открытие щелчком и клавишами с фокусом на первой
|
|
275
|
-
Категории, все пути закрытия и возврат фокуса, Escape в
|
|
276
|
-
`popover`, порядок Escape внутри `Dialog`, рендер на сервере.
|
|
300
|
+
Категории, все пути закрытия и возврат фокуса, Escape в Полке, `open` / `defaultOpen` / `onOpenChange`, слой
|
|
301
|
+
`popover`, порядок Escape внутри `Dialog`, спуск на узком экране, рендер на сервере.
|
|
277
302
|
- История: `src/components/navigation/MegaMenu.stories.tsx` — в том числе `DropdownInTopBar`, мегаменю в шапке; на ширине
|
|
278
|
-
телефона — `PhoneColumn` и `
|
|
303
|
+
телефона — `PhoneColumn`, `PhoneDrillDown`, `PhoneDropdown` и `PhoneDropdownDrillDown`.
|
|
@@ -5,7 +5,7 @@ category: overlays
|
|
|
5
5
|
status: stable
|
|
6
6
|
source: src/components/overlays/Menu.tsx
|
|
7
7
|
story: src/components/overlays/Menu.stories.tsx
|
|
8
|
-
sourceHash:
|
|
8
|
+
sourceHash: e6bb9a14d118
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Menu
|
|
@@ -57,8 +57,11 @@ and `className`; `aria-label` is required if the button has no text, and with
|
|
|
57
57
|
`iconOnly` it never does. `Menu` accepts the usual `AriaMenuProps`, plus `offset`,
|
|
58
58
|
the gap between the panel and the button in pixels (default 6), and `placement`,
|
|
59
59
|
the side where the panel opens (default for the root menu `bottom start`; also
|
|
60
|
-
`bottom end`, `top start`, `top end`, `
|
|
61
|
-
default is described below).
|
|
60
|
+
`bottom end`, `top start`, `top end`, `end top`, `start top`, `right top` and
|
|
61
|
+
`left top`; the submenu default is described below). `end top` and `start top`
|
|
62
|
+
open beside the panel at the end or the start of the line, so they swap sides in
|
|
63
|
+
right-to-left writing; `right top` and `left top` don't depend on the writing
|
|
64
|
+
direction.
|
|
62
65
|
|
|
63
66
|
`MenuItem` accepts the `AriaMenuItem` props, `icon`, `shortcut`, `tone`,
|
|
64
67
|
`disabled` and `className`; pass the action in `onAction`. `MenuSection` accepts
|
|
@@ -72,7 +75,8 @@ the right, react-aria flips it to the left. In a right-to-left layout everything
|
|
|
72
75
|
mirrored: the submenu opens to the left of its item. On a narrow screen (up to 639px wide)
|
|
73
76
|
there is no room on either side, so the submenu opens below its item, over the
|
|
74
77
|
parent menu. An explicit `placement` on the nested `Menu` wins over the default,
|
|
75
|
-
phones included
|
|
78
|
+
phones included; to pick the side yourself without breaking right-to-left
|
|
79
|
+
writing, use `end top` or `start top`. The root menu opens as before, at `bottom start`. The Editor's
|
|
76
80
|
submenus follow the same rule.
|
|
77
81
|
|
|
78
82
|
Moving the pointer diagonally toward a submenu neither closes it nor switches to a
|
|
@@ -54,8 +54,10 @@ button look props, `icon`, `children`, `disabled`, `aria-label` и `className`;
|
|
|
54
54
|
`aria-label` обязателен, если у кнопки нет подписи; с `iconOnly` её нет всегда.
|
|
55
55
|
`Menu` принимает обычные `AriaMenuProps`, плюс `offset` — отступ панели от кнопки
|
|
56
56
|
в пикселях (умолчание 6) и `placement` — сторона раскрытия (умолчание корневого
|
|
57
|
-
меню `bottom start`; ещё `bottom end`, `top start`, `top end`, `
|
|
58
|
-
`left top`; умолчание подменю — ниже).
|
|
57
|
+
меню `bottom start`; ещё `bottom end`, `top start`, `top end`, `end top`,
|
|
58
|
+
`start top`, `right top`, `left top`; умолчание подменю — ниже). `end top` и
|
|
59
|
+
`start top` — сбоку по концу и по началу строки: при письме справа налево они
|
|
60
|
+
меняются местами. `right top` и `left top` от направления письма не зависят.
|
|
59
61
|
|
|
60
62
|
`MenuItem` принимает props `AriaMenuItem`, `icon`, `shortcut`, `tone`, `disabled`
|
|
61
63
|
и `className`; действие передавайте в `onAction`. `MenuSection` принимает
|
|
@@ -69,7 +71,8 @@ react-aria переворачивает подменю влево. При пис
|
|
|
69
71
|
подменю раскрывается слева от пункта. На узком экране (до 639px шириной) места
|
|
70
72
|
сбоку нет ни справа, ни слева, и подменю встаёт под своим пунктом, поверх
|
|
71
73
|
родительского меню. Явный `placement` у вложенного `Menu` побеждает умолчание, в
|
|
72
|
-
том числе на
|
|
74
|
+
том числе на телефоне; чтобы задать сторону самому и не сломать письмо справа
|
|
75
|
+
налево, берите `end top` или `start top`. Корневое меню раскрывается как прежде — `bottom start`.
|
|
73
76
|
То же правило у подменю Редактора.
|
|
74
77
|
|
|
75
78
|
Движение курсора к подменю по диагонали его не закрывает и не переключает на
|