@olddevs/ui 4.5.1 → 4.6.1
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 +26 -0
- package/dist/editor/RichTextEditor.d.ts +2 -2
- package/dist/editor/RichTextEditor.d.ts.map +1 -1
- package/dist/editor/RichTextEditor.js +3 -0
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/editor/core/blockTypes.d.ts +2 -8
- package/dist/editor/core/blockTypes.d.ts.map +1 -1
- package/dist/editor/core/blockTypes.js +11 -54
- package/dist/editor/core/blockTypes.js.map +1 -1
- package/dist/editor/core/containers.d.ts.map +1 -1
- package/dist/editor/core/containers.js +1 -0
- package/dist/editor/core/containers.js.map +1 -1
- package/dist/editor/core/customMarkdown.js +1 -1
- package/dist/editor/core/customMarkdown.js.map +1 -1
- package/dist/editor/core/customTool.d.ts +6 -6
- package/dist/editor/core/customTool.d.ts.map +1 -1
- package/dist/editor/core/customToolEngine.d.ts +2 -2
- package/dist/editor/core/extensions.d.ts +7 -2
- package/dist/editor/core/extensions.d.ts.map +1 -1
- package/dist/editor/core/extensions.js +96 -64
- package/dist/editor/core/extensions.js.map +1 -1
- package/dist/editor/core/fieldNotice.d.ts +1 -1
- package/dist/editor/core/insertItems.d.ts +5 -28
- package/dist/editor/core/insertItems.d.ts.map +1 -1
- package/dist/editor/core/insertItems.js +3 -94
- package/dist/editor/core/insertItems.js.map +1 -1
- package/dist/editor/core/insertMenuTool.d.ts +13 -0
- package/dist/editor/core/insertMenuTool.d.ts.map +1 -0
- package/dist/editor/core/insertMenuTool.js +20 -0
- package/dist/editor/core/insertMenuTool.js.map +1 -0
- package/dist/editor/core/schema.d.ts +20 -5
- package/dist/editor/core/schema.d.ts.map +1 -1
- package/dist/editor/core/schema.js +18 -3
- package/dist/editor/core/schema.js.map +1 -1
- package/dist/editor/core/shortcuts.d.ts +56 -5
- package/dist/editor/core/shortcuts.d.ts.map +1 -1
- package/dist/editor/core/shortcuts.js +91 -156
- package/dist/editor/core/shortcuts.js.map +1 -1
- package/dist/editor/core/skeleton.d.ts +1 -1
- package/dist/editor/core/skeleton.d.ts.map +1 -1
- package/dist/editor/core/skeleton.js +1 -0
- package/dist/editor/core/skeleton.js.map +1 -1
- package/dist/editor/core/toolButtons.d.ts +3 -3
- package/dist/editor/core/toolDefinition.d.ts +3 -3
- package/dist/editor/core/toolDescriptor.d.ts +49 -30
- package/dist/editor/core/toolDescriptor.d.ts.map +1 -1
- package/dist/editor/core/toolDescriptor.js +2 -5
- package/dist/editor/core/toolDescriptor.js.map +1 -1
- package/dist/editor/core/toolLoader.d.ts +1 -1
- package/dist/editor/core/toolLoader.d.ts.map +1 -1
- package/dist/editor/core/toolOptions.d.ts +19 -2
- package/dist/editor/core/toolOptions.d.ts.map +1 -1
- package/dist/editor/core/toolOptions.js.map +1 -1
- package/dist/editor/core/tools.d.ts +10 -2
- package/dist/editor/core/tools.d.ts.map +1 -1
- package/dist/editor/core/tools.js +1 -0
- package/dist/editor/core/tools.js.map +1 -1
- package/dist/editor/features/builtins.d.ts +6 -8
- package/dist/editor/features/builtins.d.ts.map +1 -1
- package/dist/editor/features/builtins.js +48 -21
- package/dist/editor/features/builtins.js.map +1 -1
- package/dist/editor/features/code/codeBlockTool.d.ts +5 -6
- package/dist/editor/features/code/codeBlockTool.d.ts.map +1 -1
- package/dist/editor/features/code/codeBlockTool.js +10 -1
- package/dist/editor/features/code/codeBlockTool.js.map +1 -1
- package/dist/editor/features/color/colorTools.d.ts +14 -0
- package/dist/editor/features/color/colorTools.d.ts.map +1 -0
- package/dist/editor/features/color/colorTools.js +39 -0
- package/dist/editor/features/color/colorTools.js.map +1 -0
- package/dist/editor/features/details/details.d.ts +68 -0
- package/dist/editor/features/details/details.d.ts.map +1 -0
- package/dist/editor/features/details/details.js +237 -0
- package/dist/editor/features/details/details.js.map +1 -0
- package/dist/editor/features/details/detailsMarkdown.d.ts +17 -0
- package/dist/editor/features/details/detailsMarkdown.d.ts.map +1 -0
- package/dist/editor/features/details/detailsMarkdown.js +84 -0
- package/dist/editor/features/details/detailsMarkdown.js.map +1 -0
- package/dist/editor/features/details/detailsTool.d.ts +16 -0
- package/dist/editor/features/details/detailsTool.d.ts.map +1 -0
- package/dist/editor/features/details/detailsTool.js +39 -0
- package/dist/editor/features/details/detailsTool.js.map +1 -0
- package/dist/editor/features/emoji/emojiTool.d.ts +1 -1
- package/dist/editor/features/heading/headingTool.d.ts +18 -0
- package/dist/editor/features/heading/headingTool.d.ts.map +1 -0
- package/dist/editor/features/heading/headingTool.js +59 -0
- package/dist/editor/features/heading/headingTool.js.map +1 -0
- package/dist/editor/features/history/historyTool.d.ts +15 -0
- package/dist/editor/features/history/historyTool.d.ts.map +1 -0
- package/dist/editor/features/history/historyTool.js +28 -0
- package/dist/editor/features/history/historyTool.js.map +1 -0
- package/dist/editor/features/image/imageTool.d.ts +19 -0
- package/dist/editor/features/image/imageTool.d.ts.map +1 -0
- package/dist/editor/features/image/imageTool.js +28 -0
- package/dist/editor/features/image/imageTool.js.map +1 -0
- package/dist/editor/features/layout/layoutTool.d.ts +11 -0
- package/dist/editor/features/layout/layoutTool.d.ts.map +1 -0
- package/dist/editor/features/layout/layoutTool.js +20 -0
- package/dist/editor/features/layout/layoutTool.js.map +1 -0
- package/dist/editor/features/lists/listTools.d.ts +15 -0
- package/dist/editor/features/lists/listTools.d.ts.map +1 -0
- package/dist/editor/features/lists/listTools.js +78 -0
- package/dist/editor/features/lists/listTools.js.map +1 -0
- package/dist/editor/features/marks/markTools.d.ts +16 -0
- package/dist/editor/features/marks/markTools.d.ts.map +1 -0
- package/dist/editor/features/marks/markTools.js +43 -0
- package/dist/editor/features/marks/markTools.js.map +1 -0
- package/dist/editor/features/search/searchTool.d.ts +1 -1
- package/dist/editor/features/table/tableTool.d.ts +3 -3
- package/dist/editor/features/wrapper/wrapperTool.d.ts +11 -0
- package/dist/editor/features/wrapper/wrapperTool.d.ts.map +1 -0
- package/dist/editor/features/wrapper/wrapperTool.js +32 -0
- package/dist/editor/features/wrapper/wrapperTool.js.map +1 -0
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.d.ts.map +1 -1
- package/dist/editor/index.js.map +1 -1
- package/dist/editor/schema-entry.d.ts +1 -1
- package/dist/editor/styles/prose.d.ts.map +1 -1
- package/dist/editor/styles/prose.js +26 -0
- package/dist/editor/styles/prose.js.map +1 -1
- package/dist/editor/ui/EditorToolbar.d.ts.map +1 -1
- package/dist/editor/ui/EditorToolbar.js +5 -38
- package/dist/editor/ui/EditorToolbar.js.map +1 -1
- package/dist/editor/ui/SelectionBubble.d.ts.map +1 -1
- package/dist/editor/ui/SelectionBubble.js +1 -1
- package/dist/editor/ui/SelectionBubble.js.map +1 -1
- package/dist/editor/ui/ToolButtonView.d.ts.map +1 -1
- package/dist/editor/ui/ToolButtonView.js +1 -1
- package/dist/editor/ui/ToolButtonView.js.map +1 -1
- package/dist/i18n/defaults.d.ts.map +1 -1
- package/dist/i18n/defaults.js +3 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/messages.d.ts +16 -1
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/icons.d.ts +8 -1
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/icons.js.map +1 -1
- package/dist/locales/ru.d.ts.map +1 -1
- package/dist/locales/ru.js +3 -0
- package/dist/locales/ru.js.map +1 -1
- package/docs/api/editor-extension.en.md +68 -68
- package/docs/api/editor-extension.md +55 -55
- package/docs/api/messages.en.md +4 -1
- package/docs/api/messages.md +3 -0
- package/docs/api/migration-notes.en.md +11 -11
- package/docs/api/migration-notes.md +2 -2
- package/docs/api/utilities.en.md +6 -6
- package/docs/components/actions/split-button.en.md +9 -9
- package/docs/components/content/data-display.en.md +1 -1
- package/docs/components/content/layout-primitives.en.md +3 -3
- package/docs/components/content/rich-text-toc.en.md +1 -1
- package/docs/components/content/rich-text-view.en.md +23 -17
- package/docs/components/content/rich-text-view.md +17 -11
- package/docs/components/content/virtual-grid.en.md +2 -2
- package/docs/components/date-time/date-picker.en.md +1 -1
- package/docs/components/inputs/rich-text-editor.en.md +283 -230
- package/docs/components/inputs/rich-text-editor.md +139 -86
- package/docs/components/navigation/mega-menu.en.md +10 -10
- package/docs/components/overlays/menu.en.md +2 -2
- package/docs/components/overlays/shortcuts-help.en.md +1 -1
- package/docs/components/table/data-table.en.md +3 -3
- package/docs/components/table/filter-bar.en.md +1 -1
- package/docs/components/table/toolbar.en.md +1 -1
- package/docs/conventions.en.md +2 -2
- package/docs/conventions.md +1 -1
- package/docs/getting-started.en.md +9 -9
- package/docs/getting-started.md +8 -8
- package/docs/glossary.en.md +193 -133
- package/docs/glossary.md +191 -132
- package/docs/icons.en.md +12 -9
- package/docs/icons.md +3 -0
- package/docs/palette.en.md +3 -3
- package/docs/theming.en.md +6 -6
- package/package.json +2 -1
- package/src/editor/RichTextEditor.tsx +9 -6
- package/src/editor/core/atomText.ts +2 -2
- package/src/editor/core/blockTypes.ts +19 -83
- package/src/editor/core/containers.ts +4 -2
- package/src/editor/core/customMarkdown.ts +2 -2
- package/src/editor/core/customTool.ts +6 -6
- package/src/editor/core/customToolEngine.ts +9 -9
- package/src/editor/core/extensions.ts +165 -110
- package/src/editor/core/fieldNotice.ts +1 -1
- package/src/editor/core/handle.ts +2 -2
- package/src/editor/core/insertItems.ts +12 -158
- package/src/editor/core/insertMenuTool.ts +32 -0
- package/src/editor/core/jsonDocument.ts +1 -1
- package/src/editor/core/schema.ts +42 -7
- package/src/editor/core/shortcuts.ts +116 -177
- package/src/editor/core/skeleton.ts +4 -3
- package/src/editor/core/toolButtons.ts +3 -3
- package/src/editor/core/toolDefinition.ts +4 -4
- package/src/editor/core/toolDescriptor.ts +65 -52
- package/src/editor/core/toolLoader.ts +3 -3
- package/src/editor/core/toolOptions.ts +21 -2
- package/src/editor/core/tools.ts +10 -1
- package/src/editor/core/viewIsland.ts +1 -1
- package/src/editor/features/builtins.ts +76 -41
- package/src/editor/features/code/codeBlockTool.ts +16 -8
- package/src/editor/features/color/colorTools.ts +68 -0
- package/src/editor/features/details/details.ts +389 -0
- package/src/editor/features/details/detailsMarkdown.ts +158 -0
- package/src/editor/features/details/detailsTool.ts +69 -0
- package/src/editor/features/emoji/emojiTool.tsx +1 -1
- package/src/editor/features/heading/headingTool.ts +96 -0
- package/src/editor/features/history/historyTool.ts +42 -0
- package/src/editor/features/image/imageTool.ts +49 -0
- package/src/editor/features/layout/layoutTool.ts +40 -0
- package/src/editor/features/lists/listTools.ts +112 -0
- package/src/editor/features/marks/markTools.ts +78 -0
- package/src/editor/features/search/searchTool.ts +1 -1
- package/src/editor/features/table/tableTool.ts +3 -3
- package/src/editor/features/toc/tocTool.ts +1 -1
- package/src/editor/features/wrapper/wrapperTool.ts +52 -0
- package/src/editor/index.ts +1 -0
- package/src/editor/schema-entry.ts +1 -1
- package/src/editor/styles/prose.ts +49 -0
- package/src/editor/ui/EditorToolbar.tsx +7 -50
- package/src/editor/ui/SelectionBubble.tsx +2 -5
- package/src/editor/ui/ToolButtonView.tsx +2 -1
- package/src/i18n/defaults.ts +3 -0
- package/src/i18n/messages.ts +14 -1
- package/src/icons.ts +7 -0
- package/src/locales/ru.ts +3 -0
|
@@ -63,6 +63,13 @@ export const RICH_TEXT_SCHEMA = {
|
|
|
63
63
|
* Стоит на верхнем уровне и прямо в колонке Раскладки; внутри — Блоки, кроме
|
|
64
64
|
* другой Обёртки и Раскладки.
|
|
65
65
|
*
|
|
66
|
+
* `details` — сворачиваемый Блок: `details > summary` и следом
|
|
67
|
+
* `div[data-type="detailsContent"]` с Блоками; `open` — только у `details` и
|
|
68
|
+
* только пустым (`open=""`), так его пишет Редактор с Опцией `persist`.
|
|
69
|
+
* `summary` вне `details` и обёртка содержимого вне него снимаются —
|
|
70
|
+
* Блоки остаются подряд. Теги и атрибуты Блока открыты только в этой форме:
|
|
71
|
+
* `iframe` и ему подобное так не открывается.
|
|
72
|
+
*
|
|
66
73
|
* Таблица — `table > tbody > tr > th|td`, без `thead`, `colgroup` и
|
|
67
74
|
* `caption`: строка-заголовок — это обычная первая строка из `th`. На ячейке
|
|
68
75
|
* бывают только слияние (`colspan`, `rowspan`) и выравнивание столбца
|
|
@@ -94,6 +101,8 @@ export const RICH_TEXT_SCHEMA = {
|
|
|
94
101
|
"figure",
|
|
95
102
|
"img",
|
|
96
103
|
"figcaption",
|
|
104
|
+
"details",
|
|
105
|
+
"summary",
|
|
97
106
|
],
|
|
98
107
|
/**
|
|
99
108
|
* Метки и строчные узлы. `br` — мягкий перенос по Shift+Enter. `mark` —
|
|
@@ -112,9 +121,13 @@ export const RICH_TEXT_SCHEMA = {
|
|
|
112
121
|
* Раскладка и её колонка: `data-block="layout"` и `data-block="column"`;
|
|
113
122
|
* `data-layout` — вариант из `layouts`, только у Раскладки. Обёртка —
|
|
114
123
|
* `data-block="wrapper"` и, если у неё есть тон, `data-tone` — имя из
|
|
115
|
-
* `wrapperTones`, только у Обёртки.
|
|
124
|
+
* `wrapperTones`, только у Обёртки. Обёртка содержимого сворачиваемого
|
|
125
|
+
* Блока — `data-type="detailsContent"`, только прямо в `details`. У `div`
|
|
126
|
+
* пункта чек-листа атрибутов нет.
|
|
116
127
|
*/
|
|
117
|
-
div: ["data-block", "data-layout", "data-tone"],
|
|
128
|
+
div: ["data-block", "data-layout", "data-tone", "data-type"],
|
|
129
|
+
/** Открытый сворачиваемый Блок: пустой `open`, только с Опцией `persist`. */
|
|
130
|
+
details: ["open"],
|
|
118
131
|
/** Пункт чек-листа: `data-type="taskItem"` и `data-checked="true|false"`. */
|
|
119
132
|
li: ["data-type", "data-checked"],
|
|
120
133
|
/** Флажок пункта чек-листа: всегда `type="checkbox"`, `checked` — у отмеченного. */
|
|
@@ -369,8 +382,8 @@ function customAttributes(
|
|
|
369
382
|
* набор отвергает ошибкой поля, а у сервера ошибок нет — и описание с именем
|
|
370
383
|
* кита (`wrapper`, атрибут `tone`) не должно открыть обход правил кита.
|
|
371
384
|
*
|
|
372
|
-
* Читаются только данные описания (`blocks`, `marks`),
|
|
373
|
-
*
|
|
385
|
+
* Читаются только данные описания (`blocks`, `marks`), Extensions не
|
|
386
|
+
* трогаются: модуль с описанием своего Инструмента импортируется на сервере,
|
|
374
387
|
* и Tiptap здесь не нужен.
|
|
375
388
|
*/
|
|
376
389
|
export function richTextSchema(tools: readonly ToolEntry[] = []): RichTextSchema {
|
|
@@ -584,6 +597,10 @@ export interface RichTextPurifier {
|
|
|
584
597
|
* колонке Раскладки, не в другой Обёртке, списке, цитате и таблице; иначе
|
|
585
598
|
* снимается так же, вместе с тоном; `data-tone` — имя из `wrapperTones` и
|
|
586
599
|
* только на Обёртке;
|
|
600
|
+
* - сворачиваемый Блок — `details` с `summary` прямо в нём и обёрткой содержимого
|
|
601
|
+
* `div[data-type="detailsContent"]` тоже прямо в нём; `open` — только пустой и
|
|
602
|
+
* только на `details`. `summary` и обёртка вне `details` теряют тег, их Блоки
|
|
603
|
+
* остаются подряд;
|
|
587
604
|
* - `colspan`, `rowspan` — на `th`/`td`, целое от 1 до `tableSpans`;
|
|
588
605
|
* - ссылка — `href` по `protocols` (выражение конфига), `rel` всегда
|
|
589
606
|
* `"noopener noreferrer"`, ссылка без адреса становится текстом;
|
|
@@ -648,7 +665,14 @@ const ATTRIBUTE_RULES: Record<string, (node: Element, value: string) => boolean>
|
|
|
648
665
|
start: (node) => node.nodeName === "OL",
|
|
649
666
|
"data-type": (node, value) =>
|
|
650
667
|
(node.nodeName === "UL" && value === "taskList") ||
|
|
651
|
-
(node.nodeName === "LI" && value === "taskItem")
|
|
668
|
+
(node.nodeName === "LI" && value === "taskItem") ||
|
|
669
|
+
// Обёртка содержимого сворачиваемого Блока: только `div` прямо в `details`.
|
|
670
|
+
(node.nodeName === "DIV" &&
|
|
671
|
+
value === DETAILS_CONTENT &&
|
|
672
|
+
node.parentElement?.nodeName === "DETAILS"),
|
|
673
|
+
// Открытый Блок: `open=""`, как его пишет Редактор; значение `open="open"` из
|
|
674
|
+
// чужого HTML атрибут теряет, Блок остаётся закрытым.
|
|
675
|
+
open: (node, value) => node.nodeName === "DETAILS" && value === "",
|
|
652
676
|
"data-checked": (node, value) =>
|
|
653
677
|
node.nodeName === "LI" &&
|
|
654
678
|
node.getAttribute("data-type") === "taskItem" &&
|
|
@@ -787,6 +811,9 @@ const PARENT_OF: Record<string, string> = {
|
|
|
787
811
|
SPAN: `${TASK_ITEM} > label`,
|
|
788
812
|
};
|
|
789
813
|
|
|
814
|
+
/** Значение `div[data-type]` обёртки содержимого сворачиваемого Блока (`details.ts`). */
|
|
815
|
+
const DETAILS_CONTENT = "detailsContent";
|
|
816
|
+
|
|
790
817
|
/** Значения `div[data-block]`: Раскладка, её колонка и Обёртка. */
|
|
791
818
|
const LAYOUT_BLOCK = "layout";
|
|
792
819
|
const COLUMN_BLOCK = "column";
|
|
@@ -794,10 +821,10 @@ const WRAPPER_BLOCK = "wrapper";
|
|
|
794
821
|
|
|
795
822
|
/**
|
|
796
823
|
* Где Раскладки быть не может: в другой Раскладке (её колонке), в Обёртке, в
|
|
797
|
-
* списке,
|
|
824
|
+
* списке, цитате, ячейке и сворачиваемом Блоке. Редактор такую не отдаёт — у колонки и прочих контейнеров
|
|
798
825
|
* в Схеме движка её нет, — и при загрузке разворачивает в Блоки подряд.
|
|
799
826
|
*/
|
|
800
|
-
const NOT_A_LAYOUT_PLACE = "div[data-block], li, blockquote, th, td";
|
|
827
|
+
const NOT_A_LAYOUT_PLACE = "div[data-block], li, blockquote, th, td, details";
|
|
801
828
|
|
|
802
829
|
/**
|
|
803
830
|
* Раскладка, колонка и Обёртка: `true` — элемент обработан (оставлен или
|
|
@@ -868,6 +895,14 @@ function checkStructure(node: Element, schema: RichTextSchema): void {
|
|
|
868
895
|
return;
|
|
869
896
|
}
|
|
870
897
|
if (node.nodeName === "SPAN" && coloured) return;
|
|
898
|
+
// Сворачиваемый Блок: `summary` — только прямо в `details`, обёртка
|
|
899
|
+
// содержимого с `data-type` — тоже (атрибут уже снят хуком, если она не там).
|
|
900
|
+
// Вне Блока они теряют обёртку, содержимое остаётся.
|
|
901
|
+
if (node.nodeName === "SUMMARY") {
|
|
902
|
+
if (node.parentElement?.nodeName !== "DETAILS") node.replaceWith(...node.childNodes);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (node.nodeName === "DIV" && node.getAttribute("data-type") === DETAILS_CONTENT) return;
|
|
871
906
|
// Картинка — `img` с адресом прямо внутри `figure`, и только так: `figure`
|
|
872
907
|
// без годной картинки разворачивается в содержимое, `img` вне `figure` или без
|
|
873
908
|
// адреса удаляется, `figcaption` вне `figure` теряет обёртку.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { UiMessages } from "../../i18n/messages.js";
|
|
2
|
-
import {
|
|
2
|
+
import { toolLabel, type ToolList } from "./toolDescriptor.js";
|
|
3
3
|
import type { PrefixedName } from "./customTool.js";
|
|
4
4
|
import type { Tool } from "./tools.js";
|
|
5
5
|
|
|
@@ -65,7 +65,94 @@ export interface ShortcutCommand {
|
|
|
65
65
|
const LIST_TOOLS: readonly Tool[] = ["bulletList", "orderedList", "taskList"];
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Сочетания встроенных команд по id — данные, а не привязки. Строка справочника
|
|
69
|
+
* берёт клавиши отсюда, где бы она ни жила: в `SHORTCUTS` или в описании
|
|
70
|
+
* Инструмента (`shortcuts`, `keys` кнопки). Поэтому подписи Меню вставки и «¶»,
|
|
71
|
+
* которые читаются при загрузке модуля (`shortcutKeys`), не зависят от того,
|
|
72
|
+
* где лежит строка.
|
|
73
|
+
*/
|
|
74
|
+
export const SHORTCUT_KEYS = {
|
|
75
|
+
selectBlock: ["mod", "A"],
|
|
76
|
+
selectDocument: ["mod", "A"],
|
|
77
|
+
undo: ["mod", "Z"],
|
|
78
|
+
redo: ["mod", "shift", "Z"],
|
|
79
|
+
duplicateBlock: ["mod", "shift", "D"],
|
|
80
|
+
moveBlockUp: ["alt", "shift", "↑"],
|
|
81
|
+
moveBlockDown: ["alt", "shift", "↓"],
|
|
82
|
+
blockActions: ["mod", "/"],
|
|
83
|
+
clearFormatting: ["mod", "\\"],
|
|
84
|
+
bold: ["mod", "B"],
|
|
85
|
+
italic: ["mod", "I"],
|
|
86
|
+
strike: ["mod", "shift", "S"],
|
|
87
|
+
underline: ["mod", "U"],
|
|
88
|
+
code: ["mod", "E"],
|
|
89
|
+
superscript: ["mod", "."],
|
|
90
|
+
subscript: ["mod", ","],
|
|
91
|
+
paragraph: ["mod", "alt", "0"],
|
|
92
|
+
h1: ["mod", "alt", "1"],
|
|
93
|
+
h2: ["mod", "alt", "2"],
|
|
94
|
+
h3: ["mod", "alt", "3"],
|
|
95
|
+
h4: ["mod", "alt", "4"],
|
|
96
|
+
blockquote: ["mod", "shift", "B"],
|
|
97
|
+
codeBlock: ["mod", "alt", "C"],
|
|
98
|
+
bulletList: ["mod", "shift", "8"],
|
|
99
|
+
orderedList: ["mod", "shift", "7"],
|
|
100
|
+
taskList: ["mod", "shift", "9"],
|
|
101
|
+
indentListItem: ["mod", "]"],
|
|
102
|
+
outdentListItem: ["mod", "["],
|
|
103
|
+
alignLeft: ["mod", "shift", "L"],
|
|
104
|
+
alignCenter: ["mod", "shift", "E"],
|
|
105
|
+
alignRight: ["mod", "shift", "R"],
|
|
106
|
+
focusToolbar: ["alt", "F10"],
|
|
107
|
+
} as const satisfies Record<string, readonly string[]>;
|
|
108
|
+
|
|
109
|
+
/** Id строки справочника из таблицы кита. */
|
|
110
|
+
export type ShortcutId = keyof typeof SHORTCUT_KEYS;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Порядок строк справочника внутри групп — привилегия кита, как `INSERT_ORDER`
|
|
114
|
+
* у Меню вставки, и не зависит от того, где живёт строка: в `SHORTCUTS` или в
|
|
115
|
+
* описании Инструмента. Строки вне списка (кнопки описаний, свои Инструменты)
|
|
116
|
+
* встают в конец своей группы в порядке набора.
|
|
117
|
+
*/
|
|
118
|
+
export const SHORTCUT_ORDER: readonly ShortcutId[] = [
|
|
119
|
+
"selectBlock",
|
|
120
|
+
"selectDocument",
|
|
121
|
+
"undo",
|
|
122
|
+
"redo",
|
|
123
|
+
"duplicateBlock",
|
|
124
|
+
"moveBlockUp",
|
|
125
|
+
"moveBlockDown",
|
|
126
|
+
"blockActions",
|
|
127
|
+
"clearFormatting",
|
|
128
|
+
"bold",
|
|
129
|
+
"italic",
|
|
130
|
+
"strike",
|
|
131
|
+
"underline",
|
|
132
|
+
"code",
|
|
133
|
+
"superscript",
|
|
134
|
+
"subscript",
|
|
135
|
+
"paragraph",
|
|
136
|
+
"h1",
|
|
137
|
+
"h2",
|
|
138
|
+
"h3",
|
|
139
|
+
"h4",
|
|
140
|
+
"blockquote",
|
|
141
|
+
"codeBlock",
|
|
142
|
+
"bulletList",
|
|
143
|
+
"orderedList",
|
|
144
|
+
"taskList",
|
|
145
|
+
"indentListItem",
|
|
146
|
+
"outdentListItem",
|
|
147
|
+
"alignLeft",
|
|
148
|
+
"alignCenter",
|
|
149
|
+
"alignRight",
|
|
150
|
+
"focusToolbar",
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Таблица — сочетания кита: команды, которые есть при любом наборе (выделение,
|
|
155
|
+
* Блоки, сдвиг пункта списка, `Alt+F10`). Строки Инструментов — в их описаниях.
|
|
69
156
|
* Сочетание кнопки описания — поле самой кнопки (`toolDescriptor.ts`): её
|
|
70
157
|
* строку справочник берёт оттуда, клавишу вешает `toolButtons.ts`.
|
|
71
158
|
*/
|
|
@@ -75,45 +162,36 @@ export const SHORTCUTS: readonly ShortcutCommand[] = [
|
|
|
75
162
|
id: "selectBlock",
|
|
76
163
|
group: "selection",
|
|
77
164
|
tool: null,
|
|
78
|
-
keys:
|
|
165
|
+
keys: SHORTCUT_KEYS.selectBlock,
|
|
79
166
|
label: (m) => m.selectBlock,
|
|
80
167
|
},
|
|
81
168
|
{
|
|
82
169
|
id: "selectDocument",
|
|
83
170
|
group: "selection",
|
|
84
171
|
tool: null,
|
|
85
|
-
keys:
|
|
172
|
+
keys: SHORTCUT_KEYS.selectDocument,
|
|
86
173
|
label: (m) => m.selectDocument,
|
|
87
174
|
},
|
|
88
175
|
|
|
89
|
-
{ id: "undo", group: "history", tool: "history", keys: ["mod", "Z"], label: (m) => m.undo },
|
|
90
|
-
{
|
|
91
|
-
id: "redo",
|
|
92
|
-
group: "history",
|
|
93
|
-
tool: "history",
|
|
94
|
-
keys: ["mod", "shift", "Z"],
|
|
95
|
-
label: (m) => m.redo,
|
|
96
|
-
},
|
|
97
|
-
|
|
98
176
|
{
|
|
99
177
|
id: "duplicateBlock",
|
|
100
178
|
group: "blocks",
|
|
101
179
|
tool: null,
|
|
102
|
-
keys:
|
|
180
|
+
keys: SHORTCUT_KEYS.duplicateBlock,
|
|
103
181
|
label: (m) => m.duplicateBlock,
|
|
104
182
|
},
|
|
105
183
|
{
|
|
106
184
|
id: "moveBlockUp",
|
|
107
185
|
group: "blocks",
|
|
108
186
|
tool: null,
|
|
109
|
-
keys:
|
|
187
|
+
keys: SHORTCUT_KEYS.moveBlockUp,
|
|
110
188
|
label: (m) => m.moveBlockUp,
|
|
111
189
|
},
|
|
112
190
|
{
|
|
113
191
|
id: "moveBlockDown",
|
|
114
192
|
group: "blocks",
|
|
115
193
|
tool: null,
|
|
116
|
-
keys:
|
|
194
|
+
keys: SHORTCUT_KEYS.moveBlockDown,
|
|
117
195
|
label: (m) => m.moveBlockDown,
|
|
118
196
|
},
|
|
119
197
|
// Меню ручки Блока: ручка есть в любой правке, при любой Панели.
|
|
@@ -121,172 +199,41 @@ export const SHORTCUTS: readonly ShortcutCommand[] = [
|
|
|
121
199
|
id: "blockActions",
|
|
122
200
|
group: "blocks",
|
|
123
201
|
tool: null,
|
|
124
|
-
keys:
|
|
202
|
+
keys: SHORTCUT_KEYS.blockActions,
|
|
125
203
|
label: (m) => m.blockActions,
|
|
126
204
|
},
|
|
127
205
|
|
|
128
|
-
{
|
|
129
|
-
id: "clearFormatting",
|
|
130
|
-
group: "clear",
|
|
131
|
-
tool: "clearFormatting",
|
|
132
|
-
keys: ["mod", "\\"],
|
|
133
|
-
label: (m) => m.clearFormatting,
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
{ id: "bold", group: "marks", tool: "bold", keys: ["mod", "B"], label: (m) => m.bold },
|
|
137
|
-
{ id: "italic", group: "marks", tool: "italic", keys: ["mod", "I"], label: (m) => m.italic },
|
|
138
|
-
{
|
|
139
|
-
id: "strike",
|
|
140
|
-
group: "marks",
|
|
141
|
-
tool: "strike",
|
|
142
|
-
keys: ["mod", "shift", "S"],
|
|
143
|
-
label: (m) => m.strike,
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
id: "underline",
|
|
147
|
-
group: "marks",
|
|
148
|
-
tool: "underline",
|
|
149
|
-
keys: ["mod", "U"],
|
|
150
|
-
label: (m) => m.underline,
|
|
151
|
-
},
|
|
152
|
-
{ id: "code", group: "marks", tool: "code", keys: ["mod", "E"], label: (m) => m.code },
|
|
153
|
-
{
|
|
154
|
-
id: "superscript",
|
|
155
|
-
group: "marks",
|
|
156
|
-
tool: "superscript",
|
|
157
|
-
keys: ["mod", "."],
|
|
158
|
-
label: (m) => m.superscript,
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
id: "subscript",
|
|
162
|
-
group: "marks",
|
|
163
|
-
tool: "subscript",
|
|
164
|
-
keys: ["mod", ","],
|
|
165
|
-
label: (m) => m.subscript,
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
// Подпись — как у пункта «¶» («Текст»), а не «Абзац» Меню вставки: справочник
|
|
169
|
-
// называет команду тем словом, которым её выбирают на Панели.
|
|
170
|
-
{
|
|
171
|
-
id: "paragraph",
|
|
172
|
-
group: "blockType",
|
|
173
|
-
tool: "heading",
|
|
174
|
-
keys: ["mod", "alt", "0"],
|
|
175
|
-
label: (m) => m.textBlock,
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
id: "h1",
|
|
179
|
-
group: "blockType",
|
|
180
|
-
tool: "heading1",
|
|
181
|
-
requires: "heading",
|
|
182
|
-
keys: ["mod", "alt", "1"],
|
|
183
|
-
label: (m) => m.heading({ level: 1 }),
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
id: "h2",
|
|
187
|
-
group: "blockType",
|
|
188
|
-
tool: "heading",
|
|
189
|
-
keys: ["mod", "alt", "2"],
|
|
190
|
-
label: (m) => m.heading({ level: 2 }),
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
id: "h3",
|
|
194
|
-
group: "blockType",
|
|
195
|
-
tool: "heading",
|
|
196
|
-
keys: ["mod", "alt", "3"],
|
|
197
|
-
label: (m) => m.heading({ level: 3 }),
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: "h4",
|
|
201
|
-
group: "blockType",
|
|
202
|
-
tool: "heading",
|
|
203
|
-
keys: ["mod", "alt", "4"],
|
|
204
|
-
label: (m) => m.heading({ level: 4 }),
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
id: "codeBlock",
|
|
208
|
-
group: "blockType",
|
|
209
|
-
tool: "codeBlock",
|
|
210
|
-
keys: ["mod", "alt", "C"],
|
|
211
|
-
label: (m) => m.codeBlock,
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
{
|
|
215
|
-
id: "bulletList",
|
|
216
|
-
group: "lists",
|
|
217
|
-
tool: "bulletList",
|
|
218
|
-
keys: ["mod", "shift", "8"],
|
|
219
|
-
label: (m) => m.bulletList,
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
id: "orderedList",
|
|
223
|
-
group: "lists",
|
|
224
|
-
tool: "orderedList",
|
|
225
|
-
keys: ["mod", "shift", "7"],
|
|
226
|
-
label: (m) => m.orderedList,
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
id: "taskList",
|
|
230
|
-
group: "lists",
|
|
231
|
-
tool: "taskList",
|
|
232
|
-
keys: ["mod", "shift", "9"],
|
|
233
|
-
label: (m) => m.taskList,
|
|
234
|
-
},
|
|
235
206
|
{
|
|
236
207
|
id: "indentListItem",
|
|
237
208
|
group: "lists",
|
|
238
209
|
tool: LIST_TOOLS,
|
|
239
|
-
keys:
|
|
210
|
+
keys: SHORTCUT_KEYS.indentListItem,
|
|
240
211
|
label: (m) => m.indentListItem,
|
|
241
212
|
},
|
|
242
213
|
{
|
|
243
214
|
id: "outdentListItem",
|
|
244
215
|
group: "lists",
|
|
245
216
|
tool: LIST_TOOLS,
|
|
246
|
-
keys:
|
|
217
|
+
keys: SHORTCUT_KEYS.outdentListItem,
|
|
247
218
|
label: (m) => m.outdentListItem,
|
|
248
219
|
},
|
|
249
220
|
|
|
250
|
-
{
|
|
251
|
-
id: "alignLeft",
|
|
252
|
-
group: "align",
|
|
253
|
-
tool: "align",
|
|
254
|
-
keys: ["mod", "shift", "L"],
|
|
255
|
-
label: (m) => m.alignLeft,
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
id: "alignCenter",
|
|
259
|
-
group: "align",
|
|
260
|
-
tool: "align",
|
|
261
|
-
keys: ["mod", "shift", "E"],
|
|
262
|
-
label: (m) => m.alignCenter,
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
id: "alignRight",
|
|
266
|
-
group: "align",
|
|
267
|
-
tool: "align",
|
|
268
|
-
keys: ["mod", "shift", "R"],
|
|
269
|
-
label: (m) => m.alignRight,
|
|
270
|
-
},
|
|
271
|
-
|
|
272
221
|
{
|
|
273
222
|
id: "focusToolbar",
|
|
274
223
|
group: "toolbar",
|
|
275
224
|
tool: null,
|
|
276
|
-
keys:
|
|
225
|
+
keys: SHORTCUT_KEYS.focusToolbar,
|
|
277
226
|
label: (m) => m.focusToolbar,
|
|
278
227
|
},
|
|
279
228
|
];
|
|
280
229
|
|
|
281
|
-
const BY_ID = new Map(SHORTCUTS.map((command) => [command.id, command]));
|
|
282
|
-
|
|
283
230
|
/**
|
|
284
231
|
* Сочетание команды по id или `undefined`, если у команды его нет. Панель
|
|
285
232
|
* спрашивает так про каждую свою кнопку: у горизонтальной линии, картинки и
|
|
286
233
|
* меню цвета сочетаний нет.
|
|
287
234
|
*/
|
|
288
235
|
export function findShortcutKeys(id: string): readonly string[] | undefined {
|
|
289
|
-
return
|
|
236
|
+
return Object.hasOwn(SHORTCUT_KEYS, id) ? SHORTCUT_KEYS[id as ShortcutId] : undefined;
|
|
290
237
|
}
|
|
291
238
|
|
|
292
239
|
/**
|
|
@@ -318,21 +265,13 @@ export interface ShortcutSection {
|
|
|
318
265
|
commands: readonly ShortcutRow[];
|
|
319
266
|
}
|
|
320
267
|
|
|
321
|
-
/**
|
|
322
|
-
* Место строки переехавшего Инструмента в своей группе: id строки → id строки
|
|
323
|
-
* из `SHORTCUTS`, перед которой она встаёт. Остальные переехавшие строки идут
|
|
324
|
-
* в конец группы. Порядок справочника — не поле описания: место на поверхности
|
|
325
|
-
* держит кит, как `INSERT_ORDER` у Меню вставки.
|
|
326
|
-
*/
|
|
327
|
-
const MOVED_BEFORE: Readonly<Record<string, string>> = { blockquote: "codeBlock" };
|
|
328
|
-
|
|
329
268
|
/**
|
|
330
269
|
* Команды включённых инструментов по группам; пустые группы не возвращаются.
|
|
331
270
|
*
|
|
332
|
-
* Проекция списка описаний: строки
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
271
|
+
* Проекция списка описаний: строки кита — из `SHORTCUTS`, строки Инструментов —
|
|
272
|
+
* из их описаний (`toolDescriptor.ts`): клавиши
|
|
273
|
+
* Extensions — поле `shortcuts`, сочетания кнопок — поле кнопки. Место
|
|
274
|
+
* строки в группе — `SHORTCUT_ORDER`, а строки вне него — следом в порядке набора;
|
|
336
275
|
* кнопка без группы кита — в группу своего Инструмента после групп кита, в
|
|
337
276
|
* порядке списка (ADR-0058, п. 6.8).
|
|
338
277
|
*/
|
|
@@ -341,25 +280,18 @@ export function shortcutGroupsFor(
|
|
|
341
280
|
/** Локаль `UiProvider` — для подписи кнопки, заданной функцией от контекста. */
|
|
342
281
|
locale = "en-US"
|
|
343
282
|
): readonly ShortcutSection[] {
|
|
344
|
-
const described =
|
|
283
|
+
const described = tools.descriptors;
|
|
345
284
|
const moved = described.flatMap((descriptor) => descriptor.shortcuts ?? []);
|
|
346
|
-
const
|
|
347
|
-
SHORTCUTS.some((command) => command.id === MOVED_BEFORE[row.id]);
|
|
348
|
-
const commands = [
|
|
349
|
-
...SHORTCUTS.flatMap((command) => [
|
|
350
|
-
...moved.filter((row) => MOVED_BEFORE[row.id] === command.id),
|
|
351
|
-
command,
|
|
352
|
-
]),
|
|
353
|
-
...moved.filter((row) => !anchored(row)),
|
|
354
|
-
].filter((command) => enabled(command, tools));
|
|
285
|
+
const commands = [...SHORTCUTS, ...moved].filter((command) => enabled(command, tools));
|
|
355
286
|
const ownGroups: ShortcutSection[] = [];
|
|
356
287
|
for (const descriptor of described) {
|
|
357
288
|
const own: ShortcutRow[] = [];
|
|
358
289
|
for (const button of descriptor.buttons ?? []) {
|
|
359
|
-
|
|
290
|
+
const keys = button.shortcut ?? button.keys;
|
|
291
|
+
if (!keys) continue;
|
|
360
292
|
const row: ShortcutRow = {
|
|
361
293
|
id: button.id,
|
|
362
|
-
keys
|
|
294
|
+
keys,
|
|
363
295
|
label: (messages) => toolLabel(button.label, { locale, messages }),
|
|
364
296
|
};
|
|
365
297
|
// Набор уже отфильтрован: кнопка есть только у включённого Инструмента.
|
|
@@ -380,10 +312,17 @@ export function shortcutGroupsFor(
|
|
|
380
312
|
}
|
|
381
313
|
if (own.length > 0) ownGroups.push({ group: descriptor.name, commands: own });
|
|
382
314
|
}
|
|
315
|
+
// Сортировка устойчивая: строки вне `SHORTCUT_ORDER` сохраняют порядок набора.
|
|
316
|
+
const rank = (id: string) => {
|
|
317
|
+
const index = (SHORTCUT_ORDER as readonly string[]).indexOf(id);
|
|
318
|
+
return index === -1 ? SHORTCUT_ORDER.length : index;
|
|
319
|
+
};
|
|
383
320
|
return [
|
|
384
321
|
...SHORTCUT_GROUPS.map((group) => ({
|
|
385
322
|
group,
|
|
386
|
-
commands: commands
|
|
323
|
+
commands: commands
|
|
324
|
+
.filter((command) => command.group === group)
|
|
325
|
+
.sort((left, right) => rank(left.id) - rank(right.id)),
|
|
387
326
|
})),
|
|
388
327
|
...ownGroups,
|
|
389
328
|
].filter((entry) => entry.commands.length > 0);
|
|
@@ -19,9 +19,9 @@ import type { ToolLoader } from "./toolLoader.js";
|
|
|
19
19
|
набора и выгружается обратно в HTML — в документ, тоже инертный. Что Схема не
|
|
20
20
|
знает (теги, атрибуты, обработчики, `javascript:`), не доходит до страницы.
|
|
21
21
|
|
|
22
|
-
Схема — та, что получит движок, но без
|
|
22
|
+
Схема — та, что получит движок, но без Extensions, чьи загрузчики ещё
|
|
23
23
|
не отработали: их Блоки видны текстом, обёртку Схема снимает. Ленивый
|
|
24
|
-
встроенный (`codeBlock`, `table`) вместо
|
|
24
|
+
встроенный (`codeBlock`, `table`) вместо Extensions даёт
|
|
25
25
|
форму Блоков (`skeleton` описания) — его Блоки видны своей разметкой, и поле
|
|
26
26
|
не прыгает с приходом чанка. Шапка блока кода — та же, что у `RichTextView`
|
|
27
27
|
(`withCodeHeaders`): в Редакторе она есть, и без неё Скелет был бы ниже. Набор
|
|
@@ -37,6 +37,7 @@ const SCHEMA_ONLY: Omit<EditorExtensionOptions, "tools"> = {
|
|
|
37
37
|
placeholder: () => "",
|
|
38
38
|
limit: () => undefined,
|
|
39
39
|
taskCheckbox: (text) => text,
|
|
40
|
+
detailsToggle: (text) => text,
|
|
40
41
|
openPopover: quiet,
|
|
41
42
|
focusToolbar: no,
|
|
42
43
|
openBlockMenu: no,
|
|
@@ -83,7 +84,7 @@ function cleaned(html: string, tools: ToolList): string | null {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
/**
|
|
86
|
-
* Документ Скелета: `html`, очищенный Схемой набора без
|
|
87
|
+
* Документ Скелета: `html`, очищенный Схемой набора без Extensions ещё не
|
|
87
88
|
* отработавших загрузчиков (`pending`). Только в браузере.
|
|
88
89
|
*
|
|
89
90
|
* Набор с ошибкой своих Инструментов Схемы не даёт (ошибка поля, ADR-0058,
|
|
@@ -70,9 +70,9 @@ export function keymapName(keys: readonly string[]): string {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
74
|
-
* записи и единственный его источник, поэтому клавишу вешает кит, а не
|
|
75
|
-
*
|
|
73
|
+
* Сочетания у кнопок описаний (ADR-0058, п. 5.7): сочетание — поле
|
|
74
|
+
* записи и единственный его источник, поэтому клавишу вешает кит, а не
|
|
75
|
+
* Extensions Инструмента. `run` выполняется, поповер открывается у выделения.
|
|
76
76
|
* Недоступная кнопка клавишу не берёт: она идёт дальше, в Документ.
|
|
77
77
|
*/
|
|
78
78
|
export const ToolButtons = Extension.create<ToolButtonsOptions>({
|
|
@@ -10,7 +10,7 @@ import { isConfiguredTool, mergeOptions } from "./toolOptions.js";
|
|
|
10
10
|
же формой `CustomTool` (ADR-0058): отдельного пути сборки у определения нет,
|
|
11
11
|
кит собирает из него обычное описание Инструмента (`describeDefinition`), и
|
|
12
12
|
все правила своего Инструмента — Префикс потребителя, уникальность имени,
|
|
13
|
-
проверки набора, загрузчик
|
|
13
|
+
проверки набора, загрузчик Extensions — действуют как были.
|
|
14
14
|
|
|
15
15
|
ИМЯ ФАЙЛА — НЕ `defineTool.ts`. Под этим именем сборка потребителя на Next.js
|
|
16
16
|
15.5 (`check:consumer`) теряла `RichTextEditor` из клиентского манифеста RSC:
|
|
@@ -107,14 +107,14 @@ export interface ToolDefinition<
|
|
|
107
107
|
* - `defaults` — полный набор Опций; тип Опций выводится из него, второй раз его
|
|
108
108
|
* объявлять не нужно.
|
|
109
109
|
* - `create(options)` отдаёт тело описания — ту же форму, что `CustomTool`, без
|
|
110
|
-
* имени:
|
|
110
|
+
* имени: Extensions, правила Схемы, UI-записи. Зовётся один раз на запись
|
|
111
111
|
* `tools` (определение само или объект, что вернул `tool(definition, options)`):
|
|
112
112
|
* кит запоминает результат, и Остров, замкнутый над Опциями, один и тот же на
|
|
113
113
|
* каждом рендере. Запись, созданная заново на каждом рендере экрана
|
|
114
114
|
* (`tools={[tool(mention, …)]}` в теле компонента), для кита новая — выносите
|
|
115
|
-
* `tools` из рендера. Загрузчик
|
|
115
|
+
* `tools` из рендера. Загрузчик Extensions стоит держать константой
|
|
116
116
|
* модуля: кэш загрузчика живёт на самой функции, а новая функция на каждый
|
|
117
|
-
* вызов `create` загружала бы
|
|
117
|
+
* вызов `create` загружала бы Extensions заново для каждой записи.
|
|
118
118
|
* - `state({ editor, options })` — Состояние для `useRichTextToolState(ref, name)`.
|
|
119
119
|
* Зовётся при каждой транзакции и при каждом чтении хука, поэтому должна быть
|
|
120
120
|
* быстрой и без побочных эффектов; вернуть — данные без DOM-узлов. Простые данные
|