@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
|
@@ -7,35 +7,25 @@
|
|
|
7
7
|
|
|
8
8
|
import { Extension, type Extensions } from "@tiptap/core";
|
|
9
9
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
10
|
-
import { TaskItem, TaskList } from "@tiptap/extension-list";
|
|
11
|
-
import { Subscript } from "@tiptap/extension-subscript";
|
|
12
|
-
import { Superscript } from "@tiptap/extension-superscript";
|
|
13
10
|
import { Placeholder } from "@tiptap/extensions";
|
|
14
|
-
import { StarterKit } from "@tiptap/starter-kit";
|
|
11
|
+
import { StarterKit, type StarterKitOptions } from "@tiptap/starter-kit";
|
|
15
12
|
import { AtomText } from "./atomText.js";
|
|
16
13
|
import { BlockCommands, BlockMenuKey } from "./blockCommands.js";
|
|
17
14
|
import { FieldNoticeExtension, type FieldNoticeOptions } from "./fieldNotice.js";
|
|
18
|
-
import {
|
|
15
|
+
import type { RichImageOptions } from "../features/image/image.js";
|
|
19
16
|
import { createDocumentNode } from "./document.js";
|
|
20
|
-
import { LAYOUT_NODE, layoutExtensions } from "../features/layout/layout.js";
|
|
21
17
|
import { createMarkdown, MarkdownPaste } from "./markdown.js";
|
|
22
18
|
import { MaxLength } from "./maxLength.js";
|
|
19
|
+
import { TriggerCommand, type SlashBridge, type TriggerBinding } from "./slashCommand.js";
|
|
23
20
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
SlashCommand,
|
|
30
|
-
TriggerCommand,
|
|
31
|
-
type SlashBridge,
|
|
32
|
-
type TriggerBinding,
|
|
33
|
-
} from "./slashCommand.js";
|
|
34
|
-
import { describedTools, type ToolList } from "./toolDescriptor.js";
|
|
21
|
+
type BlockTraits,
|
|
22
|
+
type ToolDescriptor,
|
|
23
|
+
type ToolEngineContext,
|
|
24
|
+
type ToolList,
|
|
25
|
+
} from "./toolDescriptor.js";
|
|
35
26
|
import { ToolButtons, type ToolPopoverEntry, type ToolPopoverRequest } from "./toolButtons.js";
|
|
36
27
|
import { ToolEntryShortcuts } from "./toolEntryShortcuts.js";
|
|
37
28
|
import type { Tool } from "./tools.js";
|
|
38
|
-
import { WRAPPER_EXTENSIONS, WRAPPER_NODE } from "../features/wrapper/wrapper.js";
|
|
39
29
|
|
|
40
30
|
export interface EditorExtensionOptions {
|
|
41
31
|
/** Включённые инструменты: выключенный не попадает в Схему вовсе. */
|
|
@@ -46,6 +36,11 @@ export interface EditorExtensionOptions {
|
|
|
46
36
|
limit: () => number | undefined;
|
|
47
37
|
/** Имя флажка пункта чек-листа — из каталога, читается при каждой перерисовке пункта. */
|
|
48
38
|
taskCheckbox: (text: string) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Имя кнопки раскрытия сворачиваемого Блока по тексту его заголовка — из
|
|
41
|
+
* каталога, читается при каждой перерисовке кнопки.
|
|
42
|
+
*/
|
|
43
|
+
detailsToggle: (text: string) => string;
|
|
49
44
|
/**
|
|
50
45
|
* Мост Меню вставки по `/`. Нет — меню не открывается: инструмент
|
|
51
46
|
* `insertMenu` выключен или пунктов для него нет.
|
|
@@ -123,6 +118,115 @@ const HandledTabStaysInside = Extension.create({
|
|
|
123
118
|
],
|
|
124
119
|
});
|
|
125
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Признаки Блоков описаний (`blocks`) — в spec узлов Схемы: `blockContainer`,
|
|
123
|
+
* `turnInto` и `plainAtom`, которые читают ручка и Меню выделения. Расширение
|
|
124
|
+
* без разметки: `extendNodeSchema` у Tiptap зовётся для каждого узла набора, и
|
|
125
|
+
* признак находит свой узел по имени. Нет признаков — расширения нет.
|
|
126
|
+
*/
|
|
127
|
+
function blockTraitsExtension(traits: Readonly<Record<string, BlockTraits>>) {
|
|
128
|
+
return Extension.create({
|
|
129
|
+
name: "blockTraits",
|
|
130
|
+
extendNodeSchema: (extension) => {
|
|
131
|
+
const own = Object.hasOwn(traits, extension.name) ? traits[extension.name] : undefined;
|
|
132
|
+
return own
|
|
133
|
+
? {
|
|
134
|
+
blockContainer: own.container === true,
|
|
135
|
+
turnInto: own.turnInto === true,
|
|
136
|
+
plainAtom: own.plainAtom === true,
|
|
137
|
+
}
|
|
138
|
+
: {};
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Ключи StarterKit, которые принадлежат Инструментам. Все выключены, пока их не
|
|
145
|
+
* включит доля описания (`starterKit`): Метка, которой нет в наборе, не
|
|
146
|
+
* попадает в Схему. Цитата, разделитель, блок кода и
|
|
147
|
+
* ссылка — свои расширения кита, а `trailingNode` выключен всегда (см. выше).
|
|
148
|
+
* Документ, абзац, текст, перенос, пункт списка, его клавиши и курсоры
|
|
149
|
+
* StarterKit остаются его умолчаниями: они есть в любом наборе.
|
|
150
|
+
*/
|
|
151
|
+
const STARTER_KIT_OFF: Partial<StarterKitOptions> = {
|
|
152
|
+
heading: false,
|
|
153
|
+
bold: false,
|
|
154
|
+
italic: false,
|
|
155
|
+
underline: false,
|
|
156
|
+
strike: false,
|
|
157
|
+
code: false,
|
|
158
|
+
bulletList: false,
|
|
159
|
+
orderedList: false,
|
|
160
|
+
undoRedo: false,
|
|
161
|
+
blockquote: false,
|
|
162
|
+
codeBlock: false,
|
|
163
|
+
horizontalRule: false,
|
|
164
|
+
link: false,
|
|
165
|
+
trailingNode: false,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** Место символов-триггеров в `ENGINE_ORDER`: они не Инструмент, но порядок держат. */
|
|
169
|
+
const TRIGGERS = "triggers";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* ПОРЯДОК РЕГИСТРАЦИИ Extensions встроенных Инструментов — привилегия кита, как
|
|
173
|
+
* порядок поверхностей, и описание его не задаёт. От него зависят:
|
|
174
|
+
*
|
|
175
|
+
* - **ранг Меток** — а с ним вложенность Меток в сохранённом HTML;
|
|
176
|
+
* - **вставка и drop** (ADR-0058, п. 9.5): плагины ProseMirror у Tiptap идут в
|
|
177
|
+
* обратном порядке регистрации при равном `priority`, и зарегистрированное
|
|
178
|
+
* последним опрашивается первым. Свои Инструменты стоят после встроенных
|
|
179
|
+
* (`expandTools`), поэтому `transformPastedHTML`, `transformPasted`,
|
|
180
|
+
* `handlePaste` и `handleDrop` своего Инструмента видят событие раньше и могут
|
|
181
|
+
* забрать его. Давать встроенному плагину `priority` выше 100 нельзя без правки
|
|
182
|
+
* этого правила;
|
|
183
|
+
* - **клавиши**: `Enter`, `Backspace` и стрелки сворачиваемого Блока (`details`)
|
|
184
|
+
* срабатывают только в его заголовке и в пустом конце содержимого, поэтому
|
|
185
|
+
* с клавишами других Блоков не спорят. Он стоит сразу за контейнерами
|
|
186
|
+
* (Раскладка, Обёртка): позже ядра и StarterKit, значит, опрашивается раньше
|
|
187
|
+
* них — `Enter` в заголовке уходит в содержимое, а не режет заголовок надвое;
|
|
188
|
+
* - **клавиши**: `Alt+F10` блока кода (`codeBlockTool.ts`) зарегистрирован после
|
|
189
|
+
* `ToolbarFocus` и опрашивается раньше; Tab в таблице — раньше Раскладки.
|
|
190
|
+
*
|
|
191
|
+
* Список сохраняет порядок, который был до переезда встроенных на описания, —
|
|
192
|
+
* эталонные тесты (`rich-text-editor-golden.test.tsx`) держат сохранённый HTML и клавиши. Метки и
|
|
193
|
+
* Блоки из StarterKit регистрирует он сам, в своём порядке.
|
|
194
|
+
*/
|
|
195
|
+
const ENGINE_ORDER: readonly (Tool | typeof TRIGGERS)[] = [
|
|
196
|
+
"superscript",
|
|
197
|
+
"subscript",
|
|
198
|
+
"color",
|
|
199
|
+
"highlight",
|
|
200
|
+
"align",
|
|
201
|
+
"taskList",
|
|
202
|
+
"image",
|
|
203
|
+
"layout",
|
|
204
|
+
"wrapper",
|
|
205
|
+
"details",
|
|
206
|
+
"insertMenu",
|
|
207
|
+
TRIGGERS,
|
|
208
|
+
"history",
|
|
209
|
+
"heading",
|
|
210
|
+
"heading1",
|
|
211
|
+
"bold",
|
|
212
|
+
"italic",
|
|
213
|
+
"underline",
|
|
214
|
+
"strike",
|
|
215
|
+
"code",
|
|
216
|
+
"clearFormatting",
|
|
217
|
+
"link",
|
|
218
|
+
"bulletList",
|
|
219
|
+
"orderedList",
|
|
220
|
+
"blockquote",
|
|
221
|
+
"codeBlock",
|
|
222
|
+
"horizontalRule",
|
|
223
|
+
"table",
|
|
224
|
+
"emoji",
|
|
225
|
+
"source",
|
|
226
|
+
"search",
|
|
227
|
+
"toc",
|
|
228
|
+
];
|
|
229
|
+
|
|
126
230
|
/**
|
|
127
231
|
* Набор расширений — это и есть Схема в коде: что не зарегистрировано здесь,
|
|
128
232
|
* ProseMirror не разберёт ни из `value`, ни из буфера. Белый список ДАННЫМИ,
|
|
@@ -148,56 +252,29 @@ const HandledTabStaysInside = Extension.create({
|
|
|
148
252
|
* `codeBlock` из StarterKit: язык из `codeLanguages.ts` и подсветка
|
|
149
253
|
* декорациями lowlight (ADR-0051). В Документ по-прежнему уходит только
|
|
150
254
|
* `class="language-*"` на `<code>`. Оно, как и таблица, приходит
|
|
151
|
-
*
|
|
255
|
+
* из описания ленивыми Extensions (`codeBlockTool.ts`, `tableTool.ts`).
|
|
152
256
|
*/
|
|
153
257
|
export function createEditorExtensions(options: EditorExtensionOptions): Extensions {
|
|
154
258
|
/*
|
|
155
|
-
Движок — проекция списка описаний (`toolDescriptor.ts`):
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
между Инструментами (уровни заголовка, Обёртка в колонке) спрашивают сам
|
|
159
|
-
набор.
|
|
259
|
+
Движок — проекция списка описаний (`toolDescriptor.ts`): Extensions
|
|
260
|
+
Инструмента берутся из его описания. Связи между Инструментами (уровни
|
|
261
|
+
заголовка, Обёртка в колонке) спрашивают `has` контекста.
|
|
160
262
|
*/
|
|
161
|
-
const described =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// настройки по умолчанию.
|
|
166
|
-
const toggle = (tool: Tool) => (on(tool) ? undefined : false);
|
|
263
|
+
const described = options.tools.descriptors;
|
|
264
|
+
// Без самого набора: связи между Инструментами — только через `has`.
|
|
265
|
+
const { tools, ...bridges } = options;
|
|
266
|
+
const context: ToolEngineContext = { ...bridges, has: (tool) => tools.has(tool) };
|
|
167
267
|
// Контейнеры Блоков верхнего уровня: их принимает только свой Документ.
|
|
168
|
-
const containers = [
|
|
169
|
-
...(on("layout") ? [LAYOUT_NODE] : []),
|
|
170
|
-
...(on("wrapper") ? [WRAPPER_NODE] : []),
|
|
171
|
-
...described.flatMap((descriptor) => descriptor.documentContent ?? []),
|
|
172
|
-
];
|
|
268
|
+
const containers = described.flatMap((descriptor) => descriptor.documentContent ?? []);
|
|
173
269
|
|
|
174
270
|
const extensions: Extensions = [
|
|
175
271
|
StarterKit.configure({
|
|
272
|
+
...STARTER_KIT_OFF,
|
|
273
|
+
// Доли описаний — одним StarterKit: порядок его Меток не меняется.
|
|
274
|
+
...Object.assign({}, ...described.map((descriptor) => descriptor.starterKit?.(context))),
|
|
176
275
|
// С Раскладкой или Обёрткой Документ — свой (`document.ts`): они не в
|
|
177
276
|
// группе `block`, и принять их может только тот, кто назвал их поимённо.
|
|
178
277
|
document: containers.length > 0 ? false : undefined,
|
|
179
|
-
// H1 — только с `heading1`; без него `#` и `<h1>` остаются
|
|
180
|
-
// прежними: правило ввода и разбор HTML строятся по этим уровням.
|
|
181
|
-
heading: on("heading")
|
|
182
|
-
? { levels: options.tools.has("heading1") ? [1, 2, 3, 4] : [2, 3, 4] }
|
|
183
|
-
: false,
|
|
184
|
-
bold: toggle("bold"),
|
|
185
|
-
italic: toggle("italic"),
|
|
186
|
-
underline: toggle("underline"),
|
|
187
|
-
strike: toggle("strike"),
|
|
188
|
-
code: toggle("code"),
|
|
189
|
-
bulletList: toggle("bulletList"),
|
|
190
|
-
orderedList: toggle("orderedList"),
|
|
191
|
-
// Цитата и разделитель переехали на описание (`features/builtins.ts`): их Часть для движка — оттуда.
|
|
192
|
-
blockquote: false,
|
|
193
|
-
// Блок кода — `RichCodeBlock` из описания (`codeBlockTool.ts`): язык и подсветка (ADR-0051).
|
|
194
|
-
codeBlock: false,
|
|
195
|
-
horizontalRule: false,
|
|
196
|
-
undoRedo: toggle("history"),
|
|
197
|
-
// Ссылка — `RichLink` из описания Инструмента (`linkTool.tsx`): StarterKit
|
|
198
|
-
// отдаёт только опции, а разбор `<a>` нужно сузить до Схемы.
|
|
199
|
-
link: false,
|
|
200
|
-
trailingNode: false,
|
|
201
278
|
}),
|
|
202
279
|
...(containers.length > 0 ? [createDocumentNode(containers)] : []),
|
|
203
280
|
Placeholder.configure({ placeholder: () => options.placeholder() }),
|
|
@@ -212,64 +289,36 @@ export function createEditorExtensions(options: EditorExtensionOptions): Extensi
|
|
|
212
289
|
HandledTabStaysInside,
|
|
213
290
|
// Команды над Блоками — своей разметки нет, включены всегда (`blockCommands.ts`).
|
|
214
291
|
// После ядра Tiptap: его `Mod+A` (весь Документ) опрашивается позже нашего.
|
|
215
|
-
BlockCommands.configure({ clearFormatting:
|
|
292
|
+
BlockCommands.configure({ clearFormatting: options.tools.has("clearFormatting") }),
|
|
216
293
|
// `⌘/` — меню ручки Блока по месту клавиши, раньше карт Меток (`blockCommands.ts`).
|
|
217
294
|
BlockMenuKey.configure({ openBlockMenu: options.openBlockMenu }),
|
|
218
295
|
// `showNotice` — в каждом Редакторе: сообщить может любой Инструмент (`fieldNotice.ts`).
|
|
219
296
|
FieldNoticeExtension.configure({ onNotice: options.onNotice }),
|
|
220
297
|
];
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
// Картинка — `figure` с подписью (`image.ts`). Без инструмента `<img>` из
|
|
241
|
-
// вставки и `value` отбрасывается, а файлы из буфера не перехватываются.
|
|
242
|
-
if (on("image") && options.image) extensions.push(RichImage.configure(options.image));
|
|
243
|
-
// Раскладка — узлы колонок и их клавиатура (`layout.ts`). Без инструмента
|
|
244
|
-
// `div[data-block]` из вставки и `value` не разбирается, и Блоки колонок
|
|
245
|
-
// встают в Документ подряд.
|
|
246
|
-
if (on("layout")) extensions.push(...layoutExtensions({ wrapper: options.tools.has("wrapper") }));
|
|
247
|
-
// Обёртка — узел и выход из неё по Enter (`wrapper.ts`). Без инструмента
|
|
248
|
-
// `div[data-block="wrapper"]` из вставки и `value` не разбирается, и её
|
|
249
|
-
// Блоки встают на её место подряд.
|
|
250
|
-
if (on("wrapper")) extensions.push(...WRAPPER_EXTENSIONS);
|
|
251
|
-
if (on("insertMenu") && options.slash) {
|
|
252
|
-
extensions.push(SlashCommand.configure({ bridge: options.slash }));
|
|
298
|
+
// Признаки Блоков описаний (`blocks`) — одним расширением на весь набор.
|
|
299
|
+
const traits: Record<string, BlockTraits> = Object.assign(
|
|
300
|
+
{},
|
|
301
|
+
...described.map((descriptor) => descriptor.blocks)
|
|
302
|
+
);
|
|
303
|
+
if (Object.keys(traits).length > 0) extensions.push(blockTraitsExtension(traits));
|
|
304
|
+
// Extensions встроенных Инструментов — в порядке кита, свои — следом в порядке списка.
|
|
305
|
+
const engineOf = (descriptor: ToolDescriptor) =>
|
|
306
|
+
descriptor.engine?.(context, descriptor.options) ?? [];
|
|
307
|
+
for (const slot of ENGINE_ORDER) {
|
|
308
|
+
if (slot === TRIGGERS) {
|
|
309
|
+
if (options.triggers?.length) {
|
|
310
|
+
extensions.push(TriggerCommand.configure({ triggers: options.triggers }));
|
|
311
|
+
}
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const descriptor = described.find((candidate) => candidate.name === slot);
|
|
315
|
+
if (descriptor) extensions.push(...engineOf(descriptor));
|
|
253
316
|
}
|
|
254
|
-
|
|
255
|
-
|
|
317
|
+
for (const descriptor of described) {
|
|
318
|
+
if (!(ENGINE_ORDER as readonly string[]).includes(descriptor.name)) {
|
|
319
|
+
extensions.push(...engineOf(descriptor));
|
|
320
|
+
}
|
|
256
321
|
}
|
|
257
|
-
/*
|
|
258
|
-
Части для движка переехавших Инструментов — в порядке списка. ЗДЕСЬ ЖЕ
|
|
259
|
-
ЗАДАЁТСЯ ПОРЯДОК ВСТАВКИ И DROP (ADR-0058, п. 9.5): плагины ProseMirror у
|
|
260
|
-
Tiptap идут в обратном порядке регистрации при равном `priority`, и
|
|
261
|
-
зарегистрированное последним опрашивается первым. Свои Инструменты стоят в
|
|
262
|
-
конце списка (`expandTools`: за встроенными), а встроенные вставка Markdown,
|
|
263
|
-
загрузка картинок и Раскладка зарегистрированы выше — поэтому
|
|
264
|
-
`transformPastedHTML`, `transformPasted`, `handlePaste` и `handleDrop` своего
|
|
265
|
-
Инструмента видят событие раньше и могут забрать его. Менять порядок
|
|
266
|
-
регистрации или давать встроенному плагину `priority` выше 100 нельзя без
|
|
267
|
-
правки этого правила. На том же порядке держится `Alt+F10` блока кода
|
|
268
|
-
(`codeBlockTool.ts`): его расширение здесь, после `ToolbarFocus`, и
|
|
269
|
-
опрашивается раньше.
|
|
270
|
-
*/
|
|
271
|
-
for (const descriptor of described)
|
|
272
|
-
extensions.push(...(descriptor.engine?.(options, descriptor.options) ?? []));
|
|
273
322
|
// Сочетания кнопок описаний вешает кит: поле записи — их единственный источник.
|
|
274
323
|
const buttons = described.flatMap((descriptor) => descriptor.buttons ?? []);
|
|
275
324
|
// Поповер пункта вставки и Действия открывает та же команда, что у кнопки.
|
|
@@ -281,8 +330,14 @@ export function createEditorExtensions(options: EditorExtensionOptions): Extensi
|
|
|
281
330
|
.flatMap(({ id, label, popover }): ToolPopoverEntry[] =>
|
|
282
331
|
popover ? [{ id, label: ({ messages, locale }) => label(messages, locale), popover }] : []
|
|
283
332
|
);
|
|
284
|
-
|
|
285
|
-
|
|
333
|
+
// Кнопке без сочетания и поповера (отмена, картинка) расширение не нужно: её
|
|
334
|
+
// `run` зовёт Панель. Так набор расширений не меняется от того, что кнопка
|
|
335
|
+
// переехала в описание.
|
|
336
|
+
const keyed = buttons.filter((button) => button.shortcut || button.popover);
|
|
337
|
+
if (keyed.length + entries.length > 0) {
|
|
338
|
+
extensions.push(
|
|
339
|
+
ToolButtons.configure({ buttons: keyed, entries, openPopover: options.openPopover })
|
|
340
|
+
);
|
|
286
341
|
}
|
|
287
342
|
// То же — у пунктов вставки и Действий над Блоком.
|
|
288
343
|
const keyedItems = described.flatMap((d) => d.insertItems ?? []).filter((i) => i.shortcut);
|
|
@@ -38,7 +38,7 @@ declare module "@tiptap/core" {
|
|
|
38
38
|
* история и `onChange` не затрагиваются. `error` уходит сама со следующей
|
|
39
39
|
* Правкой автора, `status` остаётся, пока его не заменят или не снимут.
|
|
40
40
|
*
|
|
41
|
-
* Свой Инструмент зовёт её из
|
|
41
|
+
* Свой Инструмент зовёт её из Extensions (`this.editor.commands`),
|
|
42
42
|
* экран — через `handle.editor`. Сообщение без текста — то же, что `null`.
|
|
43
43
|
*/
|
|
44
44
|
showNotice: (notice: FieldNotice | null) => ReturnType;
|
|
@@ -138,8 +138,8 @@ const TAG = /<[^>]*>/g;
|
|
|
138
138
|
const UNSEEN = /<!--[\s\S]*?-->|<(script|style|template)\b[\s\S]*?<\/\1\s*>/gi;
|
|
139
139
|
/**
|
|
140
140
|
* Листья Документа — содержимое и без текста: мягкий перенос, линия, картинка,
|
|
141
|
-
* свой Блок. Свой Блок до движка — всегда содержимое: атом ли он,
|
|
142
|
-
* его
|
|
141
|
+
* свой Блок. Свой Блок до движка — всегда содержимое: атом ли он, знают только
|
|
142
|
+
* его Extensions, а атом без текста — тоже содержимое.
|
|
143
143
|
*/
|
|
144
144
|
const LEAF = /<(?:br|hr|img)\b|\sdata-block\s*=\s*["']?[A-Za-z][A-Za-z0-9]*_/i;
|
|
145
145
|
/** Таблица — содержимое и с пустыми ячейками (`emptiness.ts`). */
|
|
@@ -4,22 +4,9 @@ import type { ChainedCommands, Editor, Range } from "@tiptap/core";
|
|
|
4
4
|
import { TextSelection, type Transaction } from "@tiptap/pm/state";
|
|
5
5
|
import type { ComponentType } from "react";
|
|
6
6
|
import type { UiMessages } from "../../i18n/messages.js";
|
|
7
|
-
import {
|
|
8
|
-
ColumnLayoutIcon,
|
|
9
|
-
HeadingIcon,
|
|
10
|
-
ImageIcon,
|
|
11
|
-
ListIcon,
|
|
12
|
-
OrderedListIcon,
|
|
13
|
-
ParagraphIcon,
|
|
14
|
-
TableIcon,
|
|
15
|
-
TaskListIcon,
|
|
16
|
-
WrapperIcon,
|
|
17
|
-
type IconProps,
|
|
18
|
-
} from "../../icons.js";
|
|
7
|
+
import { TableIcon, type IconProps } from "../../icons.js";
|
|
19
8
|
import { DEFAULT_LOCALE } from "../../i18n/defaults.js";
|
|
20
9
|
import { handleBlock } from "./blockCommands.js";
|
|
21
|
-
import { DEFAULT_LAYOUT } from "../features/layout/columnLayouts.js";
|
|
22
|
-
import { shortcutKeys } from "./shortcuts.js";
|
|
23
10
|
import { projectRecords, type ToolList, type ToolPopover } from "./toolDescriptor.js";
|
|
24
11
|
import type { Tool } from "./tools.js";
|
|
25
12
|
|
|
@@ -63,12 +50,12 @@ export interface InsertItem {
|
|
|
63
50
|
keys?: readonly string[];
|
|
64
51
|
/**
|
|
65
52
|
* Сочетание, которое вешает кит (ADR-0058, п. 5.7): единственный источник
|
|
66
|
-
* клавиши, подсказки и строки справочника. У встроенных нет — клавишу
|
|
67
|
-
* их
|
|
53
|
+
* клавиши, подсказки и строки справочника. У встроенных нет — клавишу вешают
|
|
54
|
+
* их Extensions, а подпись — `keys`.
|
|
68
55
|
*/
|
|
69
56
|
shortcut?: readonly string[];
|
|
70
57
|
/**
|
|
71
|
-
* Второй нужный инструмент сверх
|
|
58
|
+
* Второй нужный инструмент сверх владельца пункта: Заголовок 1 стоит под
|
|
72
59
|
* `heading`, но есть только вместе с `heading1`, как в меню «¶».
|
|
73
60
|
*/
|
|
74
61
|
requires?: Tool;
|
|
@@ -80,136 +67,6 @@ export interface InsertItem {
|
|
|
80
67
|
popover?: ToolPopover;
|
|
81
68
|
}
|
|
82
69
|
|
|
83
|
-
/**
|
|
84
|
-
* Старый реестр пунктов Меню вставки по инструментам.
|
|
85
|
-
*
|
|
86
|
-
* Новый встроенный Инструмент сюда уже не пишется: его пункты лежат в его
|
|
87
|
-
* описании (`insertItems`, `features/builtins.ts`), и меню получает их проекцией
|
|
88
|
-
* списка описаний (`insertItemsFor`). Здесь — пункты Инструментов, ещё не
|
|
89
|
-
* переехавших на описание (ADR-0058, п. 10), одной записью без правки меню:
|
|
90
|
-
*
|
|
91
|
-
* horizontalRule: [{ id: "horizontalRule", section: "insert",
|
|
92
|
-
* label: (m) => m.horizontalRule, icon: DividerIcon,
|
|
93
|
-
* run: (chain) => chain.setHorizontalRule() }],
|
|
94
|
-
*
|
|
95
|
-
* Ключ — `Tool`: выключенный инструмент убирает свои пункты и из `/`, и из «+»
|
|
96
|
-
* сам, без проверок в меню. Порядок пунктов в разделе — `INSERT_ORDER`, а не
|
|
97
|
-
* порядок записей. Подпись — из каталога (`editor.*`), иконка —
|
|
98
|
-
* из `@olddevs/ui/icons`. Пункт, которому нужен свой интерфейс (поповер адреса
|
|
99
|
-
* картинки), открывает его из `run` — `editor` для этого и передан.
|
|
100
|
-
*
|
|
101
|
-
* Абзац записан под самим `insertMenu`: это не инструмент, он есть в Схеме
|
|
102
|
-
* всегда, и «Превратить в абзац» нужен, пока есть само меню.
|
|
103
|
-
*/
|
|
104
|
-
export const INSERT_ITEMS: Partial<Record<Tool, readonly InsertItem[]>> = {
|
|
105
|
-
insertMenu: [
|
|
106
|
-
{
|
|
107
|
-
id: "paragraph",
|
|
108
|
-
section: "turnInto",
|
|
109
|
-
label: (m) => m.paragraph,
|
|
110
|
-
icon: ParagraphIcon,
|
|
111
|
-
aliases: ["p", "text"],
|
|
112
|
-
keys: shortcutKeys("paragraph"),
|
|
113
|
-
run: (chain) => chain.setParagraph(),
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
heading: ([1, 2, 3, 4] as const).map((level): InsertItem => ({
|
|
117
|
-
id: `heading${level}`,
|
|
118
|
-
section: "turnInto",
|
|
119
|
-
label: (m) => m.heading({ level }),
|
|
120
|
-
icon: HeadingIcon,
|
|
121
|
-
aliases: [`h${level}`],
|
|
122
|
-
markdown: "#".repeat(level),
|
|
123
|
-
keys: shortcutKeys(`h${level}`),
|
|
124
|
-
requires: level === 1 ? "heading1" : undefined,
|
|
125
|
-
run: (chain) => chain.setHeading({ level }),
|
|
126
|
-
})),
|
|
127
|
-
bulletList: [
|
|
128
|
-
{
|
|
129
|
-
id: "bulletList",
|
|
130
|
-
section: "turnInto",
|
|
131
|
-
label: (m) => m.bulletList,
|
|
132
|
-
icon: ListIcon,
|
|
133
|
-
aliases: ["ul", "-"],
|
|
134
|
-
markdown: "-",
|
|
135
|
-
keys: shortcutKeys("bulletList"),
|
|
136
|
-
run: (chain) => chain.toggleBulletList(),
|
|
137
|
-
},
|
|
138
|
-
],
|
|
139
|
-
orderedList: [
|
|
140
|
-
{
|
|
141
|
-
id: "orderedList",
|
|
142
|
-
section: "turnInto",
|
|
143
|
-
label: (m) => m.orderedList,
|
|
144
|
-
icon: OrderedListIcon,
|
|
145
|
-
aliases: ["ol", "1."],
|
|
146
|
-
markdown: "1.",
|
|
147
|
-
keys: shortcutKeys("orderedList"),
|
|
148
|
-
run: (chain) => chain.toggleOrderedList(),
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
taskList: [
|
|
152
|
-
{
|
|
153
|
-
id: "taskList",
|
|
154
|
-
section: "turnInto",
|
|
155
|
-
label: (m) => m.taskList,
|
|
156
|
-
icon: TaskListIcon,
|
|
157
|
-
aliases: ["todo", "[]"],
|
|
158
|
-
markdown: "[ ]",
|
|
159
|
-
keys: shortcutKeys("taskList"),
|
|
160
|
-
run: (chain) => chain.toggleTaskList(),
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
/*
|
|
164
|
-
Картинке нужен адрес, и пункт его не знает: он готовит пустой абзац (как
|
|
165
|
-
любой пункт «Добавить») и открывает поповер картинки. Картинка из поповера
|
|
166
|
-
встаёт на место этого абзаца. Закрытый без адреса поповер оставляет
|
|
167
|
-
пустой абзац — как Блок, который передумали писать.
|
|
168
|
-
*/
|
|
169
|
-
image: [
|
|
170
|
-
{
|
|
171
|
-
id: "image",
|
|
172
|
-
section: "insert",
|
|
173
|
-
label: (m) => m.image,
|
|
174
|
-
icon: ImageIcon,
|
|
175
|
-
aliases: ["img", "picture", "photo", "![]()"],
|
|
176
|
-
run: (chain) => chain.openImageEditor(),
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
/*
|
|
180
|
-
Раскладка — пунктом без подменю: Меню вставки — плоский список
|
|
181
|
-
с фильтром по `/`, и подменю в нём не живут. Вариант по умолчанию — две
|
|
182
|
-
колонки, остальные — в «Добавить › Раскладка ›» и «Формат ›» ручки Блока.
|
|
183
|
-
Встаёт она только на верхний уровень: из колонки — под своей Раскладкой
|
|
184
|
-
(`insertLayout`).
|
|
185
|
-
*/
|
|
186
|
-
layout: [
|
|
187
|
-
{
|
|
188
|
-
id: "layout",
|
|
189
|
-
section: "insert",
|
|
190
|
-
label: (m) => m.layout,
|
|
191
|
-
icon: ColumnLayoutIcon,
|
|
192
|
-
aliases: ["columns", "cols"],
|
|
193
|
-
run: (chain) => chain.insertLayout(DEFAULT_LAYOUT),
|
|
194
|
-
},
|
|
195
|
-
],
|
|
196
|
-
/*
|
|
197
|
-
Обёртка — пустая, с одним абзацем, каретка внутри. Место —
|
|
198
|
-
Документ или колонка: из Обёртки, списка, цитаты и ячейки она встаёт под
|
|
199
|
-
их Блоком (`insertWrapper`).
|
|
200
|
-
*/
|
|
201
|
-
wrapper: [
|
|
202
|
-
{
|
|
203
|
-
id: "wrapper",
|
|
204
|
-
section: "insert",
|
|
205
|
-
label: (m) => m.wrapper,
|
|
206
|
-
icon: WrapperIcon,
|
|
207
|
-
aliases: ["group", "container", "div"],
|
|
208
|
-
run: (chain) => chain.insertWrapper(),
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
|
|
213
70
|
/**
|
|
214
71
|
* Пункт «Таблица» заданного размера: строка-заголовок всегда есть.
|
|
215
72
|
*
|
|
@@ -271,8 +128,8 @@ export function insertItemHint(
|
|
|
271
128
|
|
|
272
129
|
/**
|
|
273
130
|
* Место встроенных Инструментов в Меню вставки — порядок их пунктов внутри
|
|
274
|
-
* раздела.
|
|
275
|
-
*
|
|
131
|
+
* раздела. Пункты живут в описаниях (`insertItems`), а место на поверхности —
|
|
132
|
+
* привилегия кита, поэтому оно здесь, а не в описании.
|
|
276
133
|
*/
|
|
277
134
|
const INSERT_ORDER: readonly Tool[] = [
|
|
278
135
|
"insertMenu",
|
|
@@ -287,25 +144,22 @@ const INSERT_ORDER: readonly Tool[] = [
|
|
|
287
144
|
"image",
|
|
288
145
|
"layout",
|
|
289
146
|
"wrapper",
|
|
147
|
+
"details",
|
|
290
148
|
];
|
|
291
149
|
|
|
292
150
|
/** Разделы по порядку показа. */
|
|
293
151
|
export const INSERT_SECTIONS = ["insert", "turnInto"] as const;
|
|
294
152
|
|
|
295
153
|
/**
|
|
296
|
-
* Пункты для набора инструментов — проекция списка
|
|
297
|
-
*
|
|
298
|
-
* `INSERT_ORDER`. Меню без пункта сверх
|
|
154
|
+
* Пункты для набора инструментов — проекция списка описаний (`insertItems`) в
|
|
155
|
+
* порядке `INSERT_ORDER`. Абзац — пункт описания `insertMenu`. Меню без пункта сверх
|
|
299
156
|
* абзаца бессмысленно, и тогда список пуст: ни «+», ни `/` не появляются.
|
|
300
157
|
*/
|
|
301
158
|
export function insertItemsFor(tools: ToolList): readonly InsertItem[] {
|
|
302
159
|
if (!tools.has("insertMenu")) return [];
|
|
303
|
-
const items = projectRecords(
|
|
304
|
-
tools
|
|
305
|
-
|
|
306
|
-
(tool) => INSERT_ITEMS[tool],
|
|
307
|
-
(descriptor) => descriptor.insertItems
|
|
308
|
-
).filter((item) => item.requires === undefined || tools.has(item.requires));
|
|
160
|
+
const items = projectRecords(tools, INSERT_ORDER, (descriptor) => descriptor.insertItems).filter(
|
|
161
|
+
(item) => item.requires === undefined || tools.has(item.requires)
|
|
162
|
+
);
|
|
309
163
|
return items.length > 1 ? items : [];
|
|
310
164
|
}
|
|
311
165
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ParagraphIcon } from "../../icons.js";
|
|
4
|
+
import { shortcutKeys } from "./shortcuts.js";
|
|
5
|
+
import { SlashCommand } from "./slashCommand.js";
|
|
6
|
+
import type { ToolDescriptor } from "./toolDescriptor.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Меню вставки (`/` в тексте; «+» на Панели и «Добавить ›» Ручки Блока — виджеты
|
|
10
|
+
* кита, они читают `tools.has("insertMenu")`).
|
|
11
|
+
*
|
|
12
|
+
* Своего Блока у Инструмента нет: это способ добраться до чужих Блоков. Extensions
|
|
13
|
+
* Инструмента — это `/`, и они есть, только когда `RichTextEditor` дал мост: пунктов
|
|
14
|
+
* для меню нет — моста нет, и `/` остаётся обычным символом. Абзац записан здесь:
|
|
15
|
+
* это не Инструмент, он есть в Схеме всегда, и «Превратить в абзац» нужен, пока
|
|
16
|
+
* есть само меню.
|
|
17
|
+
*/
|
|
18
|
+
export const INSERT_MENU_TOOL: ToolDescriptor = {
|
|
19
|
+
name: "insertMenu",
|
|
20
|
+
engine: (context) => (context.slash ? [SlashCommand.configure({ bridge: context.slash })] : []),
|
|
21
|
+
insertItems: [
|
|
22
|
+
{
|
|
23
|
+
id: "paragraph",
|
|
24
|
+
section: "turnInto",
|
|
25
|
+
label: (m) => m.paragraph,
|
|
26
|
+
icon: ParagraphIcon,
|
|
27
|
+
aliases: ["p", "text"],
|
|
28
|
+
keys: shortcutKeys("paragraph"),
|
|
29
|
+
run: (chain) => chain.setParagraph(),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
@@ -80,7 +80,7 @@ export function documentFromJSON(editor: Editor, value: unknown): PmNode | null
|
|
|
80
80
|
preserveWhitespace: "full",
|
|
81
81
|
});
|
|
82
82
|
} catch (error) {
|
|
83
|
-
// Кривой файл — обычный отказ, но бросить
|
|
83
|
+
// Кривой файл — обычный отказ, но бросить могут и Extensions своего
|
|
84
84
|
// Инструмента: причина уходит в консоль разработки, иначе её не найти.
|
|
85
85
|
devError("RichTextEditor: a JSON document could not be built from its tree", error);
|
|
86
86
|
return null;
|