smartrte-react 1.0.0-beta.6 → 1.0.0-beta.8
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 +11 -0
- package/README.md +94 -0
- package/dist/components/CanonicalAuthorityEditor.d.ts +15 -1
- package/dist/components/CanonicalAuthorityEditor.js +59 -23
- package/dist/components/ToolbarPrimitives.d.ts +14 -0
- package/dist/components/ToolbarPrimitives.js +45 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/standalone/classic-editor-embed.js +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +6 -2
- package/dist/toolbarTools.d.ts +89 -0
- package/dist/toolbarTools.js +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.8
|
|
4
|
+
|
|
5
|
+
- Add a `tools` prop (`ClassicEditor`, `CanonicalAuthorityEditor`, and the standalone embed) letting a host hide any individual toolbar tool — Bold, Video, Version history, Review, and around 40 others — without wrapping or restyling the component. Every tool is visible by default; composes with existing gates (a required provider or capability must still be present). See the README's "Hiding individual toolbar tools" section for the full list and usage.
|
|
6
|
+
- Retire the never-shipped `showVersionHistory`/`showReview` props in favor of `tools.versionHistory`/`tools.comments`/`tools.suggestions`.
|
|
7
|
+
- Also export `EditorCapabilityPreset` from the package's public entry point (previously only reachable indirectly through `CanonicalAuthorityEditorProps`).
|
|
8
|
+
|
|
9
|
+
## 1.0.0-beta.7
|
|
10
|
+
|
|
11
|
+
- Fix the mobile "More tools" overflow menu getting clipped almost entirely off-screen when the editor is embedded inside a host container with `overflow: hidden` — the same fix already applied to the desktop toolbar dropdowns, now also covering this menu (the only overflow affordance on narrow viewports).
|
|
12
|
+
- Add the "List preset" control (decimal/lower-alpha/upper-alpha/roman-numeral/outline numbering styles, plus bullet-glyph presets) to the mobile "More tools" menu — previously only reachable from the desktop toolbar's "More list tools" dropdown, leaving narrow-viewport users with no way to use anything beyond plain bulleted/numbered/checklist lists.
|
|
13
|
+
|
|
3
14
|
## 1.0.0-beta.6
|
|
4
15
|
|
|
5
16
|
- Replace the media Edit overlay's plain alt-text prompt with a full "Media details" panel: link (with open-in-new-tab), corner radius, alignment, and license fields (description, source URL, type, version, attribution), matching the pre-canonical editor's own edit-panel field set. A library-picked image's existing license metadata is now carried onto the inserted atom instead of being discarded. Depends on `smartrte-core@1.0.0-beta.3`.
|
package/README.md
CHANGED
|
@@ -96,6 +96,7 @@ export default App;
|
|
|
96
96
|
| `defaultFont` | `string` | `undefined` | Default font family for the editor content |
|
|
97
97
|
| `theme` | `"light" \| "dark"` | `"light"` | Built-in theme mode |
|
|
98
98
|
| `className` | `string` | `undefined` | Custom CSS class for theming via CSS variable overrides |
|
|
99
|
+
| `tools` | `Partial<ToolbarTools>` | every tool `true` | Hide individual toolbar tools (Bold, Video, Version history, etc.) — see [Hiding individual toolbar tools](#hiding-individual-toolbar-tools) below |
|
|
99
100
|
|
|
100
101
|
### Advanced Examples
|
|
101
102
|
|
|
@@ -118,6 +119,99 @@ import { ClassicEditor } from 'smartrte-react';
|
|
|
118
119
|
/>
|
|
119
120
|
```
|
|
120
121
|
|
|
122
|
+
#### Hiding individual toolbar tools
|
|
123
|
+
|
|
124
|
+
The `table`/`media`/`formula` props above turn off a whole *capability* — no
|
|
125
|
+
tables anywhere, or media/formulas removed from the schema entirely. If you
|
|
126
|
+
just want a **smaller toolbar** while keeping every capability intact (e.g.
|
|
127
|
+
hide Video, Audio, Version history, and Review for a specific product
|
|
128
|
+
surface, without touching what content the editor can actually store), use
|
|
129
|
+
`tools` instead. It's a plain object: name the tools you want off, everything
|
|
130
|
+
else stays on by default. No wrapping, no CSS overrides, no forking the
|
|
131
|
+
component.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { ClassicEditor } from 'smartrte-react';
|
|
135
|
+
|
|
136
|
+
<ClassicEditor
|
|
137
|
+
tools={{
|
|
138
|
+
video: false,
|
|
139
|
+
audio: false,
|
|
140
|
+
versionHistory: false,
|
|
141
|
+
comments: false,
|
|
142
|
+
suggestions: false,
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**How it composes with everything else:** a tool only ever needs *both*
|
|
148
|
+
things to be true to show up — your `tools` flag says yes, *and* whatever
|
|
149
|
+
that tool actually depends on is present. So:
|
|
150
|
+
|
|
151
|
+
- `tools.video`/`tools.audio`/`tools.image` also need a `mediaProvider` (or
|
|
152
|
+
`mediaManager`) to be configured at all — turning the flag on can't make
|
|
153
|
+
media insertion appear out of nowhere if you never wired up a provider.
|
|
154
|
+
- `tools.versionHistory` also needs a `versionProvider`; `tools.comments`
|
|
155
|
+
needs a `commentProvider`; `tools.suggestions` needs a `suggestionProvider`.
|
|
156
|
+
- `tools.insertTable` also needs the table capability to actually be enabled
|
|
157
|
+
(i.e. you haven't set `table={false}`) — you can't use `tools` to bring
|
|
158
|
+
back a capability you turned off elsewhere.
|
|
159
|
+
|
|
160
|
+
In other words: `tools` can only ever hide something, never force something
|
|
161
|
+
into existence that isn't otherwise configured. This means it's always safe
|
|
162
|
+
to leave `tools` unset — every consumer who doesn't pass it sees the exact
|
|
163
|
+
toolbar they see today.
|
|
164
|
+
|
|
165
|
+
**Every toggleable key**, grouped the way they appear in the toolbar:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
interface ToolbarTools {
|
|
169
|
+
// Text formatting
|
|
170
|
+
bold, italic, underline, strikethrough, code,
|
|
171
|
+
superscript, subscript, textColor, backgroundColor, fontSize, fontFamily,
|
|
172
|
+
|
|
173
|
+
// Paragraph
|
|
174
|
+
blockType, // the Paragraph/Heading/Code block dropdown
|
|
175
|
+
alignLeft, alignCenter, alignRight, alignJustify, quote,
|
|
176
|
+
|
|
177
|
+
// Lists
|
|
178
|
+
bulletedList, numberedList, checklist,
|
|
179
|
+
listPreset, // the numbered-list style picker (1,2,3 / a,b,c / i,ii,iii / A,B,C / I,II,III)
|
|
180
|
+
|
|
181
|
+
// Insert
|
|
182
|
+
link, removeLink, image, video, audio, insertFormula, specialCharacters, insertTable,
|
|
183
|
+
|
|
184
|
+
// Document
|
|
185
|
+
import,
|
|
186
|
+
saveAsHtml, saveAsMarkdown, saveAsWord, saveAsPdf, saveAsSmartRte,
|
|
187
|
+
versionHistory, comments, suggestions,
|
|
188
|
+
|
|
189
|
+
// History
|
|
190
|
+
undo, redo,
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
A few notes on what's *not* in this list, on purpose: actions that only ever
|
|
195
|
+
apply to something you already have selected — moving a block up/down,
|
|
196
|
+
indenting a list item, adding/removing a table row or column, resizing a
|
|
197
|
+
selected image — aren't individually toggleable. They only make sense as
|
|
198
|
+
part of using the tool that created them (a table, a list, an image), so
|
|
199
|
+
they follow that tool's own visibility rather than needing a separate flag
|
|
200
|
+
each. If you turn off `insertTable`, its row/column tools go with it
|
|
201
|
+
automatically — there's no separate flag to remember.
|
|
202
|
+
|
|
203
|
+
If you're using the exported `CanonicalAuthorityEditor` directly instead of
|
|
204
|
+
`ClassicEditor`, `tools` works exactly the same way. And if you're using the
|
|
205
|
+
standalone embed (`window.SmartRTE.ClassicEditor.init(...)`), pass `tools`
|
|
206
|
+
in the same options object you pass `target`/`value`/etc.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
window.SmartRTE.ClassicEditor.init({
|
|
210
|
+
target: document.getElementById('editor'),
|
|
211
|
+
tools: { video: false, audio: false },
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
121
215
|
#### Complete Example with All Features
|
|
122
216
|
|
|
123
217
|
```tsx
|
|
@@ -6,6 +6,7 @@ import type { CommentProvider } from "../commentProvider.js";
|
|
|
6
6
|
import type { SuggestionProvider } from "../suggestionProvider.js";
|
|
7
7
|
import { type MediaPickerComponent } from "./MediaPicker.js";
|
|
8
8
|
import type { EditorCapabilityPreset } from "../capabilityPresets.js";
|
|
9
|
+
import { type ToolbarTools } from "../toolbarTools.js";
|
|
9
10
|
import { CanonicalEditorRuntime, type SmartEditorChange, type SmartEditorHandle } from "../canonicalEditorRuntime.js";
|
|
10
11
|
export interface CanonicalAuthorityEditorProps {
|
|
11
12
|
/** Initial value only. Later replacements must use SmartEditorHandle.replaceValue. */
|
|
@@ -42,7 +43,20 @@ export interface CanonicalAuthorityEditorProps {
|
|
|
42
43
|
* for images too.
|
|
43
44
|
*/
|
|
44
45
|
mediaManager?: boolean;
|
|
45
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Per-tool toolbar visibility - "developers should be able to hide tools
|
|
48
|
+
* which are not for their use" (docs/bugs/per-tool-toolbar-visibility.md).
|
|
49
|
+
* Every tool defaults to visible; a host only needs to name the ones they
|
|
50
|
+
* want off (`tools={{ video: false, versionHistory: false }}`). Composes
|
|
51
|
+
* with, never overrides, the deeper existing gates: a tool whose required
|
|
52
|
+
* provider is absent (`versionProvider`/`commentProvider`/
|
|
53
|
+
* `suggestionProvider`/`mediaProvider`) or whose plugin was excluded via
|
|
54
|
+
* `preset` (e.g. `insertTable` under `preset="simple"`) still won't
|
|
55
|
+
* render even if explicitly set to `true` here - see ToolbarTools.ts's
|
|
56
|
+
* own doc comment for the full contract and scoping decisions.
|
|
57
|
+
*/
|
|
58
|
+
tools?: Partial<ToolbarTools>;
|
|
59
|
+
/** Host-owned save/list/load/remove boundary for document version history. */
|
|
46
60
|
versionProvider?: VersionProvider;
|
|
47
61
|
/** Host-owned save/list/remove boundary for comment threads. Absent hides the comment toolbar buttons and markers entirely, mirroring versionProvider's absent-disables-the-feature contract. */
|
|
48
62
|
commentProvider?: CommentProvider;
|
|
@@ -22,6 +22,7 @@ import { MediaOverlay } from "./MediaOverlay.js";
|
|
|
22
22
|
import { MediaDetailsPopover } from "./MediaDetailsPopover.js";
|
|
23
23
|
import { ContextMenu } from "./ContextMenu.js";
|
|
24
24
|
import { ToolbarButton, ToolbarDropdown, ToolbarGroup, ToolbarMenuItem, MobileMoreMenu } from "./ToolbarPrimitives.js";
|
|
25
|
+
import { resolveToolbarTools } from "../toolbarTools.js";
|
|
25
26
|
import { exportDocxDocument, importStyledDocxDocument, importPdfDocument } from "smartrte-core/foundation";
|
|
26
27
|
import { printSmartDocumentAsPdf } from "../adapters/pdfPrint.js";
|
|
27
28
|
import { CanonicalEditorRuntime, } from "../canonicalEditorRuntime.js";
|
|
@@ -103,7 +104,7 @@ const parseBorderShorthand = (value) => {
|
|
|
103
104
|
const isCollapsedTextSelection = (selection) => selection.type === "text" && selection.anchor.offset === selection.head.offset
|
|
104
105
|
&& selection.anchor.path.length === selection.head.path.length
|
|
105
106
|
&& selection.anchor.path.every((part, index) => part === selection.head.path[index]);
|
|
106
|
-
export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEditor({ defaultValue, preset, renderFormulaHtml, onChange, onHtmlChange, onClipboardDiagnostic, mediaProvider, mediaPicker: MediaPicker = DefaultMediaPicker, mediaManager = true, versionProvider, commentProvider, suggestionProvider, authorId, placeholder = "Type here…", minHeight = 200, maxHeight = 500, readOnly = false, className, onRuntime, }, forwardedRef) {
|
|
107
|
+
export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEditor({ defaultValue, preset, renderFormulaHtml, onChange, onHtmlChange, onClipboardDiagnostic, mediaProvider, mediaPicker: MediaPicker = DefaultMediaPicker, mediaManager = true, tools, versionProvider, commentProvider, suggestionProvider, authorId, placeholder = "Type here…", minHeight = 200, maxHeight = 500, readOnly = false, className, onRuntime, }, forwardedRef) {
|
|
107
108
|
ensureStyleSheet();
|
|
108
109
|
const rootRef = useRef(null);
|
|
109
110
|
const importRef = useRef(null);
|
|
@@ -324,6 +325,19 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
|
|
|
324
325
|
// an `unknown` node (Phase 10's disable-safety contract, unchanged) -
|
|
325
326
|
// this only hides the *toolbar affordance* for creating new ones.
|
|
326
327
|
const tablesEnabled = Boolean(runtime.editor.schema.nodes.table);
|
|
328
|
+
// Per-tool toolbar visibility (toolbarTools.ts) - `tools` is a pure
|
|
329
|
+
// UI-visibility layer, composed with (never overriding) the deeper
|
|
330
|
+
// existing gates below: a required provider being absent, or a plugin
|
|
331
|
+
// having been excluded via `preset`, still wins even if a tool is
|
|
332
|
+
// explicitly left enabled here.
|
|
333
|
+
const t = resolveToolbarTools(tools);
|
|
334
|
+
const showInsertTable = t.insertTable && tablesEnabled;
|
|
335
|
+
const showImageTool = t.image && Boolean(mediaProvider);
|
|
336
|
+
const showVideoTool = t.video && Boolean(mediaProvider);
|
|
337
|
+
const showAudioTool = t.audio && Boolean(mediaProvider);
|
|
338
|
+
const showVersionHistoryTool = t.versionHistory && Boolean(versionProvider);
|
|
339
|
+
const showCommentsTool = t.comments && Boolean(commentProvider);
|
|
340
|
+
const showSuggestionsTool = t.suggestions && Boolean(suggestionProvider);
|
|
327
341
|
const tableSelected = currentTableScope.kind === "table-grid";
|
|
328
342
|
const selectedTableElement = tableSelected
|
|
329
343
|
? runtime.surface.renderer?.mapping.nodeToDom(currentTableScope.tableId)
|
|
@@ -1413,23 +1427,44 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
|
|
|
1413
1427
|
// twice; below 1440px (including mobile) this copy is what's reachable,
|
|
1414
1428
|
// same as before. "Code" isn't promoted - see the docs/bugs/ writeup on
|
|
1415
1429
|
// why it stayed the dropdown's sole remaining item at wide widths.
|
|
1416
|
-
const textStylesMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "code", label: "Code", pressed: markCoverage("inlineCode"), disabled: readOnly, onClick: () => toggleMark("inlineCode") }), _jsx(ToolbarMenuItem, { icon: "superscript", label: "Superscript", pressed: markCoverage("superscript"), disabled: readOnly, onClick: () => toggleMark("superscript"), widePromote: true }), _jsx(ToolbarMenuItem, { icon: "subscript", label: "Subscript", pressed: markCoverage("subscript"), disabled: readOnly, onClick: () => toggleMark("subscript"), widePromote: true }), _jsx(ToolbarMenuItem, { icon: "textColor", label: "Text colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1430
|
+
const textStylesMenuItems = _jsxs(_Fragment, { children: [t.code && _jsx(ToolbarMenuItem, { icon: "code", label: "Code", pressed: markCoverage("inlineCode"), disabled: readOnly, onClick: () => toggleMark("inlineCode") }), t.superscript && _jsx(ToolbarMenuItem, { icon: "superscript", label: "Superscript", pressed: markCoverage("superscript"), disabled: readOnly, onClick: () => toggleMark("superscript"), widePromote: true }), t.subscript && _jsx(ToolbarMenuItem, { icon: "subscript", label: "Subscript", pressed: markCoverage("subscript"), disabled: readOnly, onClick: () => toggleMark("subscript"), widePromote: true }), t.textColor && _jsx(ToolbarMenuItem, { icon: "textColor", label: "Text colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1417
1431
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1418
1432
|
setColorPopover({ x: rect.left, y: rect.bottom + 4, target: { kind: "mark", markId: "textColor" }, initialValue: currentMarkColor("textColor") });
|
|
1419
|
-
} }), _jsx(ToolbarMenuItem, { icon: "backgroundColor", label: "Background colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1433
|
+
} }), t.backgroundColor && _jsx(ToolbarMenuItem, { icon: "backgroundColor", label: "Background colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1420
1434
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1421
1435
|
setColorPopover({ x: rect.left, y: rect.bottom + 4, target: { kind: "mark", markId: "backgroundColor" }, initialValue: currentMarkColor("backgroundColor") });
|
|
1422
|
-
} }), _jsx(ToolbarMenuItem, { icon: "fontSize", label: "Font size", disabled: readOnly, onClick: () => applyAttributedMark("fontSize"), widePromote: true }), _jsx(ToolbarMenuItem, { icon: "fontFamily", label: "Font family", disabled: readOnly, onClick: () => applyAttributedMark("fontFamily"), widePromote: true })] });
|
|
1436
|
+
} }), t.fontSize && _jsx(ToolbarMenuItem, { icon: "fontSize", label: "Font size", disabled: readOnly, onClick: () => applyAttributedMark("fontSize"), widePromote: true }), t.fontFamily && _jsx(ToolbarMenuItem, { icon: "fontFamily", label: "Font family", disabled: readOnly, onClick: () => applyAttributedMark("fontFamily"), widePromote: true })] });
|
|
1437
|
+
const showMoreTextStyles = t.code || t.superscript || t.subscript || t.textColor || t.backgroundColor || t.fontSize || t.fontFamily;
|
|
1423
1438
|
const paragraphToolsMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "moveUp", label: "Move block up", disabled: readOnly, onClick: () => runBlock("up") }), _jsx(ToolbarMenuItem, { icon: "moveDown", label: "Move block down", disabled: readOnly, onClick: () => runBlock("down") }), _jsx(ToolbarMenuItem, { icon: "indent", label: "Indent block", disabled: readOnly, onClick: () => runBlock("indent") }), _jsx(ToolbarMenuItem, { icon: "outdent", label: "Outdent block", disabled: readOnly, onClick: () => runBlock("outdent") })] });
|
|
1439
|
+
/**
|
|
1440
|
+
* Named multi-level marker presets (decimal/lower-alpha/upper-alpha/
|
|
1441
|
+
* roman/outline) - a distinct control from the plain Bulleted/Numbered/
|
|
1442
|
+
* Checklist buttons above, which only toggle a raw list style. Extracted
|
|
1443
|
+
* to a variable so it can be reused verbatim in both the desktop "More
|
|
1444
|
+
* list tools" dropdown and MobileMoreMenu below - it was previously
|
|
1445
|
+
* hardcoded only into the dropdown, meaning a narrow/mobile viewport
|
|
1446
|
+
* (where every ToolbarDropdown hides entirely, per theme.ts's
|
|
1447
|
+
* 639px breakpoint) had no way to reach it at all - see
|
|
1448
|
+
* docs/bugs/list-preset-missing-from-mobile-more-menu.md.
|
|
1449
|
+
*/
|
|
1450
|
+
const listPresetSelect = t.listPreset && _jsx("div", { style: { padding: "4px 8px" }, children: _jsxs("select", { "aria-label": "List preset", title: "List type / preset", disabled: readOnly || currentListParts.length !== 1, value: currentListPreset, onChange: (event) => {
|
|
1451
|
+
const preset = event.target.value;
|
|
1452
|
+
if (!preset || currentListParts.length !== 1)
|
|
1453
|
+
return;
|
|
1454
|
+
// A preset choice applies to the whole list, regardless of how deep
|
|
1455
|
+
// the cursor is nested — see outermostListId.
|
|
1456
|
+
const rootId = outermostListId(currentListParts[0].listId);
|
|
1457
|
+
runtime.executeOperations(setListPreset(runtime.editor.document, { ...currentListParts[0], listId: rootId }, { preset }, blockContext()), { preserveSelectionById: true });
|
|
1458
|
+
}, style: { width: "100%" }, children: [_jsx("option", { value: "", children: "List preset" }), SMART_LIST_PRESETS.map((preset) => _jsx("option", { value: preset.id, children: preset.kind === "bullet" ? `Bullet · ${preset.label}` : `Number · ${preset.label}` }, preset.id))] }) });
|
|
1424
1459
|
const listToolsMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "checkSquare", label: "Check selected items", pressed: currentListScope.kind === "list-selection" && currentListScope.items.every((item) => findNode(runtime.editor.document, item.itemId)?.attrs?.checked === true), disabled: readOnly || currentList?.attrs?.checkable !== true, onClick: toggleCheckedItems }), _jsx(ToolbarMenuItem, { icon: "indent", label: "Indent list item", disabled: readOnly || !canIndent, onClick: () => runList("indent") }), _jsx(ToolbarMenuItem, { icon: "outdent", label: "Outdent list item", disabled: readOnly || currentListParts.length === 0, onClick: () => runList("outdent") }), _jsx(ToolbarMenuItem, { icon: "moveUp", label: "Move item up", disabled: readOnly || !canMoveUp, onClick: () => runList("up") }), _jsx(ToolbarMenuItem, { icon: "moveDown", label: "Move item down", disabled: readOnly || !canMoveDown, onClick: () => runList("down") }), _jsx(ToolbarMenuItem, { icon: "restart", label: "Restart numbering", disabled: readOnly || !orderedList, onClick: restartNumbering }), _jsx(ToolbarMenuItem, { icon: "continueNumbering", label: "Continue numbering", disabled: readOnly || !orderedList, onClick: () => runtime.executeOperations(continueListNumbering(runtime.editor.document, currentListScope, {}, blockContext()), { preserveSelectionById: true }) })] });
|
|
1425
1460
|
// Remove link/Insert formula/Special characters also get a standalone
|
|
1426
1461
|
// widePromote ToolbarButton (see textStylesMenuItems's own comment above
|
|
1427
1462
|
// for the mechanism) - Insert video/audio and the selected-media actions
|
|
1428
1463
|
// stay dropdown-only, matching the report's own named tool list.
|
|
1429
|
-
const insertMoreMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "unlink", label: "Remove link", disabled: readOnly, onClick: removeLink, widePromote: true }), _jsx(ToolbarMenuItem, { icon: "video", label: "Insert video", disabled: readOnly
|
|
1464
|
+
const insertMoreMenuItems = _jsxs(_Fragment, { children: [t.removeLink && _jsx(ToolbarMenuItem, { icon: "unlink", label: "Remove link", disabled: readOnly, onClick: removeLink, widePromote: true }), showVideoTool && _jsx(ToolbarMenuItem, { icon: "video", label: "Insert video", disabled: readOnly, onClick: () => setMediaKind("video") }), showAudioTool && _jsx(ToolbarMenuItem, { icon: "audio", label: "Insert audio", disabled: readOnly, onClick: () => setMediaKind("audio") }), t.insertFormula && _jsx(ToolbarMenuItem, { icon: "formula", label: "Insert formula", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1430
1465
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1431
1466
|
setFormulaLibraryPopover({ x: rect.left, y: rect.bottom + 4 });
|
|
1432
|
-
} }), _jsx(ToolbarMenuItem, { icon: "specialChar", label: "Special characters", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1467
|
+
} }), t.specialCharacters && _jsx(ToolbarMenuItem, { icon: "specialChar", label: "Special characters", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1433
1468
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1434
1469
|
setSpecialCharPopover({ x: rect.left, y: rect.bottom + 4 });
|
|
1435
1470
|
} }), _jsx(ToolbarMenuItem, { icon: "edit", label: "Edit selected media", disabled: readOnly || !mediaAtomSelected, onClick: () => editSelectedAtom() }), _jsx(ToolbarMenuItem, { icon: "zoomIn", label: "Enlarge selected media", disabled: readOnly || !resizableAtomSelected, onClick: () => editSelectedAtom(20) }), _jsx(ToolbarMenuItem, { icon: "zoomOut", label: "Shrink selected media", disabled: readOnly || !resizableAtomSelected, onClick: () => editSelectedAtom(-20) }), _jsx(ToolbarMenuItem, { icon: "delete", label: "Delete selected media", disabled: readOnly || !atomSelected, onClick: () => runtime.executeOperations(deleteAtom(runtime.editor.document, atomScope(), {}, blockContext())) })] });
|
|
@@ -1446,32 +1481,33 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
|
|
|
1446
1481
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1447
1482
|
setTableBorderPopover({ x: rect.left, y: rect.bottom + 4, scope: scope, initial: currentCellBorderDraft(scope) });
|
|
1448
1483
|
} })] });
|
|
1449
|
-
const saveCopyMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { label: "Save as HTML", onClick: () => runExport("html") }), _jsx(ToolbarMenuItem, { label: "Save as Markdown", onClick: () => runExport("markdown") }), _jsx(ToolbarMenuItem, { label: "Save as Word document", onClick: () => void runDocxExport() }), _jsx(ToolbarMenuItem, { label: "Save as PDF", onClick: runPdfExport }), _jsx(ToolbarMenuItem, { icon: "json", label: "Save as Smart RTE file", onClick: () => runExport("native") })] });
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1484
|
+
const saveCopyMenuItems = _jsxs(_Fragment, { children: [t.saveAsHtml && _jsx(ToolbarMenuItem, { label: "Save as HTML", onClick: () => runExport("html") }), t.saveAsMarkdown && _jsx(ToolbarMenuItem, { label: "Save as Markdown", onClick: () => runExport("markdown") }), t.saveAsWord && _jsx(ToolbarMenuItem, { label: "Save as Word document", onClick: () => void runDocxExport() }), t.saveAsPdf && _jsx(ToolbarMenuItem, { label: "Save as PDF", onClick: runPdfExport }), t.saveAsSmartRte && _jsx(ToolbarMenuItem, { icon: "json", label: "Save as Smart RTE file", onClick: () => runExport("native") })] });
|
|
1485
|
+
const showSaveCopy = t.saveAsHtml || t.saveAsMarkdown || t.saveAsWord || t.saveAsPdf || t.saveAsSmartRte;
|
|
1486
|
+
// "Review" splits into two independently-toggleable tools (comments,
|
|
1487
|
+
// suggestions) - not an arbitrary split, it maps directly onto the two
|
|
1488
|
+
// already-separate providers this architecture has (commentProvider,
|
|
1489
|
+
// suggestionProvider). Kept as ONE dropdown trigger (labeled "Review")
|
|
1490
|
+
// rather than two, showing whichever half(s) are actually enabled.
|
|
1491
|
+
const commentsMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "addComment", label: "Add comment", disabled: !canComment, onClick: startComment }), _jsx(ToolbarMenuItem, { icon: "comments", label: "Comments", pressed: commentPanelOpen, disabled: !commentProvider, onClick: () => setCommentPanelOpen((open) => !open) })] });
|
|
1492
|
+
const suggestionsMenuItems = _jsxs(_Fragment, { children: [_jsx(ToolbarMenuItem, { icon: "suggest", label: "Suggest deletion", disabled: !canSuggestDelete, onClick: suggestDelete }), _jsx(ToolbarMenuItem, { icon: "suggest", label: "Suggest insertion", disabled: readOnly || !suggestionProvider, onClick: startSuggestInsert }), _jsx(ToolbarMenuItem, { icon: "suggest", label: "Suggest removing this", disabled: readOnly || !suggestionProvider, onClick: suggestBlockRemoval }), _jsx(ToolbarMenuItem, { icon: "suggestions", label: "Suggestions", pressed: suggestionPanelOpen, disabled: !suggestionProvider, onClick: () => setSuggestionPanelOpen((open) => !open) }), _jsx(ToolbarMenuItem, { icon: "showEdits", label: "Show edits", pressed: trackChangesEnabled, title: "Tracks ordinary typing and edits within a single paragraph as suggestions. Merging paragraphs together - typing over a selection that spans multiple paragraphs, or pressing Backspace/Delete at a paragraph boundary - still applies directly and can't be reviewed as a suggestion.", disabled: readOnly || !suggestionProvider, onClick: () => setTrackChangesEnabled((enabled) => !enabled) })] });
|
|
1493
|
+
const reviewMenuItems = _jsxs(_Fragment, { children: [showCommentsTool && commentsMenuItems, showCommentsTool && showSuggestionsTool && _jsx("div", { className: "srte-menu-separator" }), showSuggestionsTool && suggestionsMenuItems] });
|
|
1494
|
+
const showReviewDropdown = showCommentsTool || showSuggestionsTool;
|
|
1495
|
+
return _jsxs("section", { className: `srte-root srte-editor srte-canonical-authority${className ? ` ${className}` : ""}`, "data-smart-authority": "canonical", children: [_jsxs("div", { className: "srte-toolbar", role: "toolbar", "aria-label": "Formatting toolbar", children: [_jsxs(ToolbarGroup, { children: [t.bold && _jsx(ToolbarButton, { icon: "bold", label: "Bold", pressed: markCoverage("bold"), disabled: readOnly, onClick: () => toggleMark("bold") }), t.italic && _jsx(ToolbarButton, { icon: "italic", label: "Italic", pressed: markCoverage("italic"), disabled: readOnly, onClick: () => toggleMark("italic") }), t.underline && _jsx(ToolbarButton, { icon: "underline", label: "Underline", pressed: markCoverage("underline"), disabled: readOnly, onClick: () => toggleMark("underline") }), t.strikethrough && _jsx(ToolbarButton, { icon: "strikethrough", label: "Strikethrough", pressed: markCoverage("strikethrough"), disabled: readOnly, onClick: () => toggleMark("strikethrough") }), t.superscript && _jsx(ToolbarButton, { icon: "superscript", label: "Superscript", pressed: markCoverage("superscript"), disabled: readOnly, onClick: () => toggleMark("superscript"), widePromote: true }), t.subscript && _jsx(ToolbarButton, { icon: "subscript", label: "Subscript", pressed: markCoverage("subscript"), disabled: readOnly, onClick: () => toggleMark("subscript"), widePromote: true }), t.textColor && _jsx(ToolbarButton, { icon: "textColor", label: "Text colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1452
1496
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1453
1497
|
setColorPopover({ x: rect.left, y: rect.bottom + 4, target: { kind: "mark", markId: "textColor" }, initialValue: currentMarkColor("textColor") });
|
|
1454
|
-
} }), _jsx(ToolbarButton, { icon: "backgroundColor", label: "Background colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1498
|
+
} }), t.backgroundColor && _jsx(ToolbarButton, { icon: "backgroundColor", label: "Background colour", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1455
1499
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1456
1500
|
setColorPopover({ x: rect.left, y: rect.bottom + 4, target: { kind: "mark", markId: "backgroundColor" }, initialValue: currentMarkColor("backgroundColor") });
|
|
1457
|
-
} }), _jsx(ToolbarButton, { icon: "fontSize", label: "Font size", disabled: readOnly, onClick: () => applyAttributedMark("fontSize"), widePromote: true }), _jsx(ToolbarButton, { icon: "fontFamily", label: "Font family", disabled: readOnly, onClick: () => applyAttributedMark("fontFamily"), widePromote: true }), _jsx(ToolbarDropdown, { icon: "textColor", label: "More text styles", priority: 2, children: textStylesMenuItems })] }), _jsxs(ToolbarGroup, { children: [_jsxs("select", { "aria-label": "Block type", title: "Block type", value: currentBlockType, disabled: readOnly, onChange: (event) => transactBlock(setBlockTypeCommand(runtime.editor.document, blockScope(), {
|
|
1501
|
+
} }), t.fontSize && _jsx(ToolbarButton, { icon: "fontSize", label: "Font size", disabled: readOnly, onClick: () => applyAttributedMark("fontSize"), widePromote: true }), t.fontFamily && _jsx(ToolbarButton, { icon: "fontFamily", label: "Font family", disabled: readOnly, onClick: () => applyAttributedMark("fontFamily"), widePromote: true }), showMoreTextStyles && _jsx(ToolbarDropdown, { icon: "textColor", label: "More text styles", priority: 2, children: textStylesMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.blockType && _jsxs("select", { "aria-label": "Block type", title: "Block type", value: currentBlockType, disabled: readOnly, onChange: (event) => transactBlock(setBlockTypeCommand(runtime.editor.document, blockScope(), {
|
|
1458
1502
|
type: event.target.value === "paragraph" ? "paragraph" : event.target.value === "code_block" ? "code_block" : "heading",
|
|
1459
1503
|
attrs: event.target.value.startsWith("heading-") ? { level: Number(event.target.value.slice(8)) } : {},
|
|
1460
|
-
}, blockContext())), children: [_jsx("option", { value: "paragraph", children: "Paragraph" }), Array.from({ length: 6 }, (_, index) => _jsxs("option", { value: `heading-${index + 1}`, children: ["Heading ", index + 1] }, index + 1)), _jsx("option", { value: "code_block", children: "Code block" })] }), _jsx(ToolbarButton, { icon: "alignLeft", label: "Align left", ariaLabel: "Align left", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "left" } }, blockContext())) }), _jsx(ToolbarButton, { icon: "alignCenter", label: "Align center", ariaLabel: "Align center", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "center" } }, blockContext())) }), _jsx(ToolbarButton, { icon: "alignRight", label: "Align right", ariaLabel: "Align right", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "right" } }, blockContext())) }), _jsx(ToolbarButton, { icon: "alignJustify", label: "Justify", ariaLabel: "Align justify", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "justify" } }, blockContext())) }), _jsx(ToolbarButton, { icon: "quote", label: "Quote", ariaLabel: "Blockquote", disabled: readOnly, onClick: toggleBlockquote }), _jsx(ToolbarDropdown, { icon: "moveUp", label: "More paragraph tools", priority: 2, children: paragraphToolsMenuItems })] }), _jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { icon: "bulletedList", label: "Bulleted list", ariaLabel: "Bulleted list", pressed: listStyleActive("disc"), disabled: readOnly, onClick: () => toggleList("disc") }), _jsx(ToolbarButton, { icon: "numberedList", label: "Numbered list", ariaLabel: "Numbered list", pressed: listStyleActive("decimal"), disabled: readOnly, onClick: () => toggleList("decimal") }), _jsx(ToolbarButton, { icon: "checklist", label: "Checklist", ariaLabel: "Checklist", pressed: listStyleActive("disc", true), disabled: readOnly, onClick: () => toggleList("disc", true) }), _jsxs(ToolbarDropdown, { icon: "restart", label: "More list tools", priority: 2, children: [_jsx("div", {
|
|
1461
|
-
const preset = event.target.value;
|
|
1462
|
-
if (!preset || currentListParts.length !== 1)
|
|
1463
|
-
return;
|
|
1464
|
-
// A preset choice applies to the whole list, regardless of how deep
|
|
1465
|
-
// the cursor is nested — see outermostListId.
|
|
1466
|
-
const rootId = outermostListId(currentListParts[0].listId);
|
|
1467
|
-
runtime.executeOperations(setListPreset(runtime.editor.document, { ...currentListParts[0], listId: rootId }, { preset }, blockContext()), { preserveSelectionById: true });
|
|
1468
|
-
}, style: { width: "100%" }, children: [_jsx("option", { value: "", children: "List preset" }), SMART_LIST_PRESETS.map((preset) => _jsx("option", { value: preset.id, children: preset.kind === "bullet" ? `Bullet · ${preset.label}` : `Number · ${preset.label}` }, preset.id))] }) }), _jsx("div", { className: "srte-menu-separator" }), listToolsMenuItems] })] }), _jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { icon: "link", label: "Link", ariaLabel: "Insert or edit link", disabled: readOnly, onClick: openLinkPopover }), _jsx(ToolbarButton, { icon: "image", label: "Image", ariaLabel: "Insert image", disabled: readOnly || !mediaProvider, onClick: () => setMediaKind("image") }), _jsx(ToolbarButton, { icon: "unlink", label: "Remove link", disabled: readOnly, onClick: removeLink, widePromote: true }), _jsx(ToolbarButton, { icon: "formula", label: "Insert formula", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1504
|
+
}, blockContext())), children: [_jsx("option", { value: "paragraph", children: "Paragraph" }), Array.from({ length: 6 }, (_, index) => _jsxs("option", { value: `heading-${index + 1}`, children: ["Heading ", index + 1] }, index + 1)), _jsx("option", { value: "code_block", children: "Code block" })] }), t.alignLeft && _jsx(ToolbarButton, { icon: "alignLeft", label: "Align left", ariaLabel: "Align left", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "left" } }, blockContext())) }), t.alignCenter && _jsx(ToolbarButton, { icon: "alignCenter", label: "Align center", ariaLabel: "Align center", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "center" } }, blockContext())) }), t.alignRight && _jsx(ToolbarButton, { icon: "alignRight", label: "Align right", ariaLabel: "Align right", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "right" } }, blockContext())) }), t.alignJustify && _jsx(ToolbarButton, { icon: "alignJustify", label: "Justify", ariaLabel: "Align justify", iconOnly: true, disabled: readOnly, onClick: () => transactBlock(setBlockAttributes(runtime.editor.document, blockScope(), { attrs: { align: "justify" } }, blockContext())) }), t.quote && _jsx(ToolbarButton, { icon: "quote", label: "Quote", ariaLabel: "Blockquote", disabled: readOnly, onClick: toggleBlockquote }), _jsx(ToolbarDropdown, { icon: "moveUp", label: "More paragraph tools", priority: 2, children: paragraphToolsMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.bulletedList && _jsx(ToolbarButton, { icon: "bulletedList", label: "Bulleted list", ariaLabel: "Bulleted list", pressed: listStyleActive("disc"), disabled: readOnly, onClick: () => toggleList("disc") }), t.numberedList && _jsx(ToolbarButton, { icon: "numberedList", label: "Numbered list", ariaLabel: "Numbered list", pressed: listStyleActive("decimal"), disabled: readOnly, onClick: () => toggleList("decimal") }), t.checklist && _jsx(ToolbarButton, { icon: "checklist", label: "Checklist", ariaLabel: "Checklist", pressed: listStyleActive("disc", true), disabled: readOnly, onClick: () => toggleList("disc", true) }), _jsxs(ToolbarDropdown, { icon: "restart", label: "More list tools", priority: 2, children: [listPresetSelect, t.listPreset && _jsx("div", { className: "srte-menu-separator" }), listToolsMenuItems] })] }), _jsxs(ToolbarGroup, { children: [t.link && _jsx(ToolbarButton, { icon: "link", label: "Link", ariaLabel: "Insert or edit link", disabled: readOnly, onClick: openLinkPopover }), showImageTool && _jsx(ToolbarButton, { icon: "image", label: "Image", ariaLabel: "Insert image", disabled: readOnly, onClick: () => setMediaKind("image") }), t.removeLink && _jsx(ToolbarButton, { icon: "unlink", label: "Remove link", disabled: readOnly, onClick: removeLink, widePromote: true }), t.insertFormula && _jsx(ToolbarButton, { icon: "formula", label: "Insert formula", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1469
1505
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1470
1506
|
setFormulaLibraryPopover({ x: rect.left, y: rect.bottom + 4 });
|
|
1471
|
-
} }), _jsx(ToolbarButton, { icon: "specialChar", label: "Special characters", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1507
|
+
} }), t.specialCharacters && _jsx(ToolbarButton, { icon: "specialChar", label: "Special characters", disabled: readOnly, widePromote: true, onClick: (event) => {
|
|
1472
1508
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1473
1509
|
setSpecialCharPopover({ x: rect.left, y: rect.bottom + 4 });
|
|
1474
|
-
} }), _jsx(ToolbarDropdown, { icon: "video", label: "More to insert", priority: 2, children: insertMoreMenuItems })] }),
|
|
1510
|
+
} }), _jsx(ToolbarDropdown, { icon: "video", label: "More to insert", priority: 2, children: insertMoreMenuItems })] }), showInsertTable && _jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { icon: "table", label: "Insert table", ariaLabel: "Insert table", disabled: readOnly, onClick: (event) => {
|
|
1475
1511
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1476
1512
|
setTableSizePopover({ x: rect.left, y: rect.bottom + 4 });
|
|
1477
1513
|
} }), _jsx(ToolbarDropdown, { icon: "mergeCells", label: "Table tools", priority: 2, children: tableToolsMenuItems })] }), _jsx("input", { ref: importRef, type: "file", accept: ".html,.htm,.md,.markdown,.docx,.pdf,text/html,text/markdown,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf", hidden: true, onChange: (event) => {
|
|
@@ -1479,7 +1515,7 @@ export const CanonicalAuthorityEditor = forwardRef(function CanonicalAuthorityEd
|
|
|
1479
1515
|
if (file)
|
|
1480
1516
|
void runImport(file);
|
|
1481
1517
|
event.currentTarget.value = "";
|
|
1482
|
-
} }), _jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { icon: "import", label: "Import", ariaLabel: "Import document", disabled: readOnly, onClick: () => importRef.current?.click() }), _jsx(ToolbarDropdown, { icon: "saveCopy", label: "Save a copy", priority: 2, children: saveCopyMenuItems }), _jsx(ToolbarButton, { icon: "history", label: "Version history", ariaLabel: "Version history", disabled: readOnly
|
|
1518
|
+
} }), _jsxs(ToolbarGroup, { children: [t.import && _jsx(ToolbarButton, { icon: "import", label: "Import", ariaLabel: "Import document", disabled: readOnly, onClick: () => importRef.current?.click() }), showSaveCopy && _jsx(ToolbarDropdown, { icon: "saveCopy", label: "Save a copy", priority: 2, children: saveCopyMenuItems }), showVersionHistoryTool && _jsx(ToolbarButton, { icon: "history", label: "Version history", ariaLabel: "Version history", disabled: readOnly, onClick: () => setVersionHistoryOpen(true) }), showReviewDropdown && _jsx(ToolbarDropdown, { icon: "comments", label: "Review", priority: 2, children: reviewMenuItems })] }), _jsxs(ToolbarGroup, { children: [t.undo && _jsx(ToolbarButton, { icon: "undo", label: "Undo", ariaLabel: "Undo", disabled: readOnly, onClick: () => { runtime.editor.undo(); runtime.focus(); } }), t.redo && _jsx(ToolbarButton, { icon: "redo", label: "Redo", ariaLabel: "Redo", disabled: readOnly, onClick: () => { runtime.editor.redo(); runtime.focus(); } })] }), _jsxs(MobileMoreMenu, { children: [textStylesMenuItems, _jsx("div", { className: "srte-menu-separator" }), paragraphToolsMenuItems, _jsx("div", { className: "srte-menu-separator" }), listPresetSelect, listToolsMenuItems, _jsx("div", { className: "srte-menu-separator" }), insertMoreMenuItems, showInsertTable && _jsxs(_Fragment, { children: [_jsx("div", { className: "srte-menu-separator" }), tableToolsMenuItems] }), showSaveCopy && _jsxs(_Fragment, { children: [_jsx("div", { className: "srte-menu-separator" }), saveCopyMenuItems] }), showReviewDropdown && _jsxs(_Fragment, { children: [_jsx("div", { className: "srte-menu-separator" }), reviewMenuItems] })] })] }), mediaKind === "image" && mediaManager && mediaProvider && _jsx(MediaManager, { open: true, onClose: () => setMediaKind(null), adapter: mediaManagerAdapter, onSelect: selectFromMediaManager }), mediaKind && mediaProvider && !(mediaKind === "image" && mediaManager) && _jsx(MediaPicker, { kind: mediaKind, onPick: (file) => void insertMediaFile(mediaKind, file), onCancel: () => setMediaKind(null) }), showVersionHistoryTool && _jsx(VersionHistoryPanel, { open: versionHistoryOpen, onClose: () => setVersionHistoryOpen(false), runtime: runtime, versionProvider: versionProvider }), showCommentsTool && rootRef.current && runtime.surface.renderer?.mapping && _jsx(CommentMarkers, { positions: runtime.editor.positions, mapping: runtime.surface.renderer.mapping, containerElement: rootRef.current, threads: threads, activeThreadId: activeThreadId, onSelectThread: selectThread }), showCommentsTool && _jsx(CommentThreadPanel, { open: commentPanelOpen, onClose: () => { setCommentPanelOpen(false); setPendingCommentRange(null); }, threads: threads, activeThreadId: activeThreadId, onSelectThread: selectThread, pendingRange: pendingCommentRange, onCreateThread: createThread, onReply: replyToThread, onResolve: setThreadResolved, onDelete: deleteThread, busyThreadId: busyThreadId }), showSuggestionsTool && rootRef.current && runtime.surface.renderer?.mapping && _jsx(StructuralSuggestionMarkers, { positions: runtime.editor.positions, mapping: runtime.surface.renderer.mapping, containerElement: rootRef.current, suggestions: structuralSuggestions, activeSuggestionId: activeSuggestionId, onSelectSuggestion: selectSuggestion }), showSuggestionsTool && _jsx(SuggestionPanel, { open: suggestionPanelOpen, onClose: () => { setSuggestionPanelOpen(false); setPendingSuggestInsertAt(null); }, document: runtime.editor.document, structuralSuggestions: structuralSuggestions, activeId: activeSuggestionId, onSelect: selectSuggestion, pendingInsert: Boolean(pendingSuggestInsertAt), onSubmitInsert: submitSuggestInsert, onAcceptInline: acceptInlineSuggestion, onRejectInline: rejectInlineSuggestion, onAcceptStructural: acceptStructural, onRejectStructural: rejectStructural, busyId: busySuggestionId }), linkPopover && _jsx(LinkEditorPopover, { x: linkPopover.x, y: linkPopover.y, initialHref: linkPopover.href, initialOpenInNewTab: linkPopover.openInNewTab, showTextInput: linkPopover.collapsed && !linkPopover.editingExisting, showRemove: linkPopover.editingExisting, autoFocus: !linkPopover.autoTriggered, onApply: applyLink, onRemove: removeLink, onCancel: () => {
|
|
1483
1519
|
setLinkOverlayDismissedAt(runtime.editor.selection.head);
|
|
1484
1520
|
setLinkPopover(null);
|
|
1485
1521
|
runtime.focus();
|
|
@@ -135,6 +135,20 @@ export declare function ToolbarGroup({ priority, children }: {
|
|
|
135
135
|
* primary row starts shedding groups - this is plain CSS visibility
|
|
136
136
|
* toggling of pre-rendered content, not JS media-query logic, matching how
|
|
137
137
|
* the rest of this responsive system already works.
|
|
138
|
+
*
|
|
139
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates,
|
|
140
|
+
* the same fix and reasoning as `ToolbarDropdown` (docs/bugs/
|
|
141
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md) - this component was
|
|
142
|
+
* NOT covered by that fix at the time (incorrectly assumed to be
|
|
143
|
+
* unaffected since it already had its own separate mobile CSS), but it has
|
|
144
|
+
* the exact same `position: absolute` vulnerability to a host's own
|
|
145
|
+
* `overflow: hidden` container, and since this is the ONLY toolbar
|
|
146
|
+
* overflow affordance on narrow viewports (every ToolbarDropdown hides
|
|
147
|
+
* entirely below the mobile breakpoint), a clipped host is more likely to
|
|
148
|
+
* matter here, not less. Confirmed live: a real narrow host wrapping the
|
|
149
|
+
* editor in `overflow: hidden` computed this panel at `left: -214px` -
|
|
150
|
+
* mostly off-screen, functionally invisible despite being technically
|
|
151
|
+
* present in the DOM.
|
|
138
152
|
*/
|
|
139
153
|
export declare function MobileMoreMenu({ children }: {
|
|
140
154
|
children: React.ReactNode;
|
|
@@ -176,9 +176,53 @@ export function ToolbarGroup({ priority, children }) {
|
|
|
176
176
|
* primary row starts shedding groups - this is plain CSS visibility
|
|
177
177
|
* toggling of pre-rendered content, not JS media-query logic, matching how
|
|
178
178
|
* the rest of this responsive system already works.
|
|
179
|
+
*
|
|
180
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates,
|
|
181
|
+
* the same fix and reasoning as `ToolbarDropdown` (docs/bugs/
|
|
182
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md) - this component was
|
|
183
|
+
* NOT covered by that fix at the time (incorrectly assumed to be
|
|
184
|
+
* unaffected since it already had its own separate mobile CSS), but it has
|
|
185
|
+
* the exact same `position: absolute` vulnerability to a host's own
|
|
186
|
+
* `overflow: hidden` container, and since this is the ONLY toolbar
|
|
187
|
+
* overflow affordance on narrow viewports (every ToolbarDropdown hides
|
|
188
|
+
* entirely below the mobile breakpoint), a clipped host is more likely to
|
|
189
|
+
* matter here, not less. Confirmed live: a real narrow host wrapping the
|
|
190
|
+
* editor in `overflow: hidden` computed this panel at `left: -214px` -
|
|
191
|
+
* mostly off-screen, functionally invisible despite being technically
|
|
192
|
+
* present in the DOM.
|
|
179
193
|
*/
|
|
180
194
|
export function MobileMoreMenu({ children }) {
|
|
181
195
|
const detailsRef = useRef(null);
|
|
182
196
|
useDismissDetailsOnOutsideClick(detailsRef);
|
|
183
|
-
|
|
197
|
+
const [placement, setPlacement] = useState(null);
|
|
198
|
+
useLayoutEffect(() => {
|
|
199
|
+
const details = detailsRef.current;
|
|
200
|
+
if (!details)
|
|
201
|
+
return;
|
|
202
|
+
const recompute = () => {
|
|
203
|
+
if (!details.open) {
|
|
204
|
+
setPlacement(null);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const margin = 8;
|
|
208
|
+
const triggerRect = details.getBoundingClientRect();
|
|
209
|
+
const menu = details.querySelector(":scope > .srte-menu");
|
|
210
|
+
const menuWidth = menu?.offsetWidth ?? Math.min(280, window.innerWidth - 16);
|
|
211
|
+
const menuHeight = menu?.offsetHeight ?? 0;
|
|
212
|
+
const viewportWidth = window.innerWidth;
|
|
213
|
+
const viewportHeight = window.innerHeight;
|
|
214
|
+
// Right-aligned to the trigger, matching this menu's original
|
|
215
|
+
// CSS-only `right: 0` intent - clamped into the viewport instead of
|
|
216
|
+
// being allowed to run off it or get clipped by an ancestor.
|
|
217
|
+
const preferredLeft = triggerRect.right - menuWidth;
|
|
218
|
+
const left = Math.min(Math.max(margin, preferredLeft), Math.max(margin, viewportWidth - menuWidth - margin));
|
|
219
|
+
const overflowsBottom = triggerRect.bottom + 6 + menuHeight > viewportHeight - margin;
|
|
220
|
+
const top = overflowsBottom ? Math.max(margin, triggerRect.top - menuHeight - 6) : triggerRect.bottom + 6;
|
|
221
|
+
setPlacement({ left, top });
|
|
222
|
+
};
|
|
223
|
+
recompute();
|
|
224
|
+
details.addEventListener("toggle", recompute);
|
|
225
|
+
return () => details.removeEventListener("toggle", recompute);
|
|
226
|
+
}, []);
|
|
227
|
+
return _jsxs("details", { ref: detailsRef, className: "srte-toolbar-menu srte-mobile-more", children: [_jsx("summary", { className: "srte-tool-button srte-menu-trigger", "aria-label": "More tools", onMouseDown: (event) => event.preventDefault(), children: toolbarIcons.more }), _jsx("div", { className: "srte-menu", role: "menu", "data-srte-menu-fixed": "true", style: { left: placement?.left ?? 0, top: placement?.top ?? 0, visibility: placement ? "visible" : "hidden" }, children: children })] });
|
|
184
228
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export { ClassicEditor } from './components/ClassicEditorAuthority.js';
|
|
|
2
2
|
export type { ClassicEditorProps } from './components/ClassicEditorAuthority.js';
|
|
3
3
|
export { CanonicalAuthorityEditor } from './components/CanonicalAuthorityEditor.js';
|
|
4
4
|
export type { CanonicalAuthorityEditorProps } from './components/CanonicalAuthorityEditor.js';
|
|
5
|
+
export type { EditorCapabilityPreset } from './capabilityPresets.js';
|
|
6
|
+
export type { ToolbarTools } from './toolbarTools.js';
|
|
7
|
+
export { DEFAULT_TOOLBAR_TOOLS, resolveToolbarTools } from './toolbarTools.js';
|
|
5
8
|
export { CanonicalEditorRuntime, createCanonicalEditorRuntime } from './canonicalEditorRuntime.js';
|
|
6
9
|
export type { SmartEditorChange, SmartEditorCheckpoint, SmartEditorHandle } from './canonicalEditorRuntime.js';
|
|
7
10
|
export type { MediaManagerAdapter, MediaItem, MediaSearchQuery } from './components/MediaManager.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ClassicEditor } from './components/ClassicEditorAuthority.js';
|
|
2
2
|
export { CanonicalAuthorityEditor } from './components/CanonicalAuthorityEditor.js';
|
|
3
|
+
export { DEFAULT_TOOLBAR_TOOLS, resolveToolbarTools } from './toolbarTools.js';
|
|
3
4
|
export { CanonicalEditorRuntime, createCanonicalEditorRuntime } from './canonicalEditorRuntime.js';
|
|
4
5
|
export { DefaultMediaPicker } from './components/MediaPicker.js';
|
|
5
6
|
export { SRTE_DEFAULT_CSS, ensureStyleSheet } from './theme.js';
|
|
@@ -44,7 +44,7 @@ function ClassicEditorHost(props, ref) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch { }
|
|
47
|
-
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize, theme: props.theme, className: props.className }) }));
|
|
47
|
+
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize, tools: props.tools, theme: props.theme, className: props.className }) }));
|
|
48
48
|
}
|
|
49
49
|
const ClassicEditorHostWithRef = React.forwardRef(ClassicEditorHost);
|
|
50
50
|
function initClassicEditor(opts) {
|
package/dist/theme.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type SrteTheme = 'light' | 'dark';
|
|
2
|
-
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-background: var(--card, #ffffff);\n --srte-canvas: var(--background, #ffffff);\n --srte-foreground: var(--foreground, #0f172a);\n --srte-muted: var(--muted, #f1f5f9);\n --srte-muted-foreground: var(--muted-foreground, #64748b);\n --srte-ring: var(--ring, #0284c7);\n --srte-radius: var(--radius, 0.625rem);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #e2e8f0);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: var(--card, #ffffff);\n --srte-modal-text: var(--foreground, #000000);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #0284c7;\n --srte-accent-bg: rgba(2, 132, 199, 0.12);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n /* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's\n 4.5:1 for normal text - used only for the pressed toolbar-button state\n (see .srte-tool-button[aria-pressed=\"true\"]), not a replacement for\n --srte-primary generally. */\n --srte-primary-pressed: #1d4ed8;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n --srte-code-bg: #f6f8fa;\n --srte-code-text: #24292f;\n}\n.srte-editor.srte-dark {\n --srte-background: var(--card, #1e293b);\n --srte-canvas: var(--background, #0f172a);\n --srte-foreground: var(--foreground, #f8fafc);\n --srte-muted: var(--muted, #334155);\n --srte-muted-foreground: var(--muted-foreground, #94a3b8);\n --srte-ring: var(--ring, #38bdf8);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #334155);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: var(--card, #1e293b);\n --srte-modal-text: var(--foreground, #e0e0e0);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #38bdf8;\n --srte-accent-bg: rgba(56, 189, 248, 0.16);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-primary-pressed: var(--srte-primary);\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n --srte-code-bg: #111827;\n --srte-code-text: #e5e7eb;\n}\n.srte-editor {\n border-radius: var(--srte-radius);\n font-family: \"IBM Plex Sans\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n container: srte-editor / inline-size;\n}\n.srte-canonical-authority > .srte-editor[contenteditable] {\n width: 100%;\n padding: 16px 20px;\n box-sizing: border-box;\n border: 1px solid var(--srte-border);\n border-radius: 0 0 var(--srte-radius) var(--srte-radius);\n outline: none;\n background: var(--srte-canvas);\n color: var(--srte-foreground);\n caret-color: var(--srte-foreground);\n line-height: 1.6;\n}\n.srte-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: 100%;\n min-height: 48px;\n padding: 8px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--srte-border);\n background: var(--srte-background);\n color: var(--srte-foreground);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n.srte-toolbar-group {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n min-width: 0;\n}\n.srte-toolbar-group + .srte-toolbar-group::before {\n content: \"\";\n width: 1px;\n height: 20px;\n margin: 0 5px 0 3px;\n background: var(--srte-border);\n}\n.srte-tool-button,\n.srte-toolbar select {\n height: 32px;\n min-width: 32px;\n box-sizing: border-box;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: var(--srte-foreground);\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n}\n.srte-tool-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n padding: 0 7px;\n cursor: pointer;\n}\n.srte-toolbar select {\n max-width: 132px;\n padding: 0 26px 0 9px;\n border-color: var(--srte-border);\n background: var(--srte-input-bg);\n cursor: pointer;\n}\n.srte-tool-button:hover,\n.srte-toolbar select:hover,\n.srte-toolbar-menu[open] > .srte-menu-trigger {\n background: var(--srte-muted);\n border-color: var(--srte-border);\n}\n.srte-tool-button.srte-active,\n.srte-tool-button[aria-pressed=\"true\"] {\n /* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,\n just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -\n a dedicated, darker pressed-state color keeps the same hue family\n while clearing the threshold, without changing --srte-primary's other,\n already-compliant usages (e.g. against solid backgrounds). */\n color: var(--srte-primary-pressed);\n background: var(--srte-accent-bg);\n border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);\n}\n.srte-tool-button:focus-visible,\n.srte-toolbar select:focus-visible,\n.srte-menu-item:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 1px;\n}\n.srte-tool-button:disabled,\n.srte-menu-item:disabled,\n.srte-toolbar select:disabled {\n cursor: not-allowed;\n opacity: .4;\n}\n.srte-toolbar-menu {\n position: relative;\n}\n.srte-toolbar-menu > summary {\n list-style: none;\n}\n.srte-toolbar-menu > summary::-webkit-details-marker {\n display: none;\n}\n.srte-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: 80;\n min-width: 210px;\n padding: 4px;\n overflow: hidden;\n border: 1px solid var(--srte-border);\n border-radius: 12px;\n background: var(--srte-menu-bg);\n color: var(--srte-menu-text);\n box-shadow: var(--srte-menu-shadow);\n}\n/*\n * ToolbarDropdown's own menu panel (docs/bugs/\n * toolbar-dropdown-clipped-by-host-overflow-hidden.md): position:fixed with\n * JS-measured left/top (ToolbarPrimitives.tsx) instead of the default\n * position:absolute above, so it always escapes an ancestor host container's\n * own overflow:hidden regardless of DOM depth - MobileMoreMenu's overflow\n * menu (no data-srte-menu-fixed attribute) is unaffected and keeps its\n * existing position:absolute/right-anchored behavior.\n */\n.srte-menu[data-srte-menu-fixed=\"true\"] {\n position: fixed;\n top: 0;\n left: 0;\n}\n.srte-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n text-align: left;\n white-space: nowrap;\n}\n.srte-menu-item:hover {\n background: var(--srte-muted);\n}\n.srte-menu-check {\n margin-left: auto;\n color: var(--srte-primary);\n font-weight: 700;\n}\n.srte-menu-separator {\n height: 1px;\n margin: 4px;\n background: var(--srte-border);\n}\n.srte-mobile-more { display: none; }\n.srte-toolbar .srte-command-proxy { display: none; }\n.srte-split-control {\n display: inline-flex;\n}\n.srte-split-control > .srte-tool-button:first-child {\n border-radius: 8px 0 0 8px;\n}\n.srte-split-control > select {\n width: 27px;\n padding: 0;\n border-radius: 0 8px 8px 0;\n border-left: 0;\n appearance: none;\n -webkit-appearance: none;\n color: transparent;\n background-color: var(--srte-muted);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: center;\n cursor: pointer;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li {\n display: grid;\n grid-template-columns: 1.1em minmax(0, 1fr);\n column-gap: .45em;\n align-items: start;\n padding-left: 0;\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal-leading-zero) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"0\"] > li::marker {\n content: \"\u25CF \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"1\"] > li::marker {\n content: \"\u25CB \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"3\"] > li::marker {\n content: \"\u25A0 \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-outline\"] > li::marker {\n content: counters(list-item, \".\") \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2756 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u25A1 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25A3 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25AA \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u279C \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25C6 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25CF \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2605 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check] {\n display: inline-flex;\n grid-column: 1;\n grid-row: 1;\n align-items: center;\n justify-content: center;\n width: 1.1em;\n height: 1.6em;\n min-width: 0;\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n appearance: none;\n outline: none;\n margin: 0;\n line-height: 1.6;\n vertical-align: top;\n position: relative;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]::before {\n content: \"\";\n width: .9em;\n height: .9em;\n box-sizing: border-box;\n border: 1.5px solid var(--srte-muted-foreground);\n border-radius: 3px;\n background: var(--srte-canvas);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::before {\n border-color: var(--srte-primary);\n background: var(--srte-primary);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--srte-on-primary) 25%, transparent);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::after {\n content: \"\";\n position: absolute;\n width: .42em;\n height: .22em;\n border-left: 1.5px solid var(--srte-on-primary);\n border-bottom: 1.5px solid var(--srte-on-primary);\n transform: translateY(-.08em) rotate(-45deg);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(p,h1,h2,h3,h4,h5,h6,blockquote,pre) {\n grid-column: 2;\n min-width: 0;\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(ul,ol) {\n grid-column: 2;\n}\n.srte-editor:focus-within {\n border-color: var(--srte-ring) !important;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);\n}\n/*\n * @container (element width), not @media (viewport width): a host can embed\n * the editor at any fraction of the browser window - a split pane next to a\n * preview panel is the case that surfaced this (docs/bugs/\n * toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A\n * plain @media breakpoint here would fire based on the *window*, wrongly\n * switching tiers for an editor instance that's actually much narrower (or\n * wider) than the window - .srte-editor already establishes its own\n * container: srte-editor / inline-size (see the .srte-editor rule above),\n * so @container always reflects this specific instance's real rendered width regardless of host\n * layout, with no @media fallback needed (container queries have full\n * support across this project's three target engines).\n */\n@container srte-editor (max-width: 639px) {\n .srte-toolbar { gap: 3px; padding: 6px; }\n .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }\n .srte-toolbar-group[data-srte-priority=\"3\"],\n .srte-toolbar-menu[data-srte-priority=\"2\"] { display: none; }\n .srte-mobile-more { display: block; }\n .srte-mobile-more .srte-menu {\n left: auto;\n right: 0;\n width: min(280px, calc(100vw - 16px));\n min-width: 0;\n max-height: min(70dvh, 480px);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n }\n .srte-menu-item { height: 40px; }\n}\n/*\n * Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):\n * below this, the single 639px breakpoint above was this system's ONLY\n * threshold - every dropdown-grouped tool stayed hidden inside its dropdown\n * at every width from 640px up to and past a 2200px+ desktop, since nothing\n * ever measured or scaled with the extra room. A handful of tools frequent\n * enough to matter once there's genuinely spare room (Superscript,\n * Subscript, Text colour, Background colour, Font size, Font family, Remove\n * link, Insert formula, Special characters) get a standalone always-visible\n * ToolbarButton copy (data-srte-wide-promote, rendered directly in the\n * toolbar row) that appears past this breakpoint, while their existing\n * dropdown/mobile-menu ToolbarMenuItem copy (the exact same\n * data-srte-wide-promote attribute, on the .srte-menu-item element instead)\n * hides so the tool isn't offered twice. Below this breakpoint - including\n * all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps\n * today's grouped-dropdown layout unchanged - the standalone copy stays\n * hidden and the dropdown copy is what's reachable.\n *\n * 1440px, not 1280px: Playwright's own default test viewport is exactly\n * 1280x720, and the vast majority of this suite's toolbar interactions run\n * at that default without ever calling page.setViewportSize - a 1280px\n * threshold would have flipped nearly every existing toolbar test's\n * dropdown-item locators (role=menuitem) over to CSS display:none out from\n * under them. 1440px clears that default with room to spare while still\n * comfortably covering \"wide desktop\" (the ~2264px width the original\n * report was filed against).\n *\n * @container only, no @media fallback - same reasoning as the mobile tier\n * above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):\n * a plain @media(min-width:1440px) fired whenever the *browser window* was\n * wide, even when this editor instance was embedded in a much narrower\n * split pane - promoting 9 extra buttons into a group with no room for\n * them, which squeezed/overlapped the group's existing buttons instead of\n * wrapping cleanly.\n */\n.srte-tool-button[data-srte-wide-promote=\"true\"] { display: none; }\n@container srte-editor (min-width: 1440px) {\n .srte-tool-button[data-srte-wide-promote=\"true\"] { display: inline-flex; }\n .srte-menu-item[data-srte-wide-promote=\"true\"] { display: none; }\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n /* --srte-muted, not the retired --srte-surface-subtle (docs/bugs/\n srte-surface-subtle-not-host-theme-aware.md): every other neutral\n surface/text token here (--srte-background, --srte-foreground,\n --srte-muted, --srte-border) is defined as var(--host-token, fallback)\n so it correctly follows a host's OWN light/dark state even if\n .srte-dark never gets applied for some host-side wiring reason -\n --srte-surface-subtle was the one exception, a bare hardcoded hex with\n no host integration, which is exactly what let a real host (dark mode,\n .srte-dark not reaching this editor instance) render this blockquote\n with a light-mode background and dark-mode text simultaneously. */\n background: var(--srte-muted);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] p,\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n color: inherit;\n}\n.srte-editor [contenteditable] p {\n display: block;\n margin: 0 0 0.75em;\n font-size: 1em;\n font-weight: 400;\n line-height: 1.6;\n}\n.srte-editor [contenteditable] p[data-srte-caret-boundary=\"true\"] {\n min-height: 1.6em;\n}\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n display: block;\n margin: 0.75em 0 0.4em;\n font-weight: 700;\n line-height: 1.25;\n}\n.srte-editor [contenteditable] h1 {\n font-size: 2em;\n}\n.srte-editor [contenteditable] h2 {\n font-size: 1.5em;\n}\n.srte-editor [contenteditable] h3 {\n font-size: 1.25em;\n}\n.srte-editor [contenteditable] > :first-child {\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor [contenteditable] table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n}\n.srte-editor [contenteditable] th,\n.srte-editor [contenteditable] td {\n padding: 8px;\n border: 1px solid var(--srte-border);\n vertical-align: top;\n}\n.srte-editor [contenteditable] [data-smart-cell-selected=\"true\"] {\n background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));\n box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);\n}\n.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {\n background: transparent;\n color: inherit;\n}\n.srte-editor [contenteditable] th {\n background: var(--srte-muted);\n font-weight: 600;\n text-align: left;\n}\n.srte-editor [contenteditable] td > h1,\n.srte-editor [contenteditable] td > h2,\n.srte-editor [contenteditable] td > h3,\n.srte-editor [contenteditable] th > h1,\n.srte-editor [contenteditable] th > h2,\n.srte-editor [contenteditable] th > h3 {\n margin: 0 0 0.4em;\n overflow-wrap: anywhere;\n}\n.srte-editor [contenteditable] pre {\n display: block;\n margin: 0.75em 0;\n padding: 12px 14px;\n overflow-x: auto;\n border: 1px solid var(--srte-border);\n border-radius: 6px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n line-height: 1.55;\n white-space: pre-wrap;\n}\n.srte-editor [contenteditable] pre code {\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n}\n.srte-editor [contenteditable] code:not(pre code) {\n padding: 0.1em 0.35em;\n border-radius: 3px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n}\n.srte-editor [contenteditable] a,\n.srte-editor [contenteditable] a:visited {\n color: var(--srte-primary) !important;\n text-decoration: underline !important;\n text-decoration-thickness: 1px !important;\n text-underline-offset: 2px !important;\n cursor: pointer;\n}\n.srte-editor [contenteditable] a:hover {\n color: var(--srte-accent) !important;\n}\n.srte-editor [contenteditable] a:focus-visible {\n outline: 2px solid var(--srte-accent);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
2
|
+
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-background: var(--card, #ffffff);\n --srte-canvas: var(--background, #ffffff);\n --srte-foreground: var(--foreground, #0f172a);\n --srte-muted: var(--muted, #f1f5f9);\n --srte-muted-foreground: var(--muted-foreground, #64748b);\n --srte-ring: var(--ring, #0284c7);\n --srte-radius: var(--radius, 0.625rem);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #e2e8f0);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: var(--card, #ffffff);\n --srte-modal-text: var(--foreground, #000000);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #0284c7;\n --srte-accent-bg: rgba(2, 132, 199, 0.12);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n /* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's\n 4.5:1 for normal text - used only for the pressed toolbar-button state\n (see .srte-tool-button[aria-pressed=\"true\"]), not a replacement for\n --srte-primary generally. */\n --srte-primary-pressed: #1d4ed8;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n --srte-code-bg: #f6f8fa;\n --srte-code-text: #24292f;\n}\n.srte-editor.srte-dark {\n --srte-background: var(--card, #1e293b);\n --srte-canvas: var(--background, #0f172a);\n --srte-foreground: var(--foreground, #f8fafc);\n --srte-muted: var(--muted, #334155);\n --srte-muted-foreground: var(--muted-foreground, #94a3b8);\n --srte-ring: var(--ring, #38bdf8);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #334155);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: var(--card, #1e293b);\n --srte-modal-text: var(--foreground, #e0e0e0);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #38bdf8;\n --srte-accent-bg: rgba(56, 189, 248, 0.16);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-primary-pressed: var(--srte-primary);\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n --srte-code-bg: #111827;\n --srte-code-text: #e5e7eb;\n}\n.srte-editor {\n border-radius: var(--srte-radius);\n font-family: \"IBM Plex Sans\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n container: srte-editor / inline-size;\n}\n.srte-canonical-authority > .srte-editor[contenteditable] {\n width: 100%;\n padding: 16px 20px;\n box-sizing: border-box;\n border: 1px solid var(--srte-border);\n border-radius: 0 0 var(--srte-radius) var(--srte-radius);\n outline: none;\n background: var(--srte-canvas);\n color: var(--srte-foreground);\n caret-color: var(--srte-foreground);\n line-height: 1.6;\n}\n.srte-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: 100%;\n min-height: 48px;\n padding: 8px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--srte-border);\n background: var(--srte-background);\n color: var(--srte-foreground);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n.srte-toolbar-group {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n min-width: 0;\n}\n.srte-toolbar-group + .srte-toolbar-group::before {\n content: \"\";\n width: 1px;\n height: 20px;\n margin: 0 5px 0 3px;\n background: var(--srte-border);\n}\n.srte-tool-button,\n.srte-toolbar select {\n height: 32px;\n min-width: 32px;\n box-sizing: border-box;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: var(--srte-foreground);\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n}\n.srte-tool-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n padding: 0 7px;\n cursor: pointer;\n}\n.srte-toolbar select {\n max-width: 132px;\n padding: 0 26px 0 9px;\n border-color: var(--srte-border);\n background: var(--srte-input-bg);\n cursor: pointer;\n}\n.srte-tool-button:hover,\n.srte-toolbar select:hover,\n.srte-toolbar-menu[open] > .srte-menu-trigger {\n background: var(--srte-muted);\n border-color: var(--srte-border);\n}\n.srte-tool-button.srte-active,\n.srte-tool-button[aria-pressed=\"true\"] {\n /* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,\n just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -\n a dedicated, darker pressed-state color keeps the same hue family\n while clearing the threshold, without changing --srte-primary's other,\n already-compliant usages (e.g. against solid backgrounds). */\n color: var(--srte-primary-pressed);\n background: var(--srte-accent-bg);\n border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);\n}\n.srte-tool-button:focus-visible,\n.srte-toolbar select:focus-visible,\n.srte-menu-item:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 1px;\n}\n.srte-tool-button:disabled,\n.srte-menu-item:disabled,\n.srte-toolbar select:disabled {\n cursor: not-allowed;\n opacity: .4;\n}\n.srte-toolbar-menu {\n position: relative;\n}\n.srte-toolbar-menu > summary {\n list-style: none;\n}\n.srte-toolbar-menu > summary::-webkit-details-marker {\n display: none;\n}\n.srte-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: 80;\n min-width: 210px;\n padding: 4px;\n overflow: hidden;\n border: 1px solid var(--srte-border);\n border-radius: 12px;\n background: var(--srte-menu-bg);\n color: var(--srte-menu-text);\n box-shadow: var(--srte-menu-shadow);\n}\n/*\n * ToolbarDropdown's own menu panel (docs/bugs/\n * toolbar-dropdown-clipped-by-host-overflow-hidden.md): position:fixed with\n * JS-measured left/top (ToolbarPrimitives.tsx) instead of the default\n * position:absolute above, so it always escapes an ancestor host container's\n * own overflow:hidden regardless of DOM depth - MobileMoreMenu's overflow\n * menu (no data-srte-menu-fixed attribute) is unaffected and keeps its\n * existing position:absolute/right-anchored behavior.\n */\n.srte-menu[data-srte-menu-fixed=\"true\"] {\n position: fixed;\n top: 0;\n left: 0;\n}\n.srte-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n text-align: left;\n white-space: nowrap;\n}\n.srte-menu-item:hover {\n background: var(--srte-muted);\n}\n.srte-menu-check {\n margin-left: auto;\n color: var(--srte-primary);\n font-weight: 700;\n}\n.srte-menu-separator {\n height: 1px;\n margin: 4px;\n background: var(--srte-border);\n}\n.srte-mobile-more { display: none; }\n.srte-toolbar .srte-command-proxy { display: none; }\n.srte-split-control {\n display: inline-flex;\n}\n.srte-split-control > .srte-tool-button:first-child {\n border-radius: 8px 0 0 8px;\n}\n.srte-split-control > select {\n width: 27px;\n padding: 0;\n border-radius: 0 8px 8px 0;\n border-left: 0;\n appearance: none;\n -webkit-appearance: none;\n color: transparent;\n background-color: var(--srte-muted);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: center;\n cursor: pointer;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li {\n display: grid;\n grid-template-columns: 1.1em minmax(0, 1fr);\n column-gap: .45em;\n align-items: start;\n padding-left: 0;\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal-leading-zero) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"0\"] > li::marker {\n content: \"\u25CF \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"1\"] > li::marker {\n content: \"\u25CB \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"3\"] > li::marker {\n content: \"\u25A0 \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-outline\"] > li::marker {\n content: counters(list-item, \".\") \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2756 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u25A1 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25A3 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25AA \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u279C \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25C6 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25CF \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2605 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check] {\n display: inline-flex;\n grid-column: 1;\n grid-row: 1;\n align-items: center;\n justify-content: center;\n width: 1.1em;\n height: 1.6em;\n min-width: 0;\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n appearance: none;\n outline: none;\n margin: 0;\n line-height: 1.6;\n vertical-align: top;\n position: relative;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]::before {\n content: \"\";\n width: .9em;\n height: .9em;\n box-sizing: border-box;\n border: 1.5px solid var(--srte-muted-foreground);\n border-radius: 3px;\n background: var(--srte-canvas);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::before {\n border-color: var(--srte-primary);\n background: var(--srte-primary);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--srte-on-primary) 25%, transparent);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::after {\n content: \"\";\n position: absolute;\n width: .42em;\n height: .22em;\n border-left: 1.5px solid var(--srte-on-primary);\n border-bottom: 1.5px solid var(--srte-on-primary);\n transform: translateY(-.08em) rotate(-45deg);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(p,h1,h2,h3,h4,h5,h6,blockquote,pre) {\n grid-column: 2;\n min-width: 0;\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(ul,ol) {\n grid-column: 2;\n}\n.srte-editor:focus-within {\n border-color: var(--srte-ring) !important;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);\n}\n/*\n * @container (element width), not @media (viewport width): a host can embed\n * the editor at any fraction of the browser window - a split pane next to a\n * preview panel is the case that surfaced this (docs/bugs/\n * toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A\n * plain @media breakpoint here would fire based on the *window*, wrongly\n * switching tiers for an editor instance that's actually much narrower (or\n * wider) than the window - .srte-editor already establishes its own\n * container: srte-editor / inline-size (see the .srte-editor rule above),\n * so @container always reflects this specific instance's real rendered width regardless of host\n * layout, with no @media fallback needed (container queries have full\n * support across this project's three target engines).\n */\n@container srte-editor (max-width: 639px) {\n .srte-toolbar { gap: 3px; padding: 6px; }\n .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }\n .srte-toolbar-group[data-srte-priority=\"3\"],\n .srte-toolbar-menu[data-srte-priority=\"2\"] { display: none; }\n .srte-mobile-more { display: block; }\n /* left/top are no longer set here - ToolbarPrimitives.tsx now computes\n them via inline style (JS-measured, position:fixed, viewport-clamped;\n see MobileMoreMenu's own doc comment), which always wins over any\n value set here regardless of specificity or source order. Sizing/\n scroll behavior stays defined here since inline positioning doesn't\n touch those properties. */\n .srte-mobile-more .srte-menu {\n width: min(280px, calc(100vw - 16px));\n min-width: 0;\n max-height: min(70dvh, 480px);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n }\n .srte-menu-item { height: 40px; }\n}\n/*\n * Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):\n * below this, the single 639px breakpoint above was this system's ONLY\n * threshold - every dropdown-grouped tool stayed hidden inside its dropdown\n * at every width from 640px up to and past a 2200px+ desktop, since nothing\n * ever measured or scaled with the extra room. A handful of tools frequent\n * enough to matter once there's genuinely spare room (Superscript,\n * Subscript, Text colour, Background colour, Font size, Font family, Remove\n * link, Insert formula, Special characters) get a standalone always-visible\n * ToolbarButton copy (data-srte-wide-promote, rendered directly in the\n * toolbar row) that appears past this breakpoint, while their existing\n * dropdown/mobile-menu ToolbarMenuItem copy (the exact same\n * data-srte-wide-promote attribute, on the .srte-menu-item element instead)\n * hides so the tool isn't offered twice. Below this breakpoint - including\n * all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps\n * today's grouped-dropdown layout unchanged - the standalone copy stays\n * hidden and the dropdown copy is what's reachable.\n *\n * 1440px, not 1280px: Playwright's own default test viewport is exactly\n * 1280x720, and the vast majority of this suite's toolbar interactions run\n * at that default without ever calling page.setViewportSize - a 1280px\n * threshold would have flipped nearly every existing toolbar test's\n * dropdown-item locators (role=menuitem) over to CSS display:none out from\n * under them. 1440px clears that default with room to spare while still\n * comfortably covering \"wide desktop\" (the ~2264px width the original\n * report was filed against).\n *\n * @container only, no @media fallback - same reasoning as the mobile tier\n * above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):\n * a plain @media(min-width:1440px) fired whenever the *browser window* was\n * wide, even when this editor instance was embedded in a much narrower\n * split pane - promoting 9 extra buttons into a group with no room for\n * them, which squeezed/overlapped the group's existing buttons instead of\n * wrapping cleanly.\n */\n.srte-tool-button[data-srte-wide-promote=\"true\"] { display: none; }\n@container srte-editor (min-width: 1440px) {\n .srte-tool-button[data-srte-wide-promote=\"true\"] { display: inline-flex; }\n .srte-menu-item[data-srte-wide-promote=\"true\"] { display: none; }\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n /* --srte-muted, not the retired --srte-surface-subtle (docs/bugs/\n srte-surface-subtle-not-host-theme-aware.md): every other neutral\n surface/text token here (--srte-background, --srte-foreground,\n --srte-muted, --srte-border) is defined as var(--host-token, fallback)\n so it correctly follows a host's OWN light/dark state even if\n .srte-dark never gets applied for some host-side wiring reason -\n --srte-surface-subtle was the one exception, a bare hardcoded hex with\n no host integration, which is exactly what let a real host (dark mode,\n .srte-dark not reaching this editor instance) render this blockquote\n with a light-mode background and dark-mode text simultaneously. */\n background: var(--srte-muted);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] p,\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n color: inherit;\n}\n.srte-editor [contenteditable] p {\n display: block;\n margin: 0 0 0.75em;\n font-size: 1em;\n font-weight: 400;\n line-height: 1.6;\n}\n.srte-editor [contenteditable] p[data-srte-caret-boundary=\"true\"] {\n min-height: 1.6em;\n}\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n display: block;\n margin: 0.75em 0 0.4em;\n font-weight: 700;\n line-height: 1.25;\n}\n.srte-editor [contenteditable] h1 {\n font-size: 2em;\n}\n.srte-editor [contenteditable] h2 {\n font-size: 1.5em;\n}\n.srte-editor [contenteditable] h3 {\n font-size: 1.25em;\n}\n.srte-editor [contenteditable] > :first-child {\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor [contenteditable] table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n}\n.srte-editor [contenteditable] th,\n.srte-editor [contenteditable] td {\n padding: 8px;\n border: 1px solid var(--srte-border);\n vertical-align: top;\n}\n.srte-editor [contenteditable] [data-smart-cell-selected=\"true\"] {\n background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));\n box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);\n}\n.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {\n background: transparent;\n color: inherit;\n}\n.srte-editor [contenteditable] th {\n background: var(--srte-muted);\n font-weight: 600;\n text-align: left;\n}\n.srte-editor [contenteditable] td > h1,\n.srte-editor [contenteditable] td > h2,\n.srte-editor [contenteditable] td > h3,\n.srte-editor [contenteditable] th > h1,\n.srte-editor [contenteditable] th > h2,\n.srte-editor [contenteditable] th > h3 {\n margin: 0 0 0.4em;\n overflow-wrap: anywhere;\n}\n.srte-editor [contenteditable] pre {\n display: block;\n margin: 0.75em 0;\n padding: 12px 14px;\n overflow-x: auto;\n border: 1px solid var(--srte-border);\n border-radius: 6px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n line-height: 1.55;\n white-space: pre-wrap;\n}\n.srte-editor [contenteditable] pre code {\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n}\n.srte-editor [contenteditable] code:not(pre code) {\n padding: 0.1em 0.35em;\n border-radius: 3px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n}\n.srte-editor [contenteditable] a,\n.srte-editor [contenteditable] a:visited {\n color: var(--srte-primary) !important;\n text-decoration: underline !important;\n text-decoration-thickness: 1px !important;\n text-underline-offset: 2px !important;\n cursor: pointer;\n}\n.srte-editor [contenteditable] a:hover {\n color: var(--srte-accent) !important;\n}\n.srte-editor [contenteditable] a:focus-visible {\n outline: 2px solid var(--srte-accent);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
3
3
|
export declare function ensureStyleSheet(): void;
|
package/dist/theme.js
CHANGED
|
@@ -426,9 +426,13 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
426
426
|
.srte-toolbar-group[data-srte-priority="3"],
|
|
427
427
|
.srte-toolbar-menu[data-srte-priority="2"] { display: none; }
|
|
428
428
|
.srte-mobile-more { display: block; }
|
|
429
|
+
/* left/top are no longer set here - ToolbarPrimitives.tsx now computes
|
|
430
|
+
them via inline style (JS-measured, position:fixed, viewport-clamped;
|
|
431
|
+
see MobileMoreMenu's own doc comment), which always wins over any
|
|
432
|
+
value set here regardless of specificity or source order. Sizing/
|
|
433
|
+
scroll behavior stays defined here since inline positioning doesn't
|
|
434
|
+
touch those properties. */
|
|
429
435
|
.srte-mobile-more .srte-menu {
|
|
430
|
-
left: auto;
|
|
431
|
-
right: 0;
|
|
432
436
|
width: min(280px, calc(100vw - 16px));
|
|
433
437
|
min-width: 0;
|
|
434
438
|
max-height: min(70dvh, 480px);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A named, per-tool toolbar-visibility toggle - "developers should be able
|
|
3
|
+
* to hide tools they don't want their users to see" (e.g. Version History,
|
|
4
|
+
* Review, Video, Audio), without wrapping/overriding the component or
|
|
5
|
+
* patching it with CSS. This is a UI-visibility layer, distinct from and
|
|
6
|
+
* composed with (never replacing) two existing, deeper mechanisms:
|
|
7
|
+
*
|
|
8
|
+
* - `EditorCapabilityPreset` (capabilityPresets.ts) controls real document
|
|
9
|
+
* *capability* - which plugins the schema itself is built with (today,
|
|
10
|
+
* only `table`). Excluding a plugin there already, correctly, hides its
|
|
11
|
+
* toolbar entry (`tablesEnabled`) - this file does not duplicate that.
|
|
12
|
+
* - Provider presence (`mediaProvider`, `versionProvider`, `commentProvider`,
|
|
13
|
+
* `suggestionProvider`) already gates whether a feature can function at
|
|
14
|
+
* all. `tools` never turns a tool ON when its required provider is
|
|
15
|
+
* absent - see `resolveToolbarTools`'s own doc comment.
|
|
16
|
+
*
|
|
17
|
+
* `tools` is for the remaining, larger set of toolbar entries that have no
|
|
18
|
+
* other on/off mechanism at all today (Bold, Italic, Video, Audio, Special
|
|
19
|
+
* characters, Version history, ...) - a host that wants to hide any of
|
|
20
|
+
* these previously had no supported way to do so.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately NOT covered by individual keys (scoping decision, not an
|
|
23
|
+
* oversight): purely contextual actions that only ever act on something
|
|
24
|
+
* already selected - block move/indent, list-item indent/move/restart
|
|
25
|
+
* numbering, table row/column/cell operations, and the selected-media
|
|
26
|
+
* edit/resize/delete actions. None of these have independent product
|
|
27
|
+
* meaning (there is no real case for "let people insert a table but hide
|
|
28
|
+
* its 'Add row' action") - they stay governed solely by their owning
|
|
29
|
+
* top-level tool (`insertTable`, `bulletedList`/`numberedList`/`checklist`,
|
|
30
|
+
* `link`, `image`/`video`/`audio`).
|
|
31
|
+
*/
|
|
32
|
+
export interface ToolbarTools {
|
|
33
|
+
bold: boolean;
|
|
34
|
+
italic: boolean;
|
|
35
|
+
underline: boolean;
|
|
36
|
+
strikethrough: boolean;
|
|
37
|
+
code: boolean;
|
|
38
|
+
superscript: boolean;
|
|
39
|
+
subscript: boolean;
|
|
40
|
+
textColor: boolean;
|
|
41
|
+
backgroundColor: boolean;
|
|
42
|
+
fontSize: boolean;
|
|
43
|
+
fontFamily: boolean;
|
|
44
|
+
/** The Paragraph/Heading 1-6/Code block <select>. */
|
|
45
|
+
blockType: boolean;
|
|
46
|
+
alignLeft: boolean;
|
|
47
|
+
alignCenter: boolean;
|
|
48
|
+
alignRight: boolean;
|
|
49
|
+
alignJustify: boolean;
|
|
50
|
+
quote: boolean;
|
|
51
|
+
bulletedList: boolean;
|
|
52
|
+
numberedList: boolean;
|
|
53
|
+
checklist: boolean;
|
|
54
|
+
/** The named multi-level marker preset picker (decimal/alpha/roman/outline/bullet-glyph) - independent of the plain list-type toggles above. */
|
|
55
|
+
listPreset: boolean;
|
|
56
|
+
link: boolean;
|
|
57
|
+
removeLink: boolean;
|
|
58
|
+
/** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
|
|
59
|
+
image: boolean;
|
|
60
|
+
/** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
|
|
61
|
+
video: boolean;
|
|
62
|
+
/** Requires `mediaProvider` to actually render - see resolveToolbarTools. */
|
|
63
|
+
audio: boolean;
|
|
64
|
+
insertFormula: boolean;
|
|
65
|
+
specialCharacters: boolean;
|
|
66
|
+
/** Requires the schema to actually have the table plugin (EditorCapabilityPreset) - see resolveToolbarTools. */
|
|
67
|
+
insertTable: boolean;
|
|
68
|
+
import: boolean;
|
|
69
|
+
saveAsHtml: boolean;
|
|
70
|
+
saveAsMarkdown: boolean;
|
|
71
|
+
saveAsWord: boolean;
|
|
72
|
+
saveAsPdf: boolean;
|
|
73
|
+
saveAsSmartRte: boolean;
|
|
74
|
+
/** Requires `versionProvider` to actually render. */
|
|
75
|
+
versionHistory: boolean;
|
|
76
|
+
/** Bundles "Add comment" and the Comments panel toggle - requires `commentProvider` to actually render. */
|
|
77
|
+
comments: boolean;
|
|
78
|
+
/** Bundles Suggest insertion/deletion/removal, the Suggestions panel toggle, and "Show edits" (track changes) - requires `suggestionProvider` to actually render. */
|
|
79
|
+
suggestions: boolean;
|
|
80
|
+
undo: boolean;
|
|
81
|
+
redo: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** Every tool defaults to visible - an existing consumer who never passes `tools` sees 100% unchanged behavior. */
|
|
84
|
+
export declare const DEFAULT_TOOLBAR_TOOLS: ToolbarTools;
|
|
85
|
+
/**
|
|
86
|
+
* Merges a host's partial `tools` override onto the all-enabled default -
|
|
87
|
+
* a host only ever needs to name the tools they want OFF.
|
|
88
|
+
*/
|
|
89
|
+
export declare const resolveToolbarTools: (tools?: Partial<ToolbarTools>) => ToolbarTools;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Every tool defaults to visible - an existing consumer who never passes `tools` sees 100% unchanged behavior. */
|
|
2
|
+
export const DEFAULT_TOOLBAR_TOOLS = {
|
|
3
|
+
bold: true, italic: true, underline: true, strikethrough: true, code: true,
|
|
4
|
+
superscript: true, subscript: true, textColor: true, backgroundColor: true, fontSize: true, fontFamily: true,
|
|
5
|
+
blockType: true, alignLeft: true, alignCenter: true, alignRight: true, alignJustify: true, quote: true,
|
|
6
|
+
bulletedList: true, numberedList: true, checklist: true, listPreset: true,
|
|
7
|
+
link: true, removeLink: true, image: true, video: true, audio: true, insertFormula: true, specialCharacters: true, insertTable: true,
|
|
8
|
+
import: true, saveAsHtml: true, saveAsMarkdown: true, saveAsWord: true, saveAsPdf: true, saveAsSmartRte: true,
|
|
9
|
+
versionHistory: true, comments: true, suggestions: true,
|
|
10
|
+
undo: true, redo: true,
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Merges a host's partial `tools` override onto the all-enabled default -
|
|
14
|
+
* a host only ever needs to name the tools they want OFF.
|
|
15
|
+
*/
|
|
16
|
+
export const resolveToolbarTools = (tools) => ({ ...DEFAULT_TOOLBAR_TOOLS, ...tools });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartrte-react",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.8",
|
|
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",
|