smartrte-react 0.3.5 → 1.0.0-beta.10
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 +55 -0
- package/README.md +205 -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 +65 -1
- package/dist/canonicalEditorRuntime.js +63 -5
- 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 +56 -1
- package/dist/components/CanonicalAuthorityEditor.js +1304 -116
- 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 +287 -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/MediaDetailsPopover.d.ts +35 -0
- package/dist/components/MediaDetailsPopover.js +136 -0
- package/dist/components/MediaManager.d.ts +4 -20
- package/dist/components/MediaManager.js +40 -11
- package/dist/components/MediaOverlay.d.ts +27 -0
- package/dist/components/MediaOverlay.js +228 -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 +149 -0
- package/dist/components/TableResizeHandles.d.ts +25 -0
- package/dist/components/TableResizeHandles.js +323 -0
- package/dist/components/TableSizePickerPopover.d.ts +16 -0
- package/dist/components/TableSizePickerPopover.js +126 -0
- package/dist/components/ToolbarPrimitives.d.ts +161 -0
- package/dist/components/ToolbarPrimitives.js +237 -0
- package/dist/components/VersionHistoryPanel.d.ts +9 -0
- package/dist/components/VersionHistoryPanel.js +157 -0
- package/dist/components/fixedPositioning.d.ts +25 -0
- package/dist/components/fixedPositioning.js +42 -0
- package/dist/formulaLibrary.d.ts +31 -0
- package/dist/formulaLibrary.js +90 -0
- package/dist/index.d.ts +25 -24
- package/dist/index.js +15 -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 +219 -30
- package/dist/toolbarTools.d.ts +95 -0
- package/dist/toolbarTools.js +17 -0
- 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
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
const HANDLE_THICKNESS = 6;
|
|
4
|
+
const MIN_SIZE = 20;
|
|
5
|
+
/**
|
|
6
|
+
* Phase 11.5 §2.2: column-width/row-height drag handles calling the
|
|
7
|
+
* existing setTableColumnWidthCommand/setTableRowHeightCommand (row/column
|
|
8
|
+
* reorder already had toolbar buttons - "Move row up/down"/"Move column
|
|
9
|
+
* left/right" - wired since before this phase; resize had no UI at all).
|
|
10
|
+
* Commit-on-release, not a live preview during drag - a deliberately
|
|
11
|
+
* bounded scope for the first pass. Recomputes handle geometry from the
|
|
12
|
+
* live table's actual rendered cell rects on every table layout change via
|
|
13
|
+
* ResizeObserver, since table content (and therefore column/row sizes)
|
|
14
|
+
* changes independently of resize actions.
|
|
15
|
+
*/
|
|
16
|
+
export function TableResizeHandles({ tableElement, onResizeColumn, onResizeRow }) {
|
|
17
|
+
const [columns, setColumns] = useState([]);
|
|
18
|
+
const [rows, setRows] = useState([]);
|
|
19
|
+
// `nextStartSize`/`nextIndex` are null for the last row/column's own
|
|
20
|
+
// outer-edge handle - that one still grows/shrinks the table's overall
|
|
21
|
+
// size (no neighbor to redistribute with); every other handle has a
|
|
22
|
+
// real neighbor and redistributes between the two instead.
|
|
23
|
+
const dragRef = useRef(null);
|
|
24
|
+
const [dragging, setDragging] = useState(null);
|
|
25
|
+
// handleUp (below) is defined inside an effect that doesn't depend on
|
|
26
|
+
// `columns`/`rows` (to avoid tearing down the pointer listeners on every
|
|
27
|
+
// ResizeObserver tick) - these refs keep it reading the latest measured
|
|
28
|
+
// sizes anyway, without that staleness risk.
|
|
29
|
+
const columnsRef = useRef([]);
|
|
30
|
+
columnsRef.current = columns;
|
|
31
|
+
const rowsRef = useRef([]);
|
|
32
|
+
rowsRef.current = rows;
|
|
33
|
+
// Shared by the passive observers below (structural/layout changes that
|
|
34
|
+
// happen outside a drag) and by handleMove during an active drag (see
|
|
35
|
+
// that effect for why an active drag can't just wait for these
|
|
36
|
+
// observers to notice).
|
|
37
|
+
const recompute = useCallback(() => {
|
|
38
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
39
|
+
const firstRowCells = [...tableElement.querySelectorAll("tr")[0]?.children || []];
|
|
40
|
+
const colBoundaries = [];
|
|
41
|
+
firstRowCells.forEach((cell, index) => {
|
|
42
|
+
const rect = cell.getBoundingClientRect();
|
|
43
|
+
colBoundaries.push({ index, position: rect.right, length: tableRect.height, start: tableRect.top, size: rect.width });
|
|
44
|
+
});
|
|
45
|
+
const rowElements = [...tableElement.querySelectorAll(":scope > tr, :scope > tbody > tr")];
|
|
46
|
+
const rowBoundaries = [];
|
|
47
|
+
rowElements.forEach((row, index) => {
|
|
48
|
+
const rect = row.getBoundingClientRect();
|
|
49
|
+
rowBoundaries.push({ index, position: rect.bottom, length: tableRect.width, start: tableRect.left, size: rect.height });
|
|
50
|
+
});
|
|
51
|
+
setColumns(colBoundaries);
|
|
52
|
+
setRows(rowBoundaries);
|
|
53
|
+
}, [tableElement]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
recompute();
|
|
56
|
+
const observer = new ResizeObserver(recompute);
|
|
57
|
+
observer.observe(tableElement);
|
|
58
|
+
// ResizeObserver only fires when tableElement's own box size changes -
|
|
59
|
+
// inserting/removing a row or column into a table that stays the same
|
|
60
|
+
// overall width (the common case: width:100%, or a pinned literal
|
|
61
|
+
// width unaffected by redistributing space across one more/fewer
|
|
62
|
+
// column) never changes that box size, so it never fires. Handles
|
|
63
|
+
// then stay stale: wrong count, wrong position, wrong `boundary.size`
|
|
64
|
+
// for whatever column/row the model now has at that index - dragging
|
|
65
|
+
// one computed a wildly wrong new width from a stale start size. A
|
|
66
|
+
// MutationObserver on the table's own structure (row/cell add/remove/
|
|
67
|
+
// reorder) catches exactly the class of change ResizeObserver misses.
|
|
68
|
+
const mutationObserver = new MutationObserver(recompute);
|
|
69
|
+
mutationObserver.observe(tableElement, { childList: true, subtree: true });
|
|
70
|
+
const ownerWindow = tableElement.ownerDocument.defaultView;
|
|
71
|
+
ownerWindow?.addEventListener("scroll", recompute, true);
|
|
72
|
+
return () => {
|
|
73
|
+
observer.disconnect();
|
|
74
|
+
mutationObserver.disconnect();
|
|
75
|
+
ownerWindow?.removeEventListener("scroll", recompute, true);
|
|
76
|
+
};
|
|
77
|
+
}, [tableElement, recompute]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const ownerWindow = tableElement.ownerDocument.defaultView;
|
|
80
|
+
if (!ownerWindow)
|
|
81
|
+
return;
|
|
82
|
+
// For an internal boundary (a real neighbor exists), clamp the raw
|
|
83
|
+
// pointer delta so neither side ever goes below MIN_SIZE, then apply
|
|
84
|
+
// the *same* clamped delta with opposite signs to both sides - their
|
|
85
|
+
// sum is therefore always exactly startSize + nextStartSize, whatever
|
|
86
|
+
// the clamp did. That invariant is what makes "the border just moves"
|
|
87
|
+
// true: the table's own total size never has to change for these.
|
|
88
|
+
//
|
|
89
|
+
// Rows can't use this arithmetic-only approach: a <tr>'s rendered
|
|
90
|
+
// height can never go below what its own cell content needs (an
|
|
91
|
+
// explicit row height behaves as a minimum, not a hard cap, in table
|
|
92
|
+
// layout) - unlike a column's width under table-layout:fixed, which
|
|
93
|
+
// genuinely can be squeezed below its content's natural width. Naively
|
|
94
|
+
// clamping to a fixed MIN_SIZE the way columns do would, the instant a
|
|
95
|
+
// row's *real* content floor was higher than that, still hand the
|
|
96
|
+
// *other* row the full requested delta anyway - the total table
|
|
97
|
+
// height would silently grow instead of staying constant, and the row
|
|
98
|
+
// actually being dragged would appear frozen while its neighbor
|
|
99
|
+
// visibly ballooned (this is what "resize rows... move cursor up
|
|
100
|
+
// then it is effecting below row height" was - see docs/bugs/
|
|
101
|
+
// row-resize-drag-up-only-grows-the-row-below.md). Measuring the real
|
|
102
|
+
// DOM after attempting a shrink, and handing the other row only what
|
|
103
|
+
// was actually ceded, keeps that same invariant true regardless of
|
|
104
|
+
// where a row's real floor turns out to be.
|
|
105
|
+
const resolveSizes = (drag, event) => {
|
|
106
|
+
const rawDelta = drag.kind === "column" ? event.clientX - drag.startClient : event.clientY - drag.startClient;
|
|
107
|
+
if (drag.kind === "column") {
|
|
108
|
+
if (drag.nextStartSize === null) {
|
|
109
|
+
return { size: Math.max(MIN_SIZE, Math.round(drag.startSize + rawDelta)), nextSize: null };
|
|
110
|
+
}
|
|
111
|
+
const minDelta = -(drag.startSize - MIN_SIZE);
|
|
112
|
+
const maxDelta = drag.nextStartSize - MIN_SIZE;
|
|
113
|
+
const delta = Math.min(Math.max(rawDelta, minDelta), maxDelta);
|
|
114
|
+
return { size: Math.round(drag.startSize + delta), nextSize: Math.round(drag.nextStartSize - delta) };
|
|
115
|
+
}
|
|
116
|
+
const rowElements = [...tableElement.querySelectorAll(":scope > tr, :scope > tbody > tr")];
|
|
117
|
+
if (drag.nextStartSize === null || drag.nextIndex === null) {
|
|
118
|
+
const target = Math.max(MIN_SIZE, Math.round(drag.startSize + rawDelta));
|
|
119
|
+
const rowElement = rowElements[drag.index];
|
|
120
|
+
if (rowElement)
|
|
121
|
+
rowElement.style.height = `${target}px`;
|
|
122
|
+
const actual = rowElement ? Math.round(rowElement.getBoundingClientRect().height) : target;
|
|
123
|
+
return { size: actual, nextSize: null };
|
|
124
|
+
}
|
|
125
|
+
const rowElement = rowElements[drag.index];
|
|
126
|
+
const nextRowElement = rowElements[drag.nextIndex];
|
|
127
|
+
if (rawDelta <= 0) {
|
|
128
|
+
// The dragged (above) row is the one shrinking; the row below
|
|
129
|
+
// only grows by however much the above row actually gave up.
|
|
130
|
+
// There's no equivalent fallback for the dragged row itself here -
|
|
131
|
+
// a row that can't shrink just can't, full stop, no matter how
|
|
132
|
+
// flexible the table's total height is willing to be.
|
|
133
|
+
const target = Math.max(MIN_SIZE, Math.round(drag.startSize + rawDelta));
|
|
134
|
+
if (rowElement)
|
|
135
|
+
rowElement.style.height = `${target}px`;
|
|
136
|
+
const actualSize = rowElement ? Math.round(rowElement.getBoundingClientRect().height) : target;
|
|
137
|
+
// If the row below is already sitting at its own natural content
|
|
138
|
+
// floor right now, it never actually had this space to begin with
|
|
139
|
+
// - a *previous* down-drag on this same boundary that hit this
|
|
140
|
+
// row's floor grew the table's total instead of taking space from
|
|
141
|
+
// it (see docs/bugs/row-resize-blocked-when-every-row-is-at-its-
|
|
142
|
+
// floor.md), and that growth is real table height, not this row's
|
|
143
|
+
// slack. Handing it back to the row here would inflate a row that
|
|
144
|
+
// never gave anything up, and a user dragging the border back to
|
|
145
|
+
// where it started would find the table permanently bloated with
|
|
146
|
+
// no way back except Undo (docs/bugs/row-resize-round-trip-
|
|
147
|
+
// inflates-neighbor.md). Measure the row's own floor by
|
|
148
|
+
// temporarily clearing its inline height override; only
|
|
149
|
+
// redistribute into it when it's currently holding real slack
|
|
150
|
+
// above that floor.
|
|
151
|
+
if (nextRowElement) {
|
|
152
|
+
const previousNextHeight = nextRowElement.style.height;
|
|
153
|
+
nextRowElement.style.height = "";
|
|
154
|
+
const nextFloor = Math.round(nextRowElement.getBoundingClientRect().height);
|
|
155
|
+
nextRowElement.style.height = previousNextHeight;
|
|
156
|
+
if (drag.nextStartSize <= nextFloor + 1)
|
|
157
|
+
return { size: actualSize, nextSize: drag.nextStartSize };
|
|
158
|
+
}
|
|
159
|
+
return { size: actualSize, nextSize: Math.round(drag.nextStartSize - (actualSize - drag.startSize)) };
|
|
160
|
+
}
|
|
161
|
+
// Dragging down: the row below is asked to shrink to pay for the
|
|
162
|
+
// dragged row's growth, exactly like the mirror case above - but
|
|
163
|
+
// unlike a row shrinking, a row *growing* is never floor-limited (a
|
|
164
|
+
// <tr>'s content only ever imposes a minimum, never a maximum), so
|
|
165
|
+
// there's no symmetric "the dragged row just can't grow" outcome to
|
|
166
|
+
// fall back to here. Making the dragged row's growth conditional on
|
|
167
|
+
// the neighbor actually having room to give (the way the shrink
|
|
168
|
+
// case correctly is) meant that on a table where every row already
|
|
169
|
+
// sits at its own content floor - true of almost any freshly
|
|
170
|
+
// created or pasted table with short cell text - internal
|
|
171
|
+
// row-boundary dragging did *nothing at all* in either direction:
|
|
172
|
+
// shrinking had nowhere to go, and growing was blocked because the
|
|
173
|
+
// neighbor had nowhere to give either. See docs/bugs/
|
|
174
|
+
// row-resize-blocked-when-every-row-is-at-its-floor.md. Grow the
|
|
175
|
+
// dragged row by the full requested amount unconditionally, and let
|
|
176
|
+
// the neighbor give up only what it actually can; if that's less
|
|
177
|
+
// than what was asked, the shortfall becomes real table growth -
|
|
178
|
+
// the only place that space could possibly come from - rather than
|
|
179
|
+
// silently refusing the drag altogether.
|
|
180
|
+
const size = Math.max(MIN_SIZE, Math.round(drag.startSize + rawDelta));
|
|
181
|
+
if (rowElement)
|
|
182
|
+
rowElement.style.height = `${size}px`;
|
|
183
|
+
const target = Math.max(MIN_SIZE, Math.round(drag.nextStartSize - rawDelta));
|
|
184
|
+
if (nextRowElement)
|
|
185
|
+
nextRowElement.style.height = `${target}px`;
|
|
186
|
+
const nextSize = nextRowElement ? Math.round(nextRowElement.getBoundingClientRect().height) : target;
|
|
187
|
+
return { size, nextSize };
|
|
188
|
+
};
|
|
189
|
+
const handleMove = (event) => {
|
|
190
|
+
const drag = dragRef.current;
|
|
191
|
+
if (!drag)
|
|
192
|
+
return;
|
|
193
|
+
setDragging({ kind: drag.kind, index: drag.index, position: drag.kind === "column" ? event.clientX : event.clientY });
|
|
194
|
+
// Live preview: mutate the real <col>/<tr> directly during the drag
|
|
195
|
+
// (commit-on-release still only touches the model, via onResizeColumn/
|
|
196
|
+
// onResizeRow below) - previously nothing rendered until pointerup, so
|
|
197
|
+
// the drag looked frozen until it suddenly jumped to the final size.
|
|
198
|
+
// Undoing this on the next real render is automatic: committing
|
|
199
|
+
// triggers the renderer's own re-render from authoritative model
|
|
200
|
+
// state, which overwrites whatever this loop wrote.
|
|
201
|
+
const { size: liveSize, nextSize: liveNextSize } = resolveSizes(drag, event);
|
|
202
|
+
if (drag.kind === "column") {
|
|
203
|
+
const cols = [...(tableElement.querySelector("colgroup")?.children || [])];
|
|
204
|
+
const col = cols[drag.index];
|
|
205
|
+
if (col)
|
|
206
|
+
col.style.width = `${liveSize}px`;
|
|
207
|
+
if (liveNextSize !== null && drag.nextIndex !== null) {
|
|
208
|
+
const nextCol = cols[drag.nextIndex];
|
|
209
|
+
if (nextCol)
|
|
210
|
+
nextCol.style.width = `${liveNextSize}px`;
|
|
211
|
+
}
|
|
212
|
+
// The stylesheet's `table-layout: fixed` + `width: 100%` treats
|
|
213
|
+
// <col> widths as proportions of the table's own rendered width,
|
|
214
|
+
// not literal pixels, unless the table's own width matches their
|
|
215
|
+
// sum (see surface/renderer.ts's table-width fix) - keep that sum
|
|
216
|
+
// live too, or every other column visibly compresses for the
|
|
217
|
+
// duration of the drag even though the fix restores them on
|
|
218
|
+
// release. Redistributing between two columns (liveNextSize !==
|
|
219
|
+
// null) never changes this sum by construction, so only the
|
|
220
|
+
// last column's own resize (liveNextSize === null) needs this.
|
|
221
|
+
if (liveNextSize === null) {
|
|
222
|
+
const total = cols.reduce((sum, candidate, index) => sum + (index === drag.index ? liveSize : (parseFloat(candidate.style.width) || 0)), 0);
|
|
223
|
+
if (total > 0)
|
|
224
|
+
tableElement.style.width = `${total}px`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const rowElements = [...tableElement.querySelectorAll(":scope > tr, :scope > tbody > tr")];
|
|
229
|
+
const rowElement = rowElements[drag.index];
|
|
230
|
+
if (rowElement)
|
|
231
|
+
rowElement.style.height = `${liveSize}px`;
|
|
232
|
+
if (liveNextSize !== null && drag.nextIndex !== null) {
|
|
233
|
+
const nextRow = rowElements[drag.nextIndex];
|
|
234
|
+
if (nextRow)
|
|
235
|
+
nextRow.style.height = `${liveNextSize}px`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// The handles' own on-screen position/length come from `columns`/
|
|
239
|
+
// `rows` state, which the effect above only refreshes via
|
|
240
|
+
// ResizeObserver/MutationObserver/scroll - none of which reliably
|
|
241
|
+
// fire here. Column drags happen to nudge the table's own rendered
|
|
242
|
+
// width by sub-pixel rounding (table-layout: fixed redistributing a
|
|
243
|
+
// stray pixel), which incidentally retriggers ResizeObserver; an
|
|
244
|
+
// internal row-boundary drag keeps the table's total height exactly
|
|
245
|
+
// constant by construction (see resolveSizes above), so it never
|
|
246
|
+
// does. Left alone, the row handle (and the blue line it renders)
|
|
247
|
+
// stays frozen at its pre-drag position while the real row border
|
|
248
|
+
// moves live underneath it - reported as a misplaced, "very slow"
|
|
249
|
+
// blue line that overflows past the table/editor as the gap between
|
|
250
|
+
// the frozen handle and the actual, now-taller table grows. Forcing
|
|
251
|
+
// a synchronous re-measure after every live style mutation (a
|
|
252
|
+
// getBoundingClientRect read right after a style write forces the
|
|
253
|
+
// browser to flush layout first, so this always reflects the
|
|
254
|
+
// mutation just above, not a stale value) keeps both column and row
|
|
255
|
+
// handles tracking the real border on every pointermove instead of
|
|
256
|
+
// waiting on an observer that may not fire at all.
|
|
257
|
+
recompute();
|
|
258
|
+
};
|
|
259
|
+
const handleUp = (event) => {
|
|
260
|
+
const drag = dragRef.current;
|
|
261
|
+
if (!drag)
|
|
262
|
+
return;
|
|
263
|
+
const { size, nextSize } = resolveSizes(drag, event);
|
|
264
|
+
if (drag.kind === "column") {
|
|
265
|
+
// A table pasted/created with no columnWidths yet (natural,
|
|
266
|
+
// stretched rendering - see docs/bugs/table-shrinks-after-paste.md)
|
|
267
|
+
// has no real per-column data for the command layer to fall back
|
|
268
|
+
// on; without this, setTableColumnWidthCommand's own fallback
|
|
269
|
+
// (Array(columns).fill(120)) would silently reset every OTHER
|
|
270
|
+
// column to a fabricated 120px default the instant any one column
|
|
271
|
+
// was resized, shrinking the whole table - passing the real,
|
|
272
|
+
// currently-measured width of every column lets the caller seed a
|
|
273
|
+
// complete, accurate columnWidths array instead of guessing.
|
|
274
|
+
const currentWidths = columnsRef.current.map((boundary) => boundary.size);
|
|
275
|
+
if (nextSize !== null && drag.nextIndex !== null)
|
|
276
|
+
currentWidths[drag.nextIndex] = nextSize;
|
|
277
|
+
onResizeColumn(drag.index, size, currentWidths);
|
|
278
|
+
}
|
|
279
|
+
else if (nextSize !== null && drag.nextIndex !== null) {
|
|
280
|
+
onResizeRow(drag.index, size, { index: drag.nextIndex, height: nextSize });
|
|
281
|
+
}
|
|
282
|
+
else
|
|
283
|
+
onResizeRow(drag.index, size);
|
|
284
|
+
dragRef.current = null;
|
|
285
|
+
setDragging(null);
|
|
286
|
+
};
|
|
287
|
+
ownerWindow.addEventListener("pointermove", handleMove);
|
|
288
|
+
ownerWindow.addEventListener("pointerup", handleUp);
|
|
289
|
+
return () => {
|
|
290
|
+
ownerWindow.removeEventListener("pointermove", handleMove);
|
|
291
|
+
ownerWindow.removeEventListener("pointerup", handleUp);
|
|
292
|
+
};
|
|
293
|
+
}, [tableElement, onResizeColumn, onResizeRow, recompute]);
|
|
294
|
+
const startDrag = (kind, boundary) => (event) => {
|
|
295
|
+
event.preventDefault();
|
|
296
|
+
const siblings = kind === "column" ? columnsRef.current : rowsRef.current;
|
|
297
|
+
const next = siblings[boundary.index + 1] ?? null;
|
|
298
|
+
dragRef.current = {
|
|
299
|
+
kind, index: boundary.index, startClient: kind === "column" ? event.clientX : event.clientY, startSize: boundary.size,
|
|
300
|
+
nextIndex: next?.index ?? null, nextStartSize: next?.size ?? null,
|
|
301
|
+
};
|
|
302
|
+
setDragging({ kind, index: boundary.index, position: kind === "column" ? event.clientX : event.clientY });
|
|
303
|
+
};
|
|
304
|
+
return (_jsxs("div", { "data-srte-table-resize-handles": "true", style: { position: "fixed", inset: 0, pointerEvents: "none", zIndex: 60 }, children: [rows.map((boundary) => (_jsx("div", { role: "separator", "aria-orientation": "horizontal", "aria-label": `Resize row ${boundary.index + 1}`, "data-srte-row-resize-handle": boundary.index, onPointerDown: startDrag("row", boundary), style: {
|
|
305
|
+
position: "fixed",
|
|
306
|
+
left: boundary.start,
|
|
307
|
+
top: boundary.position - HANDLE_THICKNESS / 2,
|
|
308
|
+
width: boundary.length,
|
|
309
|
+
height: HANDLE_THICKNESS,
|
|
310
|
+
cursor: "row-resize",
|
|
311
|
+
pointerEvents: "auto",
|
|
312
|
+
background: dragging?.kind === "row" && dragging.index === boundary.index ? "var(--srte-primary)" : "transparent",
|
|
313
|
+
} }, `row-${boundary.index}`))), columns.map((boundary) => (_jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": `Resize column ${boundary.index + 1}`, "data-srte-column-resize-handle": boundary.index, onPointerDown: startDrag("column", boundary), style: {
|
|
314
|
+
position: "fixed",
|
|
315
|
+
left: boundary.position - HANDLE_THICKNESS / 2,
|
|
316
|
+
top: boundary.start,
|
|
317
|
+
width: HANDLE_THICKNESS,
|
|
318
|
+
height: boundary.length,
|
|
319
|
+
cursor: "col-resize",
|
|
320
|
+
pointerEvents: "auto",
|
|
321
|
+
background: dragging?.kind === "column" && dragging.index === boundary.index ? "var(--srte-primary)" : "transparent",
|
|
322
|
+
} }, `col-${boundary.index}`)))] }));
|
|
323
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface TableSizePickerPopoverProps {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
onInsert: (rows: number, columns: number) => void;
|
|
5
|
+
onCancel: () => void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* "Insert table" size picker - CKEditor's own pattern: hover an N×M grid to
|
|
9
|
+
* preview the size, click a cell to insert immediately (one decisive click,
|
|
10
|
+
* no separate Apply/Confirm step, since a grid cell click is already a
|
|
11
|
+
* single deliberate choice - the same reasoning ColorPickerPopover's
|
|
12
|
+
* recent-swatch buttons and the removed preset grid used). A numeric
|
|
13
|
+
* fallback below covers sizes past the grid's cap without capping what the
|
|
14
|
+
* tool can actually do.
|
|
15
|
+
*/
|
|
16
|
+
export declare function TableSizePickerPopover({ x, y, onInsert, onCancel }: TableSizePickerPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
const MAX_GRID = 8;
|
|
4
|
+
const buttonStyle = {
|
|
5
|
+
minHeight: 32,
|
|
6
|
+
padding: "0 10px",
|
|
7
|
+
border: "1px solid var(--srte-input-border)",
|
|
8
|
+
borderRadius: 8,
|
|
9
|
+
background: "var(--srte-input-bg)",
|
|
10
|
+
color: "var(--srte-menu-text)",
|
|
11
|
+
cursor: "pointer",
|
|
12
|
+
fontWeight: 500,
|
|
13
|
+
fontSize: 13,
|
|
14
|
+
};
|
|
15
|
+
const inputStyle = {
|
|
16
|
+
width: 48,
|
|
17
|
+
height: 32,
|
|
18
|
+
boxSizing: "border-box",
|
|
19
|
+
padding: "0 8px",
|
|
20
|
+
border: "1px solid var(--srte-input-border)",
|
|
21
|
+
borderRadius: 8,
|
|
22
|
+
outline: "none",
|
|
23
|
+
background: "var(--srte-input-bg)",
|
|
24
|
+
color: "var(--srte-input-text)",
|
|
25
|
+
font: "inherit",
|
|
26
|
+
textAlign: "center",
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* "Insert table" size picker - CKEditor's own pattern: hover an N×M grid to
|
|
30
|
+
* preview the size, click a cell to insert immediately (one decisive click,
|
|
31
|
+
* no separate Apply/Confirm step, since a grid cell click is already a
|
|
32
|
+
* single deliberate choice - the same reasoning ColorPickerPopover's
|
|
33
|
+
* recent-swatch buttons and the removed preset grid used). A numeric
|
|
34
|
+
* fallback below covers sizes past the grid's cap without capping what the
|
|
35
|
+
* tool can actually do.
|
|
36
|
+
*/
|
|
37
|
+
export function TableSizePickerPopover({ x, y, onInsert, onCancel }) {
|
|
38
|
+
const rootRef = useRef(null);
|
|
39
|
+
const rowsRef = useRef(null);
|
|
40
|
+
const [placement, setPlacement] = useState(null);
|
|
41
|
+
const [hover, setHover] = useState(null);
|
|
42
|
+
const [customRows, setCustomRows] = useState("2");
|
|
43
|
+
const [customColumns, setCustomColumns] = useState("2");
|
|
44
|
+
const onCancelRef = useRef(onCancel);
|
|
45
|
+
onCancelRef.current = onCancel;
|
|
46
|
+
useLayoutEffect(() => {
|
|
47
|
+
const el = rootRef.current;
|
|
48
|
+
if (!el)
|
|
49
|
+
return;
|
|
50
|
+
const margin = 8;
|
|
51
|
+
const { width, height } = el.getBoundingClientRect();
|
|
52
|
+
const viewportWidth = window.innerWidth;
|
|
53
|
+
const viewportHeight = window.innerHeight;
|
|
54
|
+
const overflowsRight = x + width > viewportWidth - margin;
|
|
55
|
+
const left = overflowsRight ? x - width : x;
|
|
56
|
+
const clampedLeft = Math.min(Math.max(margin, left), Math.max(margin, viewportWidth - width - margin));
|
|
57
|
+
const overflowsBottom = y + height > viewportHeight - margin;
|
|
58
|
+
const top = overflowsBottom ? y - height : y;
|
|
59
|
+
const clampedTop = Math.min(Math.max(margin, top), Math.max(margin, viewportHeight - margin));
|
|
60
|
+
setPlacement({ left: clampedLeft, top: clampedTop });
|
|
61
|
+
}, [x, y]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const dismissIfOutside = (event) => {
|
|
64
|
+
if (rootRef.current && !rootRef.current.contains(event.target))
|
|
65
|
+
onCancelRef.current();
|
|
66
|
+
};
|
|
67
|
+
window.addEventListener("pointerdown", dismissIfOutside, true);
|
|
68
|
+
window.addEventListener("mousedown", dismissIfOutside, true);
|
|
69
|
+
return () => {
|
|
70
|
+
window.removeEventListener("pointerdown", dismissIfOutside, true);
|
|
71
|
+
window.removeEventListener("mousedown", dismissIfOutside, true);
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
// Escape must dismiss regardless of focus - unlike LinkEditorPopover/
|
|
75
|
+
// ColorPickerPopover, nothing here is autofocused on open (the grid is
|
|
76
|
+
// meant to be hovered, not tabbed into first), so focus stays wherever it
|
|
77
|
+
// was (typically the "Insert table" toolbar button) and the div's own
|
|
78
|
+
// onKeyDown below would never see the keypress.
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
const onWindowKeyDown = (event) => {
|
|
81
|
+
if (event.key === "Escape")
|
|
82
|
+
onCancelRef.current();
|
|
83
|
+
};
|
|
84
|
+
window.addEventListener("keydown", onWindowKeyDown);
|
|
85
|
+
return () => window.removeEventListener("keydown", onWindowKeyDown);
|
|
86
|
+
}, []);
|
|
87
|
+
const insertCustom = () => {
|
|
88
|
+
const rows = Math.min(50, Math.max(1, Math.round(Number(customRows)) || 0));
|
|
89
|
+
const columns = Math.min(50, Math.max(1, Math.round(Number(customColumns)) || 0));
|
|
90
|
+
if (!rows || !columns)
|
|
91
|
+
return;
|
|
92
|
+
onInsert(rows, columns);
|
|
93
|
+
};
|
|
94
|
+
return (_jsxs("div", { ref: rootRef, "data-srte-table-size-popover": "true", role: "dialog", "aria-label": "Insert table", style: {
|
|
95
|
+
position: "fixed",
|
|
96
|
+
left: placement?.left ?? x,
|
|
97
|
+
top: placement?.top ?? y,
|
|
98
|
+
visibility: placement ? "visible" : "hidden",
|
|
99
|
+
zIndex: 70,
|
|
100
|
+
width: 232,
|
|
101
|
+
boxSizing: "border-box",
|
|
102
|
+
background: "var(--srte-menu-bg)",
|
|
103
|
+
color: "var(--srte-menu-text)",
|
|
104
|
+
border: "1px solid var(--srte-border)",
|
|
105
|
+
borderRadius: 12,
|
|
106
|
+
boxShadow: "var(--srte-menu-shadow)",
|
|
107
|
+
padding: 14,
|
|
108
|
+
}, children: [_jsx("div", { style: { fontWeight: 650, marginBottom: 10 }, children: "Insert table" }), _jsx("div", { "data-srte-table-size-grid": "true", style: { display: "grid", gridTemplateColumns: `repeat(${MAX_GRID}, 1fr)`, gap: 3, marginBottom: 8 }, onMouseLeave: () => setHover(null), children: Array.from({ length: MAX_GRID * MAX_GRID }, (_, index) => {
|
|
109
|
+
const row = Math.floor(index / MAX_GRID);
|
|
110
|
+
const column = index % MAX_GRID;
|
|
111
|
+
const active = hover ? row <= hover.row && column <= hover.column : false;
|
|
112
|
+
return (_jsx("button", { type: "button", "data-srte-table-size-cell": `${row + 1}x${column + 1}`, "aria-label": `Insert a ${row + 1} by ${column + 1} table`, onMouseEnter: () => setHover({ row, column }), onFocus: () => setHover({ row, column }), onClick: () => onInsert(row + 1, column + 1), style: {
|
|
113
|
+
width: 20, height: 20, padding: 0, boxSizing: "border-box",
|
|
114
|
+
border: `1px solid ${active ? "var(--srte-primary)" : "var(--srte-input-border)"}`,
|
|
115
|
+
borderRadius: 3,
|
|
116
|
+
background: active ? "var(--srte-primary)" : "var(--srte-input-bg)",
|
|
117
|
+
cursor: "pointer",
|
|
118
|
+
} }, index));
|
|
119
|
+
}) }), _jsx("div", { "data-srte-table-size-label": "true", style: { fontSize: 12, marginBottom: 12, minHeight: 16 }, children: hover ? `${hover.row + 1} × ${hover.column + 1} table` : "Hover to choose a size" }), _jsx("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 6 }, children: "Custom size" }), _jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 12 }, children: [_jsx("input", { ref: rowsRef, type: "number", min: 1, max: 50, "aria-label": "Rows", value: customRows, onChange: (event) => setCustomRows(event.target.value), onKeyDown: (event) => { if (event.key === "Enter") {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
insertCustom();
|
|
122
|
+
} }, style: inputStyle }), _jsx("span", { children: "\u00D7" }), _jsx("input", { type: "number", min: 1, max: 50, "aria-label": "Columns", value: customColumns, onChange: (event) => setCustomColumns(event.target.value), onKeyDown: (event) => { if (event.key === "Enter") {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
insertCustom();
|
|
125
|
+
} }, style: inputStyle }), _jsx("button", { type: "button", onClick: insertCustom, style: { ...buttonStyle, marginLeft: "auto", borderColor: "var(--srte-primary)", background: "var(--srte-primary)", color: "var(--srte-on-primary)" }, children: "Insert" })] }), _jsx("div", { style: { display: "flex", justifyContent: "flex-end" }, children: _jsx("button", { type: "button", onClick: onCancel, style: buttonStyle, children: "Cancel" }) })] }));
|
|
126
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const toolbarIcons: {
|
|
3
|
+
readonly bold: import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
readonly italic: import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
readonly underline: import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
readonly strikethrough: import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
readonly code: import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
readonly superscript: import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
readonly subscript: import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
readonly textColor: import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
readonly backgroundColor: import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
readonly fontSize: import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
readonly fontFamily: import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
readonly paragraphStyle: import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
readonly alignLeft: import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
readonly alignCenter: import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
readonly alignRight: import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
readonly alignJustify: import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
readonly quote: import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
readonly moveUp: import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
readonly moveDown: import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
readonly moveLeft: import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
readonly moveRight: import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
readonly indent: import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
readonly outdent: import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
readonly link: import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
readonly unlink: import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
readonly bulletedList: import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
readonly numberedList: import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
readonly checklist: import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
readonly checkSquare: import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
readonly restart: import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
readonly continueNumbering: import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
readonly table: import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
readonly mergeCells: import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
readonly splitCell: import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
readonly addRow: import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
readonly addColumn: import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
readonly headerRow: import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
readonly deleteTable: import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
readonly image: import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
readonly video: import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
readonly audio: import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
readonly formula: import("react/jsx-runtime").JSX.Element;
|
|
45
|
+
readonly specialChar: import("react/jsx-runtime").JSX.Element;
|
|
46
|
+
readonly cellBorder: import("react/jsx-runtime").JSX.Element;
|
|
47
|
+
readonly edit: import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
readonly zoomIn: import("react/jsx-runtime").JSX.Element;
|
|
49
|
+
readonly zoomOut: import("react/jsx-runtime").JSX.Element;
|
|
50
|
+
readonly delete: import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
readonly import: import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
readonly saveCopy: import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
readonly json: import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
readonly history: import("react/jsx-runtime").JSX.Element;
|
|
55
|
+
readonly addComment: import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
readonly comments: import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
readonly suggest: import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
readonly suggestions: import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
readonly showEdits: import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
readonly undo: import("react/jsx-runtime").JSX.Element;
|
|
61
|
+
readonly redo: import("react/jsx-runtime").JSX.Element;
|
|
62
|
+
readonly more: import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
readonly divider: import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
readonly pageBreak: import("react/jsx-runtime").JSX.Element;
|
|
65
|
+
readonly lineHeight: import("react/jsx-runtime").JSX.Element;
|
|
66
|
+
};
|
|
67
|
+
export type ToolbarIconKey = keyof typeof toolbarIcons;
|
|
68
|
+
/** A single always-visible toolbar control: icon + plain-language label together, per the redesign's core principle. */
|
|
69
|
+
export declare function ToolbarButton({ icon, label, ariaLabel, pressed, disabled, title, onClick, iconOnly, widePromote }: {
|
|
70
|
+
icon: ToolbarIconKey;
|
|
71
|
+
label: string;
|
|
72
|
+
ariaLabel?: string;
|
|
73
|
+
/** "mixed" renders aria-pressed="mixed" - the selection is partially covered by this mark (see markCoverage in CanonicalAuthorityEditor.tsx). */
|
|
74
|
+
pressed?: boolean | "mixed";
|
|
75
|
+
disabled?: boolean;
|
|
76
|
+
title?: string;
|
|
77
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
78
|
+
/** Only for genuinely space-constrained spots (e.g. a 4-way align cluster) - the accessible name is preserved via ariaLabel/label regardless. */
|
|
79
|
+
iconOnly?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* This is the wide-viewport standalone copy of a tool that also lives in a
|
|
82
|
+
* dropdown (via the matching `widePromote` ToolbarMenuItem below) - hidden
|
|
83
|
+
* by default, shown only past theme.ts's wide-promotion breakpoint. See
|
|
84
|
+
* docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md.
|
|
85
|
+
*/
|
|
86
|
+
widePromote?: boolean;
|
|
87
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
88
|
+
/** One row inside an open dropdown menu - closes the dropdown after acting, matching ContextMenu.tsx's own dismiss-on-action convention. */
|
|
89
|
+
export declare function ToolbarMenuItem({ icon, label, ariaLabel, pressed, disabled, title, onClick, widePromote }: {
|
|
90
|
+
icon?: ToolbarIconKey;
|
|
91
|
+
label: string;
|
|
92
|
+
ariaLabel?: string;
|
|
93
|
+
pressed?: boolean | "mixed";
|
|
94
|
+
disabled?: boolean;
|
|
95
|
+
title?: string;
|
|
96
|
+
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
97
|
+
/** This tool also has a standalone wide-viewport ToolbarButton copy - hide this dropdown/mobile-menu row once that copy is showing (see ToolbarButton's own doc comment). */
|
|
98
|
+
widePromote?: boolean;
|
|
99
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
100
|
+
/**
|
|
101
|
+
* A labeled dropdown for a group's long-tail tools - wires up the
|
|
102
|
+
* `.srte-toolbar-menu`/`.srte-menu` CSS `theme.ts` already defines but the
|
|
103
|
+
* pre-redesign toolbar never used. `<details>/<summary>` gives free
|
|
104
|
+
* keyboard support (Enter/Space to open, native focus handling) without a
|
|
105
|
+
* new open/close state to manage; outside-click dismiss is handled by
|
|
106
|
+
* useDismissDetailsOnOutsideClick above.
|
|
107
|
+
*
|
|
108
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates
|
|
109
|
+
* (docs/bugs/toolbar-dropdown-clipped-by-host-overflow-hidden.md), the same
|
|
110
|
+
* pattern ColorPickerPopover/TableBorderPopover already use - a real host
|
|
111
|
+
* can (and, reported live, does) wrap the editor in an `overflow: hidden`
|
|
112
|
+
* container for its own layout reasons; a `position: absolute` panel
|
|
113
|
+
* anchored inside that container gets visually clipped the moment it needs
|
|
114
|
+
* to extend past the container's own bounds, which is exactly when a
|
|
115
|
+
* narrow host is most likely to need the dropdown in the first place.
|
|
116
|
+
* `position: fixed`'s containing block is normally the viewport, so this
|
|
117
|
+
* escapes ANY ancestor's overflow clipping regardless of DOM depth - unless
|
|
118
|
+
* an ancestor has its own transform/perspective/filter, which becomes the
|
|
119
|
+
* containing block instead (see fixedPositioning.ts, and
|
|
120
|
+
* docs/bugs/toolbar-menu-misplaced-inside-transformed-ancestor.md); the
|
|
121
|
+
* placement math below accounts for that case too.
|
|
122
|
+
*/
|
|
123
|
+
export declare function ToolbarDropdown({ icon, label, priority, children }: {
|
|
124
|
+
icon?: ToolbarIconKey;
|
|
125
|
+
label: string;
|
|
126
|
+
/** Mirrors theme.ts's `[data-srte-priority]` responsive rules - 2 collapses into the mobile "More" menu before 1 (never collapses), leaving priority 3 whole *groups* to collapse first. */
|
|
127
|
+
priority?: 2;
|
|
128
|
+
children: React.ReactNode;
|
|
129
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
130
|
+
/** A visually-separated cluster of controls, optionally collapsing as a whole unit on narrow viewports (theme.ts's `.srte-toolbar-group[data-srte-priority="3"]`). */
|
|
131
|
+
export declare function ToolbarGroup({ priority, children }: {
|
|
132
|
+
priority?: 3;
|
|
133
|
+
children: React.ReactNode;
|
|
134
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
135
|
+
/**
|
|
136
|
+
* The narrow-viewport overflow trigger - `.srte-mobile-more` is CSS-hidden
|
|
137
|
+
* except under theme.ts's `max-width: 639px` rules, which also hide every
|
|
138
|
+
* `[data-srte-priority="2"]`/`[data-srte-priority="3"]` element. Its own
|
|
139
|
+
* menu statically lists every tool that lives inside one of those
|
|
140
|
+
* collapsing dropdowns/groups, so nothing becomes unreachable once the
|
|
141
|
+
* primary row starts shedding groups - this is plain CSS visibility
|
|
142
|
+
* toggling of pre-rendered content, not JS media-query logic, matching how
|
|
143
|
+
* the rest of this responsive system already works.
|
|
144
|
+
*
|
|
145
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates,
|
|
146
|
+
* the same fix and reasoning as `ToolbarDropdown` (docs/bugs/
|
|
147
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md) - this component was
|
|
148
|
+
* NOT covered by that fix at the time (incorrectly assumed to be
|
|
149
|
+
* unaffected since it already had its own separate mobile CSS), but it has
|
|
150
|
+
* the exact same `position: absolute` vulnerability to a host's own
|
|
151
|
+
* `overflow: hidden` container, and since this is the ONLY toolbar
|
|
152
|
+
* overflow affordance on narrow viewports (every ToolbarDropdown hides
|
|
153
|
+
* entirely below the mobile breakpoint), a clipped host is more likely to
|
|
154
|
+
* matter here, not less. Confirmed live: a real narrow host wrapping the
|
|
155
|
+
* editor in `overflow: hidden` computed this panel at `left: -214px` -
|
|
156
|
+
* mostly off-screen, functionally invisible despite being technically
|
|
157
|
+
* present in the DOM.
|
|
158
|
+
*/
|
|
159
|
+
export declare function MobileMoreMenu({ children }: {
|
|
160
|
+
children: React.ReactNode;
|
|
161
|
+
}): import("react/jsx-runtime").JSX.Element;
|