@byline/richtext-lexical 5.2.0 → 6.0.1
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
package/dist/field/editor.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { memo, useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
4
5
|
import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
|
|
5
6
|
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
|
|
6
7
|
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
|
7
8
|
import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
|
|
8
9
|
import { PlainTextPlugin } from "@lexical/react/LexicalPlainTextPlugin";
|
|
9
10
|
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
10
|
-
import { useExtensionDependency
|
|
11
|
+
import { useExtensionDependency } from "@lexical/react/useExtensionComponent";
|
|
12
|
+
import { transformersFor } from "./capabilities/filter-transformers.js";
|
|
13
|
+
import { supportsNodeType } from "./capabilities/registered-node-types.js";
|
|
11
14
|
import { useEditorConfig } from "./config/editor-config-context.js";
|
|
12
15
|
import { ContentEditable } from "./content-editable.js";
|
|
13
16
|
import { useMarkdownMode } from "./context/markdown-mode-context.js";
|
|
@@ -15,7 +18,6 @@ import { useSharedHistoryContext } from "./context/shared-history-context.js";
|
|
|
15
18
|
import { useSharedOnChange } from "./context/shared-on-change-context.js";
|
|
16
19
|
import { Debug } from "./debug.js";
|
|
17
20
|
import { BylineFloatingUIExtension, selectFloatingUIItems } from "./extensions/byline-floating-ui/byline-floating-ui-extension.js";
|
|
18
|
-
import { TableExtension } from "./extensions/table/table-extension.js";
|
|
19
21
|
import { BYLINE_TRANSFORMERS } from "./markdown/transformers.js";
|
|
20
22
|
import { TablePlugin } from "./plugins/table-plugin/index.js";
|
|
21
23
|
import { ToolbarPlugin } from "./plugins/toolbar-plugin/index.js";
|
|
@@ -33,8 +35,13 @@ const editor_Editor = /*#__PURE__*/ memo(function({ minHeight, maxHeight }) {
|
|
|
33
35
|
const { onChange } = useSharedOnChange();
|
|
34
36
|
const { historyState } = useSharedHistoryContext();
|
|
35
37
|
const { markdownModeRef } = useMarkdownMode();
|
|
36
|
-
const { config: {
|
|
37
|
-
const
|
|
38
|
+
const { config: { mode, markdownShortcuts, controls: { treeView }, debug, placeholderText } } = useEditorConfig();
|
|
39
|
+
const richText = 'richText' === mode;
|
|
40
|
+
const [editor] = useLexicalComposerContext();
|
|
41
|
+
const hasTableNode = supportsNodeType(editor, 'table');
|
|
42
|
+
const activeTransformers = useMemo(()=>transformersFor(editor, BYLINE_TRANSFORMERS), [
|
|
43
|
+
editor
|
|
44
|
+
]);
|
|
38
45
|
const floatingUIItems = selectFloatingUIItems(useExtensionDependency(BylineFloatingUIExtension).config.items);
|
|
39
46
|
const onRef = useCallback((_floatingAnchorElem)=>{
|
|
40
47
|
if (null != _floatingAnchorElem) setFloatingAnchorElem(_floatingAnchorElem);
|
|
@@ -84,10 +91,10 @@ const editor_Editor = /*#__PURE__*/ memo(function({ minHeight, maxHeight }) {
|
|
|
84
91
|
]);
|
|
85
92
|
const content = /*#__PURE__*/ jsxs(Fragment, {
|
|
86
93
|
children: [
|
|
87
|
-
|
|
94
|
+
hasTableNode && /*#__PURE__*/ jsx(TablePlugin, {}),
|
|
88
95
|
richText && /*#__PURE__*/ jsx(ToolbarPlugin, {}),
|
|
89
96
|
/*#__PURE__*/ jsxs("div", {
|
|
90
|
-
className: `editor-container ${
|
|
97
|
+
className: `editor-container ${treeView ? 'tree-view' : ''} ${!richText ? 'plain-text' : ''}`,
|
|
91
98
|
children: [
|
|
92
99
|
/*#__PURE__*/ jsx(OnChangePlugin, {
|
|
93
100
|
ignoreSelectionChange: true,
|
|
@@ -111,8 +118,8 @@ const editor_Editor = /*#__PURE__*/ memo(function({ minHeight, maxHeight }) {
|
|
|
111
118
|
/*#__PURE__*/ jsx(HistoryPlugin, {
|
|
112
119
|
externalHistoryState: historyState
|
|
113
120
|
}),
|
|
114
|
-
|
|
115
|
-
transformers:
|
|
121
|
+
markdownShortcuts && /*#__PURE__*/ jsx(MarkdownShortcutPlugin, {
|
|
122
|
+
transformers: activeTransformers
|
|
116
123
|
}),
|
|
117
124
|
null != floatingAnchorElem && !isSmallWidthViewport && floatingUIItems.map(({ id, Component })=>/*#__PURE__*/ jsx(Component, {
|
|
118
125
|
anchorElem: floatingAnchorElem
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Owns the node classes that belong to no switchable feature.
|
|
10
|
+
*
|
|
11
|
+
* `MarkNode` and `OverflowNode` ship without an upstream extension, and
|
|
12
|
+
* neither corresponds to something a field can turn off. Without an
|
|
13
|
+
* owner both would lose their only registration once the editor root
|
|
14
|
+
* stopped registering a blanket node list.
|
|
15
|
+
*
|
|
16
|
+
* `EditorContext` injects this extension directly into the root's
|
|
17
|
+
* dependencies, outside `editorConfig.extensions`. Leaving it out of
|
|
18
|
+
* `builtInExtensions` would NOT have made it non-removable: this module
|
|
19
|
+
* is publicly exported and `ExtensionsList.remove()` accepts an
|
|
20
|
+
* extension object as readily as a name string, so site code could have
|
|
21
|
+
* removed it from the configurable list. Injection at the root is what
|
|
22
|
+
* enforces its presence.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CoreNodesExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@byline/richtext-lexical/CoreNodes", unknown, unknown>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MarkNode } from "@lexical/mark";
|
|
2
|
+
import { OverflowNode } from "@lexical/overflow";
|
|
3
|
+
import { defineExtension } from "lexical";
|
|
4
|
+
const CoreNodesExtension = defineExtension({
|
|
5
|
+
name: '@byline/richtext-lexical/CoreNodes',
|
|
6
|
+
nodes: ()=>[
|
|
7
|
+
MarkNode,
|
|
8
|
+
OverflowNode
|
|
9
|
+
]
|
|
10
|
+
});
|
|
11
|
+
export { CoreNodesExtension };
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Owns `HeadingNode`.
|
|
10
|
+
*
|
|
11
|
+
* Byline deliberately does not adopt `@lexical/rich-text`'s
|
|
12
|
+
* `RichTextExtension` for this. That extension binds heading and quote
|
|
13
|
+
* registration to general rich-text behaviour, so adopting it would
|
|
14
|
+
* register headings in every rich-text field — and a field configured
|
|
15
|
+
* without headings would still accept one from a paste, which is the
|
|
16
|
+
* defect this package had. Owning the node here keeps headings
|
|
17
|
+
* removable on their own, independently of quotes and of rich-text
|
|
18
|
+
* behaviour itself.
|
|
19
|
+
*/
|
|
20
|
+
export declare const HeadingExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@byline/richtext-lexical/Heading", unknown, unknown>;
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Owns `QuoteNode`.
|
|
10
|
+
*
|
|
11
|
+
* Separate from {@link HeadingExtension} on purpose: a field may want
|
|
12
|
+
* headings without block quotes, or neither. See the heading extension
|
|
13
|
+
* for why Byline owns these nodes rather than adopting
|
|
14
|
+
* `RichTextExtension`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const QuoteExtension: import("lexical").LexicalExtension<import("lexical").ExtensionConfigBase, "@byline/richtext-lexical/Quote", unknown, unknown>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LexicalEditor } from 'lexical';
|
|
1
2
|
/**
|
|
2
3
|
* Document-level "view as markdown source" toggle for a single editor.
|
|
3
4
|
*
|
|
@@ -13,6 +14,20 @@
|
|
|
13
14
|
* - Edits made in markdown produce a single conversion back to rich
|
|
14
15
|
* nodes on exit, emitting one field value change → **one patch**.
|
|
15
16
|
*/
|
|
17
|
+
/**
|
|
18
|
+
* Whether this editor can offer Markdown source mode.
|
|
19
|
+
*
|
|
20
|
+
* Source mode holds the Markdown text in a `CodeNode`, so it needs that
|
|
21
|
+
* class registered — `$createCodeNode()` throws
|
|
22
|
+
* "Attempted to create node CodeNode that was not configured to be used
|
|
23
|
+
* on the editor" otherwise. A field with the code-highlight extension
|
|
24
|
+
* removed therefore cannot offer the toggle.
|
|
25
|
+
*
|
|
26
|
+
* The fix is to withhold the control, NOT to register `CodeNode` for the
|
|
27
|
+
* toggle's benefit: registering it would let code blocks back in through
|
|
28
|
+
* paste and storage, which is the acceptance hole this work closed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function canUseMarkdownSourceMode(editor: LexicalEditor): boolean;
|
|
16
31
|
export declare function useMarkdownToggle(): {
|
|
17
32
|
isMarkdown: boolean;
|
|
18
33
|
toggleMarkdown: () => void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
-
import { $createCodeNode, $isCodeNode } from "@lexical/code";
|
|
3
|
+
import { $createCodeNode, $isCodeNode, CodeNode } from "@lexical/code";
|
|
4
4
|
import { $convertFromMarkdownString, $convertToMarkdownString } from "@lexical/markdown";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { $getRoot, RootNode } from "lexical";
|
|
7
|
+
import { transformersFor } from "../capabilities/filter-transformers.js";
|
|
7
8
|
import { APPLY_VALUE_TAG } from "../constants.js";
|
|
8
9
|
import { useMarkdownMode } from "../context/markdown-mode-context.js";
|
|
9
10
|
import { BYLINE_TRANSFORMERS } from "../markdown/transformers.js";
|
|
@@ -11,6 +12,11 @@ const MARKDOWN_LANGUAGE = 'markdown';
|
|
|
11
12
|
function normalize(markdown) {
|
|
12
13
|
return markdown.replace(/\n+$/, '');
|
|
13
14
|
}
|
|
15
|
+
function canUseMarkdownSourceMode(editor) {
|
|
16
|
+
return editor.hasNodes([
|
|
17
|
+
CodeNode
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
14
20
|
function useMarkdownToggle() {
|
|
15
21
|
const [editor] = useLexicalComposerContext();
|
|
16
22
|
const { isMarkdown, setIsMarkdown, markdownModeRef } = useMarkdownMode();
|
|
@@ -18,6 +24,7 @@ function useMarkdownToggle() {
|
|
|
18
24
|
const originalMarkdownRef = useRef('');
|
|
19
25
|
const unregisterTransformRef = useRef(null);
|
|
20
26
|
const registerRootGuard = useCallback(()=>{
|
|
27
|
+
if (!canUseMarkdownSourceMode(editor)) return;
|
|
21
28
|
unregisterTransformRef.current?.();
|
|
22
29
|
unregisterTransformRef.current = editor.registerNodeTransform(RootNode, (rootNode)=>{
|
|
23
30
|
let codeNode = rootNode.getChildren().find($isCodeNode);
|
|
@@ -38,10 +45,11 @@ function useMarkdownToggle() {
|
|
|
38
45
|
unregisterTransformRef.current = null;
|
|
39
46
|
}, []);
|
|
40
47
|
const enterMarkdown = useCallback(()=>{
|
|
48
|
+
if (!canUseMarkdownSourceMode(editor)) return;
|
|
41
49
|
originalEditorStateRef.current = editor.getEditorState();
|
|
42
50
|
markdownModeRef.current = true;
|
|
43
51
|
editor.update(()=>{
|
|
44
|
-
const markdown = $convertToMarkdownString(BYLINE_TRANSFORMERS, void 0, true);
|
|
52
|
+
const markdown = $convertToMarkdownString(transformersFor(editor, BYLINE_TRANSFORMERS), void 0, true);
|
|
45
53
|
originalMarkdownRef.current = markdown;
|
|
46
54
|
const codeNode = $createCodeNode(MARKDOWN_LANGUAGE);
|
|
47
55
|
$getRoot().clear().append(codeNode);
|
|
@@ -66,7 +74,7 @@ function useMarkdownToggle() {
|
|
|
66
74
|
if (edited || null == originalEditorStateRef.current) {
|
|
67
75
|
markdownModeRef.current = false;
|
|
68
76
|
editor.update(()=>{
|
|
69
|
-
$convertFromMarkdownString(currentMarkdown, BYLINE_TRANSFORMERS, void 0, true);
|
|
77
|
+
$convertFromMarkdownString(currentMarkdown, transformersFor(editor, BYLINE_TRANSFORMERS), void 0, true);
|
|
70
78
|
});
|
|
71
79
|
} else {
|
|
72
80
|
editor.setEditorState(originalEditorStateRef.current, {
|
|
@@ -99,4 +107,4 @@ function useMarkdownToggle() {
|
|
|
99
107
|
toggleMarkdown
|
|
100
108
|
};
|
|
101
109
|
}
|
|
102
|
-
export { useMarkdownToggle };
|
|
110
|
+
export { canUseMarkdownSourceMode, useMarkdownToggle };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether to label keyboard shortcuts the Apple way.
|
|
3
|
+
*
|
|
4
|
+
* `IS_APPLE` is resolved at module scope from `navigator.platform`, which
|
|
5
|
+
* is false on the server and true on a Mac client. Reading it directly
|
|
6
|
+
* during render therefore produced markup the server and client
|
|
7
|
+
* disagreed about — React reported a hydration mismatch on the toolbar's
|
|
8
|
+
* `title` and `aria-label` attributes and, as it warns, did not patch
|
|
9
|
+
* them up: a Mac user could be shown `Ctrl+B`, and a screen reader could
|
|
10
|
+
* announce it.
|
|
11
|
+
*
|
|
12
|
+
* `useSyncExternalStore` uses its server snapshot for the initial client
|
|
13
|
+
* render as well as for SSR, so the first client render matches the
|
|
14
|
+
* server exactly and the real platform is adopted immediately after.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useIsApplePlatform(): boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
import { IS_APPLE } from "../shared/environment.js";
|
|
4
|
+
const subscribe = ()=>()=>{};
|
|
5
|
+
function useIsApplePlatform() {
|
|
6
|
+
return useSyncExternalStore(subscribe, ()=>IS_APPLE, ()=>false);
|
|
7
|
+
}
|
|
8
|
+
export { useIsApplePlatform };
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { type ElementTransformer, type MultilineElementTransformer, type Transformer } from '@lexical/markdown';
|
|
22
22
|
export declare const TABLE: ElementTransformer;
|
|
23
|
+
export declare const ADMONITION_BODY_TRANSFORMERS: Array<Transformer>;
|
|
23
24
|
export declare const ADMONITION: MultilineElementTransformer;
|
|
24
25
|
/**
|
|
25
26
|
* Stock transformers plus Byline's custom-node handlers. Self-referenced by
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { $convertFromMarkdownString, $convertToMarkdownString, LINK, TEXT_FORMAT_TRANSFORMERS, TRANSFORMERS } from "@lexical/markdown";
|
|
2
2
|
import { $createTableCellNode, $createTableNode, $createTableRowNode, $isTableCellNode, $isTableNode, $isTableRowNode, TableCellHeaderStates, TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
3
|
-
import { $createParagraphNode, $isParagraphNode, $isTextNode } from "lexical";
|
|
3
|
+
import { $createParagraphNode, $getEditor, $isParagraphNode, $isTextNode } from "lexical";
|
|
4
|
+
import { transformersFor } from "../capabilities/filter-transformers.js";
|
|
4
5
|
import { $createAdmonitionNode, $isAdmonitionNode, AdmonitionNode } from "../extensions/admonition/admonition-node.js";
|
|
5
6
|
const TABLE_ROW_REG_EXP = /^(?:\|)(.+)(?:\|)\s?$/;
|
|
6
7
|
const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-+:? ?)+\|?\s?$/;
|
|
@@ -86,7 +87,7 @@ function getTableColumnsSize(table) {
|
|
|
86
87
|
const $createTableCell = (textContent)=>{
|
|
87
88
|
const content = textContent.replace(/\\n/g, '\n');
|
|
88
89
|
const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
|
|
89
|
-
$convertFromMarkdownString(content, BYLINE_TRANSFORMERS, cell);
|
|
90
|
+
$convertFromMarkdownString(content, transformersFor($getEditor(), BYLINE_TRANSFORMERS), cell);
|
|
90
91
|
return cell;
|
|
91
92
|
};
|
|
92
93
|
const mapToTableCells = (textContent)=>{
|
|
@@ -135,7 +136,7 @@ const ADMONITION = {
|
|
|
135
136
|
if (null != children) node.append(...children);
|
|
136
137
|
else if (null != linesInBetween) {
|
|
137
138
|
const body = linesInBetween.join('\n').trim();
|
|
138
|
-
if (body) $convertFromMarkdownString(body, ADMONITION_BODY_TRANSFORMERS, node);
|
|
139
|
+
if (body) $convertFromMarkdownString(body, transformersFor($getEditor(), ADMONITION_BODY_TRANSFORMERS), node);
|
|
139
140
|
}
|
|
140
141
|
if (0 === node.getChildrenSize()) node.append($createParagraphNode());
|
|
141
142
|
rootNode.append(node);
|
|
@@ -148,4 +149,4 @@ const BYLINE_TRANSFORMERS = [
|
|
|
148
149
|
ADMONITION,
|
|
149
150
|
...TRANSFORMERS
|
|
150
151
|
];
|
|
151
|
-
export { ADMONITION, BYLINE_TRANSFORMERS, TABLE };
|
|
152
|
+
export { ADMONITION, ADMONITION_BODY_TRANSFORMERS, BYLINE_TRANSFORMERS, TABLE };
|
|
@@ -6,4 +6,14 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
import type { Klass, LexicalNode } from 'lexical';
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Every node class Byline knows how to READ.
|
|
11
|
+
*
|
|
12
|
+
* This is not a registration list — registration belongs to the
|
|
13
|
+
* extension that owns each node, so that removing an extension removes
|
|
14
|
+
* what the field accepts as well as what it offers. This array is the
|
|
15
|
+
* vocabulary the normalizer and the capability manifest work from, so a
|
|
16
|
+
* stored document can still be inspected by a field that supports far
|
|
17
|
+
* less than the document contains.
|
|
18
|
+
*/
|
|
19
|
+
export declare const READABLE_NODES: Array<Klass<LexicalNode>>;
|
|
@@ -12,7 +12,7 @@ import { LayoutItemNode } from "../extensions/layout/layout-item-node.js";
|
|
|
12
12
|
import { AutoLinkNode, LinkNode } from "../extensions/link/index.js";
|
|
13
13
|
import { VimeoNode } from "../extensions/vimeo/vimeo-node.js";
|
|
14
14
|
import { YouTubeNode } from "../extensions/youtube/youtube-node.js";
|
|
15
|
-
const
|
|
15
|
+
const READABLE_NODES = [
|
|
16
16
|
HeadingNode,
|
|
17
17
|
ListNode,
|
|
18
18
|
ListItemNode,
|
|
@@ -34,4 +34,4 @@ const Nodes = [
|
|
|
34
34
|
LayoutContainerNode,
|
|
35
35
|
LayoutItemNode
|
|
36
36
|
];
|
|
37
|
-
export {
|
|
37
|
+
export { READABLE_NODES };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@layer base, byline-base, theme, byline-theme, byline-functional, byline-typography, components;
|
|
2
|
+
|
|
3
|
+
@layer byline-components {
|
|
4
|
+
.byline-richtext-notice {
|
|
5
|
+
border-radius: var(--border-radius-sm, 4px);
|
|
6
|
+
font-size: var(--font-size-sm, .8125rem);
|
|
7
|
+
margin-bottom: 8px;
|
|
8
|
+
padding: 8px 10px;
|
|
9
|
+
line-height: 1.4;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.byline-richtext-notice p {
|
|
13
|
+
margin: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.byline-richtext-notice p + p {
|
|
17
|
+
margin-top: 4px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.byline-richtext-notice--adapted {
|
|
21
|
+
background-color: var(--fill-warning-weak);
|
|
22
|
+
border: 1px solid var(--stroke-warning);
|
|
23
|
+
color: var(--text-on-warning-weak);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.byline-richtext-notice--refused {
|
|
27
|
+
background-color: var(--fill-danger-weak);
|
|
28
|
+
border: 1px solid var(--stroke-danger);
|
|
29
|
+
color: var(--text-on-danger-weak);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@layer utilities, byline-utilities;
|
|
34
|
+
|
|
@@ -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 * as React from 'react';
|
|
9
|
+
import './adapted-notice.css';
|
|
10
|
+
/**
|
|
11
|
+
* Shown when stored content held a structure this field no longer
|
|
12
|
+
* supports and it was adapted for editing.
|
|
13
|
+
*
|
|
14
|
+
* Inline and non-blocking: the content is editable, and the adaptation
|
|
15
|
+
* only reaches storage if the reader actually saves an edit.
|
|
16
|
+
*/
|
|
17
|
+
export declare function AdaptedNotice(): React.JSX.Element;
|
|
18
|
+
/**
|
|
19
|
+
* Shown when stored content holds something with no safe conversion —
|
|
20
|
+
* an image, an embed, or a node type this build does not know.
|
|
21
|
+
*
|
|
22
|
+
* Those carry information no structural rewrite can preserve: an inline
|
|
23
|
+
* image has a media relation and a caption held in a nested editor.
|
|
24
|
+
* Discarding them silently would be worse than declining to edit, so the
|
|
25
|
+
* field opens read-only rather than adapting.
|
|
26
|
+
*/
|
|
27
|
+
export declare function UnsupportedContentNotice({ unsupportedTypes, }: {
|
|
28
|
+
unsupportedTypes: string[];
|
|
29
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { contentLabelKey, joinLabels } from "./content-labels.js";
|
|
4
|
+
import { useNoticeText } from "./use-notice-text.js";
|
|
5
|
+
import "./adapted-notice.css";
|
|
6
|
+
function AdaptedNotice() {
|
|
7
|
+
const { t } = useNoticeText();
|
|
8
|
+
return /*#__PURE__*/ jsx("div", {
|
|
9
|
+
className: "byline-richtext-notice byline-richtext-notice--adapted",
|
|
10
|
+
role: "status",
|
|
11
|
+
children: t('richtext.adapted.notice')
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function UnsupportedContentNotice({ unsupportedTypes }) {
|
|
15
|
+
const { t, locale } = useNoticeText();
|
|
16
|
+
const labels = unsupportedTypes.map((type)=>{
|
|
17
|
+
const key = contentLabelKey(type);
|
|
18
|
+
return null != key ? t(key) : type;
|
|
19
|
+
});
|
|
20
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
21
|
+
className: "byline-richtext-notice byline-richtext-notice--refused",
|
|
22
|
+
role: "alert",
|
|
23
|
+
children: [
|
|
24
|
+
/*#__PURE__*/ jsx("p", {
|
|
25
|
+
children: t('richtext.unsupported.notice', {
|
|
26
|
+
items: joinLabels(labels, locale)
|
|
27
|
+
})
|
|
28
|
+
}),
|
|
29
|
+
/*#__PURE__*/ jsx("p", {
|
|
30
|
+
children: t('richtext.unsupported.action')
|
|
31
|
+
})
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export { AdaptedNotice, UnsupportedContentNotice };
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
export declare function contentLabelKey(type: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Join labels the way the active locale writes a list.
|
|
11
|
+
*
|
|
12
|
+
* `Intl.ListFormat` is not available in every runtime this may render
|
|
13
|
+
* in, so fall back to a comma join rather than throwing over a notice.
|
|
14
|
+
*/
|
|
15
|
+
export declare function joinLabels(labels: string[], locale: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const CONTENT_LABEL_KEYS = {
|
|
2
|
+
'inline-image': 'richtext.content.image',
|
|
3
|
+
youtube: 'richtext.content.youtube',
|
|
4
|
+
vimeo: 'richtext.content.vimeo'
|
|
5
|
+
};
|
|
6
|
+
function contentLabelKey(type) {
|
|
7
|
+
return CONTENT_LABEL_KEYS[type];
|
|
8
|
+
}
|
|
9
|
+
function joinLabels(labels, locale) {
|
|
10
|
+
try {
|
|
11
|
+
return new Intl.ListFormat(locale, {
|
|
12
|
+
style: 'long',
|
|
13
|
+
type: 'conjunction'
|
|
14
|
+
}).format(labels);
|
|
15
|
+
} catch {
|
|
16
|
+
return labels.join(', ');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export { contentLabelKey, joinLabels };
|
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
/** A serialized Lexical node, as stored. */
|
|
9
|
+
export interface SerializedNode {
|
|
10
|
+
type: string;
|
|
11
|
+
children?: SerializedNode[];
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* What a convertible structure CONTAINS, which is what decides a
|
|
16
|
+
* structurally valid replacement for it.
|
|
17
|
+
*
|
|
18
|
+
* - `to-paragraph` — a block holding inline content. Becomes exactly one
|
|
19
|
+
* paragraph, so two adjacent headings stay two paragraphs and an empty
|
|
20
|
+
* heading stays an empty paragraph instead of vanishing.
|
|
21
|
+
* - `lift-blocks` — a block holding blocks. Its children are lifted in
|
|
22
|
+
* place, already block-level and already valid where the parent sat.
|
|
23
|
+
* - `unwrap-inline` — inline content. Its children are lifted inline and
|
|
24
|
+
* stay inside the paragraph that held them.
|
|
25
|
+
* - `to-text` — a text-carrying leaf whose class is unsupported, such as
|
|
26
|
+
* a syntax-highlighted code token. Becomes a plain text node with its
|
|
27
|
+
* text and inline formats intact.
|
|
28
|
+
* - `drop` — removed entirely; neighbours are untouched.
|
|
29
|
+
*/
|
|
30
|
+
export type ConversionKind = 'to-paragraph' | 'lift-blocks' | 'unwrap-inline' | 'to-text' | 'drop';
|
|
31
|
+
/**
|
|
32
|
+
* Serialized types Byline knows to be block-level.
|
|
33
|
+
*
|
|
34
|
+
* Needed because a converted node's children are not always inline: a
|
|
35
|
+
* `listitem` may hold a nested `list`, so turning every item straight
|
|
36
|
+
* into a paragraph would nest a block inside a paragraph.
|
|
37
|
+
*/
|
|
38
|
+
export declare const BLOCK_TYPES: ReadonlySet<string>;
|
|
39
|
+
export declare function isBlockType(type: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Serialized types Byline knows to be inline.
|
|
42
|
+
*
|
|
43
|
+
* The counterpart to {@link BLOCK_TYPES}, and the reason both lists
|
|
44
|
+
* exist rather than one: a node's structural role cannot be recovered
|
|
45
|
+
* from its serialized shape. An inline `link` and a block `quote` are
|
|
46
|
+
* both elements with `children`, `direction`, `format` and `indent`, so
|
|
47
|
+
* there is no reliable metadata to infer the role from.
|
|
48
|
+
*
|
|
49
|
+
* A SUPPORTED node can reach this test — it arrives as the child of an
|
|
50
|
+
* unsupported parent being converted — so "not a known block" cannot be
|
|
51
|
+
* read as "inline". A custom block from a downstream site would be
|
|
52
|
+
* wrapped into a paragraph and produce a tree Lexical rejects. When a
|
|
53
|
+
* type appears in neither list its role is unknown, and normalization
|
|
54
|
+
* refuses the document rather than guessing at it.
|
|
55
|
+
*/
|
|
56
|
+
export declare const INLINE_TYPES: ReadonlySet<string>;
|
|
57
|
+
export declare function isInlineType(type: string): boolean;
|
|
58
|
+
export interface DeclaredConversion {
|
|
59
|
+
kind: ConversionKind;
|
|
60
|
+
/**
|
|
61
|
+
* Information carried on the node's own properties rather than in its
|
|
62
|
+
* children, which would otherwise be lost silently. Emitted as a
|
|
63
|
+
* leading paragraph for `lift-blocks` (an admonition title becomes the
|
|
64
|
+
* first line) and appended to the text for `unwrap-inline` (a link URL
|
|
65
|
+
* follows its text).
|
|
66
|
+
*/
|
|
67
|
+
preserveText?: (node: SerializedNode) => string | undefined;
|
|
68
|
+
}
|
|
69
|
+
declare const urlText: (node: SerializedNode) => string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* How each convertible structure degrades when a field does not support
|
|
72
|
+
* it. Type strings are the node classes' own `getType()` values.
|
|
73
|
+
*
|
|
74
|
+
* A type absent from this table has NO conversion and is never guessed
|
|
75
|
+
* at — normalization refuses the document instead. That covers custom
|
|
76
|
+
* nodes from downstream sites as well as inline images and embeds, whose
|
|
77
|
+
* media relations and nested-editor captions cannot survive any
|
|
78
|
+
* structural rewrite.
|
|
79
|
+
*/
|
|
80
|
+
export declare const DECLARED_CONVERSIONS: {
|
|
81
|
+
readonly heading: {
|
|
82
|
+
readonly kind: 'to-paragraph';
|
|
83
|
+
};
|
|
84
|
+
readonly quote: {
|
|
85
|
+
readonly kind: 'to-paragraph';
|
|
86
|
+
};
|
|
87
|
+
readonly code: {
|
|
88
|
+
readonly kind: 'to-paragraph';
|
|
89
|
+
};
|
|
90
|
+
readonly listitem: {
|
|
91
|
+
readonly kind: 'to-paragraph';
|
|
92
|
+
};
|
|
93
|
+
readonly table: {
|
|
94
|
+
readonly kind: 'lift-blocks';
|
|
95
|
+
};
|
|
96
|
+
readonly tablerow: {
|
|
97
|
+
readonly kind: 'lift-blocks';
|
|
98
|
+
};
|
|
99
|
+
readonly tablecell: {
|
|
100
|
+
readonly kind: 'lift-blocks';
|
|
101
|
+
};
|
|
102
|
+
readonly list: {
|
|
103
|
+
readonly kind: 'lift-blocks';
|
|
104
|
+
};
|
|
105
|
+
readonly 'layout-container': {
|
|
106
|
+
readonly kind: 'lift-blocks';
|
|
107
|
+
};
|
|
108
|
+
readonly 'layout-item': {
|
|
109
|
+
readonly kind: 'lift-blocks';
|
|
110
|
+
};
|
|
111
|
+
readonly admonition: {
|
|
112
|
+
readonly kind: 'lift-blocks';
|
|
113
|
+
readonly preserveText: (node: SerializedNode) => string | undefined;
|
|
114
|
+
};
|
|
115
|
+
readonly 'code-highlight': {
|
|
116
|
+
readonly kind: 'to-text';
|
|
117
|
+
};
|
|
118
|
+
readonly link: {
|
|
119
|
+
readonly kind: 'unwrap-inline';
|
|
120
|
+
readonly preserveText: typeof urlText;
|
|
121
|
+
};
|
|
122
|
+
readonly autolink: {
|
|
123
|
+
readonly kind: 'unwrap-inline';
|
|
124
|
+
readonly preserveText: typeof urlText;
|
|
125
|
+
};
|
|
126
|
+
readonly horizontalrule: {
|
|
127
|
+
readonly kind: 'drop';
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
export declare function conversionFor(type: string): DeclaredConversion | undefined;
|
|
131
|
+
export {};
|