@byline/richtext-lexical 5.2.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +1 -1
- package/dist/field/apply-value-plugin.js +31 -6
- package/dist/field/capabilities/filter-transformers.d.ts +29 -0
- package/dist/field/capabilities/filter-transformers.js +8 -0
- package/dist/field/capabilities/registered-node-types.d.ts +29 -0
- package/dist/field/capabilities/registered-node-types.js +7 -0
- package/dist/field/config/built-in-extension-names.d.ts +42 -0
- package/dist/field/config/built-in-extension-names.js +12 -2
- package/dist/field/config/default-extensions.js +4 -0
- package/dist/field/config/default.js +8 -8
- package/dist/field/config/editor-config-context.d.ts +2 -2
- package/dist/field/config/editor-config-context.js +10 -13
- package/dist/field/config/resolve-editor-config.js +3 -3
- package/dist/field/config/root-dependencies.d.ts +26 -0
- package/dist/field/config/root-dependencies.js +8 -0
- package/dist/field/config/types.d.ts +61 -8
- package/dist/field/editor-context.js +44 -27
- package/dist/field/editor.js +15 -8
- package/dist/field/extensions/core-nodes/core-nodes-extension.d.ts +24 -0
- package/dist/field/extensions/core-nodes/core-nodes-extension.js +11 -0
- package/dist/field/extensions/heading/heading-extension.d.ts +20 -0
- package/dist/field/extensions/heading/heading-extension.js +9 -0
- package/dist/field/extensions/quote/quote-extension.d.ts +16 -0
- package/dist/field/extensions/quote/quote-extension.js +9 -0
- package/dist/field/hooks/use-markdown-toggle.d.ts +15 -0
- package/dist/field/hooks/use-markdown-toggle.js +12 -4
- package/dist/field/hooks/use-platform-modifier.d.ts +16 -0
- package/dist/field/hooks/use-platform-modifier.js +8 -0
- package/dist/field/markdown/transformers.d.ts +1 -0
- package/dist/field/markdown/transformers.js +5 -4
- package/dist/field/nodes/index.d.ts +11 -1
- package/dist/field/nodes/index.js +2 -2
- package/dist/field/normalize/adapted-notice.css +34 -0
- package/dist/field/normalize/adapted-notice.d.ts +29 -0
- package/dist/field/normalize/adapted-notice.js +35 -0
- package/dist/field/normalize/content-labels.d.ts +15 -0
- package/dist/field/normalize/content-labels.js +19 -0
- package/dist/field/normalize/declared-conversions.d.ts +131 -0
- package/dist/field/normalize/declared-conversions.js +93 -0
- package/dist/field/normalize/normalization-status.d.ts +33 -0
- package/dist/field/normalize/normalization-status.js +28 -0
- package/dist/field/normalize/normalize-value.d.ts +38 -0
- package/dist/field/normalize/normalize-value.js +132 -0
- package/dist/field/normalize/use-notice-text.d.ts +11 -0
- package/dist/field/normalize/use-notice-text.js +34 -0
- package/dist/field/plugins/toolbar-plugin/index.d.ts +23 -0
- package/dist/field/plugins/toolbar-plugin/index.js +104 -78
- package/dist/field/test-support/build-test-editor.js +49 -0
- package/dist/field/test-support/capture-editor.js +7 -0
- package/dist/field/test-support/frame-control.js +40 -0
- package/dist/field/test-support/jsdom-setup.js +21 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +6 -1
- package/dist/scan/capability-manifest.d.ts +30 -0
- package/dist/scan/capability-manifest.js +43 -0
- package/dist/scan/scan-documents.d.ts +29 -0
- package/dist/scan/scan-documents.js +39 -0
- package/dist/scan/types.d.ts +63 -0
- package/dist/scan/types.js +1 -0
- package/dist/scan.d.ts +25 -0
- package/dist/scan.js +1 -0
- package/package.json +14 -5
- package/src/config.ts +1 -1
- package/src/field/apply-value-plugin.tsx +71 -5
- package/src/field/capabilities/filter-transformers.test.tsx +71 -0
- package/src/field/capabilities/filter-transformers.ts +42 -0
- package/src/field/capabilities/markdown-behaviour.test.tsx +188 -0
- package/src/field/capabilities/no-blanket-registration.test.tsx +86 -0
- package/src/field/capabilities/registered-node-types.test.tsx +29 -0
- package/src/field/capabilities/registered-node-types.ts +36 -0
- package/src/field/config/built-in-extension-names.test.node.ts +38 -14
- package/src/field/config/built-in-extension-names.ts +29 -1
- package/src/field/config/default-extensions.ts +6 -0
- package/src/field/config/default.ts +8 -8
- package/src/field/config/editor-config-context.tsx +14 -9
- package/src/field/config/list-removal.test.tsx +44 -0
- package/src/field/config/resolve-editor-config.test.node.ts +27 -6
- package/src/field/config/resolve-editor-config.ts +3 -3
- package/src/field/config/root-dependencies.ts +32 -0
- package/src/field/config/types.ts +61 -17
- package/src/field/editor-component.test.tsx +18 -3
- package/src/field/editor-context.regression.test.tsx +124 -0
- package/src/field/editor-context.tsx +90 -22
- package/src/field/editor.tsx +22 -12
- package/src/field/extensions/core-nodes/core-nodes-extension.test.tsx +13 -0
- package/src/field/extensions/core-nodes/core-nodes-extension.ts +32 -0
- package/src/field/extensions/heading/heading-extension.test.tsx +32 -0
- package/src/field/extensions/heading/heading-extension.ts +27 -0
- package/src/field/extensions/quote/quote-extension.ts +23 -0
- package/src/field/hooks/markdown-source-mode.test.tsx +221 -0
- package/src/field/hooks/use-markdown-toggle.ts +39 -4
- package/src/field/hooks/use-platform-modifier.test.tsx +81 -0
- package/src/field/hooks/use-platform-modifier.ts +41 -0
- package/src/field/markdown/transformers.ts +18 -4
- package/src/field/nodes/index.ts +11 -1
- package/src/field/normalize/adapted-notice.css +52 -0
- package/src/field/normalize/adapted-notice.tsx +63 -0
- package/src/field/normalize/clipboard-equivalence.test.tsx +330 -0
- package/src/field/normalize/content-labels.ts +44 -0
- package/src/field/normalize/declared-conversions.test.node.ts +67 -0
- package/src/field/normalize/declared-conversions.ts +160 -0
- package/src/field/normalize/emission-gate.test.tsx +302 -0
- package/src/field/normalize/normalization-status.tsx +48 -0
- package/src/field/normalize/normalize-load.test.tsx +371 -0
- package/src/field/normalize/normalize-value.test.node.ts +530 -0
- package/src/field/normalize/normalize-value.ts +180 -0
- package/src/field/normalize/notice-i18n.test.tsx +128 -0
- package/src/field/normalize/use-notice-text.ts +82 -0
- package/src/field/normalize/wiring.test.tsx +348 -0
- package/src/field/plugins/toolbar-plugin/block-format-capabilities.test.tsx +57 -0
- package/src/field/plugins/toolbar-plugin/index.tsx +131 -68
- package/src/field/test-support/build-test-editor.test.tsx +17 -0
- package/src/field/test-support/build-test-editor.ts +99 -0
- package/src/field/test-support/capture-editor.tsx +20 -0
- package/src/field/test-support/frame-control.ts +91 -0
- package/src/field/test-support/jsdom-setup.ts +41 -0
- package/src/index.ts +12 -1
- package/src/scan/capability-manifest.test.tsx +82 -0
- package/src/scan/capability-manifest.ts +81 -0
- package/src/scan/scan-documents.test.node.ts +125 -0
- package/src/scan/scan-documents.ts +78 -0
- package/src/scan/types.ts +66 -0
- package/src/scan.ts +27 -0
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Fragment as external_react_Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { $createCodeNode, $isCodeNode, CODE_LANGUAGE_FRIENDLY_NAME_MAP, CODE_LANGUAGE_MAP, CodeNode, getLanguageFriendlyName } from "@lexical/code";
|
|
5
|
-
import { $isListNode, CheckListExtension, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND,
|
|
5
|
+
import { $isListNode, CheckListExtension, INSERT_CHECK_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListItemNode, ListNode, REMOVE_LIST_COMMAND } from "@lexical/list";
|
|
6
6
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
7
7
|
import { $isDecoratorBlockNode } from "@lexical/react/LexicalDecoratorBlockNode";
|
|
8
8
|
import { useExtensionDependency, useOptionalExtensionDependency } from "@lexical/react/useExtensionComponent";
|
|
9
|
-
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode } from "@lexical/rich-text";
|
|
9
|
+
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
10
10
|
import { $setBlocksType } from "@lexical/selection";
|
|
11
11
|
import { $isTableNode } from "@lexical/table";
|
|
12
12
|
import { $findMatchingParent, $getNearestBlockElementAncestorOrThrow, $getNearestNodeOfType, mergeRegister } from "@lexical/utils";
|
|
@@ -14,8 +14,8 @@ import { $createParagraphNode, $getNodeByKey, $getSelection, $isRangeSelection,
|
|
|
14
14
|
import { useEditorConfig } from "../../config/editor-config-context.js";
|
|
15
15
|
import { BylineToolbarExtension, selectToolbarItems } from "../../extensions/byline-toolbar/byline-toolbar-extension.js";
|
|
16
16
|
import { $isLinkNode, LinkExtension, OPEN_LINK_MODAL_COMMAND, TOGGLE_LINK_COMMAND } from "../../extensions/link/index.js";
|
|
17
|
-
import { useMarkdownToggle } from "../../hooks/use-markdown-toggle.js";
|
|
18
|
-
import {
|
|
17
|
+
import { canUseMarkdownSourceMode, useMarkdownToggle } from "../../hooks/use-markdown-toggle.js";
|
|
18
|
+
import { useIsApplePlatform } from "../../hooks/use-platform-modifier.js";
|
|
19
19
|
import { DropDown, DropDownItem } from "../../ui/dropdown.js";
|
|
20
20
|
import { getSelectedNode } from "../../utils/getSelectedNode.js";
|
|
21
21
|
import { sanitizeUrl } from "../../utils/url.js";
|
|
@@ -47,13 +47,32 @@ function dropDownActiveClass(active) {
|
|
|
47
47
|
if (active) return 'active dropdown-item-active';
|
|
48
48
|
return '';
|
|
49
49
|
}
|
|
50
|
+
function availableBlockFormats(editor, hasCheckListBehaviour) {
|
|
51
|
+
const formats = [
|
|
52
|
+
'paragraph'
|
|
53
|
+
];
|
|
54
|
+
if (editor.hasNodes([
|
|
55
|
+
HeadingNode
|
|
56
|
+
])) formats.push('h1', 'h2', 'h3', 'h4');
|
|
57
|
+
if (editor.hasNodes([
|
|
58
|
+
ListNode,
|
|
59
|
+
ListItemNode
|
|
60
|
+
])) {
|
|
61
|
+
formats.push('bullet', 'number');
|
|
62
|
+
if (hasCheckListBehaviour) formats.push('check');
|
|
63
|
+
}
|
|
64
|
+
if (editor.hasNodes([
|
|
65
|
+
QuoteNode
|
|
66
|
+
])) formats.push('quote');
|
|
67
|
+
if (editor.hasNodes([
|
|
68
|
+
CodeNode
|
|
69
|
+
])) formats.push('code');
|
|
70
|
+
return formats;
|
|
71
|
+
}
|
|
50
72
|
function BlockFormatDropDown({ editor, blockType, rootType, disabled = false }) {
|
|
51
|
-
const hasList = void 0 !== useOptionalExtensionDependency(ListExtension);
|
|
52
|
-
const hasCheckList = void 0 !== useOptionalExtensionDependency(CheckListExtension);
|
|
53
73
|
const [composerEditor] = useLexicalComposerContext();
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
]);
|
|
74
|
+
const hasCheckListBehaviour = void 0 !== useOptionalExtensionDependency(CheckListExtension);
|
|
75
|
+
const formats = availableBlockFormats(composerEditor, hasCheckListBehaviour);
|
|
57
76
|
const formatParagraph = ()=>{
|
|
58
77
|
editor.update(()=>{
|
|
59
78
|
const selection = $getSelection();
|
|
@@ -99,6 +118,7 @@ function BlockFormatDropDown({ editor, blockType, rootType, disabled = false })
|
|
|
99
118
|
}
|
|
100
119
|
});
|
|
101
120
|
};
|
|
121
|
+
if (1 === formats.length) return null;
|
|
102
122
|
return /*#__PURE__*/ jsxs(DropDown, {
|
|
103
123
|
disabled: disabled,
|
|
104
124
|
buttonClassName: "toolbar-item block-controls",
|
|
@@ -119,67 +139,71 @@ function BlockFormatDropDown({ editor, blockType, rootType, disabled = false })
|
|
|
119
139
|
})
|
|
120
140
|
]
|
|
121
141
|
}),
|
|
122
|
-
/*#__PURE__*/ jsxs(
|
|
123
|
-
className: `item ${dropDownActiveClass('h1' === blockType)}`,
|
|
124
|
-
onClick: ()=>{
|
|
125
|
-
formatHeading('h1');
|
|
126
|
-
},
|
|
142
|
+
formats.includes('h1') && /*#__PURE__*/ jsxs(Fragment, {
|
|
127
143
|
children: [
|
|
128
|
-
/*#__PURE__*/
|
|
129
|
-
className:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
children:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
children: [
|
|
143
|
-
/*#__PURE__*/ jsx("i", {
|
|
144
|
-
className: "icon h2"
|
|
144
|
+
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
145
|
+
className: `item ${dropDownActiveClass('h1' === blockType)}`,
|
|
146
|
+
onClick: ()=>{
|
|
147
|
+
formatHeading('h1');
|
|
148
|
+
},
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ jsx("i", {
|
|
151
|
+
className: "icon h1"
|
|
152
|
+
}),
|
|
153
|
+
/*#__PURE__*/ jsx("span", {
|
|
154
|
+
className: "text",
|
|
155
|
+
children: "Heading 1"
|
|
156
|
+
})
|
|
157
|
+
]
|
|
145
158
|
}),
|
|
146
|
-
/*#__PURE__*/
|
|
147
|
-
className:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
160
|
+
className: `item ${dropDownActiveClass('h2' === blockType)}`,
|
|
161
|
+
onClick: ()=>{
|
|
162
|
+
formatHeading('h2');
|
|
163
|
+
},
|
|
164
|
+
children: [
|
|
165
|
+
/*#__PURE__*/ jsx("i", {
|
|
166
|
+
className: "icon h2"
|
|
167
|
+
}),
|
|
168
|
+
/*#__PURE__*/ jsx("span", {
|
|
169
|
+
className: "text",
|
|
170
|
+
children: "Heading 2"
|
|
171
|
+
})
|
|
172
|
+
]
|
|
160
173
|
}),
|
|
161
|
-
/*#__PURE__*/
|
|
162
|
-
className:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
175
|
+
className: `item ${dropDownActiveClass('h3' === blockType)}`,
|
|
176
|
+
onClick: ()=>{
|
|
177
|
+
formatHeading('h3');
|
|
178
|
+
},
|
|
179
|
+
children: [
|
|
180
|
+
/*#__PURE__*/ jsx("i", {
|
|
181
|
+
className: "icon h3"
|
|
182
|
+
}),
|
|
183
|
+
/*#__PURE__*/ jsx("span", {
|
|
184
|
+
className: "text",
|
|
185
|
+
children: "Heading 3"
|
|
186
|
+
})
|
|
187
|
+
]
|
|
175
188
|
}),
|
|
176
|
-
/*#__PURE__*/
|
|
177
|
-
className:
|
|
178
|
-
|
|
189
|
+
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
190
|
+
className: `item ${dropDownActiveClass('h4' === blockType)}`,
|
|
191
|
+
onClick: ()=>{
|
|
192
|
+
formatHeading('h4');
|
|
193
|
+
},
|
|
194
|
+
children: [
|
|
195
|
+
/*#__PURE__*/ jsx("i", {
|
|
196
|
+
className: "icon h4"
|
|
197
|
+
}),
|
|
198
|
+
/*#__PURE__*/ jsx("span", {
|
|
199
|
+
className: "text",
|
|
200
|
+
children: "Heading 4"
|
|
201
|
+
})
|
|
202
|
+
]
|
|
179
203
|
})
|
|
180
204
|
]
|
|
181
205
|
}),
|
|
182
|
-
|
|
206
|
+
formats.includes('bullet') && /*#__PURE__*/ jsxs(Fragment, {
|
|
183
207
|
children: [
|
|
184
208
|
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
185
209
|
className: `item ${dropDownActiveClass('bullet' === blockType)}`,
|
|
@@ -209,7 +233,7 @@ function BlockFormatDropDown({ editor, blockType, rootType, disabled = false })
|
|
|
209
233
|
})
|
|
210
234
|
]
|
|
211
235
|
}),
|
|
212
|
-
|
|
236
|
+
formats.includes('check') && /*#__PURE__*/ jsxs(DropDownItem, {
|
|
213
237
|
className: `item ${dropDownActiveClass('check' === blockType)}`,
|
|
214
238
|
onClick: formatCheckList,
|
|
215
239
|
children: [
|
|
@@ -222,7 +246,7 @@ function BlockFormatDropDown({ editor, blockType, rootType, disabled = false })
|
|
|
222
246
|
})
|
|
223
247
|
]
|
|
224
248
|
}),
|
|
225
|
-
/*#__PURE__*/ jsxs(DropDownItem, {
|
|
249
|
+
formats.includes('quote') && /*#__PURE__*/ jsxs(DropDownItem, {
|
|
226
250
|
className: `item ${dropDownActiveClass('quote' === blockType)}`,
|
|
227
251
|
onClick: formatQuote,
|
|
228
252
|
children: [
|
|
@@ -235,7 +259,7 @@ function BlockFormatDropDown({ editor, blockType, rootType, disabled = false })
|
|
|
235
259
|
})
|
|
236
260
|
]
|
|
237
261
|
}),
|
|
238
|
-
|
|
262
|
+
formats.includes('code') && /*#__PURE__*/ jsxs(DropDownItem, {
|
|
239
263
|
className: `item ${dropDownActiveClass('code' === blockType)}`,
|
|
240
264
|
onClick: formatCode,
|
|
241
265
|
children: [
|
|
@@ -284,8 +308,10 @@ function ToolbarPlugin() {
|
|
|
284
308
|
toolbarConfig.items
|
|
285
309
|
]);
|
|
286
310
|
const hasLinkExtension = void 0 !== useOptionalExtensionDependency(LinkExtension);
|
|
287
|
-
const { uuid, config: {
|
|
311
|
+
const { uuid, config: { controls: { textAlignment, undoRedo, blockFormat, inlineCode, markdownToggle } } } = useEditorConfig();
|
|
288
312
|
const { isMarkdown, toggleMarkdown } = useMarkdownToggle();
|
|
313
|
+
const isApple = useIsApplePlatform();
|
|
314
|
+
const canToggleMarkdown = canUseMarkdownSourceMode(editor);
|
|
289
315
|
const $updateToolbar = useCallback(()=>{
|
|
290
316
|
const selection = $getSelection();
|
|
291
317
|
if ($isRangeSelection(selection)) {
|
|
@@ -434,7 +460,7 @@ function ToolbarPlugin() {
|
|
|
434
460
|
onClick: ()=>{
|
|
435
461
|
activeEditor.dispatchCommand(UNDO_COMMAND, void 0);
|
|
436
462
|
},
|
|
437
|
-
title:
|
|
463
|
+
title: isApple ? 'Undo (⌘Z)' : 'Undo (Ctrl+Z)',
|
|
438
464
|
type: "button",
|
|
439
465
|
className: "toolbar-item spaced",
|
|
440
466
|
"aria-label": "Undo",
|
|
@@ -447,7 +473,7 @@ function ToolbarPlugin() {
|
|
|
447
473
|
onClick: ()=>{
|
|
448
474
|
activeEditor.dispatchCommand(REDO_COMMAND, void 0);
|
|
449
475
|
},
|
|
450
|
-
title:
|
|
476
|
+
title: isApple ? 'Redo (⌘Y)' : 'Redo (Ctrl+Y)',
|
|
451
477
|
type: "button",
|
|
452
478
|
className: "toolbar-item",
|
|
453
479
|
"aria-label": "Redo",
|
|
@@ -460,7 +486,7 @@ function ToolbarPlugin() {
|
|
|
460
486
|
}),
|
|
461
487
|
blockType in blockTypeToBlockName && activeEditor === editor && /*#__PURE__*/ jsxs(Fragment, {
|
|
462
488
|
children: [
|
|
463
|
-
|
|
489
|
+
blockFormat && /*#__PURE__*/ jsxs(Fragment, {
|
|
464
490
|
children: [
|
|
465
491
|
/*#__PURE__*/ jsx(BlockFormatDropDown, {
|
|
466
492
|
disabled: !isEditable,
|
|
@@ -601,9 +627,9 @@ function ToolbarPlugin() {
|
|
|
601
627
|
activeEditor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold');
|
|
602
628
|
},
|
|
603
629
|
className: `toolbar-item spaced ${isBold ? 'active' : ''}`,
|
|
604
|
-
title:
|
|
630
|
+
title: isApple ? 'Bold (⌘B)' : 'Bold (Ctrl+B)',
|
|
605
631
|
type: "button",
|
|
606
|
-
"aria-label": `Format text as bold. Shortcut: ${
|
|
632
|
+
"aria-label": `Format text as bold. Shortcut: ${isApple ? '⌘B' : 'Ctrl+B'}`,
|
|
607
633
|
children: /*#__PURE__*/ jsx("i", {
|
|
608
634
|
className: "format bold"
|
|
609
635
|
})
|
|
@@ -614,9 +640,9 @@ function ToolbarPlugin() {
|
|
|
614
640
|
activeEditor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic');
|
|
615
641
|
},
|
|
616
642
|
className: `toolbar-item spaced ${isItalic ? 'active' : ''}`,
|
|
617
|
-
title:
|
|
643
|
+
title: isApple ? 'Italic (⌘I)' : 'Italic (Ctrl+I)',
|
|
618
644
|
type: "button",
|
|
619
|
-
"aria-label": `Format text as italics. Shortcut: ${
|
|
645
|
+
"aria-label": `Format text as italics. Shortcut: ${isApple ? '⌘I' : 'Ctrl+I'}`,
|
|
620
646
|
children: /*#__PURE__*/ jsx("i", {
|
|
621
647
|
className: "format italic"
|
|
622
648
|
})
|
|
@@ -627,9 +653,9 @@ function ToolbarPlugin() {
|
|
|
627
653
|
activeEditor.dispatchCommand(FORMAT_TEXT_COMMAND, 'underline');
|
|
628
654
|
},
|
|
629
655
|
className: `toolbar-item spaced ${isUnderline ? 'active' : ''}`,
|
|
630
|
-
title:
|
|
656
|
+
title: isApple ? 'Underline (⌘U)' : 'Underline (Ctrl+U)',
|
|
631
657
|
type: "button",
|
|
632
|
-
"aria-label": `Format text to underlined. Shortcut: ${
|
|
658
|
+
"aria-label": `Format text to underlined. Shortcut: ${isApple ? '⌘U' : 'Ctrl+U'}`,
|
|
633
659
|
children: /*#__PURE__*/ jsx("i", {
|
|
634
660
|
className: "format underline"
|
|
635
661
|
})
|
|
@@ -759,7 +785,7 @@ function ToolbarPlugin() {
|
|
|
759
785
|
children: item.node
|
|
760
786
|
}, item.id))
|
|
761
787
|
}),
|
|
762
|
-
markdownToggle && /*#__PURE__*/ jsxs(Fragment, {
|
|
788
|
+
markdownToggle && canToggleMarkdown && /*#__PURE__*/ jsxs(Fragment, {
|
|
763
789
|
children: [
|
|
764
790
|
/*#__PURE__*/ jsx(Divider, {}),
|
|
765
791
|
/*#__PURE__*/ jsx("button", {
|
|
@@ -780,4 +806,4 @@ function ToolbarPlugin() {
|
|
|
780
806
|
]
|
|
781
807
|
});
|
|
782
808
|
}
|
|
783
|
-
export { ToolbarPlugin };
|
|
809
|
+
export { ToolbarPlugin, availableBlockFormats };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { buildEditorFromExtensions } from "@lexical/extension";
|
|
2
|
+
import { $generateNodesFromDOM } from "@lexical/html";
|
|
3
|
+
import { ReactPluginHostExtension } from "@lexical/react/ReactPluginHostExtension";
|
|
4
|
+
import { defineExtension } from "lexical";
|
|
5
|
+
import { builtInExtensions } from "../config/built-in-extension-names.js";
|
|
6
|
+
import { defaultExtensionsList } from "../config/default-extensions.js";
|
|
7
|
+
import { rootDependencies } from "../config/root-dependencies.js";
|
|
8
|
+
const NAMESPACE = 'LexicalRichText';
|
|
9
|
+
function build(extensions) {
|
|
10
|
+
return buildEditorFromExtensions(defineExtension({
|
|
11
|
+
name: '[test-root]',
|
|
12
|
+
namespace: NAMESPACE,
|
|
13
|
+
dependencies: [
|
|
14
|
+
ReactPluginHostExtension,
|
|
15
|
+
...rootDependencies(extensions)
|
|
16
|
+
]
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function buildFullEditor() {
|
|
20
|
+
return build(defaultExtensionsList());
|
|
21
|
+
}
|
|
22
|
+
function buildRestrictedEditor(removals) {
|
|
23
|
+
const names = removals ?? [
|
|
24
|
+
...Object.values(builtInExtensions),
|
|
25
|
+
'@lexical/list/List',
|
|
26
|
+
'@lexical/list/CheckList'
|
|
27
|
+
];
|
|
28
|
+
const list = defaultExtensionsList();
|
|
29
|
+
for (const name of names)list.remove(name);
|
|
30
|
+
return build(list);
|
|
31
|
+
}
|
|
32
|
+
function parseHtmlToTypes(editor, html) {
|
|
33
|
+
const dom = new DOMParser().parseFromString(html, 'text/html');
|
|
34
|
+
let result = {
|
|
35
|
+
types: [],
|
|
36
|
+
text: ''
|
|
37
|
+
};
|
|
38
|
+
editor.update(()=>{
|
|
39
|
+
const nodes = $generateNodesFromDOM(editor, dom);
|
|
40
|
+
result = {
|
|
41
|
+
types: nodes.map((node)=>node.getType()),
|
|
42
|
+
text: nodes.map((node)=>node.getTextContent()).join('|')
|
|
43
|
+
};
|
|
44
|
+
}, {
|
|
45
|
+
discrete: true
|
|
46
|
+
});
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
export { buildFullEditor, buildRestrictedEditor, parseHtmlToTypes };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function installFrameControl() {
|
|
2
|
+
const frameCallbacks = [];
|
|
3
|
+
const idleCallbacks = [];
|
|
4
|
+
const originalRaf = globalThis.requestAnimationFrame;
|
|
5
|
+
const originalCaf = globalThis.cancelAnimationFrame;
|
|
6
|
+
const originalRic = globalThis.requestIdleCallback;
|
|
7
|
+
const originalCic = globalThis.cancelIdleCallback;
|
|
8
|
+
globalThis.requestAnimationFrame = (callback)=>{
|
|
9
|
+
frameCallbacks.push(callback);
|
|
10
|
+
return frameCallbacks.length;
|
|
11
|
+
};
|
|
12
|
+
globalThis.cancelAnimationFrame = ()=>{};
|
|
13
|
+
globalThis.requestIdleCallback = (callback)=>{
|
|
14
|
+
idleCallbacks.push(callback);
|
|
15
|
+
return idleCallbacks.length;
|
|
16
|
+
};
|
|
17
|
+
globalThis.cancelIdleCallback = ()=>{};
|
|
18
|
+
const flushFrames = async (generations = 1)=>{
|
|
19
|
+
for(let generation = 0; generation < generations; generation++){
|
|
20
|
+
await Promise.resolve();
|
|
21
|
+
for (const callback of frameCallbacks.splice(0))callback(performance.now());
|
|
22
|
+
}
|
|
23
|
+
await Promise.resolve();
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
flushFrames,
|
|
27
|
+
flushIdle: ()=>{
|
|
28
|
+
for (const callback of idleCallbacks.splice(0))callback();
|
|
29
|
+
},
|
|
30
|
+
pendingIdle: ()=>idleCallbacks.length,
|
|
31
|
+
settleBaseline: ()=>flushFrames(2),
|
|
32
|
+
restore: ()=>{
|
|
33
|
+
globalThis.requestAnimationFrame = originalRaf;
|
|
34
|
+
globalThis.cancelAnimationFrame = originalCaf;
|
|
35
|
+
globalThis.requestIdleCallback = originalRic;
|
|
36
|
+
globalThis.cancelIdleCallback = originalCic;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export { installFrameControl };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
if ("u" > typeof window) {
|
|
2
|
+
if ('function' != typeof window.matchMedia) Object.defineProperty(window, 'matchMedia', {
|
|
3
|
+
writable: true,
|
|
4
|
+
value: (query)=>({
|
|
5
|
+
matches: false,
|
|
6
|
+
media: query,
|
|
7
|
+
onchange: null,
|
|
8
|
+
addListener: ()=>{},
|
|
9
|
+
removeListener: ()=>{},
|
|
10
|
+
addEventListener: ()=>{},
|
|
11
|
+
removeEventListener: ()=>{},
|
|
12
|
+
dispatchEvent: ()=>false
|
|
13
|
+
})
|
|
14
|
+
});
|
|
15
|
+
if (void 0 === globalThis.ResizeObserver) globalThis.ResizeObserver = class {
|
|
16
|
+
observe() {}
|
|
17
|
+
unobserve() {}
|
|
18
|
+
disconnect() {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export { };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,20 +9,26 @@ export { AutoEmbedExtension } from './field/extensions/auto-embed/auto-embed-ext
|
|
|
9
9
|
export { type BylineFloatingUIConfig, BylineFloatingUIExtension, type BylineFloatingUIItem, type BylineFloatingUIProps, selectFloatingUIItems, } from './field/extensions/byline-floating-ui/index.js';
|
|
10
10
|
export { type BylineToolbarConfig, BylineToolbarExtension, type BylineToolbarItem, type BylineToolbarPlacement, selectToolbarItems, } from './field/extensions/byline-toolbar/index.js';
|
|
11
11
|
export { CodeHighlightExtension } from './field/extensions/code-highlight/code-highlight-extension.js';
|
|
12
|
+
export { CoreNodesExtension } from './field/extensions/core-nodes/core-nodes-extension.js';
|
|
12
13
|
export { FloatingTextFormatExtension } from './field/extensions/floating-text-format/floating-text-format-extension.js';
|
|
14
|
+
export { HeadingExtension } from './field/extensions/heading/heading-extension.js';
|
|
13
15
|
export { HorizontalRuleExtension } from './field/extensions/horizontal-rule/horizontal-rule-extension.js';
|
|
14
16
|
export { type InlineImageConfig, InlineImageExtension, } from './field/extensions/inline-image/inline-image-extension.js';
|
|
15
17
|
export { LayoutExtension } from './field/extensions/layout/layout-extension.js';
|
|
16
18
|
export { AutoLinkExtension, LinkExtension } from './field/extensions/link/index.js';
|
|
19
|
+
export { QuoteExtension } from './field/extensions/quote/quote-extension.js';
|
|
17
20
|
export { TableExtension } from './field/extensions/table/table-extension.js';
|
|
18
21
|
export { VimeoExtension } from './field/extensions/vimeo/vimeo-extension.js';
|
|
19
22
|
export { YouTubeExtension } from './field/extensions/youtube/youtube-extension.js';
|
|
20
|
-
export {
|
|
23
|
+
export { READABLE_NODES } from './field/nodes/index.js';
|
|
21
24
|
export { ToolbarActiveEditorProvider, useToolbarActiveEditor, } from './field/plugins/toolbar-plugin/toolbar-active-editor.js';
|
|
22
25
|
export { lexicalEditor } from './lexical-editor.js';
|
|
23
26
|
export { RichTextField } from './richtext-field.js';
|
|
27
|
+
export { buildManifest, capabilitiesFor, capabilitiesFromEditor } from './scan/capability-manifest.js';
|
|
28
|
+
export { scanDocument, summarise } from './scan/scan-documents.js';
|
|
24
29
|
export { createEmptyEditorState } from './validate/createEmptyEditorState.js';
|
|
25
30
|
export { hasText } from './validate/hasText.js';
|
|
26
31
|
export type { EditorConfig, EditorSettings, EditorSettingsOverride } from './field/config/types.js';
|
|
27
32
|
export type { InlineImageAttributes, Position as InlineImagePosition, SerializedInlineImageNode, } from './field/extensions/inline-image/node-types.js';
|
|
28
33
|
export type { DocumentRelation } from './field/nodes/document-relation.js';
|
|
34
|
+
export type { CapabilityManifest, FieldCapabilities, ScanFinding } from './scan/types.js';
|
package/dist/index.js
CHANGED
|
@@ -8,17 +8,22 @@ export { AutoEmbedExtension } from "./field/extensions/auto-embed/auto-embed-ext
|
|
|
8
8
|
export { BylineFloatingUIExtension, selectFloatingUIItems } from "./field/extensions/byline-floating-ui/index.js";
|
|
9
9
|
export { BylineToolbarExtension, selectToolbarItems } from "./field/extensions/byline-toolbar/index.js";
|
|
10
10
|
export { CodeHighlightExtension } from "./field/extensions/code-highlight/code-highlight-extension.js";
|
|
11
|
+
export { CoreNodesExtension } from "./field/extensions/core-nodes/core-nodes-extension.js";
|
|
11
12
|
export { FloatingTextFormatExtension } from "./field/extensions/floating-text-format/floating-text-format-extension.js";
|
|
13
|
+
export { HeadingExtension } from "./field/extensions/heading/heading-extension.js";
|
|
12
14
|
export { HorizontalRuleExtension } from "./field/extensions/horizontal-rule/horizontal-rule-extension.js";
|
|
13
15
|
export { InlineImageExtension } from "./field/extensions/inline-image/inline-image-extension.js";
|
|
14
16
|
export { LayoutExtension } from "./field/extensions/layout/layout-extension.js";
|
|
15
17
|
export { AutoLinkExtension, LinkExtension } from "./field/extensions/link/index.js";
|
|
18
|
+
export { QuoteExtension } from "./field/extensions/quote/quote-extension.js";
|
|
16
19
|
export { TableExtension } from "./field/extensions/table/table-extension.js";
|
|
17
20
|
export { VimeoExtension } from "./field/extensions/vimeo/vimeo-extension.js";
|
|
18
21
|
export { YouTubeExtension } from "./field/extensions/youtube/youtube-extension.js";
|
|
19
|
-
export {
|
|
22
|
+
export { READABLE_NODES } from "./field/nodes/index.js";
|
|
20
23
|
export { ToolbarActiveEditorProvider, useToolbarActiveEditor } from "./field/plugins/toolbar-plugin/toolbar-active-editor.js";
|
|
21
24
|
export { lexicalEditor } from "./lexical-editor.js";
|
|
22
25
|
export { RichTextField } from "./richtext-field.js";
|
|
26
|
+
export { buildManifest, capabilitiesFor, capabilitiesFromEditor } from "./scan/capability-manifest.js";
|
|
27
|
+
export { scanDocument, summarise } from "./scan/scan-documents.js";
|
|
23
28
|
export { createEmptyEditorState } from "./validate/createEmptyEditorState.js";
|
|
24
29
|
export { hasText } from "./validate/hasText.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { registeredNodeTypes } from '../field/capabilities/registered-node-types.js';
|
|
2
|
+
import type { EditorConfig } from '../field/config/types.js';
|
|
3
|
+
import type { CapabilityManifest, FieldCapabilities } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What one field accepts, measured by BUILDING its editor.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately not derived by walking declared `nodes` on the extension
|
|
8
|
+
* list: that would recreate the resolver — dependency-contributed nodes,
|
|
9
|
+
* conflicts, replacements and always-on core nodes — and a manifest that
|
|
10
|
+
* disagrees with the runtime is worse than no manifest. This is the same
|
|
11
|
+
* construction `EditorContext` performs, so the two cannot drift.
|
|
12
|
+
*
|
|
13
|
+
* Requires a DOM and a React-capable module graph, because several
|
|
14
|
+
* extensions host React decorators. It therefore runs in a browser or
|
|
15
|
+
* jsdom, never in a plain Node script — which is why the manifest is a
|
|
16
|
+
* file handed to the scanner rather than something the scanner derives.
|
|
17
|
+
*/
|
|
18
|
+
export declare function capabilitiesFor(collectionPath: string, fieldPath: string, config: Pick<EditorConfig, 'extensions' | 'lexical'>): FieldCapabilities;
|
|
19
|
+
/** Assemble a manifest from per-field capabilities. */
|
|
20
|
+
export declare function buildManifest(fields: FieldCapabilities[]): CapabilityManifest;
|
|
21
|
+
/**
|
|
22
|
+
* What a live editor accepts.
|
|
23
|
+
*
|
|
24
|
+
* `capabilitiesFor` needs a resolved `EditorConfig`, which is not always
|
|
25
|
+
* reachable: `lexicalEditor()` closes over its configure callback inside
|
|
26
|
+
* a lazy component, so a site's own preset exposes no config to inspect.
|
|
27
|
+
* Measuring a mounted editor works for any field however its editor was
|
|
28
|
+
* registered, which is what a manifest walk over real collections needs.
|
|
29
|
+
*/
|
|
30
|
+
export declare function capabilitiesFromEditor(collectionPath: string, fieldPath: string, editor: Parameters<typeof registeredNodeTypes>[0]): FieldCapabilities;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { buildEditorFromExtensions } from "@lexical/extension";
|
|
3
|
+
import { ReactPluginHostExtension } from "@lexical/react/ReactPluginHostExtension";
|
|
4
|
+
import { defineExtension } from "lexical";
|
|
5
|
+
import { registeredNodeTypes } from "../field/capabilities/registered-node-types.js";
|
|
6
|
+
import { defaultExtensionsList } from "../field/config/default-extensions.js";
|
|
7
|
+
import { rootDependencies } from "../field/config/root-dependencies.js";
|
|
8
|
+
function capabilitiesFor(collectionPath, fieldPath, config) {
|
|
9
|
+
const extensions = config.extensions ?? defaultExtensionsList();
|
|
10
|
+
const editor = buildEditorFromExtensions(defineExtension({
|
|
11
|
+
name: '[capability-manifest]',
|
|
12
|
+
namespace: config.lexical?.namespace ?? 'LexicalRichText',
|
|
13
|
+
dependencies: [
|
|
14
|
+
ReactPluginHostExtension,
|
|
15
|
+
...rootDependencies(extensions)
|
|
16
|
+
]
|
|
17
|
+
}));
|
|
18
|
+
const supportedTypes = [
|
|
19
|
+
...registeredNodeTypes(editor)
|
|
20
|
+
].sort();
|
|
21
|
+
editor.dispose();
|
|
22
|
+
return {
|
|
23
|
+
collectionPath,
|
|
24
|
+
fieldPath,
|
|
25
|
+
supportedTypes
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function buildManifest(fields) {
|
|
29
|
+
return {
|
|
30
|
+
generatedAt: new Date().toISOString(),
|
|
31
|
+
fields
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function capabilitiesFromEditor(collectionPath, fieldPath, editor) {
|
|
35
|
+
return {
|
|
36
|
+
collectionPath,
|
|
37
|
+
fieldPath,
|
|
38
|
+
supportedTypes: [
|
|
39
|
+
...registeredNodeTypes(editor)
|
|
40
|
+
].sort()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export { buildManifest, capabilitiesFor, capabilitiesFromEditor };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { SerializedEditorState } from 'lexical';
|
|
9
|
+
import type { FieldCapabilities, ScanFinding } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Report what a field's current configuration will do with one stored
|
|
12
|
+
* value.
|
|
13
|
+
*
|
|
14
|
+
* Runs the same `normalizeValue` the editor runs, rather than a parallel
|
|
15
|
+
* rule set, so the report cannot drift from the behaviour it predicts.
|
|
16
|
+
* Pure: no database, no DOM, no editor.
|
|
17
|
+
*/
|
|
18
|
+
export declare function scanDocument(value: SerializedEditorState | null | undefined, capabilities: FieldCapabilities, ids: {
|
|
19
|
+
documentId: string;
|
|
20
|
+
versionId: string;
|
|
21
|
+
locale?: string;
|
|
22
|
+
instancePath?: string;
|
|
23
|
+
}): ScanFinding | undefined;
|
|
24
|
+
/** Group findings for a report: what will adapt, and what will open read-only. */
|
|
25
|
+
export declare function summarise(findings: ScanFinding[]): {
|
|
26
|
+
adapted: ScanFinding[];
|
|
27
|
+
refused: ScanFinding[];
|
|
28
|
+
unmapped: ScanFinding[];
|
|
29
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { normalizeValue } from "../field/normalize/normalize-value.js";
|
|
2
|
+
function scanDocument(value, capabilities, ids) {
|
|
3
|
+
if (null == value) return;
|
|
4
|
+
if (null == capabilities.supportedTypes) {
|
|
5
|
+
const unmapped = {
|
|
6
|
+
collectionPath: capabilities.collectionPath,
|
|
7
|
+
fieldPath: capabilities.fieldPath,
|
|
8
|
+
documentId: ids.documentId,
|
|
9
|
+
versionId: ids.versionId,
|
|
10
|
+
adaptedTypes: [],
|
|
11
|
+
refusedTypes: [],
|
|
12
|
+
unmapped: true
|
|
13
|
+
};
|
|
14
|
+
if (null != ids.locale) unmapped.locale = ids.locale;
|
|
15
|
+
if (null != ids.instancePath) unmapped.instancePath = ids.instancePath;
|
|
16
|
+
return unmapped;
|
|
17
|
+
}
|
|
18
|
+
const result = normalizeValue(value, new Set(capabilities.supportedTypes));
|
|
19
|
+
if ('unchanged' === result.status) return;
|
|
20
|
+
const finding = {
|
|
21
|
+
collectionPath: capabilities.collectionPath,
|
|
22
|
+
fieldPath: capabilities.fieldPath,
|
|
23
|
+
documentId: ids.documentId,
|
|
24
|
+
versionId: ids.versionId,
|
|
25
|
+
adaptedTypes: 'adapted' === result.status ? result.convertedTypes : [],
|
|
26
|
+
refusedTypes: 'refused' === result.status ? result.unsupportedTypes : []
|
|
27
|
+
};
|
|
28
|
+
if (null != ids.locale) finding.locale = ids.locale;
|
|
29
|
+
if (null != ids.instancePath) finding.instancePath = ids.instancePath;
|
|
30
|
+
return finding;
|
|
31
|
+
}
|
|
32
|
+
function summarise(findings) {
|
|
33
|
+
return {
|
|
34
|
+
adapted: findings.filter((finding)=>true !== finding.unmapped && 0 === finding.refusedTypes.length),
|
|
35
|
+
refused: findings.filter((finding)=>true !== finding.unmapped && finding.refusedTypes.length > 0),
|
|
36
|
+
unmapped: findings.filter((finding)=>true === finding.unmapped)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export { scanDocument, summarise };
|