smartrte-react 0.3.5 → 1.0.0-beta.2
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 +14 -0
- package/README.md +106 -14
- package/dist/adapters/domChecklistCommandBridge.js +1 -1
- package/dist/adapters/domCommandBridge.d.ts +2 -2
- package/dist/adapters/domCommandBridge.js +2 -2
- package/dist/adapters/domInlineAtomCommandBridge.d.ts +10 -9
- package/dist/adapters/domInlineAtomCommandBridge.js +154 -50
- package/dist/adapters/domInlineImageCommandBridge.d.ts +7 -7
- package/dist/adapters/domInlineImageCommandBridge.js +6 -72
- package/dist/adapters/domMoveCommandBridge.js +1 -1
- package/dist/adapters/domSelectionBridge.d.ts +3 -3
- package/dist/adapters/domSmartDocument.d.ts +4 -4
- package/dist/adapters/domSmartDocument.js +2 -2
- package/dist/adapters/domTableCommandBridge.d.ts +42 -16
- package/dist/adapters/domTableCommandBridge.js +203 -173
- package/dist/adapters/internalFlags.d.ts +0 -8
- package/dist/adapters/internalFlags.js +0 -24
- package/dist/adapters/legacyListShadowComparator.d.ts +1 -1
- package/dist/adapters/legacyListShadowComparator.js +1 -1
- package/dist/adapters/pdfPrint.d.ts +8 -0
- package/dist/adapters/pdfPrint.js +22 -0
- package/dist/adapters/shadowMode.d.ts +3 -3
- package/dist/adapters/shadowMode.js +1 -1
- package/dist/canonicalEditorRuntime.d.ts +53 -1
- package/dist/canonicalEditorRuntime.js +61 -4
- package/dist/capabilityPresets.d.ts +27 -0
- package/dist/capabilityPresets.js +21 -0
- package/dist/collabTransport.d.ts +66 -0
- package/dist/collabTransport.js +1 -0
- package/dist/commentProvider.d.ts +24 -0
- package/dist/commentProvider.js +1 -0
- package/dist/components/CanonicalAuthorityEditor.d.ts +35 -1
- package/dist/components/CanonicalAuthorityEditor.js +1086 -112
- package/dist/components/ClassicEditorAuthority.d.ts +93 -22
- package/dist/components/ClassicEditorAuthority.js +11 -43
- package/dist/components/ColorPickerPopover.d.ts +39 -0
- package/dist/components/ColorPickerPopover.js +285 -0
- package/dist/components/CommentMarkers.d.ts +25 -0
- package/dist/components/CommentMarkers.js +105 -0
- package/dist/components/CommentThreadPanel.d.ts +26 -0
- package/dist/components/CommentThreadPanel.js +47 -0
- package/dist/components/ContextMenu.d.ts +23 -0
- package/dist/components/ContextMenu.js +141 -0
- package/dist/components/FormulaLibraryPopover.d.ts +16 -0
- package/dist/components/FormulaLibraryPopover.js +107 -0
- package/dist/components/LinkEditorPopover.d.ts +3 -1
- package/dist/components/LinkEditorPopover.js +22 -6
- package/dist/components/MediaManager.d.ts +4 -20
- package/dist/components/MediaManager.js +35 -6
- package/dist/components/MediaOverlay.d.ts +36 -0
- package/dist/components/MediaOverlay.js +163 -0
- package/dist/components/SpecialCharacterPopover.d.ts +18 -0
- package/dist/components/SpecialCharacterPopover.js +99 -0
- package/dist/components/StructuralSuggestionMarkers.d.ts +20 -0
- package/dist/components/StructuralSuggestionMarkers.js +56 -0
- package/dist/components/SuggestionPanel.d.ts +26 -0
- package/dist/components/SuggestionPanel.js +33 -0
- package/dist/components/TableBorderPopover.d.ts +59 -0
- package/dist/components/TableBorderPopover.js +147 -0
- package/dist/components/TableResizeHandles.d.ts +25 -0
- package/dist/components/TableResizeHandles.js +301 -0
- package/dist/components/TableSizePickerPopover.d.ts +16 -0
- package/dist/components/TableSizePickerPopover.js +126 -0
- package/dist/components/ToolbarPrimitives.d.ts +129 -0
- package/dist/components/ToolbarPrimitives.js +146 -0
- package/dist/components/VersionHistoryPanel.d.ts +9 -0
- package/dist/components/VersionHistoryPanel.js +157 -0
- package/dist/formulaLibrary.d.ts +31 -0
- package/dist/formulaLibrary.js +90 -0
- package/dist/index.d.ts +22 -24
- package/dist/index.js +14 -11
- package/dist/mediaManagerAdapter.d.ts +12 -0
- package/dist/mediaManagerAdapter.js +34 -0
- package/dist/mediaProvider.d.ts +21 -1
- package/dist/specialCharacters.d.ts +22 -0
- package/dist/specialCharacters.js +86 -0
- package/dist/standalone/classic-editor-embed.js +19 -12
- package/dist/suggestionProvider.d.ts +17 -0
- package/dist/suggestionProvider.js +1 -0
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +150 -20
- package/dist/versionProvider.d.ts +29 -0
- package/dist/versionProvider.js +1 -0
- package/package.json +28 -14
- package/dist/adapters/canonicalInlineCommandBridge.d.ts +0 -15
- package/dist/adapters/canonicalInlineCommandBridge.js +0 -269
- package/dist/adapters/canonicalListCommandBridge.d.ts +0 -26
- package/dist/adapters/canonicalListCommandBridge.js +0 -430
- package/dist/adapters/coreBold.d.ts +0 -5
- package/dist/adapters/coreBold.js +0 -3
- package/dist/adapters/documentFormats.d.ts +0 -26
- package/dist/adapters/documentFormats.js +0 -49
- package/dist/adapters/docxFormat.d.ts +0 -5
- package/dist/adapters/docxFormat.js +0 -272
- package/dist/adapters/domBlockCommandBridge.d.ts +0 -19
- package/dist/adapters/domBlockCommandBridge.js +0 -79
- package/dist/adapters/inlineMarkCoreExecution.d.ts +0 -9
- package/dist/adapters/inlineMarkCoreExecution.js +0 -25
- package/dist/adapters/pdfFormat.d.ts +0 -23
- package/dist/adapters/pdfFormat.js +0 -204
- package/dist/adapters/portableDocxAtoms.d.ts +0 -4
- package/dist/adapters/portableDocxAtoms.js +0 -58
- package/dist/adapters/styledDocxFormat.d.ts +0 -8
- package/dist/adapters/styledDocxFormat.js +0 -190
- package/dist/builtInFormatDefinitions.d.ts +0 -5
- package/dist/builtInFormatDefinitions.js +0 -82
- package/dist/canonicalAuthorityFlag.d.ts +0 -32
- package/dist/canonicalAuthorityFlag.js +0 -49
- package/dist/canonicalClipboardRuntime.d.ts +0 -15
- package/dist/canonicalClipboardRuntime.js +0 -76
- package/dist/components/ClassicEditor.d.ts +0 -76
- package/dist/components/ClassicEditor.js +0 -6729
- package/dist/editorController.d.ts +0 -78
- package/dist/editorController.js +0 -298
- package/dist/formatFidelity.d.ts +0 -14
- package/dist/formatFidelity.js +0 -107
- package/dist/formatRuntime.d.ts +0 -50
- package/dist/formatRuntime.js +0 -20
- package/dist/pluginRuntime.d.ts +0 -68
- package/dist/pluginRuntime.js +0 -78
- package/dist/standalone/editor.js +0 -1068
- package/dist/test-harness/atomShadowComparator.d.ts +0 -14
- package/dist/test-harness/atomShadowComparator.js +0 -63
- package/dist/test-harness/blockShadowComparator.d.ts +0 -30
- package/dist/test-harness/blockShadowComparator.js +0 -140
- package/dist/test-harness/clipboardShadowComparator.d.ts +0 -12
- package/dist/test-harness/clipboardShadowComparator.js +0 -46
- package/dist/test-harness/inlineShadowComparator.d.ts +0 -32
- package/dist/test-harness/inlineShadowComparator.js +0 -152
- package/dist/test-harness/legacyAtomEngine.d.ts +0 -10
- package/dist/test-harness/legacyAtomEngine.js +0 -15
- package/dist/test-harness/legacyBlockEngine.d.ts +0 -31
- package/dist/test-harness/legacyBlockEngine.js +0 -31
- package/dist/test-harness/legacyClipboardEngine.d.ts +0 -14
- package/dist/test-harness/legacyClipboardEngine.js +0 -67
- package/dist/test-harness/legacyInlineEngine.d.ts +0 -19
- package/dist/test-harness/legacyInlineEngine.js +0 -49
- package/dist/test-harness/legacyTableEngine.d.ts +0 -12
- package/dist/test-harness/legacyTableEngine.js +0 -25
- package/dist/test-harness/tableShadowComparator.d.ts +0 -29
- package/dist/test-harness/tableShadowComparator.js +0 -101
package/dist/theme.js
CHANGED
|
@@ -27,6 +27,11 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
27
27
|
--srte-accent-bg: rgba(2, 132, 199, 0.12);
|
|
28
28
|
--srte-danger: #dc2626;
|
|
29
29
|
--srte-primary: #2563eb;
|
|
30
|
+
/* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's
|
|
31
|
+
4.5:1 for normal text - used only for the pressed toolbar-button state
|
|
32
|
+
(see .srte-tool-button[aria-pressed="true"]), not a replacement for
|
|
33
|
+
--srte-primary generally. */
|
|
34
|
+
--srte-primary-pressed: #1d4ed8;
|
|
30
35
|
--srte-surface-subtle: #f3f4f6;
|
|
31
36
|
--srte-on-primary: #ffffff;
|
|
32
37
|
--srte-cancel-bg: #f3f4f6;
|
|
@@ -60,6 +65,7 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
60
65
|
--srte-accent-bg: rgba(56, 189, 248, 0.16);
|
|
61
66
|
--srte-danger: #ef4444;
|
|
62
67
|
--srte-primary: #3b82f6;
|
|
68
|
+
--srte-primary-pressed: var(--srte-primary);
|
|
63
69
|
--srte-surface-subtle: #333333;
|
|
64
70
|
--srte-on-primary: #ffffff;
|
|
65
71
|
--srte-cancel-bg: #333333;
|
|
@@ -71,6 +77,18 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
71
77
|
font-family: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
72
78
|
container: srte-editor / inline-size;
|
|
73
79
|
}
|
|
80
|
+
.srte-canonical-authority > .srte-editor[contenteditable] {
|
|
81
|
+
width: 100%;
|
|
82
|
+
padding: 16px 20px;
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
border: 1px solid var(--srte-border);
|
|
85
|
+
border-radius: 0 0 var(--srte-radius) var(--srte-radius);
|
|
86
|
+
outline: none;
|
|
87
|
+
background: var(--srte-canvas);
|
|
88
|
+
color: var(--srte-foreground);
|
|
89
|
+
caret-color: var(--srte-foreground);
|
|
90
|
+
line-height: 1.6;
|
|
91
|
+
}
|
|
74
92
|
.srte-toolbar {
|
|
75
93
|
display: flex;
|
|
76
94
|
flex-wrap: wrap;
|
|
@@ -134,7 +152,12 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
134
152
|
}
|
|
135
153
|
.srte-tool-button.srte-active,
|
|
136
154
|
.srte-tool-button[aria-pressed="true"] {
|
|
137
|
-
|
|
155
|
+
/* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,
|
|
156
|
+
just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -
|
|
157
|
+
a dedicated, darker pressed-state color keeps the same hue family
|
|
158
|
+
while clearing the threshold, without changing --srte-primary's other,
|
|
159
|
+
already-compliant usages (e.g. against solid backgrounds). */
|
|
160
|
+
color: var(--srte-primary-pressed);
|
|
138
161
|
background: var(--srte-accent-bg);
|
|
139
162
|
border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);
|
|
140
163
|
}
|
|
@@ -241,6 +264,56 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
241
264
|
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="3"] > li::marker {
|
|
242
265
|
content: counter(list-item, lower-roman) ") ";
|
|
243
266
|
}
|
|
267
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="0"] > li::marker {
|
|
268
|
+
content: counter(list-item, decimal) ". ";
|
|
269
|
+
}
|
|
270
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="1"] > li::marker {
|
|
271
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
272
|
+
}
|
|
273
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="2"] > li::marker,
|
|
274
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="3"] > li::marker {
|
|
275
|
+
content: counter(list-item, lower-roman) ". ";
|
|
276
|
+
}
|
|
277
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="0"] > li::marker {
|
|
278
|
+
content: counter(list-item, upper-alpha) ". ";
|
|
279
|
+
}
|
|
280
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="1"] > li::marker {
|
|
281
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
282
|
+
}
|
|
283
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="2"] > li::marker,
|
|
284
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="3"] > li::marker {
|
|
285
|
+
content: counter(list-item, lower-roman) ". ";
|
|
286
|
+
}
|
|
287
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="0"] > li::marker {
|
|
288
|
+
content: counter(list-item, upper-roman) ". ";
|
|
289
|
+
}
|
|
290
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="1"] > li::marker {
|
|
291
|
+
content: counter(list-item, upper-alpha) ". ";
|
|
292
|
+
}
|
|
293
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="2"] > li::marker,
|
|
294
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="3"] > li::marker {
|
|
295
|
+
content: counter(list-item, decimal) ". ";
|
|
296
|
+
}
|
|
297
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="0"] > li::marker {
|
|
298
|
+
content: counter(list-item, decimal-leading-zero) ". ";
|
|
299
|
+
}
|
|
300
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="1"] > li::marker {
|
|
301
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
302
|
+
}
|
|
303
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="2"] > li::marker,
|
|
304
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="3"] > li::marker {
|
|
305
|
+
content: counter(list-item, lower-roman) ". ";
|
|
306
|
+
}
|
|
307
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="0"] > li::marker {
|
|
308
|
+
content: "● ";
|
|
309
|
+
}
|
|
310
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="1"] > li::marker {
|
|
311
|
+
content: "○ ";
|
|
312
|
+
}
|
|
313
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="2"] > li::marker,
|
|
314
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="3"] > li::marker {
|
|
315
|
+
content: "■ ";
|
|
316
|
+
}
|
|
244
317
|
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-outline"] > li::marker {
|
|
245
318
|
content: counters(list-item, ".") ". ";
|
|
246
319
|
}
|
|
@@ -267,11 +340,26 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
267
340
|
justify-content: center;
|
|
268
341
|
width: 1.1em;
|
|
269
342
|
height: 1.6em;
|
|
343
|
+
min-width: 0;
|
|
344
|
+
box-sizing: border-box;
|
|
345
|
+
padding: 0;
|
|
346
|
+
border: 0;
|
|
347
|
+
border-radius: 0;
|
|
348
|
+
background: transparent;
|
|
349
|
+
color: inherit;
|
|
350
|
+
font: inherit;
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
appearance: none;
|
|
353
|
+
outline: none;
|
|
270
354
|
margin: 0;
|
|
271
355
|
line-height: 1.6;
|
|
272
356
|
vertical-align: top;
|
|
273
357
|
position: relative;
|
|
274
358
|
}
|
|
359
|
+
.srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]:focus-visible {
|
|
360
|
+
outline: 2px solid var(--srte-ring);
|
|
361
|
+
outline-offset: 2px;
|
|
362
|
+
}
|
|
275
363
|
.srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]::before {
|
|
276
364
|
content: "";
|
|
277
365
|
width: .9em;
|
|
@@ -307,25 +395,19 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
307
395
|
border-color: var(--srte-ring) !important;
|
|
308
396
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);
|
|
309
397
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
overflow-y: auto;
|
|
324
|
-
overscroll-behavior: contain;
|
|
325
|
-
-webkit-overflow-scrolling: touch;
|
|
326
|
-
}
|
|
327
|
-
.srte-menu-item { height: 40px; }
|
|
328
|
-
}
|
|
398
|
+
/*
|
|
399
|
+
* @container (element width), not @media (viewport width): a host can embed
|
|
400
|
+
* the editor at any fraction of the browser window - a split pane next to a
|
|
401
|
+
* preview panel is the case that surfaced this (docs/bugs/
|
|
402
|
+
* toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A
|
|
403
|
+
* plain @media breakpoint here would fire based on the *window*, wrongly
|
|
404
|
+
* switching tiers for an editor instance that's actually much narrower (or
|
|
405
|
+
* wider) than the window - .srte-editor already establishes its own
|
|
406
|
+
* container: srte-editor / inline-size (see the .srte-editor rule above),
|
|
407
|
+
* so @container always reflects this specific instance's real rendered width regardless of host
|
|
408
|
+
* layout, with no @media fallback needed (container queries have full
|
|
409
|
+
* support across this project's three target engines).
|
|
410
|
+
*/
|
|
329
411
|
@container srte-editor (max-width: 639px) {
|
|
330
412
|
.srte-toolbar { gap: 3px; padding: 6px; }
|
|
331
413
|
.srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }
|
|
@@ -345,6 +427,46 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
345
427
|
}
|
|
346
428
|
.srte-menu-item { height: 40px; }
|
|
347
429
|
}
|
|
430
|
+
/*
|
|
431
|
+
* Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):
|
|
432
|
+
* below this, the single 639px breakpoint above was this system's ONLY
|
|
433
|
+
* threshold - every dropdown-grouped tool stayed hidden inside its dropdown
|
|
434
|
+
* at every width from 640px up to and past a 2200px+ desktop, since nothing
|
|
435
|
+
* ever measured or scaled with the extra room. A handful of tools frequent
|
|
436
|
+
* enough to matter once there's genuinely spare room (Superscript,
|
|
437
|
+
* Subscript, Text colour, Background colour, Font size, Font family, Remove
|
|
438
|
+
* link, Insert formula, Special characters) get a standalone always-visible
|
|
439
|
+
* ToolbarButton copy (data-srte-wide-promote, rendered directly in the
|
|
440
|
+
* toolbar row) that appears past this breakpoint, while their existing
|
|
441
|
+
* dropdown/mobile-menu ToolbarMenuItem copy (the exact same
|
|
442
|
+
* data-srte-wide-promote attribute, on the .srte-menu-item element instead)
|
|
443
|
+
* hides so the tool isn't offered twice. Below this breakpoint - including
|
|
444
|
+
* all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps
|
|
445
|
+
* today's grouped-dropdown layout unchanged - the standalone copy stays
|
|
446
|
+
* hidden and the dropdown copy is what's reachable.
|
|
447
|
+
*
|
|
448
|
+
* 1440px, not 1280px: Playwright's own default test viewport is exactly
|
|
449
|
+
* 1280x720, and the vast majority of this suite's toolbar interactions run
|
|
450
|
+
* at that default without ever calling page.setViewportSize - a 1280px
|
|
451
|
+
* threshold would have flipped nearly every existing toolbar test's
|
|
452
|
+
* dropdown-item locators (role=menuitem) over to CSS display:none out from
|
|
453
|
+
* under them. 1440px clears that default with room to spare while still
|
|
454
|
+
* comfortably covering "wide desktop" (the ~2264px width the original
|
|
455
|
+
* report was filed against).
|
|
456
|
+
*
|
|
457
|
+
* @container only, no @media fallback - same reasoning as the mobile tier
|
|
458
|
+
* above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):
|
|
459
|
+
* a plain @media(min-width:1440px) fired whenever the *browser window* was
|
|
460
|
+
* wide, even when this editor instance was embedded in a much narrower
|
|
461
|
+
* split pane - promoting 9 extra buttons into a group with no room for
|
|
462
|
+
* them, which squeezed/overlapped the group's existing buttons instead of
|
|
463
|
+
* wrapping cleanly.
|
|
464
|
+
*/
|
|
465
|
+
.srte-tool-button[data-srte-wide-promote="true"] { display: none; }
|
|
466
|
+
@container srte-editor (min-width: 1440px) {
|
|
467
|
+
.srte-tool-button[data-srte-wide-promote="true"] { display: inline-flex; }
|
|
468
|
+
.srte-menu-item[data-srte-wide-promote="true"] { display: none; }
|
|
469
|
+
}
|
|
348
470
|
.srte-editor [contenteditable] blockquote {
|
|
349
471
|
border-left: 4px solid var(--srte-accent);
|
|
350
472
|
margin: 0.75em 0;
|
|
@@ -419,6 +541,14 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
419
541
|
border: 1px solid var(--srte-border);
|
|
420
542
|
vertical-align: top;
|
|
421
543
|
}
|
|
544
|
+
.srte-editor [contenteditable] [data-smart-cell-selected="true"] {
|
|
545
|
+
background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));
|
|
546
|
+
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);
|
|
547
|
+
}
|
|
548
|
+
.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {
|
|
549
|
+
background: transparent;
|
|
550
|
+
color: inherit;
|
|
551
|
+
}
|
|
422
552
|
.srte-editor [contenteditable] th {
|
|
423
553
|
background: var(--srte-surface-subtle);
|
|
424
554
|
font-weight: 600;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DocumentVersion } from "smartrte-core/foundation";
|
|
2
|
+
/**
|
|
3
|
+
* The listing-cheap projection of a `DocumentVersion` - a host's backend
|
|
4
|
+
* shouldn't have to ship every saved version's full document body just to
|
|
5
|
+
* render a history list. Mirrors `MediaProvider.search` returning
|
|
6
|
+
* `MediaItem` (metadata) rather than raw asset bytes.
|
|
7
|
+
*/
|
|
8
|
+
export interface VersionListEntry {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly label?: string;
|
|
11
|
+
readonly createdAt: number;
|
|
12
|
+
readonly authorId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Host-owned version-storage boundary, mirroring `MediaProvider`'s shape
|
|
16
|
+
* and its "the editor never persists anything itself" contract: the host
|
|
17
|
+
* backend is the source of truth, and `save`/`list`/`load`/`remove` are
|
|
18
|
+
* the entire boundary. There is deliberately no shipped default
|
|
19
|
+
* implementation, exactly like `MediaProvider` - when a host doesn't
|
|
20
|
+
* supply one, the version-history feature is simply unavailable, the same
|
|
21
|
+
* way the media toolbar buttons are disabled when `mediaProvider` is
|
|
22
|
+
* absent.
|
|
23
|
+
*/
|
|
24
|
+
export interface VersionProvider {
|
|
25
|
+
save(version: DocumentVersion): Promise<VersionListEntry>;
|
|
26
|
+
list(): Promise<readonly VersionListEntry[]>;
|
|
27
|
+
load(id: string): Promise<DocumentVersion>;
|
|
28
|
+
remove(id: string): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartrte-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./standalone/classic-editor-embed": {
|
|
15
|
+
"types": "./dist/standalone/classic-editor-embed.d.ts",
|
|
16
|
+
"import": "./dist/standalone/classic-editor-embed.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
9
19
|
"files": [
|
|
10
20
|
"dist",
|
|
11
21
|
"!dist/**/*.test.js",
|
|
12
22
|
"!dist/**/*.test.d.ts",
|
|
23
|
+
"!dist/test-harness/**",
|
|
13
24
|
"README.md",
|
|
14
25
|
"CHANGELOG.md"
|
|
15
26
|
],
|
|
@@ -41,16 +52,6 @@
|
|
|
41
52
|
},
|
|
42
53
|
"author": "Smart RTE Contributors",
|
|
43
54
|
"license": "MIT",
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsc -p tsconfig.json",
|
|
46
|
-
"prepublishOnly": "npm run build",
|
|
47
|
-
"dev": "pnpm build",
|
|
48
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
49
|
-
"test": "vitest run --passWithNoTests",
|
|
50
|
-
"storybook": "storybook dev -p 6006",
|
|
51
|
-
"build-storybook": "storybook build",
|
|
52
|
-
"e2e": "playwright test --pass-with-no-tests"
|
|
53
|
-
},
|
|
54
55
|
"publishConfig": {
|
|
55
56
|
"access": "public"
|
|
56
57
|
},
|
|
@@ -60,16 +61,18 @@
|
|
|
60
61
|
"react-dom": ">=18"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
64
|
+
"@axe-core/playwright": "^4.12.1",
|
|
63
65
|
"@playwright/test": "^1.48.2",
|
|
64
66
|
"@storybook/addon-essentials": "^8.6.14",
|
|
65
67
|
"@storybook/react": "^8.6.14",
|
|
66
68
|
"@storybook/react-vite": "^8.6.14",
|
|
69
|
+
"@types/node": "^22",
|
|
67
70
|
"@types/pdfjs-dist": "^2.10.377",
|
|
68
71
|
"@types/react": "^18.3.4",
|
|
69
72
|
"@types/react-dom": "^18.3.0",
|
|
73
|
+
"jsdom": "^25.0.1",
|
|
70
74
|
"react": "18.3.1",
|
|
71
75
|
"react-dom": "18.3.1",
|
|
72
|
-
"jsdom": "^25.0.1",
|
|
73
76
|
"storybook": "^8.6.14",
|
|
74
77
|
"typescript": "^5.6.2",
|
|
75
78
|
"vite": "^5.4.8",
|
|
@@ -77,8 +80,19 @@
|
|
|
77
80
|
},
|
|
78
81
|
"dependencies": {
|
|
79
82
|
"jszip": "^3.10.1",
|
|
83
|
+
"katex": "^0.18.4",
|
|
84
|
+
"lucide-react": "^1.35.0",
|
|
80
85
|
"mammoth": "^1.11.0",
|
|
81
86
|
"pdfjs-dist": "^5.4.530",
|
|
82
|
-
"smartrte-core": "^0.
|
|
87
|
+
"smartrte-core": "^1.0.0-beta.1"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
91
|
+
"dev": "pnpm build",
|
|
92
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
93
|
+
"test": "vitest run --passWithNoTests",
|
|
94
|
+
"storybook": "storybook dev -p 6006",
|
|
95
|
+
"build-storybook": "storybook build",
|
|
96
|
+
"e2e": "pnpm --filter smartrte-core build && pnpm build && playwright test --pass-with-no-tests"
|
|
83
97
|
}
|
|
84
|
-
}
|
|
98
|
+
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { inlineToolDeclarations, type Attrs, type SmartMark } from "smartrte-core";
|
|
2
|
-
export type CanonicalInlineToolId = typeof inlineToolDeclarations[number]["id"];
|
|
3
|
-
export type CanonicalInlineIntent = "apply" | "remove" | "toggle" | "setAttrs" | "clearAll" | "editLink";
|
|
4
|
-
export interface CanonicalInlineExecution {
|
|
5
|
-
readonly changed: boolean;
|
|
6
|
-
readonly operations: number;
|
|
7
|
-
readonly coverage: "all" | "partial" | "none";
|
|
8
|
-
}
|
|
9
|
-
export declare const executeCanonicalInlineTool: (root: HTMLElement, id: CanonicalInlineToolId, intent?: CanonicalInlineIntent, attrs?: Attrs) => CanonicalInlineExecution;
|
|
10
|
-
/** Consumes adapter stored marks for ordinary text input. Composition remains
|
|
11
|
-
* owned by the canonical surface; atom-aware composition is explicitly Phase 7. */
|
|
12
|
-
export declare const installCanonicalInlineStoredMarkInput: (root: HTMLElement) => (() => void);
|
|
13
|
-
export declare const canonicalInlineStoredMarks: (root: HTMLElement) => readonly SmartMark[];
|
|
14
|
-
export declare const hasCanonicalInlineStoredMarkOverride: (root: HTMLElement) => boolean;
|
|
15
|
-
export declare const describeCanonicalInlineCoverage: (root: HTMLElement) => Readonly<Record<string, "all" | "partial">>;
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { applyMarkCommand, applyOperations, clearAllMarksCommand, createFoundationEditor, createNodeId, createScopeIndex, editLinkCommand, executeMarkTool, foundationSchema, inlineToolDeclarations, parseCanonicalListHtml, removeLinkCommand, removeMarkCommand, serializeCanonicalListHtml, toggleMarkCommand, } from "smartrte-core";
|
|
2
|
-
const storedMarks = new WeakMap();
|
|
3
|
-
const ownerSelector = "p,h1,h2,h3,h4,h5,h6";
|
|
4
|
-
const ownerId = (owner) => {
|
|
5
|
-
var _a;
|
|
6
|
-
(_a = owner.dataset).smartId || (_a.smartId = createNodeId());
|
|
7
|
-
return owner.dataset.smartId;
|
|
8
|
-
};
|
|
9
|
-
const contentWidth = (node) => {
|
|
10
|
-
if (node.nodeType === node.TEXT_NODE)
|
|
11
|
-
return node.nodeValue?.length || 0;
|
|
12
|
-
if (!(node instanceof HTMLElement))
|
|
13
|
-
return [...node.childNodes].reduce((sum, child) => sum + contentWidth(child), 0);
|
|
14
|
-
if (node.closest("[data-smart-ui]"))
|
|
15
|
-
return 0;
|
|
16
|
-
if (node.matches("br,[data-smart-atomic='true']"))
|
|
17
|
-
return 1;
|
|
18
|
-
return [...node.childNodes].reduce((sum, child) => sum + contentWidth(child), 0);
|
|
19
|
-
};
|
|
20
|
-
const pointInOwner = (root, node, offset) => {
|
|
21
|
-
const element = node instanceof HTMLElement ? node : node?.parentElement;
|
|
22
|
-
const owner = element?.closest(ownerSelector);
|
|
23
|
-
if (!owner || !root.contains(owner) || !node)
|
|
24
|
-
return null;
|
|
25
|
-
const range = root.ownerDocument.createRange();
|
|
26
|
-
range.selectNodeContents(owner);
|
|
27
|
-
try {
|
|
28
|
-
range.setEnd(node, offset);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
return { ownerId: ownerId(owner), offset: contentWidth(range.cloneContents()) };
|
|
34
|
-
};
|
|
35
|
-
const captureSelection = (root) => {
|
|
36
|
-
const native = root.ownerDocument.defaultView?.getSelection();
|
|
37
|
-
if (!native?.anchorNode || !native.focusNode)
|
|
38
|
-
return null;
|
|
39
|
-
const anchor = pointInOwner(root, native.anchorNode, native.anchorOffset);
|
|
40
|
-
const head = pointInOwner(root, native.focusNode, native.focusOffset);
|
|
41
|
-
return anchor && head ? { anchor, head } : null;
|
|
42
|
-
};
|
|
43
|
-
const domPointAt = (owner, requested) => {
|
|
44
|
-
let remaining = Math.max(0, requested);
|
|
45
|
-
const visit = (parent) => {
|
|
46
|
-
for (let index = 0; index < parent.childNodes.length; index += 1) {
|
|
47
|
-
const child = parent.childNodes[index];
|
|
48
|
-
if (child.nodeType === child.TEXT_NODE) {
|
|
49
|
-
const length = child.nodeValue?.length || 0;
|
|
50
|
-
if (remaining <= length)
|
|
51
|
-
return { node: child, offset: remaining };
|
|
52
|
-
remaining -= length;
|
|
53
|
-
}
|
|
54
|
-
else if (child instanceof HTMLElement && child.matches("br,[data-smart-atomic='true']")) {
|
|
55
|
-
if (remaining === 0)
|
|
56
|
-
return { node: parent, offset: index };
|
|
57
|
-
if (remaining === 1)
|
|
58
|
-
return { node: parent, offset: index + 1 };
|
|
59
|
-
remaining -= 1;
|
|
60
|
-
}
|
|
61
|
-
else if (!(child instanceof HTMLElement && child.closest("[data-smart-ui]"))) {
|
|
62
|
-
const found = visit(child);
|
|
63
|
-
if (found)
|
|
64
|
-
return found;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
};
|
|
69
|
-
return visit(owner) || { node: owner, offset: owner.childNodes.length };
|
|
70
|
-
};
|
|
71
|
-
const restoreSelection = (root, snapshot) => {
|
|
72
|
-
const byId = (id) => Array.from(root.querySelectorAll("[data-smart-id]"))
|
|
73
|
-
.find((node) => node.matches(ownerSelector) && node.dataset.smartId === id) || null;
|
|
74
|
-
const anchorOwner = byId(snapshot.anchor.ownerId);
|
|
75
|
-
const headOwner = byId(snapshot.head.ownerId);
|
|
76
|
-
const native = root.ownerDocument.defaultView?.getSelection();
|
|
77
|
-
if (!anchorOwner || !headOwner || !native)
|
|
78
|
-
return;
|
|
79
|
-
const anchor = domPointAt(anchorOwner, snapshot.anchor.offset);
|
|
80
|
-
const head = domPointAt(headOwner, snapshot.head.offset);
|
|
81
|
-
native.setBaseAndExtent(anchor.node, anchor.offset, head.node, head.offset);
|
|
82
|
-
};
|
|
83
|
-
const selectedOwners = (root, snapshot) => {
|
|
84
|
-
const owners = Array.from(root.querySelectorAll(ownerSelector));
|
|
85
|
-
const indexes = [snapshot.anchor.ownerId, snapshot.head.ownerId].map((id) => owners.findIndex((owner) => ownerId(owner) === id));
|
|
86
|
-
if (indexes.some((index) => index < 0))
|
|
87
|
-
return [];
|
|
88
|
-
const [from, to] = [Math.min(...indexes), Math.max(...indexes)];
|
|
89
|
-
return owners.slice(from, to + 1);
|
|
90
|
-
};
|
|
91
|
-
const modelSelection = (document, snapshot) => {
|
|
92
|
-
const positions = createScopeIndex().positions(document, foundationSchema);
|
|
93
|
-
const point = (value) => {
|
|
94
|
-
const content = positions.contentRangeOf(value.ownerId);
|
|
95
|
-
return content ? { path: [...content.from.path], offset: value.offset } : null;
|
|
96
|
-
};
|
|
97
|
-
const anchor = point(snapshot.anchor);
|
|
98
|
-
const head = point(snapshot.head);
|
|
99
|
-
return anchor && head ? { type: "text", anchor, head } : null;
|
|
100
|
-
};
|
|
101
|
-
const parseOwners = (owners) => parseCanonicalListHtml(owners.map((owner) => owner.outerHTML).join(""));
|
|
102
|
-
const renderOwners = (root, owners, document, preserveEditedLink = false) => {
|
|
103
|
-
const template = root.ownerDocument.createElement("template");
|
|
104
|
-
template.innerHTML = serializeCanonicalListHtml(document, { fragment: true });
|
|
105
|
-
owners.forEach((owner) => {
|
|
106
|
-
const id = ownerId(owner);
|
|
107
|
-
const replacement = Array.from(template.content.querySelectorAll("[data-smart-id]"))
|
|
108
|
-
.find((candidate) => candidate.dataset.smartId === id);
|
|
109
|
-
if (replacement) {
|
|
110
|
-
const existingLinks = owner.querySelectorAll("a");
|
|
111
|
-
const replacementLinks = replacement.querySelectorAll("a");
|
|
112
|
-
if (preserveEditedLink && existingLinks.length === 1 && replacementLinks.length === 1
|
|
113
|
-
&& existingLinks[0].textContent === replacementLinks[0].textContent
|
|
114
|
-
&& owner.textContent === replacement.textContent) {
|
|
115
|
-
const next = replacementLinks[0];
|
|
116
|
-
existingLinks[0].setAttribute("href", next.getAttribute("href") || "");
|
|
117
|
-
if (next.target) {
|
|
118
|
-
existingLinks[0].target = next.target;
|
|
119
|
-
existingLinks[0].rel = "noopener noreferrer";
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
existingLinks[0].removeAttribute("target");
|
|
123
|
-
existingLinks[0].removeAttribute("rel");
|
|
124
|
-
}
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
owner.innerHTML = replacement.innerHTML;
|
|
128
|
-
owner.querySelectorAll("span[style]").forEach((span) => {
|
|
129
|
-
const color = span.style.color;
|
|
130
|
-
const background = span.style.backgroundColor;
|
|
131
|
-
const size = span.style.fontSize;
|
|
132
|
-
const family = span.style.fontFamily;
|
|
133
|
-
span.removeAttribute("style");
|
|
134
|
-
if (color)
|
|
135
|
-
span.style.color = color;
|
|
136
|
-
if (background)
|
|
137
|
-
span.style.backgroundColor = background;
|
|
138
|
-
if (size)
|
|
139
|
-
span.style.fontSize = size;
|
|
140
|
-
if (family)
|
|
141
|
-
span.style.fontFamily = family;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
const declarationFor = (id) => {
|
|
147
|
-
const declaration = inlineToolDeclarations.find((tool) => tool.id === id);
|
|
148
|
-
if (!declaration)
|
|
149
|
-
throw new Error(`Unknown inline tool "${id}".`);
|
|
150
|
-
return declaration;
|
|
151
|
-
};
|
|
152
|
-
export const executeCanonicalInlineTool = (root, id, intent = "toggle", attrs) => {
|
|
153
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
154
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
155
|
-
const cleanupTemporaryIds = () => ownerElements.forEach((owner) => {
|
|
156
|
-
if (!ownersWithPersistedIds.has(owner))
|
|
157
|
-
owner.removeAttribute("data-smart-id");
|
|
158
|
-
});
|
|
159
|
-
const snapshot = captureSelection(root);
|
|
160
|
-
if (!snapshot)
|
|
161
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
162
|
-
const owners = selectedOwners(root, snapshot);
|
|
163
|
-
if (!owners.length)
|
|
164
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
165
|
-
const document = parseOwners(owners);
|
|
166
|
-
const selection = modelSelection(document, snapshot);
|
|
167
|
-
if (!selection)
|
|
168
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
169
|
-
const declaration = declarationFor(id);
|
|
170
|
-
const index = createScopeIndex();
|
|
171
|
-
const scope = index.resolve(document, selection, { want: "inline-range" }, foundationSchema);
|
|
172
|
-
const description = index.resolve(document, selection, { want: "describe" }, foundationSchema);
|
|
173
|
-
const coverage = description.marks.some((entry) => entry.mark.type === declaration.markType && entry.coverage === "all")
|
|
174
|
-
? "all" : description.marks.some((entry) => entry.mark.type === declaration.markType) ? "partial" : "none";
|
|
175
|
-
if (scope.collapsed && !(declaration.markType === "link" && (intent === "remove" || intent === "editLink"))) {
|
|
176
|
-
const editor = createFoundationEditor({ document, selection, storedMarks: storedMarks.get(root) });
|
|
177
|
-
executeMarkTool(editor, declaration, intent, attrs);
|
|
178
|
-
storedMarks.set(root, [...(editor.storedMarks || [])]);
|
|
179
|
-
cleanupTemporaryIds();
|
|
180
|
-
return { changed: false, operations: 0, coverage };
|
|
181
|
-
}
|
|
182
|
-
const ctx = { schema: foundationSchema, positions: index.positions(document, foundationSchema) };
|
|
183
|
-
const operations = intent === "clearAll" ? clearAllMarksCommand(document, scope, {}, ctx)
|
|
184
|
-
: intent === "remove" && declaration.markType === "link" ? removeLinkCommand(document, scope, { markType: "link" }, ctx)
|
|
185
|
-
: intent === "editLink" && declaration.markType === "link" ? editLinkCommand(document, scope, attrs, ctx)
|
|
186
|
-
: intent === "remove" ? removeMarkCommand(document, scope, { markType: declaration.markType }, ctx)
|
|
187
|
-
: intent === "toggle" ? toggleMarkCommand(document, scope, { markType: declaration.markType, attrs, coverage }, ctx)
|
|
188
|
-
: applyMarkCommand(document, scope, { markType: declaration.markType, attrs }, ctx);
|
|
189
|
-
if (!operations.length)
|
|
190
|
-
return { changed: false, operations: 0, coverage };
|
|
191
|
-
const output = applyOperations(document, operations);
|
|
192
|
-
renderOwners(root, owners, output, intent === "editLink");
|
|
193
|
-
restoreSelection(root, snapshot);
|
|
194
|
-
cleanupTemporaryIds();
|
|
195
|
-
storedMarks.delete(root);
|
|
196
|
-
return { changed: true, operations: operations.length, coverage };
|
|
197
|
-
};
|
|
198
|
-
/** Consumes adapter stored marks for ordinary text input. Composition remains
|
|
199
|
-
* owned by the canonical surface; atom-aware composition is explicitly Phase 7. */
|
|
200
|
-
export const installCanonicalInlineStoredMarkInput = (root) => {
|
|
201
|
-
const beforeInput = (event) => {
|
|
202
|
-
if (!storedMarks.has(root) || event.inputType !== "insertText" || !event.data)
|
|
203
|
-
return;
|
|
204
|
-
const marks = storedMarks.get(root) || [];
|
|
205
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
206
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
207
|
-
const snapshot = captureSelection(root);
|
|
208
|
-
if (!snapshot || snapshot.anchor.ownerId !== snapshot.head.ownerId || snapshot.anchor.offset !== snapshot.head.offset)
|
|
209
|
-
return;
|
|
210
|
-
event.preventDefault();
|
|
211
|
-
const owners = selectedOwners(root, snapshot);
|
|
212
|
-
const document = parseOwners(owners);
|
|
213
|
-
const selection = modelSelection(document, snapshot);
|
|
214
|
-
if (!selection)
|
|
215
|
-
return;
|
|
216
|
-
const editor = createFoundationEditor({ document, selection, storedMarks: marks });
|
|
217
|
-
if (marks.length)
|
|
218
|
-
editor.typeText(event.data, { timestamp: Date.now() });
|
|
219
|
-
else
|
|
220
|
-
editor.transact((builder) => {
|
|
221
|
-
builder.operations.push({ type: "insertText", pos: selection.head, text: event.data });
|
|
222
|
-
const next = { path: [...selection.head.path], offset: selection.head.offset + event.data.length };
|
|
223
|
-
builder.setSelection({ type: "text", anchor: next, head: next });
|
|
224
|
-
builder.setStoredMarks([]);
|
|
225
|
-
}, { source: "input", timestamp: Date.now(), addToHistory: true });
|
|
226
|
-
renderOwners(root, owners, editor.document);
|
|
227
|
-
const next = { anchor: { ...snapshot.anchor, offset: snapshot.anchor.offset + event.data.length }, head: { ...snapshot.head, offset: snapshot.head.offset + event.data.length } };
|
|
228
|
-
restoreSelection(root, next);
|
|
229
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
230
|
-
owner.removeAttribute("data-smart-id"); });
|
|
231
|
-
storedMarks.set(root, [...(editor.storedMarks || [])]);
|
|
232
|
-
};
|
|
233
|
-
const selectionChange = () => {
|
|
234
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
235
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
236
|
-
const snapshot = captureSelection(root);
|
|
237
|
-
if (!snapshot || snapshot.anchor.ownerId !== snapshot.head.ownerId || snapshot.anchor.offset !== snapshot.head.offset)
|
|
238
|
-
storedMarks.delete(root);
|
|
239
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
240
|
-
owner.removeAttribute("data-smart-id"); });
|
|
241
|
-
};
|
|
242
|
-
root.addEventListener("beforeinput", beforeInput);
|
|
243
|
-
root.ownerDocument.addEventListener("selectionchange", selectionChange);
|
|
244
|
-
return () => {
|
|
245
|
-
root.removeEventListener("beforeinput", beforeInput);
|
|
246
|
-
root.ownerDocument.removeEventListener("selectionchange", selectionChange);
|
|
247
|
-
storedMarks.delete(root);
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
|
-
export const canonicalInlineStoredMarks = (root) => storedMarks.get(root) || [];
|
|
251
|
-
export const hasCanonicalInlineStoredMarkOverride = (root) => storedMarks.has(root);
|
|
252
|
-
export const describeCanonicalInlineCoverage = (root) => {
|
|
253
|
-
if (storedMarks.has(root))
|
|
254
|
-
return Object.fromEntries((storedMarks.get(root) || []).map((mark) => [mark.type, "all"]));
|
|
255
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
256
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
257
|
-
const snapshot = captureSelection(root);
|
|
258
|
-
if (!snapshot)
|
|
259
|
-
return {};
|
|
260
|
-
const owners = selectedOwners(root, snapshot);
|
|
261
|
-
const document = parseOwners(owners);
|
|
262
|
-
const selection = modelSelection(document, snapshot);
|
|
263
|
-
const description = selection
|
|
264
|
-
? createScopeIndex().resolve(document, selection, { want: "describe" }, foundationSchema)
|
|
265
|
-
: null;
|
|
266
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
267
|
-
owner.removeAttribute("data-smart-id"); });
|
|
268
|
-
return Object.fromEntries((description?.marks || []).map((entry) => [entry.mark.type, entry.coverage]));
|
|
269
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type SmartSelection } from "smartrte-core";
|
|
2
|
-
export declare const executeCanonicalListDepth: (root: HTMLElement, items: readonly HTMLElement[], direction: "indent" | "outdent") => boolean;
|
|
3
|
-
export declare const executeCanonicalListMove: (root: HTMLElement, items: readonly HTMLElement[], direction: "up" | "down") => boolean;
|
|
4
|
-
export declare const executeCanonicalListStyle: (args: {
|
|
5
|
-
root: HTMLElement;
|
|
6
|
-
items: readonly HTMLElement[];
|
|
7
|
-
style?: string;
|
|
8
|
-
preset?: string;
|
|
9
|
-
checkable?: boolean;
|
|
10
|
-
}) => boolean;
|
|
11
|
-
export declare const executeCanonicalListCheck: (root: HTMLElement, item: HTMLElement, checked: boolean) => boolean;
|
|
12
|
-
export declare const executeCanonicalListToggle: (args: {
|
|
13
|
-
root: HTMLElement;
|
|
14
|
-
items: readonly HTMLElement[];
|
|
15
|
-
blocks: readonly HTMLElement[];
|
|
16
|
-
listTag: "ul" | "ol";
|
|
17
|
-
range?: Range;
|
|
18
|
-
style?: string;
|
|
19
|
-
preset?: string;
|
|
20
|
-
checkable?: boolean;
|
|
21
|
-
}) => boolean;
|
|
22
|
-
export interface CanonicalListBridgeResult {
|
|
23
|
-
readonly changed: boolean;
|
|
24
|
-
readonly selection: SmartSelection | null;
|
|
25
|
-
}
|
|
26
|
-
export declare const executeCanonicalListStructuralInput: (root: HTMLElement, input: "enter" | "backspace" | "delete") => boolean;
|