@inkbrick/react 0.0.0-stage → 0.1.0-alpha.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 (131) hide show
  1. package/dist/FormulaPalette-WFRPRHS5.js +320 -0
  2. package/dist/FormulaPalette-WFRPRHS5.js.map +1 -0
  3. package/dist/chunk-TPUX4C45.js +2309 -0
  4. package/dist/chunk-TPUX4C45.js.map +1 -0
  5. package/dist/index.d.ts +1431 -0
  6. package/dist/index.js +4880 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/mathCss-FLPE6QBM.js +361 -0
  9. package/dist/mathCss-FLPE6QBM.js.map +1 -0
  10. package/package.json +46 -3
  11. package/src/block-handle/BlockHandleController.ts +93 -0
  12. package/src/block-handle/BlockHandleHost.tsx +108 -0
  13. package/src/block-handle/DefaultBlockHandle.tsx +71 -0
  14. package/src/block-handle/DefaultBlockMenu.tsx +114 -0
  15. package/src/block-handle/DropIndicatorHost.tsx +58 -0
  16. package/src/block-handle/actions.tsx +146 -0
  17. package/src/block-handle/blockDrag.ts +215 -0
  18. package/src/block-handle/geometry.ts +64 -0
  19. package/src/block-handle/gripGesture.ts +108 -0
  20. package/src/block-handle/types.ts +65 -0
  21. package/src/block-handle/useBlockDrag.ts +21 -0
  22. package/src/block-handle/useBlockHandles.ts +125 -0
  23. package/src/blocks/BlockChildren.tsx +17 -0
  24. package/src/blocks/BlockProps.ts +5 -0
  25. package/src/blocks/CalloutBlock.tsx +61 -0
  26. package/src/blocks/CodeBlock.tsx +96 -0
  27. package/src/blocks/DividerBlock.tsx +15 -0
  28. package/src/blocks/Editable.tsx +84 -0
  29. package/src/blocks/FormulaBlock.tsx +173 -0
  30. package/src/blocks/HeadingBlock.tsx +22 -0
  31. package/src/blocks/ImageBlock.tsx +362 -0
  32. package/src/blocks/ListBlock.tsx +86 -0
  33. package/src/blocks/NoteBlock.tsx +10 -0
  34. package/src/blocks/PageBlock.tsx +10 -0
  35. package/src/blocks/ParagraphBlock.tsx +20 -0
  36. package/src/blocks/QuoteBlock.tsx +18 -0
  37. package/src/blocks/UnknownBlockView.tsx +27 -0
  38. package/src/blocks/defineBlock.ts +49 -0
  39. package/src/clipboard/html/blocks.ts +379 -0
  40. package/src/clipboard/html/inline.ts +92 -0
  41. package/src/clipboard/html/style.ts +92 -0
  42. package/src/clipboard/html/tags.ts +77 -0
  43. package/src/clipboard/htmlToFragment.ts +41 -0
  44. package/src/clipboard/useClipboard.ts +296 -0
  45. package/src/code/highlight.ts +56 -0
  46. package/src/code/languages.ts +236 -0
  47. package/src/code/scan/css.ts +164 -0
  48. package/src/code/scan/generic.ts +328 -0
  49. package/src/code/scan/html.ts +129 -0
  50. package/src/code/scan/markdown.ts +58 -0
  51. package/src/code/scan/shared.ts +81 -0
  52. package/src/code/scan/yaml.ts +34 -0
  53. package/src/code/types.ts +34 -0
  54. package/src/context/EditorContext.ts +9 -0
  55. package/src/context/EditorProvider.tsx +172 -0
  56. package/src/controllers/adapters.tsx +63 -0
  57. package/src/editing/BlockSelectionController.ts +27 -0
  58. package/src/editing/CaretController.ts +27 -0
  59. package/src/editing/EditorView.ts +108 -0
  60. package/src/editing/ViewportController.ts +63 -0
  61. package/src/editing/beforeInput.ts +276 -0
  62. package/src/editing/deleteAtBoundary.ts +16 -0
  63. package/src/editing/inputActions.ts +109 -0
  64. package/src/editing/renderCode.ts +69 -0
  65. package/src/editing/renderInline.ts +144 -0
  66. package/src/editing/selection.ts +262 -0
  67. package/src/editing/useEditorInput.ts +223 -0
  68. package/src/editing/useSelectionState.ts +46 -0
  69. package/src/formula/FormulaController.ts +51 -0
  70. package/src/formula/FormulaPalette.tsx +79 -0
  71. package/src/formula/FormulaPopover.tsx +181 -0
  72. package/src/formula/LazyFormulaPalette.tsx +42 -0
  73. package/src/formula/actions.ts +138 -0
  74. package/src/formula/snippet.ts +70 -0
  75. package/src/formula/symbols.ts +264 -0
  76. package/src/hooks/useBlock.ts +21 -0
  77. package/src/hooks/useBlockController.ts +30 -0
  78. package/src/hooks/useChildren.ts +16 -0
  79. package/src/hooks/useDocument.ts +16 -0
  80. package/src/hooks/useEditor.ts +13 -0
  81. package/src/hooks/useEditorView.ts +13 -0
  82. package/src/hooks/useHistory.ts +46 -0
  83. package/src/hooks/useReadOnly.ts +21 -0
  84. package/src/hooks/useT.ts +10 -0
  85. package/src/i18n/en.ts +182 -0
  86. package/src/i18n/ru.ts +174 -0
  87. package/src/i18n/translate.ts +77 -0
  88. package/src/icons/ICONS_LICENSE.md +50 -0
  89. package/src/icons/Icon.tsx +41 -0
  90. package/src/icons/lucide.ts +234 -0
  91. package/src/images/ImageError.ts +39 -0
  92. package/src/images/UploadTracker.ts +49 -0
  93. package/src/images/insertImages.ts +142 -0
  94. package/src/images/types.ts +22 -0
  95. package/src/images/upload.ts +62 -0
  96. package/src/index.ts +102 -0
  97. package/src/keymap/defaultKeymap.ts +189 -0
  98. package/src/keymap/keymap.ts +42 -0
  99. package/src/keymap/shortcut.ts +105 -0
  100. package/src/keymap/types.ts +40 -0
  101. package/src/math/FormulaView.tsx +62 -0
  102. package/src/math/mathCss.ts +4 -0
  103. package/src/math/renderer.ts +198 -0
  104. package/src/registry/defaultBlockComponents.ts +26 -0
  105. package/src/registry/resolveBlockComponent.ts +14 -0
  106. package/src/renderer/BlockRenderer.tsx +63 -0
  107. package/src/renderer/DocumentRenderer.tsx +33 -0
  108. package/src/renderer/DocumentView.tsx +94 -0
  109. package/src/renderer/virtual/VirtualChildList.tsx +355 -0
  110. package/src/renderer/virtual/estimate.ts +62 -0
  111. package/src/renderer/virtual/virtualRenderer.tsx +35 -0
  112. package/src/slash/DefaultSlashMenu.tsx +119 -0
  113. package/src/slash/SlashMenuController.ts +66 -0
  114. package/src/slash/SlashMenuHost.tsx +48 -0
  115. package/src/slash/applySlashItem.ts +57 -0
  116. package/src/slash/defaultSlashItems.tsx +186 -0
  117. package/src/slash/filter.ts +29 -0
  118. package/src/slash/items.ts +30 -0
  119. package/src/slash/syncSlashMenu.ts +67 -0
  120. package/src/slash/types.ts +21 -0
  121. package/src/toolbar/DefaultToolbar.tsx +281 -0
  122. package/src/toolbar/ToolbarController.ts +100 -0
  123. package/src/toolbar/ToolbarHost.tsx +81 -0
  124. package/src/toolbar/formatting.ts +75 -0
  125. package/src/toolbar/rich.ts +58 -0
  126. package/src/toolbar/syncToolbar.ts +39 -0
  127. package/src/toolbar/types.ts +21 -0
  128. package/src/ui/EditorUIContext.ts +26 -0
  129. package/src/ui/presentation.ts +24 -0
  130. package/src/ui/types.ts +70 -0
  131. package/README.md +0 -3
@@ -0,0 +1,100 @@
1
+ import type { RangeFormats } from '@inkbrick/engine';
2
+
3
+ import type { DocSelection } from '../editing/selection';
4
+ import type { ToolbarPanel } from './types';
5
+
6
+ export interface ToolbarState {
7
+ readonly visible: boolean;
8
+ readonly position: { readonly x: number; readonly y: number } | null;
9
+ readonly formats: RangeFormats | null;
10
+ readonly range: DocSelection | null;
11
+ readonly linkOpen: boolean;
12
+ readonly panel: ToolbarPanel | null;
13
+ }
14
+
15
+ const HIDDEN: ToolbarState = Object.freeze({
16
+ visible: false,
17
+ position: null,
18
+ formats: null,
19
+ range: null,
20
+ linkOpen: false,
21
+ panel: null,
22
+ });
23
+
24
+ function sameFormats(a: RangeFormats | null, b: RangeFormats | null): boolean {
25
+ return (
26
+ a === b ||
27
+ (!!a &&
28
+ !!b &&
29
+ a.bold === b.bold &&
30
+ a.italic === b.italic &&
31
+ a.underline === b.underline &&
32
+ a.strike === b.strike &&
33
+ a.code === b.code &&
34
+ a.link === b.link &&
35
+ a.color === b.color &&
36
+ a.backgroundColor === b.backgroundColor &&
37
+ a.align === b.align)
38
+ );
39
+ }
40
+
41
+ export class ToolbarController {
42
+ private state: ToolbarState = HIDDEN;
43
+
44
+ private readonly listeners = new Set<() => void>();
45
+
46
+ getState = (): ToolbarState => this.state;
47
+
48
+ subscribe = (listener: () => void): (() => void) => {
49
+ this.listeners.add(listener);
50
+
51
+ return () => {
52
+ this.listeners.delete(listener);
53
+ };
54
+ };
55
+
56
+ show(range: DocSelection, formats: RangeFormats, position: { x: number; y: number }): void {
57
+ const prev = this.state;
58
+ const same =
59
+ prev.visible &&
60
+ sameFormats(prev.formats, formats) &&
61
+ prev.position?.x === position.x &&
62
+ prev.position?.y === position.y &&
63
+ prev.range?.from.blockId === range.from.blockId &&
64
+ prev.range?.from.offset === range.from.offset &&
65
+ prev.range?.to.blockId === range.to.blockId &&
66
+ prev.range?.to.offset === range.to.offset;
67
+
68
+ if (!same) this.set({ ...prev, visible: true, position, formats, range });
69
+ }
70
+
71
+ hide(): void {
72
+ if (this.state.visible) this.set(HIDDEN);
73
+ }
74
+
75
+ openLink(): void {
76
+ if (this.state.visible && !this.state.linkOpen) {
77
+ this.set({ ...this.state, linkOpen: true, panel: null });
78
+ }
79
+ }
80
+
81
+ togglePanel(panel: ToolbarPanel): void {
82
+ if (!this.state.visible || this.state.linkOpen) return;
83
+
84
+ this.set({ ...this.state, panel: this.state.panel === panel ? null : panel });
85
+ }
86
+
87
+ closePanel(): void {
88
+ if (this.state.panel) this.set({ ...this.state, panel: null });
89
+ }
90
+
91
+ closeLink(): void {
92
+ if (this.state.linkOpen) this.set({ ...this.state, linkOpen: false });
93
+ }
94
+
95
+ private set(state: ToolbarState): void {
96
+ this.state = state;
97
+
98
+ for (const listener of [...this.listeners]) listener();
99
+ }
100
+ }
@@ -0,0 +1,81 @@
1
+ import { createPortal } from 'react-dom';
2
+ import { useSyncExternalStore } from 'react';
3
+
4
+ import { useEditor } from '../hooks/useEditor';
5
+ import { useEditorView } from '../hooks/useEditorView';
6
+ import { useReadOnly } from '../hooks/useReadOnly';
7
+ import { openInlineFormula } from '../formula/actions';
8
+ import { useEditorUI } from '../ui/EditorUIContext';
9
+ import { DefaultToolbar } from './DefaultToolbar';
10
+ import { setLinkInSelection, toggleFormatInSelection } from './formatting';
11
+ import { setAlignInSelection, setColorInSelection } from './rich';
12
+ import { syncToolbar } from './syncToolbar';
13
+
14
+ export function ToolbarHost() {
15
+ const editor = useEditor();
16
+ const view = useEditorView();
17
+ const { slots } = useEditorUI();
18
+ const readOnly = useReadOnly();
19
+ const state = useSyncExternalStore(
20
+ view.toolbar.subscribe,
21
+ view.toolbar.getState,
22
+ view.toolbar.getState,
23
+ );
24
+
25
+ if (slots.Toolbar === null || readOnly) return null;
26
+
27
+ if (
28
+ !state.visible ||
29
+ !state.position ||
30
+ !state.formats ||
31
+ !state.range ||
32
+ typeof document === 'undefined'
33
+ ) {
34
+ return null;
35
+ }
36
+
37
+ const Toolbar = slots.Toolbar ?? DefaultToolbar;
38
+ const range = state.range;
39
+
40
+ return createPortal(
41
+ <Toolbar
42
+ formats={state.formats}
43
+ position={state.position}
44
+ linkOpen={state.linkOpen}
45
+ panel={state.panel}
46
+ onToggle={(format) => {
47
+ toggleFormatInSelection(editor, view, range, format);
48
+ syncToolbar(editor, view);
49
+ }}
50
+ onOpenLink={() => view.toolbar.openLink()}
51
+ onApplyLink={(url) => {
52
+ if (!setLinkInSelection(editor, view, range, url)) return false;
53
+
54
+ view.toolbar.closeLink();
55
+ syncToolbar(editor, view);
56
+
57
+ return true;
58
+ }}
59
+ onTogglePanel={(panel) => view.toolbar.togglePanel(panel)}
60
+ onApplyColor={(kind, value) => {
61
+ setColorInSelection(editor, view, range, kind, value);
62
+ view.toolbar.closePanel();
63
+ syncToolbar(editor, view);
64
+ }}
65
+ onAlign={(align) => {
66
+ setAlignInSelection(editor, view, range, align);
67
+ syncToolbar(editor, view);
68
+ }}
69
+ onInsertFormula={() => {
70
+ view.placeSelection(range.from, range.to);
71
+ openInlineFormula(editor, view);
72
+ }}
73
+ onCancelLink={() => {
74
+ view.toolbar.closeLink();
75
+ view.placeSelection(range.from, range.to);
76
+ syncToolbar(editor, view);
77
+ }}
78
+ />,
79
+ document.body,
80
+ );
81
+ }
@@ -0,0 +1,75 @@
1
+ import { flushSync } from 'react-dom';
2
+
3
+ import { normalizeLinkUrl } from '@inkbrick/document';
4
+ import type { Editor } from '@inkbrick/engine';
5
+
6
+ import type { EditorView } from '../editing/EditorView';
7
+ import type { DocSelection } from '../editing/selection';
8
+
9
+ const isSingleBlock = (selection: DocSelection) => selection.from.blockId === selection.to.blockId;
10
+
11
+ export function isFormattable(editor: Editor, selection: DocSelection): boolean {
12
+ return !(
13
+ isSingleBlock(selection) &&
14
+ editor.store.getBlock(selection.from.blockId)?.flavour === 'block:code'
15
+ );
16
+ }
17
+
18
+ export function toggleFormatInSelection(
19
+ editor: Editor,
20
+ view: EditorView,
21
+ selection: DocSelection,
22
+ format: string,
23
+ ): void {
24
+ if (selection.collapsed || !isFormattable(editor, selection)) return;
25
+
26
+ flushSync(() => {
27
+ if (isSingleBlock(selection)) {
28
+ editor.dispatch({
29
+ type: 'toggleFormat',
30
+ payload: {
31
+ blockId: selection.from.blockId,
32
+ from: selection.from.offset,
33
+ to: selection.to.offset,
34
+ format,
35
+ },
36
+ });
37
+ } else {
38
+ editor.dispatch({
39
+ type: 'toggleFormatRange',
40
+ payload: { from: selection.from, to: selection.to, format },
41
+ });
42
+ }
43
+ });
44
+
45
+ view.placeSelection(selection.from, selection.to);
46
+ }
47
+
48
+ /**
49
+ * Ставит или снимает ссылку на выделении. Ввод нормализуется («example.com» → https://example.com);
50
+ * null или пустая строка снимают ссылку. Возвращает false, если адрес небезопасен — документ не меняется.
51
+ */
52
+ export function setLinkInSelection(
53
+ editor: Editor,
54
+ view: EditorView,
55
+ selection: DocSelection,
56
+ input: string | null,
57
+ ): boolean {
58
+ if (selection.collapsed || !isFormattable(editor, selection)) return false;
59
+
60
+ const remove = input === null || input.trim() === '';
61
+ const link = remove ? undefined : (normalizeLinkUrl(input) ?? null);
62
+
63
+ if (link === null) return false;
64
+
65
+ flushSync(() => {
66
+ editor.dispatch({
67
+ type: 'setInlineAttributes',
68
+ payload: { from: selection.from, to: selection.to, attributes: { link } },
69
+ });
70
+ });
71
+
72
+ view.placeSelection(selection.from, selection.to);
73
+
74
+ return true;
75
+ }
@@ -0,0 +1,58 @@
1
+ import { flushSync } from 'react-dom';
2
+
3
+ import type { Alignment } from '@inkbrick/document';
4
+ import type { Editor } from '@inkbrick/engine';
5
+
6
+ import type { EditorView } from '../editing/EditorView';
7
+ import type { DocSelection } from '../editing/selection';
8
+ import { isFormattable } from './formatting';
9
+
10
+ /**
11
+ * Ставит или снимает цвет текста (`color`) или подсветки (`backgroundColor`) на выделении. Значение — имя цвета
12
+ * палитры; `null` снимает цвет. Возвращает false, если применять не к чему (свёрнутое выделение, код).
13
+ */
14
+ export function setColorInSelection(
15
+ editor: Editor,
16
+ view: EditorView,
17
+ selection: DocSelection,
18
+ kind: 'color' | 'backgroundColor',
19
+ value: string | null,
20
+ ): boolean {
21
+ if (selection.collapsed || !isFormattable(editor, selection)) return false;
22
+
23
+ const payload = {
24
+ from: selection.from,
25
+ to: selection.to,
26
+ attributes: { [kind]: value ?? undefined },
27
+ };
28
+
29
+ if (!editor.can('setInlineAttributes', payload)) return false;
30
+
31
+ flushSync(() => {
32
+ editor.dispatch({ type: 'setInlineAttributes', payload });
33
+ });
34
+
35
+ view.placeSelection(selection.from, selection.to);
36
+
37
+ return true;
38
+ }
39
+
40
+ export function setAlignInSelection(
41
+ editor: Editor,
42
+ view: EditorView,
43
+ selection: DocSelection,
44
+ align: Alignment,
45
+ ): boolean {
46
+ if (!editor.can('setAlign', { from: selection.from, to: selection.to, align })) return false;
47
+
48
+ flushSync(() => {
49
+ editor.dispatch({
50
+ type: 'setAlign',
51
+ payload: { from: selection.from, to: selection.to, align },
52
+ });
53
+ });
54
+
55
+ view.placeSelection(selection.from, selection.to);
56
+
57
+ return true;
58
+ }
@@ -0,0 +1,39 @@
1
+ import type { Editor } from '@inkbrick/engine';
2
+
3
+ import type { EditorView } from '../editing/EditorView';
4
+ import { readDocSelection } from '../editing/selection';
5
+ import { isFormattable } from './formatting';
6
+
7
+ export function syncToolbar(editor: Editor, view: EditorView): void {
8
+ const state = view.toolbar.getState();
9
+
10
+ if (state.linkOpen) return;
11
+
12
+ const root = view.getRoot();
13
+ const selection = root ? readDocSelection(root) : null;
14
+ const native = globalThis.getSelection();
15
+
16
+ if (
17
+ view.pointerDown ||
18
+ !selection ||
19
+ selection.collapsed ||
20
+ !isFormattable(editor, selection) ||
21
+ !native ||
22
+ native.rangeCount === 0
23
+ ) {
24
+ view.toolbar.hide();
25
+ return;
26
+ }
27
+
28
+ const rect = native.getRangeAt(0).getBoundingClientRect();
29
+
30
+ if (rect.width === 0 && rect.height === 0) {
31
+ view.toolbar.hide();
32
+ return;
33
+ }
34
+
35
+ view.toolbar.show(selection, editor.formatsAt(selection.from, selection.to), {
36
+ x: rect.left + rect.width / 2,
37
+ y: rect.top,
38
+ });
39
+ }
@@ -0,0 +1,21 @@
1
+ import type { Alignment } from '@inkbrick/document';
2
+ import type { RangeFormats } from '@inkbrick/engine';
3
+
4
+ export type InlineFormat = 'bold' | 'italic' | 'underline' | 'strike' | 'code';
5
+
6
+ export type ToolbarPanel = 'color' | 'highlight';
7
+
8
+ export interface ToolbarProps {
9
+ readonly formats: RangeFormats;
10
+ readonly position: { readonly x: number; readonly y: number };
11
+ readonly linkOpen: boolean;
12
+ readonly onToggle: (format: InlineFormat) => void;
13
+ readonly onOpenLink: () => void;
14
+ readonly onApplyLink: (url: string | null) => boolean;
15
+ readonly onCancelLink: () => void;
16
+ readonly panel: ToolbarPanel | null;
17
+ readonly onTogglePanel: (panel: ToolbarPanel) => void;
18
+ readonly onApplyColor: (kind: 'color' | 'backgroundColor', value: string | null) => void;
19
+ readonly onAlign: (align: Alignment) => void;
20
+ readonly onInsertFormula: () => void;
21
+ }
@@ -0,0 +1,26 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ import { highlightCode } from '../code/highlight';
4
+ import { temmlRenderer } from '../math/renderer';
5
+ import { defaultT } from '../i18n/translate';
6
+ import { defaultKeymap } from '../keymap/defaultKeymap';
7
+ import { defaultSlashItems } from '../slash/defaultSlashItems';
8
+ import type { EditorUIConfig } from './types';
9
+
10
+ export const EMPTY_UI_CONFIG: EditorUIConfig = Object.freeze({
11
+ components: Object.freeze({}),
12
+ slots: Object.freeze({}),
13
+ slashItems: defaultSlashItems,
14
+ keymap: defaultKeymap,
15
+ highlighter: highlightCode,
16
+ formulaRenderer: temmlRenderer,
17
+ locale: 'en',
18
+ t: defaultT,
19
+ images: Object.freeze({}),
20
+ });
21
+
22
+ export const EditorUIContext = createContext<EditorUIConfig>(EMPTY_UI_CONFIG);
23
+
24
+ export function useEditorUI(): EditorUIConfig {
25
+ return useContext(EditorUIContext);
26
+ }
@@ -0,0 +1,24 @@
1
+ import type { CodeHighlighter } from '../code/types';
2
+ import type { FormulaRenderer } from '../math/renderer';
3
+ import type { SlashMenuItem } from '../slash/types';
4
+ import type { BlockComponentRegistry, EditorSlots } from './types';
5
+
6
+ /**
7
+ * Визуальный слой редактора одним объектом: представления блоков, элементы оболочки (меню, toolbar, ручки),
8
+ * пункты slash-меню, подсветка кода и отрисовка формул. Модель документа и команды от него не зависят, поэтому
9
+ * дизайн-систему можно подключить к любому редактору: `<EditorProvider presentation={myDesignSystem}>`.
10
+ *
11
+ * Отдельные props провайдера (`components`, `slots`, …) уточняют presentation: `components` и `slots`
12
+ * сливаются с ним (одинаковый ключ — из prop), остальное заменяется целиком.
13
+ */
14
+ export interface EditorPresentation {
15
+ readonly components?: BlockComponentRegistry;
16
+ readonly slots?: EditorSlots;
17
+ readonly slashItems?: readonly SlashMenuItem[] | false;
18
+ readonly highlighter?: CodeHighlighter | false;
19
+ readonly formulaRenderer?: FormulaRenderer | false;
20
+ }
21
+
22
+ export function definePresentation<T extends EditorPresentation>(presentation: T): T {
23
+ return presentation;
24
+ }
@@ -0,0 +1,70 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+
3
+ import type { BaseBlock } from '@inkbrick/document';
4
+ import type { BlockController, Editor } from '@inkbrick/engine';
5
+
6
+ import type { ImageOptions } from '../images/types';
7
+ import type { SlashMenuItem, SlashMenuProps } from '../slash/types';
8
+ import type { BlockHandleProps, BlockMenuProps, DropIndicatorProps } from '../block-handle/types';
9
+ import type { CodeHighlighter } from '../code/types';
10
+ import type { FormulaRenderer } from '../math/renderer';
11
+ import type { TFunction } from '../i18n/translate';
12
+ import type { KeyBinding } from '../keymap/types';
13
+ import type { ToolbarProps } from '../toolbar/types';
14
+
15
+ /**
16
+ * Props, которые получает React-представление блока.
17
+ *
18
+ * Блок нельзя изменять напрямую: все изменения документа идут через
19
+ * `editor.dispatch(...)`. Для редактируемого текста используйте компонент
20
+ * `<Editable block={block} placeholder="…" />` — он подключает ввод, выделение и
21
+ * клавиатурные действия редактора. Дочерние блоки выводит `<BlockChildren id={block.id} />`.
22
+ */
23
+ export interface BlockViewProps<TBlock extends BaseBlock = BaseBlock> {
24
+ readonly block: TBlock;
25
+ readonly editor: Editor;
26
+ readonly selected: boolean;
27
+ readonly readOnly: boolean;
28
+ /**
29
+ * Контроллер типа блока (`defineBlock({ controller })`), если есть. Удобнее хук `useBlockController(block)`:
30
+ * его `execute`/`can` сами подставляют `blockId`.
31
+ */
32
+ readonly controller?: BlockController<any>;
33
+ }
34
+
35
+ export type BlockComponent<TBlock extends BaseBlock = BaseBlock> = ComponentType<
36
+ BlockViewProps<TBlock>
37
+ >;
38
+
39
+ export type BlockComponentRegistry = Readonly<Partial<Record<string, BlockComponent>>>;
40
+
41
+ export interface UnknownBlockViewProps extends BlockViewProps {
42
+ /** Уже выведенные дочерние блоки (данные не теряются, даже если тип блока неизвестен). */
43
+ readonly children?: ReactNode;
44
+ }
45
+
46
+ export interface EditorSlots {
47
+ readonly UnknownBlock?: ComponentType<UnknownBlockViewProps>;
48
+ readonly SlashMenu?: ComponentType<SlashMenuProps>;
49
+ readonly Toolbar?: ComponentType<ToolbarProps> | null;
50
+ /**
51
+ * Ручка слева от блока («+» и хваталка для переноса); `null` отключает ручки, меню и встроенное перетаскивание.
52
+ * Свой DnD подключается через `useBlockDrag()` — в своей ручке или в любом компоненте внутри `<EditorProvider>`.
53
+ */
54
+ readonly BlockHandle?: ComponentType<BlockHandleProps> | null;
55
+ readonly BlockMenu?: ComponentType<BlockMenuProps>;
56
+ readonly DropIndicator?: ComponentType<DropIndicatorProps> | null;
57
+ }
58
+
59
+ export interface EditorUIConfig {
60
+ readonly components: BlockComponentRegistry;
61
+ readonly slots: EditorSlots;
62
+ readonly slashItems: readonly SlashMenuItem[];
63
+ readonly highlighter: CodeHighlighter | null;
64
+ readonly formulaRenderer: FormulaRenderer | null;
65
+ readonly locale: string;
66
+ readonly t: TFunction;
67
+ readonly keymap: readonly KeyBinding[];
68
+ readonly images: ImageOptions;
69
+ readonly readOnly?: boolean;
70
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.