@sofereditor/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +27 -0
- package/dist/index.cjs +3759 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +353 -0
- package/dist/index.d.ts +353 -0
- package/dist/index.js +3778 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { RefObject, ReactNode, JSX } from 'react';
|
|
2
|
+
import { EditorDocument, EditorHistory, SerializedDocument, Selection, MarkName, MarkAttrs, BlockType, BlockAttrs, ListKind, PageSettings, TableLocation, TableRect, ImageEmbed, SerializedBlock, DeltaOp } from '@sofereditor/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sub-phase 3.2 — page layout with intra-paragraph fragmentation.
|
|
6
|
+
*
|
|
7
|
+
* Two-phase render cycle:
|
|
8
|
+
* 1. "measure" phase: editor renders with the default (unfragmented) layout
|
|
9
|
+
* — every top-level on page 1. After the DOM commits, `usePageLayout`
|
|
10
|
+
* reads each top-level's geometry, runs `computeLayout`, and sets the
|
|
11
|
+
* real layout (with page splits and fragment offsets).
|
|
12
|
+
* 2. "stable" phase: editor renders the real layout. No further measurement
|
|
13
|
+
* until `deps` change (typically `[topLevels]`), at which point we go
|
|
14
|
+
* back to step 1.
|
|
15
|
+
*
|
|
16
|
+
* Fragmentation only applies to "fragmentable" top-levels (paragraph, heading,
|
|
17
|
+
* blockquote — single Y.Text blocks). List groups and code blocks are placed
|
|
18
|
+
* atomically: if they don't fit they jump to the next page and bleed if larger
|
|
19
|
+
* than a page.
|
|
20
|
+
*/
|
|
21
|
+
interface PageGeometry {
|
|
22
|
+
/** Page width in CSS px (A4 @ 96dpi = ~794). */
|
|
23
|
+
width: number;
|
|
24
|
+
/** Page height in CSS px (A4 @ 96dpi = ~1123). */
|
|
25
|
+
height: number;
|
|
26
|
+
marginTop: number;
|
|
27
|
+
marginBottom: number;
|
|
28
|
+
marginLeft: number;
|
|
29
|
+
marginRight: number;
|
|
30
|
+
}
|
|
31
|
+
declare const A4_PAGE: PageGeometry;
|
|
32
|
+
declare function contentHeight(g: PageGeometry): number;
|
|
33
|
+
declare function contentWidth(g: PageGeometry): number;
|
|
34
|
+
interface BlockFragment {
|
|
35
|
+
index: number;
|
|
36
|
+
start: number;
|
|
37
|
+
end: number;
|
|
38
|
+
}
|
|
39
|
+
interface TableRowFragment {
|
|
40
|
+
/** 0, 1, 2... sequential fragment id within the table. */
|
|
41
|
+
index: number;
|
|
42
|
+
/** First row included (inclusive). */
|
|
43
|
+
rowStart: number;
|
|
44
|
+
/** First row NOT included (exclusive). */
|
|
45
|
+
rowEnd: number;
|
|
46
|
+
}
|
|
47
|
+
interface PageSlot {
|
|
48
|
+
topLevelIndex: number;
|
|
49
|
+
/** Set when this slot represents a paragraph/heading/blockquote line-level fragment. */
|
|
50
|
+
fragment?: BlockFragment;
|
|
51
|
+
/** Set when this slot is a row-range fragment of a table (Sub-phase 4.6). */
|
|
52
|
+
tableFragment?: TableRowFragment;
|
|
53
|
+
}
|
|
54
|
+
interface PageEntry {
|
|
55
|
+
slots: PageSlot[];
|
|
56
|
+
}
|
|
57
|
+
interface PageLayout {
|
|
58
|
+
pages: PageEntry[];
|
|
59
|
+
}
|
|
60
|
+
declare function defaultPageLayout(topLevelCount: number): PageLayout;
|
|
61
|
+
declare function usePageLayout(rootRef: RefObject<HTMLElement>, geometry: PageGeometry, topLevelCount: number, deps: ReadonlyArray<unknown>): PageLayout;
|
|
62
|
+
|
|
63
|
+
interface UseEditorOptions {
|
|
64
|
+
document?: EditorDocument;
|
|
65
|
+
/**
|
|
66
|
+
* Quando definido, `insertImageFromFile` chama esta função pra obter a URL
|
|
67
|
+
* final em vez de embutir o arquivo como data URL base64 no Y.Doc.
|
|
68
|
+
*
|
|
69
|
+
* Use isto quando o app tem storage externo (S3, Azure Blob, etc.) e não quer
|
|
70
|
+
* inflar o documento com imagens base64. O componente `<Editor>` propaga o
|
|
71
|
+
* mesmo upload para paste/drop/file picker.
|
|
72
|
+
*/
|
|
73
|
+
uploadImage?: (file: File) => Promise<string>;
|
|
74
|
+
}
|
|
75
|
+
type PendingMarks = Partial<Record<MarkName, MarkAttrs[MarkName] | null>>;
|
|
76
|
+
interface LinkRequest {
|
|
77
|
+
initialHref: string;
|
|
78
|
+
resolve: (href: string | null) => void;
|
|
79
|
+
}
|
|
80
|
+
interface PageConfigRequest {
|
|
81
|
+
initial: PageSettings;
|
|
82
|
+
resolve: (applied: boolean) => void;
|
|
83
|
+
}
|
|
84
|
+
type CaptionAlign = "left" | "center" | "right";
|
|
85
|
+
interface ImageCaptionResult {
|
|
86
|
+
caption: string;
|
|
87
|
+
align: CaptionAlign;
|
|
88
|
+
}
|
|
89
|
+
interface ImageCaptionRequest {
|
|
90
|
+
initialCaption: string;
|
|
91
|
+
initialAlign: CaptionAlign;
|
|
92
|
+
/**
|
|
93
|
+
* Resolves with the new caption + alignment, or null on cancel. An empty
|
|
94
|
+
* `caption` string means "remove caption" (align is ignored in that case).
|
|
95
|
+
*/
|
|
96
|
+
resolve: (result: ImageCaptionResult | null) => void;
|
|
97
|
+
}
|
|
98
|
+
interface UseEditorResult {
|
|
99
|
+
doc: EditorDocument;
|
|
100
|
+
history: EditorHistory;
|
|
101
|
+
/** Snapshot of the model, re-computed on every Y.js update. */
|
|
102
|
+
snapshot: SerializedDocument;
|
|
103
|
+
/** Current model selection. Use `setSelection` to move the caret. */
|
|
104
|
+
selection: Selection;
|
|
105
|
+
setSelection: (sel: Selection) => void;
|
|
106
|
+
/** Stable getter — commands read this to know where the caret is. */
|
|
107
|
+
getSelection: () => Selection;
|
|
108
|
+
isMarkActive: (name: MarkName, value?: MarkAttrs[MarkName]) => boolean;
|
|
109
|
+
getActiveMarks: () => Partial<Record<MarkName, MarkAttrs[MarkName] | "mixed">>;
|
|
110
|
+
toggleMark: (name: MarkName, value?: MarkAttrs[MarkName]) => void;
|
|
111
|
+
setMark: (name: MarkName, value: MarkAttrs[MarkName]) => void;
|
|
112
|
+
removeMark: (name: MarkName) => void;
|
|
113
|
+
/** Returns the type of the focus block (or "mixed" when the selection covers heterogeneous blocks). */
|
|
114
|
+
getBlockType: () => BlockType | "mixed";
|
|
115
|
+
/** Returns one block attribute at the focus block, or "mixed" when not uniform across the selection. */
|
|
116
|
+
getBlockAttr: <K extends keyof BlockAttrs>(key: K) => BlockAttrs[K] | "mixed" | undefined;
|
|
117
|
+
setBlockType: (type: BlockType, attrs?: BlockAttrs) => void;
|
|
118
|
+
setBlockAttr: <K extends keyof BlockAttrs>(key: K, value: BlockAttrs[K] | null) => void;
|
|
119
|
+
isListActive: (kind: ListKind) => boolean;
|
|
120
|
+
toggleList: (kind: ListKind) => void;
|
|
121
|
+
indentList: () => void;
|
|
122
|
+
dedentList: () => void;
|
|
123
|
+
/** Returns true when handled (current block is a listItem); false to fall back to insertParagraph. */
|
|
124
|
+
splitListItem: () => boolean;
|
|
125
|
+
/** Marks queued for the next `insertText` (e.g. user pressed Cmd+B with a collapsed caret). */
|
|
126
|
+
pendingMarks: PendingMarks;
|
|
127
|
+
/** Consumed by the input layer right before calling `insertText`. */
|
|
128
|
+
consumePendingMarks: () => PendingMarks | undefined;
|
|
129
|
+
linkRequest: LinkRequest | null;
|
|
130
|
+
/** Open the link dialog; resolves with the entered href, empty string to remove, or null on cancel. */
|
|
131
|
+
requestLink: (initialHref?: string) => Promise<string | null>;
|
|
132
|
+
/** Called by the LinkDialog itself when the user submits or cancels. */
|
|
133
|
+
resolveLinkRequest: (href: string | null) => void;
|
|
134
|
+
/** Reactive page settings snapshot (re-renders when the Y.Doc docSettings changes). */
|
|
135
|
+
pageSettings: PageSettings;
|
|
136
|
+
/** Merge a partial PageSettings into the Y.Doc. Not undoable. */
|
|
137
|
+
setPageSettings: (partial: Partial<PageSettings>) => void;
|
|
138
|
+
/** Page-config dialog request (null when closed). */
|
|
139
|
+
pageConfigRequest: PageConfigRequest | null;
|
|
140
|
+
/** Open the page-config dialog; resolves true on apply, false on cancel. */
|
|
141
|
+
requestPageConfig: () => Promise<boolean>;
|
|
142
|
+
/** Resolver called by `<PageConfigDialog>` on apply/cancel. */
|
|
143
|
+
resolvePageConfigRequest: (applied: boolean) => void;
|
|
144
|
+
imageCaptionRequest: ImageCaptionRequest | null;
|
|
145
|
+
/** Open the image-caption dialog with the current caption + alignment pre-filled. */
|
|
146
|
+
requestImageCaption: (initialCaption?: string, initialAlign?: CaptionAlign) => Promise<ImageCaptionResult | null>;
|
|
147
|
+
/** Resolver called by `<ImageCaptionDialog>` on apply/cancel. */
|
|
148
|
+
resolveImageCaptionRequest: (result: ImageCaptionResult | null) => void;
|
|
149
|
+
/** True iff the focus position lies inside a table cell. */
|
|
150
|
+
isInTable: () => boolean;
|
|
151
|
+
/** Returns `{blockIndex, row, col}` for the focus position, or null when outside a table. */
|
|
152
|
+
getTableLocation: () => TableLocation | null;
|
|
153
|
+
insertTable: (rows: number, cols: number) => void;
|
|
154
|
+
/** Removes the table at `blockIndex`. Defaults to the current focus table. */
|
|
155
|
+
deleteTable: (blockIndex?: number) => void;
|
|
156
|
+
insertRowAbove: () => void;
|
|
157
|
+
insertRowBelow: () => void;
|
|
158
|
+
deleteCurrentRow: () => void;
|
|
159
|
+
insertColumnLeft: () => void;
|
|
160
|
+
insertColumnRight: () => void;
|
|
161
|
+
deleteCurrentColumn: () => void;
|
|
162
|
+
moveToNextCell: () => void;
|
|
163
|
+
moveToPrevCell: () => void;
|
|
164
|
+
mergeRight: () => boolean;
|
|
165
|
+
mergeDown: () => boolean;
|
|
166
|
+
splitCell: () => boolean;
|
|
167
|
+
setColumnWidth: (blockIndex: number, col: number, widthPx: number) => void;
|
|
168
|
+
/** Returns the table rectangle if the current selection is a multi-cell range, else null. */
|
|
169
|
+
getTableRect: () => TableRect | null;
|
|
170
|
+
/** True when there's a rectangular table-cell selection. */
|
|
171
|
+
hasTableRectSelection: () => boolean;
|
|
172
|
+
/** Merge every cell in the rectangular selection. Returns true on success. */
|
|
173
|
+
mergeSelection: () => boolean;
|
|
174
|
+
insertImage: (embed: ImageEmbed) => void;
|
|
175
|
+
setImageAttrs: (blockIndex: number, offset: number, partial: Partial<ImageEmbed>, cellIndex?: number, newOffset?: number) => void;
|
|
176
|
+
/** Read a File (image) and insert it inline at the caret as a data-URL embed. */
|
|
177
|
+
insertImageFromFile: (file: File) => Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Returns the embed currently selected (single 1-char range over an image),
|
|
180
|
+
* or null when no embed is solely selected.
|
|
181
|
+
*/
|
|
182
|
+
getSelectedEmbed: () => {
|
|
183
|
+
blockIndex: number;
|
|
184
|
+
offset: number;
|
|
185
|
+
cellIndex?: number;
|
|
186
|
+
embed: ImageEmbed;
|
|
187
|
+
} | null;
|
|
188
|
+
}
|
|
189
|
+
declare function useEditor(opts?: UseEditorOptions): UseEditorResult;
|
|
190
|
+
|
|
191
|
+
interface PageRenderContext {
|
|
192
|
+
pageNumber: number;
|
|
193
|
+
pageCount: number;
|
|
194
|
+
}
|
|
195
|
+
type PageRenderProp = (ctx: PageRenderContext) => ReactNode;
|
|
196
|
+
interface EditorProps {
|
|
197
|
+
editor?: UseEditorResult;
|
|
198
|
+
className?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Page geometry override. When **omitted**, the editor reads page settings
|
|
201
|
+
* from `editor.doc` (the `docSettings` Y.Map populated via `setPageSettings`
|
|
202
|
+
* or `import-docx`). When **set**, this value wins for rendering — but is
|
|
203
|
+
* NOT written back to the Y.Doc (view-only override; useful when the host
|
|
204
|
+
* app must pin a size regardless of doc state, e.g. a prova generator).
|
|
205
|
+
* Pass **null** to disable pagination entirely (flat flow).
|
|
206
|
+
*/
|
|
207
|
+
pageGeometry?: PageGeometry | null;
|
|
208
|
+
/**
|
|
209
|
+
* Custom page header. Receives `{ pageNumber, pageCount }` and renders inside
|
|
210
|
+
* the top margin of every page (above the content area). Pass `null` to omit.
|
|
211
|
+
*/
|
|
212
|
+
renderPageHeader?: PageRenderProp | null;
|
|
213
|
+
/**
|
|
214
|
+
* Custom page footer. Receives `{ pageNumber, pageCount }`. Defaults to
|
|
215
|
+
* `"<n> / <total>"` centered. Pass `null` to omit.
|
|
216
|
+
*/
|
|
217
|
+
renderPageFooter?: PageRenderProp | null;
|
|
218
|
+
}
|
|
219
|
+
declare function Editor({ editor: providedEditor, className, pageGeometry, renderPageHeader, renderPageFooter, }: EditorProps): JSX.Element;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Modal dialog for entering a link URL. Replaces the bare `window.prompt`
|
|
223
|
+
* previously wired to Cmd+K and the toolbar link button. Renders only when
|
|
224
|
+
* `editor.linkRequest` is set; consumes that state via `resolveLinkRequest`.
|
|
225
|
+
*
|
|
226
|
+
* Uses the native `<dialog>` element for focus trapping, ESC handling, and the
|
|
227
|
+
* browser-provided backdrop.
|
|
228
|
+
*/
|
|
229
|
+
declare function LinkDialog(): JSX.Element | null;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Modal dialog to configure page size and margins. Mirrors `LinkDialog` —
|
|
233
|
+
* driven by `editor.pageConfigRequest`. On apply, writes via
|
|
234
|
+
* `editor.setPageSettings`; on cancel, no write.
|
|
235
|
+
*/
|
|
236
|
+
declare function PageConfigDialog(): JSX.Element | null;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Modal dialog for entering an image caption. Mirrors `LinkDialog` — driven by
|
|
240
|
+
* `editor.imageCaptionRequest`. Empty input is treated as "remove caption".
|
|
241
|
+
*
|
|
242
|
+
* The only configurable property besides the text itself is the caption's
|
|
243
|
+
* horizontal alignment (left / center / right). Word-style figure-caption
|
|
244
|
+
* controls stop here intentionally — see `ImageEmbed.captionAlign`.
|
|
245
|
+
*/
|
|
246
|
+
declare function ImageCaptionDialog(): JSX.Element | null;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Reactive snapshot of an `EditorDocument`'s page settings. Re-renders the
|
|
250
|
+
* component when `docSettings` changes (either locally via `setPageSettings`
|
|
251
|
+
* or remotely via Y.js sync).
|
|
252
|
+
*
|
|
253
|
+
* Caches the snapshot reference to satisfy `useSyncExternalStore`'s stable-
|
|
254
|
+
* identity requirement — `getPageSettings` returns a fresh object each call,
|
|
255
|
+
* which would loop the store forever without this caching.
|
|
256
|
+
*/
|
|
257
|
+
declare function usePageSettings(doc: EditorDocument): PageSettings;
|
|
258
|
+
|
|
259
|
+
interface NodeViewFragment {
|
|
260
|
+
index: number;
|
|
261
|
+
start: number;
|
|
262
|
+
end: number;
|
|
263
|
+
}
|
|
264
|
+
interface NodeViewProps {
|
|
265
|
+
block: SerializedBlock;
|
|
266
|
+
index: number;
|
|
267
|
+
/** Optional nested content. Used by `listItem` to host the nested <ol>/<ul>. */
|
|
268
|
+
children?: ReactNode;
|
|
269
|
+
/** Optional fragment slice — when present, the rendered DOM covers only chars [start, end). */
|
|
270
|
+
fragment?: NodeViewFragment;
|
|
271
|
+
/**
|
|
272
|
+
* Optional table-row fragmentation slice for Sub-phase 4.6. When set, only
|
|
273
|
+
* the rows in `[rowStart, rowEnd)` are rendered, and the resize overlay is
|
|
274
|
+
* suppressed except on the FIRST fragment.
|
|
275
|
+
*/
|
|
276
|
+
tableFragment?: {
|
|
277
|
+
index: number;
|
|
278
|
+
rowStart: number;
|
|
279
|
+
rowEnd: number;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
declare function NodeView({ block, index, children, fragment, tableFragment }: NodeViewProps): JSX.Element;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Convert a Y.Text delta into ReactNodes.
|
|
286
|
+
*
|
|
287
|
+
* Wrap order (innermost → outermost): bold → italic → underline → strike → font/color span → link.
|
|
288
|
+
* Semantic tags carry bold/italic/underline/strike; color and fonts go on a styled <span>.
|
|
289
|
+
*
|
|
290
|
+
* Keys are stable on `${blockIndex}-${runningOffset}` so React reconciliation
|
|
291
|
+
* survives split/merge of runs under collaboration.
|
|
292
|
+
*/
|
|
293
|
+
declare function renderInline(delta: DeltaOp[], keyPrefix: number | string): ReactNode;
|
|
294
|
+
|
|
295
|
+
interface EditorProviderProps {
|
|
296
|
+
editor: UseEditorResult;
|
|
297
|
+
children: ReactNode;
|
|
298
|
+
}
|
|
299
|
+
declare function EditorProvider({ editor, children }: EditorProviderProps): JSX.Element;
|
|
300
|
+
declare function useEditorContext(): UseEditorResult;
|
|
301
|
+
|
|
302
|
+
interface ToolbarProps {
|
|
303
|
+
className?: string;
|
|
304
|
+
}
|
|
305
|
+
declare function Toolbar({ className }: ToolbarProps): JSX.Element;
|
|
306
|
+
|
|
307
|
+
/** Total number of inserted characters. Embeds (non-string inserts) count as 1. */
|
|
308
|
+
declare function deltaLength(delta: DeltaOp[]): number;
|
|
309
|
+
/**
|
|
310
|
+
* Return the subset of `delta` covering `[start, end)` characters. Attributes
|
|
311
|
+
* are preserved on each intersecting op. Embeds are atomic length-1 ops:
|
|
312
|
+
* included whole if their slot intersects the range.
|
|
313
|
+
*/
|
|
314
|
+
declare function sliceDelta(delta: DeltaOp[], start: number, end: number): DeltaOp[];
|
|
315
|
+
|
|
316
|
+
declare function findBlockIndex(node: Node, root: HTMLElement): number | null;
|
|
317
|
+
/**
|
|
318
|
+
* Returns the first DOM element for `blockIndex`. For a fragmented block this
|
|
319
|
+
* is the first fragment. Prefer `getFragmentForOffset` when you need the
|
|
320
|
+
* fragment that contains a specific offset.
|
|
321
|
+
*/
|
|
322
|
+
declare function getBlockElement(root: HTMLElement, blockIndex: number): HTMLElement | null;
|
|
323
|
+
/**
|
|
324
|
+
* Returns the DOM element responsible for `offset` inside `blockIndex`.
|
|
325
|
+
* - Unfragmented block (single element): returns it.
|
|
326
|
+
* - Fragmented block: returns the fragment whose `[start, end)` covers `offset`.
|
|
327
|
+
* For the end-of-block offset, returns the last fragment.
|
|
328
|
+
*/
|
|
329
|
+
declare function getFragmentForOffset(root: HTMLElement, blockIndex: number, offset: number): HTMLElement | null;
|
|
330
|
+
/**
|
|
331
|
+
* Returns the `<td>` element for `(blockIndex, cellIndex)`. Tables are
|
|
332
|
+
* unfragmented in 4.1 so there's at most one block element to search inside.
|
|
333
|
+
*/
|
|
334
|
+
declare function getCellElement(root: HTMLElement, blockIndex: number, cellIndex: number): HTMLElement | null;
|
|
335
|
+
/**
|
|
336
|
+
* Sum length of all text nodes plus 1 for each image embed inside `container`,
|
|
337
|
+
* up to the boundary defined by `(node, offsetInNode)`. Mirrors how the model
|
|
338
|
+
* counts characters: embeds are atomic length-1 inserts.
|
|
339
|
+
*/
|
|
340
|
+
declare function textOffsetWithin(container: HTMLElement, node: Node, offsetInNode: number): number;
|
|
341
|
+
declare function readDomSelection(root: HTMLElement): Selection | null;
|
|
342
|
+
declare function selectionsEqual(a: Selection, b: Selection): boolean;
|
|
343
|
+
/**
|
|
344
|
+
* `true` when the selection is a rectangular table-cell range — both endpoints
|
|
345
|
+
* inside cells of the same block, addressing different cells. The visual
|
|
346
|
+
* highlight comes from CSS classes applied by `<TableView>`; we deliberately
|
|
347
|
+
* leave the native DOM selection empty so the browser doesn't draw a confusing
|
|
348
|
+
* cross-`<td>` range.
|
|
349
|
+
*/
|
|
350
|
+
declare function isTableRectSelection(sel: Selection): boolean;
|
|
351
|
+
declare function applyDomSelection(root: HTMLElement, modelSel: Selection): void;
|
|
352
|
+
|
|
353
|
+
export { A4_PAGE, type BlockFragment, type CaptionAlign, Editor, type EditorProps, EditorProvider, type EditorProviderProps, ImageCaptionDialog, type ImageCaptionRequest, type ImageCaptionResult, LinkDialog, type LinkRequest, NodeView, type NodeViewFragment, PageConfigDialog, type PageConfigRequest, type PageEntry, type PageGeometry, type PageLayout, type PageRenderContext, type PageRenderProp, type PageSlot, type PendingMarks, type TableRowFragment, Toolbar, type ToolbarProps, type UseEditorOptions, type UseEditorResult, applyDomSelection, contentHeight, contentWidth, defaultPageLayout, deltaLength, findBlockIndex, getBlockElement, getCellElement, getFragmentForOffset, isTableRectSelection, readDomSelection, renderInline, selectionsEqual, sliceDelta, textOffsetWithin, useEditor, useEditorContext, usePageLayout, usePageSettings };
|