@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.
Files changed (84) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/components/navigation/MegaMenu.d.ts +43 -23
  3. package/dist/components/navigation/MegaMenu.d.ts.map +1 -1
  4. package/dist/components/navigation/MegaMenu.js +110 -47
  5. package/dist/components/navigation/MegaMenu.js.map +1 -1
  6. package/dist/components/navigation/useMenuAim.d.ts +8 -8
  7. package/dist/components/navigation/useMenuAim.d.ts.map +1 -1
  8. package/dist/components/overlays/Menu.d.ts +4 -2
  9. package/dist/components/overlays/Menu.d.ts.map +1 -1
  10. package/dist/components/overlays/Menu.js.map +1 -1
  11. package/dist/editor/RichTextEditor.d.ts.map +1 -1
  12. package/dist/editor/RichTextEditor.js +38 -4
  13. package/dist/editor/RichTextEditor.js.map +1 -1
  14. package/dist/editor/core/handle.d.ts +1 -5
  15. package/dist/editor/core/handle.d.ts.map +1 -1
  16. package/dist/editor/core/handle.js +1 -13
  17. package/dist/editor/core/handle.js.map +1 -1
  18. package/dist/editor/core/handleWatchers.d.ts +6 -0
  19. package/dist/editor/core/handleWatchers.d.ts.map +1 -0
  20. package/dist/editor/core/handleWatchers.js +14 -0
  21. package/dist/editor/core/handleWatchers.js.map +1 -0
  22. package/dist/editor/core/tools.d.ts +7 -2
  23. package/dist/editor/core/tools.d.ts.map +1 -1
  24. package/dist/editor/core/tools.js +2 -1
  25. package/dist/editor/core/tools.js.map +1 -1
  26. package/dist/editor/features/toc/RichTextToc.d.ts +63 -0
  27. package/dist/editor/features/toc/RichTextToc.d.ts.map +1 -0
  28. package/dist/editor/features/toc/RichTextToc.js +372 -0
  29. package/dist/editor/features/toc/RichTextToc.js.map +1 -0
  30. package/dist/editor/features/toc/headings.d.ts +31 -0
  31. package/dist/editor/features/toc/headings.d.ts.map +1 -0
  32. package/dist/editor/features/toc/headings.js +25 -0
  33. package/dist/editor/features/toc/headings.js.map +1 -0
  34. package/dist/editor/features/toc/stickyOffset.d.ts +3 -0
  35. package/dist/editor/features/toc/stickyOffset.d.ts.map +1 -0
  36. package/dist/editor/features/toc/stickyOffset.js +14 -0
  37. package/dist/editor/features/toc/stickyOffset.js.map +1 -0
  38. package/dist/editor/index.d.ts +2 -0
  39. package/dist/editor/index.d.ts.map +1 -1
  40. package/dist/editor/index.js +1 -0
  41. package/dist/editor/index.js.map +1 -1
  42. package/dist/i18n/defaults.d.ts.map +1 -1
  43. package/dist/i18n/defaults.js +1 -0
  44. package/dist/i18n/defaults.js.map +1 -1
  45. package/dist/i18n/messages.d.ts +5 -0
  46. package/dist/i18n/messages.d.ts.map +1 -1
  47. package/dist/locales/ru.d.ts.map +1 -1
  48. package/dist/locales/ru.js +1 -0
  49. package/dist/locales/ru.js.map +1 -1
  50. package/docs/api/messages.en.md +743 -0
  51. package/docs/api/messages.md +1 -0
  52. package/docs/api/utilities.en.md +233 -0
  53. package/docs/components/content/rich-text-toc.en.md +130 -0
  54. package/docs/components/content/rich-text-toc.md +129 -0
  55. package/docs/components/index.en.md +3 -2
  56. package/docs/components/index.md +2 -1
  57. package/docs/components/inputs/rich-text-editor.en.md +52 -34
  58. package/docs/components/inputs/rich-text-editor.md +20 -2
  59. package/docs/components/navigation/mega-menu.en.md +86 -61
  60. package/docs/components/navigation/mega-menu.md +81 -56
  61. package/docs/components/overlays/menu.en.md +8 -4
  62. package/docs/components/overlays/menu.md +6 -3
  63. package/docs/conventions.en.md +318 -0
  64. package/docs/getting-started.en.md +5 -5
  65. package/docs/getting-started.md +4 -4
  66. package/docs/glossary.en.md +24 -23
  67. package/docs/glossary.md +23 -22
  68. package/docs/index.en.md +53 -0
  69. package/llms.txt +2 -1
  70. package/package.json +1 -1
  71. package/src/components/navigation/MegaMenu.tsx +300 -140
  72. package/src/components/navigation/useMenuAim.ts +19 -19
  73. package/src/components/overlays/Menu.tsx +12 -2
  74. package/src/editor/RichTextEditor.tsx +107 -3
  75. package/src/editor/core/handle.ts +2 -23
  76. package/src/editor/core/handleWatchers.ts +27 -0
  77. package/src/editor/core/tools.ts +9 -2
  78. package/src/editor/features/toc/RichTextToc.tsx +712 -0
  79. package/src/editor/features/toc/headings.ts +70 -0
  80. package/src/editor/features/toc/stickyOffset.ts +21 -0
  81. package/src/editor/index.ts +2 -0
  82. package/src/i18n/defaults.ts +1 -0
  83. package/src/i18n/messages.ts +5 -0
  84. package/src/locales/ru.ts +1 -0
@@ -37,7 +37,7 @@ category: inputs
37
37
  status: stable
38
38
  source: src/editor/RichTextEditor.tsx
39
39
  story: src/editor/RichTextEditor.stories.tsx
40
- sourceHash: 95badb4a657c
40
+ sourceHash: 3eaf71d10511
41
41
  ---
42
42
 
43
43
  # RichTextEditor
@@ -305,43 +305,44 @@ The `link` Tool: the "Link" button on the Toolbar and `Ctrl/⌘+K` open a popove
305
305
 
306
306
  ## Tools and Presets
307
307
 
308
- `preset="full"` (default) is everything in the release. `preset="basic"` is for comments and short descriptions. `tools` is an exact list and takes precedence over a Preset; descriptions of custom Tools can be mixed in with the kit's names (see the "Custom Tool" section). The type of a `tools` element is `Tool` (a kit name or a `CustomTool`), the type of a kit name is `ToolName`; both are exported from `@olddevs/ui/editor`.
308
+ `preset="full"` (default) is everything in the release except the Table of contents `toc`: it changes the layout of the field, so it is switched on only explicitly. `preset="basic"` is for comments and short descriptions. `tools` is an exact list and takes precedence over a Preset; descriptions of custom Tools can be mixed in with the kit's names (see the "Custom Tool" section). The type of a `tools` element is `Tool` (a kit name or a `CustomTool`), the type of a kit name is `ToolName`; both are exported from `@olddevs/ui/editor`.
309
309
 
310
310
  Presets are also exported as arrays of names — `RICH_TEXT_PRESETS.full` and `RICH_TEXT_PRESETS.basic` — so that you can assemble your own set from them: `tools={[...RICH_TEXT_PRESETS.full, mergeTagTool]}`. The arrays are frozen. A Preset's content is "what the kit considers the full set", not a frozen list: a new built-in Tool arrives in a Preset in a minor version. If you need a stable set, pass an exact list of names.
311
311
 
312
312
  **A disabled Tool removes both the button and the Schema extension.** Its Block or Mark cannot be created with a keyboard shortcut, a Markdown shortcut or a paste, and from `value` they arrive without Block markup, and the text stays.
313
313
 
314
- | `Tool` | Button | Markup | Shortcut | Markdown while typing | `basic` |
315
- | ----------------- | -------------------------------------- | ----------------------------------------- | ------------------------------------------------- | ---------------------- | ------- |
316
- | `heading` | "¶": Heading 2–4 | `h2`, `h3`, `h4` | `Ctrl/⌘+Alt+2…4` | `## `, `### `, `#### ` | — |
317
- | `heading1` | "¶": adds Heading 1 | `h1` | `Ctrl/⌘+Alt+1` | `# ` | — |
318
- | `history` | Undo, Redo | — | `Ctrl/⌘+Z`, `+Shift+Z` | — | yes |
319
- | `bold` | Bold | `strong` | `Ctrl/⌘+B` | `**text**` | yes |
320
- | `italic` | Italic | `em` | `Ctrl/⌘+I` | `*text*` | yes |
321
- | `underline` | "…": Underline | `u` | `Ctrl/⌘+U` | — | — |
322
- | `strike` | Strikethrough | `s` | `Ctrl/⌘+Shift+S` | `~~text~~` | yes |
323
- | `code` | "…": Inline code | `code` | `Ctrl/⌘+E` | `` `text` `` | yes |
324
- | `superscript` | "…": Superscript | `sup` | `Ctrl/⌘+.` | — | — |
325
- | `subscript` | "…": Subscript | `sub` | `Ctrl/⌘+,` | — | — |
326
- | `clearFormatting` | "…": Clear formatting | — (removes Marks) | `Ctrl/⌘+\` | — | — |
327
- | `link` | Link (address popover) | `a[href]` | `Ctrl/⌘+K` | `[text](url)` on paste | yes |
328
- | `color` | Color ▾ (Selection menu): "Text color" | `span[data-color]`, `span[style]` | — | — | — |
329
- | `highlight` | Highlight ▾ (Toolbar), "Color ▾" | `mark[data-color]`, `mark[style]` | — | — | — |
330
- | `align` | Alignment ▾ (menu) | `p`, `h2`–`h4` with `data-align` | `Ctrl/⌘+Shift+L`, `+E`, `+R` | — | — |
331
- | `bulletList` | Lists ▾: Bulleted list | `ul`, `li` | `Ctrl/⌘+Shift+8` | `- ` or `* ` | yes |
332
- | `orderedList` | Lists ▾: Numbered list | `ol`, `li` | `Ctrl/⌘+Shift+7` | `1. ` | yes |
333
- | `taskList` | Lists ▾: Checklist | `ul[data-type=taskList]` and items | `Ctrl/⌘+Shift+9` | `[ ] ` or `[x] ` | — |
334
- | `blockquote` | "…": Quote | `blockquote` | `Ctrl/⌘+Shift+B` | `> ` | yes |
335
- | `codeBlock` | "…": Code block | `pre`, `code class="language-*"` | `Ctrl/⌘+Alt+C` | ` ``` ` | — |
336
- | `horizontalRule` | "+": Divider | `hr` | — | `---` | — |
337
- | `table` | Table ▾ (menu) | `table`, `tbody`, `tr`, `th`, `td` | `Tab`, `Shift+Tab`, `Alt+↓` in a table | — | — |
338
- | `image` | Image (address popover) | `figure`, `img`, `figcaption` | — | `![alt](url)` on paste | — |
339
- | `emoji` | Emoji (popover with a grid) | — (plain text) | — | — | — |
340
- | `source` | HTML (mode toggle) | — (edits the Document's HTML) | — | — | — |
341
- | `search` | Find (find and replace panel) | — (searches the Document) | `Ctrl/⌘+F`, `Ctrl/⌘+Alt+F` | — | yes |
342
- | `layout` | "+": Layout | `div` with `data-block` and `data-layout` | `Tab`, `Shift+Tab`, arrow keys at the column edge | — | — |
343
- | `wrapper` | "+": Wrapper | `div[data-block=wrapper]` | `Enter` in an empty last paragraph — exit | — | — |
344
- | `insertMenu` | "+" Insert menu | — (Blocks of other Tools) | — | `/` | — |
314
+ | `Tool` | Button | Markup | Shortcut | Markdown while typing | `basic` |
315
+ | ----------------- | ---------------------------------------------------- | ----------------------------------------- | ------------------------------------------------- | ---------------------- | ------- |
316
+ | `heading` | "¶": Heading 2–4 | `h2`, `h3`, `h4` | `Ctrl/⌘+Alt+2…4` | `## `, `### `, `#### ` | — |
317
+ | `heading1` | "¶": adds Heading 1 | `h1` | `Ctrl/⌘+Alt+1` | `# ` | — |
318
+ | `history` | Undo, Redo | — | `Ctrl/⌘+Z`, `+Shift+Z` | — | yes |
319
+ | `bold` | Bold | `strong` | `Ctrl/⌘+B` | `**text**` | yes |
320
+ | `italic` | Italic | `em` | `Ctrl/⌘+I` | `*text*` | yes |
321
+ | `underline` | "…": Underline | `u` | `Ctrl/⌘+U` | — | — |
322
+ | `strike` | Strikethrough | `s` | `Ctrl/⌘+Shift+S` | `~~text~~` | yes |
323
+ | `code` | "…": Inline code | `code` | `Ctrl/⌘+E` | `` `text` `` | yes |
324
+ | `superscript` | "…": Superscript | `sup` | `Ctrl/⌘+.` | — | — |
325
+ | `subscript` | "…": Subscript | `sub` | `Ctrl/⌘+,` | — | — |
326
+ | `clearFormatting` | "…": Clear formatting | — (removes Marks) | `Ctrl/⌘+\` | — | — |
327
+ | `link` | Link (address popover) | `a[href]` | `Ctrl/⌘+K` | `[text](url)` on paste | yes |
328
+ | `color` | Color ▾ (Selection menu): "Text color" | `span[data-color]`, `span[style]` | — | — | — |
329
+ | `highlight` | Highlight ▾ (Toolbar), "Color ▾" | `mark[data-color]`, `mark[style]` | — | — | — |
330
+ | `align` | Alignment ▾ (menu) | `p`, `h2`–`h4` with `data-align` | `Ctrl/⌘+Shift+L`, `+E`, `+R` | — | — |
331
+ | `bulletList` | Lists ▾: Bulleted list | `ul`, `li` | `Ctrl/⌘+Shift+8` | `- ` or `* ` | yes |
332
+ | `orderedList` | Lists ▾: Numbered list | `ol`, `li` | `Ctrl/⌘+Shift+7` | `1. ` | yes |
333
+ | `taskList` | Lists ▾: Checklist | `ul[data-type=taskList]` and items | `Ctrl/⌘+Shift+9` | `[ ] ` or `[x] ` | — |
334
+ | `blockquote` | "…": Quote | `blockquote` | `Ctrl/⌘+Shift+B` | `> ` | yes |
335
+ | `codeBlock` | "…": Code block | `pre`, `code class="language-*"` | `Ctrl/⌘+Alt+C` | ` ``` ` | — |
336
+ | `horizontalRule` | "+": Divider | `hr` | — | `---` | — |
337
+ | `table` | Table ▾ (menu) | `table`, `tbody`, `tr`, `th`, `td` | `Tab`, `Shift+Tab`, `Alt+↓` in a table | — | — |
338
+ | `image` | Image (address popover) | `figure`, `img`, `figcaption` | — | `![alt](url)` on paste | — |
339
+ | `emoji` | Emoji (popover with a grid) | — (plain text) | — | — | — |
340
+ | `source` | HTML (mode toggle) | — (edits the Document's HTML) | — | — | — |
341
+ | `search` | Find (find and replace panel) | — (searches the Document) | `Ctrl/⌘+F`, `Ctrl/⌘+Alt+F` | — | yes |
342
+ | `layout` | "+": Layout | `div` with `data-block` and `data-layout` | `Tab`, `Shift+Tab`, arrow keys at the column edge | — | — |
343
+ | `wrapper` | "+": Wrapper | `div[data-block=wrapper]` | `Enter` in an empty last paragraph — exit | — | — |
344
+ | `insertMenu` | "+" Insert menu | — (Blocks of other Tools) | — | `/` | — |
345
+ | `toc` | Table of contents in the left gutter (not in `full`) | — (reads the headings) | — | — | — |
345
346
 
346
347
  A paragraph is always there; a soft line break is `Shift+Enter`. `insertMenu` and `emoji`, like `clearFormatting`, have no markup of their own: `insertMenu` provides access to other Tools' Blocks, and it is disabled where `/` is an ordinary character (paths, "and/or"); `basic` lacks it for the same reason. `emoji` inserts plain text — the Schema, the sanitizer and Markdown do not change because of it (see the "Emoji" section); `basic` lacks it: in a comment the OS's own palette is easier to reach, and a button would take up room in a short row. `source` is the HTML mode (see the "HTML mode" section): it has no markup of its own either, an HTML edit goes through the same Schema as `value`, and it cannot be used to get around the set of Tools; `basic` lacks it — in a comment an "HTML" button is superfluous unless the screen explicitly asks for it.
347
348
 
@@ -349,7 +350,7 @@ A paragraph is always there; a soft line break is `Shift+Enter`. `insertMenu` an
349
350
 
350
351
  Earlier, "no H1 in the Document" was a kit decision (the page heading sits outside the Editor); now it is the screen's decision — ADR-0053 revises the earlier decision, and ADR-0050 links to ADR-0053.
351
352
 
352
- A name enters the `Tool` type together with the Tool itself, not earlier: widening the input type does not break the consumer, whereas a name without an implementation would compile and do nothing. That is how `link` arrived — straight into the `basic` Preset too — and `color`, `highlight`, `align`, `table`, `image`, `emoji`, `layout`, `wrapper`, `source`; there are no reserved names now. Unknown names in `tools` (from JS without types) are dropped silently.
353
+ A name enters the `Tool` type together with the Tool itself, not earlier: widening the input type does not break the consumer, whereas a name without an implementation would compile and do nothing. That is how `link` arrived — straight into the `basic` Preset too — and `color`, `highlight`, `align`, `table`, `image`, `emoji`, `layout`, `wrapper`, `source`, `toc`; there are no reserved names now. Unknown names in `tools` (from JS without types) are dropped silently.
353
354
 
354
355
  `preset` and `tools` are read **when the engine is created**: the Schema of a live ProseMirror cannot be changed. To change the set on a mounted field, remount it with a key (`key`) — the edit history is lost in the process.
355
356
 
@@ -1170,6 +1171,23 @@ The `search` Tool (present in `full` and in `basic`) is find and replace over th
1170
1171
 
1171
1172
  Message catalog strings: `editor.search` (the button and the `Ctrl/⌘+F` Shortcuts reference row), `searchReplace` (`Ctrl/⌘+Alt+F`), `searchPanel`, `searchField`, `replaceField`, `searchPrevious`, `searchNext`, `searchClose`, `searchMatchCase`, `searchWholeWord`, `searchRegex`, `replaceOne`, `replaceAll`, `searchCount({ current, total })`, `searchNoMatches`, `searchInvalid`.
1172
1173
 
1174
+ ## Table of contents
1175
+
1176
+ The `toc` Tool puts the Table of contents of the Document in the Editor's left gutter: the same strip of strokes and the same list as [`RichTextToc`](../content/rich-text-toc.md), but the Editor holds the place itself (ADR-0068). It is in neither Preset, `full` or `basic`: the Table of contents moves the text, and a kit update does not change the layout of screens that did not ask for it. Switch it on by name:
1177
+
1178
+ ```tsx
1179
+ <RichTextEditor tools={[...RICH_TEXT_PRESETS.full, "toc"]} defaultValue={html} />
1180
+ ```
1181
+
1182
+ - **Place.** A column to the left of the Document, inside the field frame. It exists only while there is a strip, from two `h1`–`h4` headings at the top level and in Wrappers: with the second heading the text moves right; without it the text stays at the edge, as without the Tool. The first stroke is level with the first line of the first heading.
1183
+ - **Stickiness.** The strip sticks under the fixed Toolbar (and under the app header: `--topbar-h`, ADR-0016); without a Toolbar, to the edge of the window. In a field with `maxHeight` or `resizable` it sticks to the top of the scrolling area, and the highlight and the jump count from there; the page does not scroll.
1184
+ - **The list** works as in `RichTextToc`: hover opens it to the right of the strip, Enter and Space open it from the keyboard, arrows move between items, Escape closes it; the current section is highlighted in the strip and in the list; the width is 240px, and a long heading is cut with an ellipsis. A click on an item does not close the list: moving the pointer away does.
1185
+ - **A jump while editing** puts the caret at the end of the heading, so you can type right away, as in the document outline of Google Docs; the Table of contents does the scrolling, and the heading flashes a fading background. In `readOnly` there is no caret, so focus goes to the heading itself.
1186
+ - **A live Document.** The list is rebuilt shortly after each edit: a new, renamed or removed heading shows in the strip. In HTML mode there is no Table of contents, because the Document is hidden.
1187
+ - **No Toolbar button.** To show or hide the Table of contents, the screen changes `tools` (and remounts the field with a `key`, as for any set of Tools).
1188
+
1189
+ Message catalog string: `editor.toc` ("Table of contents"), the name of the strip and the label of the list.
1190
+
1173
1191
  ## Status line and Field notice
1174
1192
 
1175
1193
  Under the field inside the frame there are two independent areas. The **Status line** is what the screen puts there itself: "Saved", a word count, a co-author. The **Field notice** is what the kit and Tools report: an upload result, a paste rejection. The first is a React node in the `status` prop, the second is data and an engine command.
@@ -304,7 +304,7 @@ import { RichTextEditor } from "@olddevs/ui/editor";
304
304
 
305
305
  ## Инструменты и Пресеты
306
306
 
307
- `preset="full"` (по умолчанию) — всё, что есть в выпуске. `preset="basic"` — для комментариев и коротких описаний. `tools` — точный список, сильнее Пресета; в нём вперемешку с именами кита стоят описания своих Инструментов (раздел «Свой Инструмент»). Тип элемента `tools` — `Tool` (имя кита или `CustomTool`), тип имени кита — `ToolName`; оба экспортируются из `@olddevs/ui/editor`.
307
+ `preset="full"` (по умолчанию) — всё, что есть в выпуске, кроме Оглавления `toc`: оно меняет раскладку поля, и его включают только явно. `preset="basic"` — для комментариев и коротких описаний. `tools` — точный список, сильнее Пресета; в нём вперемешку с именами кита стоят описания своих Инструментов (раздел «Свой Инструмент»). Тип элемента `tools` — `Tool` (имя кита или `CustomTool`), тип имени кита — `ToolName`; оба экспортируются из `@olddevs/ui/editor`.
308
308
 
309
309
  Пресеты экспортируются и массивами имён — `RICH_TEXT_PRESETS.full` и `RICH_TEXT_PRESETS.basic`, чтобы собирать из них свой набор: `tools={[...RICH_TEXT_PRESETS.full, mergeTagTool]}`. Массивы заморожены. Состав Пресета — «то, что кит считает полным набором», а не замороженный список: новый встроенный Инструмент приходит в Пресет minor-версией. Нужен стабильный состав — передайте точный список имён.
310
310
 
@@ -341,6 +341,7 @@ import { RichTextEditor } from "@olddevs/ui/editor";
341
341
  | `layout` | «+»: Раскладка | `div` с `data-block` и `data-layout` | `Tab`, `Shift+Tab`, стрелки у края колонки | — | — |
342
342
  | `wrapper` | «+»: Обёртка | `div[data-block=wrapper]` | `Enter` в пустом последнем абзаце — выход | — | — |
343
343
  | `insertMenu` | «+» Меню вставки | — (Блоки других Инструментов) | — | `/` | — |
344
+ | `toc` | Оглавление в левом поле (не в `full`) | — (читает заголовки) | — | — | — |
344
345
 
345
346
  Абзац есть всегда, мягкий перенос строки — `Shift+Enter`. `insertMenu` и `emoji`, как и `clearFormatting`, своей разметки не имеют: `insertMenu` даёт доступ к Блокам других Инструментов, а выключают его там, где `/` — обычный символ (пути, «и/или»); в `basic` его нет по той же причине. `emoji` вставляет обычный текст — Схема, санитайзер и Markdown от него не меняются (раздел «Эмодзи»); в `basic` его нет: в комментарии системная палитра ОС удобнее, а кнопка занимала бы место у короткого ряда. `source` — HTML-режим (раздел «HTML-режим»): своей разметки у него тоже нет, правка HTML проходит ту же Схему, что `value`, и обойти набор Инструментов им нельзя; в `basic` его нет — в комментарии кнопка «HTML» лишняя, если экран её не просил.
346
347
 
@@ -348,7 +349,7 @@ import { RichTextEditor } from "@olddevs/ui/editor";
348
349
 
349
350
  Раньше «H1 в Документе нет» было решением кита (заголовок страницы стоит вне Редактора); теперь это решение экрана — ADR-0053 пересматривает прежнее решение; ссылка на ADR-0053 стоит в ADR-0050.
350
351
 
351
- Имя приходит в тип `Tool` вместе с самим Инструментом, не раньше: расширение входного типа потребителя не ломает, а имя без реализации компилировалось бы и ничего не давало. Так пришли `link` — сразу и в Пресет `basic` — и `color`, `highlight`, `align`, `table`, `image`, `emoji`, `layout`, `wrapper`, `source`; зарезервированных имён сейчас нет. Неизвестные имена в `tools` (из JS без типов) отбрасываются молча.
352
+ Имя приходит в тип `Tool` вместе с самим Инструментом, не раньше: расширение входного типа потребителя не ломает, а имя без реализации компилировалось бы и ничего не давало. Так пришли `link` — сразу и в Пресет `basic` — и `color`, `highlight`, `align`, `table`, `image`, `emoji`, `layout`, `wrapper`, `source`, `toc`; зарезервированных имён сейчас нет. Неизвестные имена в `tools` (из JS без типов) отбрасываются молча.
352
353
 
353
354
  `preset` и `tools` читаются **при создании движка**: Схему живого ProseMirror не поменять. Чтобы сменить набор у смонтированного поля, перемонтируйте его ключом (`key`) — история правок при этом теряется.
354
355
 
@@ -1169,6 +1170,23 @@ ARIA сетки — шаблон `grid` из APG: `role="grid"` → `row` → `g
1169
1170
 
1170
1171
  Строки каталога сообщений: `editor.search` (кнопка и строка Справочника сочетаний `Ctrl/⌘+F`), `searchReplace` (`Ctrl/⌘+Alt+F`), `searchPanel`, `searchField`, `replaceField`, `searchPrevious`, `searchNext`, `searchClose`, `searchMatchCase`, `searchWholeWord`, `searchRegex`, `replaceOne`, `replaceAll`, `searchCount({ current, total })`, `searchNoMatches`, `searchInvalid`.
1171
1172
 
1173
+ ## Оглавление
1174
+
1175
+ Инструмент `toc` — Оглавление Документа в левом поле Редактора: та же полоса штрихов и тот же список, что у [`RichTextToc`](../content/rich-text-toc.md), но место держит сам Редактор (ADR-0068). В Пресеты он не входит — ни в `full`, ни в `basic`: Оглавление сдвигает текст, и обновление кита не меняет раскладку экранов, которые его не просили. Включают его именем:
1176
+
1177
+ ```tsx
1178
+ <RichTextEditor tools={[...RICH_TEXT_PRESETS.full, "toc"]} defaultValue={html} />
1179
+ ```
1180
+
1181
+ - **Место.** Колонка слева от Документа внутри рамки поля. Она есть, только пока есть полоса — от двух заголовков `h1`–`h4` на верхнем уровне и в Обёртке: со вторым заголовком текст сдвигается вправо, без него стоит у края, как без Инструмента. Первый штрих стоит на уровне первой строки первого заголовка.
1182
+ - **Липкость.** Полоса липнет под фиксированной Панелью (и под шапкой приложения: `--topbar-h`, ADR-0016), без Панели — к краю окна. В поле с `maxHeight` или `resizable` она липнет к верху прокручиваемой области, а подсветка и переход считают от неё — страница при этом не прокручивается.
1183
+ - **Список** — как у `RichTextToc`: наведение раскрывает его справа от полосы, Enter и Space — с клавиатуры, стрелки ходят по пунктам, Escape закрывает; текущий раздел подсвечен в полосе и в списке; ширина 240px, длинный заголовок обрезан многоточием. Клик по пункту список не закрывает: его закрывает уход курсора.
1184
+ - **Переход в правке** ставит каретку в конец заголовка — можно сразу писать, как в структуре документа Google Docs; прокрутку делает Оглавление, заголовок подсвечивается гаснущим фоном. В `readOnly` каретки нет — фокус встаёт на сам заголовок.
1185
+ - **Живой Документ.** Список пересобирается на правке с короткой задержкой: новый заголовок, переименование и удаление видны в полосе. В HTML-режиме Оглавления нет — Документ скрыт.
1186
+ - **Кнопки на Панели нет.** Показать или спрятать Оглавление — смена `tools` экраном (с перемонтажом ключом, как у любого набора Инструментов).
1187
+
1188
+ Строка каталога сообщений — `editor.toc` («Оглавление»): имя полосы и подпись списка.
1189
+
1172
1190
  ## Строка статуса и Сообщение поля
1173
1191
 
1174
1192
  Под полем внутри рамки две независимые области. **Строка статуса** — то, что экран кладёт сам: «Сохранено», число слов, соавтор. **Сообщение поля** — то, что сообщают кит и Инструменты: результат загрузки, отказ вставки. Первое — React-узел пропа `status`, второе — данные и команда движка.
@@ -5,7 +5,7 @@ category: navigation
5
5
  status: stable
6
6
  source: src/components/navigation/MegaMenu.tsx
7
7
  story: src/components/navigation/MegaMenu.stories.tsx
8
- sourceHash: 4c7401326a3f
8
+ sourceHash: 42179963266b
9
9
  ---
10
10
 
11
11
  # MegaMenu
@@ -13,10 +13,11 @@ sourceHash: 4c7401326a3f
13
13
  ## Summary for AI
14
14
 
15
15
  `MegaMenu` is an Amazon-style store catalog: a category column on the left and, next to the hovered
16
- category, its showcase with columns of links and any content of your own. It is link navigation, not a command
16
+ category, its flyout with columns of links and any content of your own. It is link navigation, not a command
17
17
  menu: the markup follows the WAI-ARIA Disclosure Navigation pattern (`nav`, buttons with `aria-expanded`, real
18
- `a` elements). Categories are `MegaMenuCategory` as direct children, columns are `MegaMenuGroup`, links are
19
- `MegaMenuLink`. The same catalog opens as a dropdown overlay from a button: `MegaMenuTrigger` with
18
+ `a` elements). Categories are `MegaMenuCategory` (directly or inside your own components, server components
19
+ included), columns are `MegaMenuGroup`, links are `MegaMenuLink`. The current section and the current page are
20
+ marked with the `active` prop, as on `NavItem`. The same catalog opens as a dropdown overlay from a button: `MegaMenuTrigger` with
20
21
  `MegaMenuButton` and an unchanged `MegaMenu`.
21
22
 
22
23
  ## When to use
@@ -24,7 +25,7 @@ menu: the markup follows the WAI-ARIA Disclosure Navigation pattern (`nav`, butt
24
25
  - A catalog with ten or more sections, each with subsections in columns: a store, a marketplace, a help portal.
25
26
  - The sections should be visible at once and the subsections should appear on hover, without taking the user
26
27
  off the page.
27
- - The showcase needs content of its own next to the links: a promo card, an image, a selection.
28
+ - The flyout needs content of its own next to the links: a promo card, an image, a selection.
28
29
 
29
30
  ## When not to use
30
31
 
@@ -39,10 +40,10 @@ menu: the markup follows the WAI-ARIA Disclosure Navigation pattern (`nav`, butt
39
40
  import { MegaMenu, MegaMenuCategory, MegaMenuGroup, MegaMenuLink } from "@olddevs/ui";
40
41
 
41
42
  <MegaMenu aria-label="Catalog">
42
- <MegaMenuCategory id="computers" label="Computers" href="/computers">
43
+ <MegaMenuCategory id="computers" label="Computers" href="/computers" active>
43
44
  <MegaMenuGroup title="Laptops" href="/computers/laptops">
44
45
  <MegaMenuLink href="/computers/laptops/gaming">Gaming</MegaMenuLink>
45
- <MegaMenuLink href="/computers/laptops/ultrabooks" current>
46
+ <MegaMenuLink href="/computers/laptops/ultrabooks" active>
46
47
  Ultrabooks
47
48
  </MegaMenuLink>
48
49
  </MegaMenuGroup>
@@ -59,67 +60,80 @@ import { MegaMenu, MegaMenuCategory, MegaMenuGroup, MegaMenuLink } from "@olddev
59
60
  | ------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------- |
60
61
  | `aria-label` | — | The accessible name of the navigation. Required: on a page with several `nav` elements, the name tells them apart |
61
62
  | `value` / `defaultValue` / `onValueChange` | `null` | The open category (its `id`) or `null`, controlled or uncontrolled |
62
- | `showcaseWidth` | `"40rem"` | The width of the showcase, shared by all categories so that the catalog does not jump between them |
63
+ | `showcaseWidth` | `"40rem"` | The width of the flyout, shared by all categories so that the catalog does not jump between them |
63
64
  | `className` | — | Classes for the `nav`; the default width of the category column is `w-64` |
64
65
 
65
66
  `MegaMenuCategory`: `id` is the key for `value`; `label` is the text in the category column and the name of the
66
- showcase; `icon`; `href` or `link` is the address of the whole section; `disabled`; the children are the content of
67
- the showcase. For a category with a showcase, the address becomes a title link at the top of the showcase; for a
67
+ flyout; `icon`; `href` or `link` is the address of the whole section; `disabled`; `active` means the current page is in
68
+ this section; the children are the content of the flyout. For a category with a flyout, the address becomes a title link at the top of the flyout; for a
68
69
  category without children, the row in the category column is itself the link. `link` is a router link passed as an
69
70
  empty element (`link={<Link href="/x" />}`), and the label moves inside it. It is a separate prop rather than
70
- `asChild` because the children of a category are its showcase.
71
+ `asChild` because the children of a category are its flyout.
71
72
 
72
- `MegaMenuGroup`: `title` is the title of the column and the name of its list; `href` turns the title into a link;
73
- the children are `MegaMenuLink` elements, and each one is placed in its own list item.
73
+ `active` on a category sets `aria-current="true"` and gives the label the accent color, the same one a current
74
+ link gets. The mark stays whether or not the flyout is open: the route decides the current section, not hover. The
75
+ open category is still marked with a background, so one row can carry both. While no flyout is open, Tab enters the
76
+ category column on the current section. Your code decides which section is current, as with `active` on `NavItem`.
74
77
 
75
- `MegaMenuLink`: the usual `a` props, `icon`, `current` and `asChild`. `current` sets `aria-current="page"` and the
78
+ Categories don't have to be direct children of `MegaMenu`: you can split them across your own components and render
79
+ them from a Next.js server component. Each category reports itself to the column, and the column doesn't inspect its
80
+ children by type, because a server component's element arrives on the client with a different type. Before
81
+ hydration, while the categories haven't reported themselves yet, every available category is a Tab stop; after
82
+ hydration the column is a single stop again.
83
+
84
+ `MegaMenuGroup`: `title` is the title of the column and the name of its list; `href` or `link` turns the title into
85
+ a link (`link` is a router link passed as an empty element, as on a category); the children are `MegaMenuLink`
86
+ elements, and each one is placed in its own list item, including those passed in a fragment.
87
+
88
+ `MegaMenuLink`: the usual `a` props, `icon`, `active` and `asChild`. `active` sets `aria-current="page"` and the
76
89
  look of the current link. The kit does not work out the current page itself, because every router compares
77
90
  addresses by its own rules. `asChild` renders the link as a router element:
78
- `<MegaMenuLink asChild><Link href="/x">Label</Link></MegaMenuLink>`.
91
+ `<MegaMenuLink asChild><Link href="/x">Label</Link></MegaMenuLink>`. The old name, `current`, works until 5.0 and is
92
+ marked `@deprecated`: the kit calls the current navigation item `active`.
79
93
 
80
- ## Hover and the showcase
94
+ ## Hover and the flyout
81
95
 
82
- Hovering a category with the mouse opens its showcase at once, with no delay. Exactly one category is highlighted,
83
- the one whose showcase is open: the highlight follows `aria-expanded`, not `:hover`. Hovering a category without a
84
- showcase closes the open one, with the same caveat about aim as below.
96
+ Hovering a category with the mouse opens its flyout at once, with no delay. Exactly one category is highlighted,
97
+ the one whose flyout is open: the highlight follows `aria-expanded`, not `:hover`. Hovering a category without a
98
+ flyout closes the open one, with the same caveat about aim as below.
85
99
 
86
- The showcase closes 150 ms after the pointer has left both the category column and the showcase, so a brief
100
+ The flyout closes 150 ms after the pointer has left both the category column and the flyout, so a brief
87
101
  excursion past the edge does not reset the catalog. It also closes on a pointer press outside the catalog and when
88
102
  focus leaves the catalog.
89
103
 
90
- The content of a showcase is mounted only for the open category, so a catalog of a hundred links and images does
104
+ The content of a flyout is mounted only for the open category, so a catalog of a hundred links and images does
91
105
  not load everything at once. The region itself is always there, hidden: the button's `aria-controls` points to it.
92
106
 
93
- The showcase is a DOM descendant of its category, not a portal. That is why the Tab order runs "category, its
94
- links, then on down the page", and why the showcase sits flush against the category column without any position
107
+ The flyout is a DOM descendant of its category, not a portal. That is why the Tab order runs "category, its
108
+ links, then on down the page", and why the flyout sits flush against the category column without any position
95
109
  calculation. An ancestor with `overflow: hidden` clips it, so put the catalog where there is room beside it.
96
110
 
97
- The showcase sits on the end side of the line: on the right, or on the left in right-to-left writing. If there is
111
+ The flyout sits on the end side of the line: on the right, or on the left in right-to-left writing. If there is
98
112
  not enough room there and there is enough on the other side, it moves to the other side. The kit measures the side
99
- ahead of time and again when the window is resized, so the arrow key that points "toward the showcase" already
100
- points to where the showcase will appear.
113
+ ahead of time and again when the window is resized, so the arrow key that points "toward the flyout" already
114
+ points to where the flyout will appear.
101
115
 
102
116
  ## Touch and narrow screens
103
117
 
104
118
  Touch has no hover, so the touch itself (`click`) opens a category, without aim. A pointer whose `pointerType` is
105
- "touch" or "pen" neither opens a category on hover nor closes the showcase when it leaves: a pen behaves like a
106
- finger. Touching an open category again neither closes it nor goes anywhere. The showcase closes on a touch outside
119
+ "touch" or "pen" neither opens a category on hover nor closes the flyout when it leaves: a pen behaves like a
120
+ finger. Touching an open category again neither closes it nor goes anywhere. The flyout closes on a touch outside
107
121
  the catalog or on Escape.
108
122
 
109
123
  The mouse behaves as before: hover, aim and closing after 150 ms all remain while the screen is wide.
110
124
 
111
125
  On a narrow screen, under 640px (the same breakpoint as `Menu` submenus and Editor submenus), there is no room beside
112
- the category column for the showcase: the edge of the window would cut it off on the right. So the catalog works as a
126
+ the category column for the flyout: the edge of the window would cut it off on the right. So the catalog works as a
113
127
  drill-down:
114
128
 
115
129
  - The category column takes the full width, and category labels wrap instead of being truncated.
116
- - An open category (by touch, click, Enter or Space) replaces the category column with its showcase. The showcase
130
+ - An open category (by touch, click, Enter or Space) replaces the category column with its flyout. The flyout
117
131
  starts with a "Back" row that shows the category name. If the category has a link of its own, the link shows the
118
132
  name, on the row below, instead of the "Back" row. Below that, the columns of links follow, one under another.
119
- - "Back" returns the category column and puts focus on the category the person came from. Escape inside the showcase
133
+ - "Back" returns the category column and puts focus on the category the person came from. Escape inside the flyout
120
134
  works like "Back".
121
135
  - When you drill down, focus moves to "Back": the category button is hidden along with the category column, and focus
122
- left on it would be lost. In the showcase on a narrow screen, the arrow key toward the category column does
136
+ left on it would be lost. In the flyout on a narrow screen, the arrow key toward the category column does
123
137
  nothing, because the column is not beside it.
124
138
  - There is no aim on a narrow screen: hovering with the mouse does not open a category, and moving the mouse away does
125
139
  not cancel the drill-down. Replacing the column under the cursor would pull it out from under the person's hand.
@@ -133,25 +147,25 @@ catalog supplies "Назад".
133
147
  The kit reads the width through `matchMedia`, not through CSS, because the width determines the contents of the
134
148
  markup (the category column is hidden), not just the look. Before hydration and on the server, the hook reports a wide
135
149
  screen (ADR-0018): the server does not know the width, and the markup it produces is always the regular one. On a phone,
136
- after hydration, a showcase opened by `defaultValue` is re-rendered once as a drill-down. On a phone the `nav` takes
150
+ after hydration, a flyout opened by `defaultValue` is re-rendered once as a drill-down. On a phone the `nav` takes
137
151
  the full width through the `max-sm:w-full` class; a `w-*` class from `className` applies on a wide screen.
138
152
 
139
153
  ## Aim
140
154
 
141
- On its way from a category to its showcase, the pointer cuts diagonally and brushes against the neighboring
142
- categories. If hover opened them at once, the showcase would change under the person's hand, and the links would be
143
- out of reach. Aim watches where the pointer is heading: while it moves toward the open showcase, a neighboring
155
+ On its way from a category to its flyout, the pointer cuts diagonally and brushes against the neighboring
156
+ categories. If hover opened them at once, the flyout would change under the person's hand, and the links would be
157
+ out of reach. Aim watches where the pointer is heading: while it moves toward the open flyout, a neighboring
144
158
  category does not take over the hover.
145
159
 
146
160
  - The direction of the pointer's last step is compared with the wedge from its previous position to the two corners
147
- of the showcase's near edge: Kamens's triangle from "Where it comes from". A step into the wedge means the pointer
161
+ of the flyout's near edge: Kamens's triangle from "Where it comes from". A step into the wedge means the pointer
148
162
  is aiming.
149
163
  - One movement off course is forgiven; two in a row are not, and then the category under the pointer opens.
150
164
  - If the pointer stops for 300 ms, the category under it opens even if it was aiming. The menu does not get stuck.
151
- - When the pointer reaches the showcase, the deferred switch is canceled.
165
+ - When the pointer reaches the flyout, the deferred switch is canceled.
152
166
  - Clicking a category opens it at once, bypassing aim. The keyboard knows nothing about aim.
153
167
  - Mouse only, and only on a wide screen: touch and pen open a category at once, and a narrow screen has no aim at all.
154
- - The first hover, while there is no showcase yet, opens the category with no delay at all: there is nothing to aim at.
168
+ - The first hover, while there is no flyout yet, opens the category with no delay at all: there is nothing to aim at.
155
169
 
156
170
  The model is the same as for react-aria submenus (`Menu` with `SubmenuTrigger`), so that the two menus of the kit
157
171
  behave alike. There are two differences. The category switch is deferred, not suppressed with `pointer-events: none`
@@ -208,16 +222,21 @@ Primary sources:
208
222
  ## Keyboard and names
209
223
 
210
224
  - The category column is a single Tab stop. The Up and Down arrows, Home and End move through the categories;
211
- disabled ones are skipped. When the user comes back with Tab, they land on the category they were on.
212
- - Enter, Space and the arrow toward the showcase open it and put focus on the first link. On a category that is a
225
+ disabled ones are skipped. When the user comes back with Tab, they land on the category they were on; the first
226
+ time, on the open one, otherwise on the current section (`active`), otherwise on the first one.
227
+ - Typing a letter on a category moves focus to the next category whose label starts with it, wrapping around; the
228
+ same letter again moves to the one after. Letters typed in quick succession match the start of a label, and a
229
+ half-second pause clears them. A Space in the middle of typing is part of the label ("Home and garden"), not a
230
+ key that opens the category. A letter with Ctrl, Cmd or Alt is a keyboard shortcut, and the catalog leaves it alone.
231
+ - Enter, Space and the arrow toward the flyout open it and put focus on the first link. On a category that is a
213
232
  link, Enter follows it.
214
- - Inside the showcase, plain Tab works. The arrow toward the category column returns focus to the category, and so
215
- does Escape, which also closes the showcase. Tab past the last link moves on down the page and closes the showcase.
216
- - While the showcase is open, it follows the arrow-key focus the way it follows hover.
217
- - The arrow "toward the showcase" is the one that points to where the showcase sits: in right-to-left writing and at
233
+ - Inside the flyout, plain Tab works. The arrow toward the category column returns focus to the category, and so
234
+ does Escape, which also closes the flyout. Tab past the last link moves on down the page and closes the flyout.
235
+ - While the flyout is open, it follows focus moved by arrows and letters the way it follows hover.
236
+ - The arrow "toward the flyout" is the one that points to where the flyout sits: in right-to-left writing and at
218
237
  the edge of the screen, the arrows swap places.
219
238
  - A screen reader hears a navigation with the name from `aria-label`, the categories as expand/collapse buttons, the
220
- showcase as a region named after the category, and a column as a list named after its title.
239
+ flyout as a region named after the category, and a column as a list named after its title.
221
240
 
222
241
  ## Dropdown mode
223
242
 
@@ -257,11 +276,11 @@ into one. `MegaMenuButton` accepts the kit's button look (`variant`, `tone`, `si
257
276
  - **Opening.** Click, Enter, Space and the Down arrow on the button. Hover does not open the catalog: it should not
258
277
  jump out at a stray mouse movement.
259
278
  - **Focus.** On opening, focus goes to the first available category (or, if one is already open through `value` or
260
- `defaultValue`, to that one), and the showcase stays closed until a category is chosen. After that the category column
261
- and the showcase work as in the embedded mode.
279
+ `defaultValue`, to that one), and the flyout stays closed until a category is chosen. After that the category column
280
+ and the flyout work as in the embedded mode.
262
281
  - **Closing.** Escape on a category, a press outside the catalog, a second click on the button, following any link (a
263
282
  category, a column title, a `MegaMenuLink`, router links included) and Tab past the last element.
264
- The first Escape inside a showcase closes only the showcase and returns focus to its category; the next one closes
283
+ The first Escape inside a flyout closes only the flyout and returns focus to its category; the next one closes
265
284
  the overlay.
266
285
  - **Returning focus.** Focus returns to the button on Escape and on a second click. After Tab past the last element,
267
286
  focus moves on down the page, past the button; Shift+Tab before the first element returns it to the button. After a
@@ -270,10 +289,14 @@ into one. `MegaMenuButton` accepts the kit's button look (`variant`, `tone`, `si
270
289
  overlay is neither a menu nor a dialog, but the same navigation with links. The overlay has no `dialog` role.
271
290
  - **Layers.** The overlay sits on the `popover` layer: above the scrim of an open `Dialog`, below the confirmation.
272
291
  Inside a dialog, the first Escape closes the catalog and the next one closes the dialog. If focus is in an open
273
- showcase, the very first Escape closes only the showcase and returns focus to its category.
292
+ flyout, the very first Escape closes only the flyout and returns focus to its category.
293
+ - **Narrow screens.** Under 640px the overlay spans the window, minus 8px at each edge, instead of shrinking to the
294
+ longest label: labels wrap, a long one can't push the catalog off screen, and the width stays put when you drill
295
+ into a category. The drill-down, "Back" and Escape work inside the overlay just as in the embedded mode: the first
296
+ Escape in a flyout brings the column back, the next one closes the catalog.
274
297
  - **Height.** The overlay is no taller than the visible area from the button to the edge of the window. The category
275
- column and the showcase each scroll within themselves (`overscroll-contain`), not the overlay as a whole: otherwise the
276
- overlay's frame would clip the showcase, which stands beside the category column. On iOS the height is capped at
298
+ column and the flyout each scroll within themselves (`overscroll-contain`), not the overlay as a whole: otherwise the
299
+ overlay's frame would clip the flyout, which stands beside the category column. On iOS the height is capped at
277
300
  `min(24rem, 60dvh)`, like the `Menu` panel.
278
301
  - **Appearance.** Smooth, on the kit's motion scale; with `prefers-reduced-motion`, no animation.
279
302
  - **Server.** The server renders a closed button; the overlay is mounted in a portal after opening.
@@ -282,15 +305,17 @@ into one. `MegaMenuButton` accepts the kit's button look (`variant`, `tone`, `si
282
305
 
283
306
  - Implementation: `src/components/navigation/MegaMenu.tsx`; aim is in `src/components/navigation/useMenuAim.ts`.
284
307
  - Tests: `tests/mega-menu.test.tsx` covers roles and `aria-controls`, hover and mounting only the open
285
- showcase, closing with a delay, roving tabindex, Enter, Escape and the arrows, Tab past the last link,
286
- a category that is a link, a disabled category, `current`, `asChild` and `link`, the controlled value, a press
287
- outside the catalog and server rendering; `tests/mega-menu-aim.test.tsx` covers aim on mocked geometry and fake
308
+ flyout, closing with a delay, roving tabindex, Enter, Escape and the arrows, Tab past the last link,
309
+ a category that is a link, a disabled category, `active` and the `current` alias, `asChild` and `link` (on a
310
+ category and on a column), fragments in a column, the current section and the Tab stop, typeahead, categories
311
+ inside your own components, the controlled value, a press outside the catalog and server rendering; `tests/mega-menu-aim.test.tsx` covers aim on mocked geometry and fake
288
312
  time, and the flip at the edge; `tests/mega-menu-touch.test.tsx` covers touch and pen, a mocked `matchMedia`,
289
- the drill-down in place of the category column, "Back" and focus, Escape, labels from the message catalog and server
290
- rendering; the browser test `tests-visual/mega-menu.spec.ts` uses a real mouse moving diagonally and down
291
- the category column.
313
+ the drill-down in place of the category column, "Back" and focus, Escape, labels from the message catalog, the
314
+ drill-down with categories inside your own components and server rendering; the browser test
315
+ `tests-visual/mega-menu.spec.ts` uses a real mouse moving diagonally and down the category column, and checks the
316
+ width of the dropdown on a phone.
292
317
  - Dropdown mode: `tests/mega-menu-dropdown.test.tsx` covers opening by click and keys with focus on the first
293
- category, every way of closing and returning focus, Escape in the showcase, `open` / `defaultOpen` / `onOpenChange`, the
294
- `popover` layer, the order of Escape inside a `Dialog` and server rendering.
318
+ category, every way of closing and returning focus, Escape in the flyout, `open` / `defaultOpen` / `onOpenChange`, the
319
+ `popover` layer, the order of Escape inside a `Dialog`, the drill-down on a narrow screen and server rendering.
295
320
  - Story: `src/components/navigation/MegaMenu.stories.tsx`, including `DropdownInTopBar`, a mega menu in a top bar; at phone
296
- width, `PhoneColumn` and `PhoneDrillDown`.
321
+ width, `PhoneColumn`, `PhoneDrillDown`, `PhoneDropdown` and `PhoneDropdownDrillDown`.