@blue-patterns/richtext 0.0.0-stage → 0.7.18
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 +275 -0
- package/LICENSE +22 -0
- package/README.md +88 -2
- package/astryx.integration.mjs +11 -0
- package/blocks/RichTextEditorShowcase.template.mjs +13 -0
- package/blocks/RichTextEditorShowcase.tsx +19 -0
- package/dist/RichTextEditor.d.ts +282 -0
- package/dist/RichTextEditor.d.ts.map +1 -0
- package/dist/RichTextEditor.js +657 -0
- package/dist/RichTextEditorAutoLinkPlugin.d.ts +83 -0
- package/dist/RichTextEditorAutoLinkPlugin.d.ts.map +1 -0
- package/dist/RichTextEditorAutoLinkPlugin.js +99 -0
- package/dist/RichTextEditorToolbar.d.ts +186 -0
- package/dist/RichTextEditorToolbar.d.ts.map +1 -0
- package/dist/RichTextEditorToolbar.js +688 -0
- package/dist/RichTextView.d.ts +92 -0
- package/dist/RichTextView.d.ts.map +1 -0
- package/dist/RichTextView.js +267 -0
- package/dist/codeLanguages.d.ts +14 -0
- package/dist/codeLanguages.d.ts.map +1 -0
- package/dist/codeLanguages.js +34 -0
- package/dist/codeLineTokens.d.ts +53 -0
- package/dist/codeLineTokens.d.ts.map +1 -0
- package/dist/codeLineTokens.js +188 -0
- package/dist/editorNodes.d.ts +8 -0
- package/dist/editorNodes.d.ts.map +1 -0
- package/dist/editorNodes.js +30 -0
- package/dist/editorTheme.d.ts +7 -0
- package/dist/editorTheme.d.ts.map +1 -0
- package/dist/editorTheme.js +328 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/internal/CodeBackspacePlugin.d.ts +2 -0
- package/dist/internal/CodeBackspacePlugin.d.ts.map +1 -0
- package/dist/internal/CodeBackspacePlugin.js +70 -0
- package/dist/internal/CodeBlockHeaderPlugin.d.ts +11 -0
- package/dist/internal/CodeBlockHeaderPlugin.d.ts.map +1 -0
- package/dist/internal/CodeBlockHeaderPlugin.js +204 -0
- package/dist/internal/CodePastePlugin.d.ts +5 -0
- package/dist/internal/CodePastePlugin.d.ts.map +1 -0
- package/dist/internal/CodePastePlugin.js +104 -0
- package/dist/internal/CodeSyntaxPlugin.d.ts +8 -0
- package/dist/internal/CodeSyntaxPlugin.d.ts.map +1 -0
- package/dist/internal/CodeSyntaxPlugin.js +397 -0
- package/dist/internal/CodeTabPlugin.d.ts +2 -0
- package/dist/internal/CodeTabPlugin.d.ts.map +1 -0
- package/dist/internal/CodeTabPlugin.js +256 -0
- package/dist/internal/DisabledLinkGuardPlugin.d.ts +4 -0
- package/dist/internal/DisabledLinkGuardPlugin.d.ts.map +1 -0
- package/dist/internal/DisabledLinkGuardPlugin.js +93 -0
- package/dist/internal/ImageSourceCaretPlugin.d.ts +3 -0
- package/dist/internal/ImageSourceCaretPlugin.d.ts.map +1 -0
- package/dist/internal/ImageSourceCaretPlugin.js +105 -0
- package/dist/internal/ListItemShortcutsPlugin.d.ts +6 -0
- package/dist/internal/ListItemShortcutsPlugin.d.ts.map +1 -0
- package/dist/internal/ListItemShortcutsPlugin.js +314 -0
- package/dist/internal/MarkdownExtensionsPlugin.d.ts +17 -0
- package/dist/internal/MarkdownExtensionsPlugin.d.ts.map +1 -0
- package/dist/internal/MarkdownExtensionsPlugin.js +185 -0
- package/dist/internal/SelectAllScopePlugin.d.ts +2 -0
- package/dist/internal/SelectAllScopePlugin.d.ts.map +1 -0
- package/dist/internal/SelectAllScopePlugin.js +67 -0
- package/dist/internal/TableColumnFloorPlugin.d.ts +12 -0
- package/dist/internal/TableColumnFloorPlugin.d.ts.map +1 -0
- package/dist/internal/TableColumnFloorPlugin.js +84 -0
- package/dist/internal/TablePastePlugin.d.ts +9 -0
- package/dist/internal/TablePastePlugin.d.ts.map +1 -0
- package/dist/internal/TablePastePlugin.js +183 -0
- package/dist/internal/TableScrollRegionPlugin.d.ts +9 -0
- package/dist/internal/TableScrollRegionPlugin.d.ts.map +1 -0
- package/dist/internal/TableScrollRegionPlugin.js +95 -0
- package/dist/internal/TaskCheckboxPlugin.d.ts +7 -0
- package/dist/internal/TaskCheckboxPlugin.d.ts.map +1 -0
- package/dist/internal/TaskCheckboxPlugin.js +193 -0
- package/dist/internal/ToolbarLinkPopover.d.ts +74 -0
- package/dist/internal/ToolbarLinkPopover.d.ts.map +1 -0
- package/dist/internal/ToolbarLinkPopover.js +292 -0
- package/dist/linkEditing.d.ts +6 -0
- package/dist/linkEditing.d.ts.map +1 -0
- package/dist/linkEditing.js +20 -0
- package/dist/linkForm.d.ts +28 -0
- package/dist/linkForm.d.ts.map +1 -0
- package/dist/linkForm.js +137 -0
- package/dist/linkUtils.d.ts +27 -0
- package/dist/linkUtils.d.ts.map +1 -0
- package/dist/linkUtils.js +101 -0
- package/dist/markdown.d.ts +17 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +17 -0
- package/dist/markdownCharacterReferences.d.ts +135 -0
- package/dist/markdownCharacterReferences.d.ts.map +1 -0
- package/dist/markdownCharacterReferences.js +1127 -0
- package/dist/markdownCodeFence.d.ts +4 -0
- package/dist/markdownCodeFence.d.ts.map +1 -0
- package/dist/markdownCodeFence.js +167 -0
- package/dist/markdownExtensionNode.d.ts +88 -0
- package/dist/markdownExtensionNode.d.ts.map +1 -0
- package/dist/markdownExtensionNode.js +248 -0
- package/dist/markdownExtensions.d.ts +115 -0
- package/dist/markdownExtensions.d.ts.map +1 -0
- package/dist/markdownExtensions.js +392 -0
- package/dist/markdownHardLineBreak.d.ts +25 -0
- package/dist/markdownHardLineBreak.d.ts.map +1 -0
- package/dist/markdownHardLineBreak.js +59 -0
- package/dist/markdownHeading.d.ts +20 -0
- package/dist/markdownHeading.d.ts.map +1 -0
- package/dist/markdownHeading.js +30 -0
- package/dist/markdownLink.d.ts +5 -0
- package/dist/markdownLink.d.ts.map +1 -0
- package/dist/markdownLink.js +80 -0
- package/dist/markdownListExport.d.ts +3 -0
- package/dist/markdownListExport.d.ts.map +1 -0
- package/dist/markdownListExport.js +68 -0
- package/dist/markdownListIndentation.d.ts +8 -0
- package/dist/markdownListIndentation.d.ts.map +1 -0
- package/dist/markdownListIndentation.js +101 -0
- package/dist/markdownOrderedList.d.ts +3 -0
- package/dist/markdownOrderedList.d.ts.map +1 -0
- package/dist/markdownOrderedList.js +51 -0
- package/dist/markdownQuote.d.ts +22 -0
- package/dist/markdownQuote.d.ts.map +1 -0
- package/dist/markdownQuote.js +32 -0
- package/dist/markdownSerializers.d.ts +49 -0
- package/dist/markdownSerializers.d.ts.map +1 -0
- package/dist/markdownSerializers.js +83 -0
- package/dist/markdownSource.d.ts +71 -0
- package/dist/markdownSource.d.ts.map +1 -0
- package/dist/markdownSource.js +804 -0
- package/dist/markdownSpaceRuns.d.ts +24 -0
- package/dist/markdownSpaceRuns.d.ts.map +1 -0
- package/dist/markdownSpaceRuns.js +147 -0
- package/dist/markdownTable.d.ts +35 -0
- package/dist/markdownTable.d.ts.map +1 -0
- package/dist/markdownTable.js +227 -0
- package/dist/markdownTaskList.d.ts +48 -0
- package/dist/markdownTaskList.d.ts.map +1 -0
- package/dist/markdownTaskList.js +166 -0
- package/dist/markdownThematicBreak.d.ts +3 -0
- package/dist/markdownThematicBreak.d.ts.map +1 -0
- package/dist/markdownThematicBreak.js +55 -0
- package/dist/privateAttributes/layerPortalBoundary.stylex.d.ts +12 -0
- package/dist/privateAttributes/layerPortalBoundary.stylex.d.ts.map +1 -0
- package/dist/privateAttributes/layerPortalBoundary.stylex.js +16 -0
- package/dist/richtext.css +213 -0
- package/dist/textSemantics.d.ts +3 -0
- package/dist/textSemantics.d.ts.map +1 -0
- package/dist/textSemantics.js +87 -0
- package/package.json +158 -3
- package/src/RichTextEditor.doc.mjs +308 -0
- package/src/RichTextEditor.tsx +1109 -0
- package/src/RichTextEditorAutoLinkPlugin.doc.mjs +43 -0
- package/src/RichTextEditorAutoLinkPlugin.tsx +126 -0
- package/src/RichTextEditorToolbar.doc.mjs +92 -0
- package/src/RichTextEditorToolbar.tsx +871 -0
- package/src/RichTextView.doc.mjs +101 -0
- package/src/RichTextView.tsx +351 -0
- package/src/codeLanguages.ts +42 -0
- package/src/codeLineTokens.ts +241 -0
- package/src/doc-mjs.d.ts +23 -0
- package/src/editorNodes.ts +44 -0
- package/src/editorTheme.ts +550 -0
- package/src/index.ts +59 -0
- package/src/internal/CodeBackspacePlugin.tsx +96 -0
- package/src/internal/CodeBlockHeaderPlugin.tsx +282 -0
- package/src/internal/CodePastePlugin.tsx +144 -0
- package/src/internal/CodeSyntaxPlugin.tsx +493 -0
- package/src/internal/CodeTabPlugin.tsx +298 -0
- package/src/internal/DisabledLinkGuardPlugin.tsx +93 -0
- package/src/internal/ImageSourceCaretPlugin.tsx +159 -0
- package/src/internal/ListItemShortcutsPlugin.tsx +404 -0
- package/src/internal/MarkdownExtensionsPlugin.tsx +289 -0
- package/src/internal/SelectAllScopePlugin.tsx +86 -0
- package/src/internal/TableColumnFloorPlugin.tsx +99 -0
- package/src/internal/TablePastePlugin.tsx +223 -0
- package/src/internal/TableScrollRegionPlugin.tsx +107 -0
- package/src/internal/TaskCheckboxPlugin.tsx +246 -0
- package/src/internal/ToolbarLinkPopover.tsx +392 -0
- package/src/linkEditing.ts +20 -0
- package/src/linkForm.tsx +156 -0
- package/src/linkUtils.ts +106 -0
- package/src/markdown.ts +25 -0
- package/src/markdownCharacterReferences.ts +1137 -0
- package/src/markdownCodeFence.ts +216 -0
- package/src/markdownExtensionNode.ts +339 -0
- package/src/markdownExtensions.ts +496 -0
- package/src/markdownHardLineBreak.ts +73 -0
- package/src/markdownHeading.ts +33 -0
- package/src/markdownLink.ts +86 -0
- package/src/markdownListExport.ts +85 -0
- package/src/markdownListIndentation.ts +105 -0
- package/src/markdownOrderedList.ts +60 -0
- package/src/markdownQuote.ts +35 -0
- package/src/markdownSerializers.ts +108 -0
- package/src/markdownSource.ts +1045 -0
- package/src/markdownSpaceRuns.ts +164 -0
- package/src/markdownTable.ts +309 -0
- package/src/markdownTaskList.ts +222 -0
- package/src/markdownThematicBreak.ts +66 -0
- package/src/privateAttributes/layerPortalBoundary.stylex.ts +18 -0
- package/src/textSemantics.ts +104 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file CodeBlockHeaderPlugin.tsx
|
|
7
|
+
* @input Uses @lexical/react (composer context), @lexical/code (CodeNode),
|
|
8
|
+
* lexical, and core IconButton, Icon, useClipboard, and useTranslator.
|
|
9
|
+
* @output Exports CodeBlockHeaderPlugin, which draws core CodeBlock's header
|
|
10
|
+
* over every fenced code block: the language label and a copy button, or
|
|
11
|
+
* just the copy button in the corner when the block names no language.
|
|
12
|
+
* @position Rendered by RichTextEditor and RichTextView inside the positioned
|
|
13
|
+
* element that holds the content editable. The headers are siblings of the
|
|
14
|
+
* editable text, never inside it, so they are not part of the document and
|
|
15
|
+
* cannot be edited (spec:AST-061 FR8, DEC-4). The code block's theme class
|
|
16
|
+
* reserves the header's height, so the code sits where core Markdown's does.
|
|
17
|
+
*/
|
|
18
|
+
import * as stylex from '@stylexjs/stylex';
|
|
19
|
+
import { useEffect, useState } from 'react';
|
|
20
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
21
|
+
import { $isCodeNode } from '@lexical/code';
|
|
22
|
+
import { $getNodeByKey, $getRoot } from 'lexical';
|
|
23
|
+
import { Icon } from '@blue-patterns/core/Icon';
|
|
24
|
+
import { IconButton } from '@blue-patterns/core/IconButton';
|
|
25
|
+
import { useClipboard } from '@blue-patterns/core/hooks';
|
|
26
|
+
import { useTranslator } from '@blue-patterns/core/i18n';
|
|
27
|
+
import { listedLanguage } from "../codeLanguages.js";
|
|
28
|
+
import '@blue-patterns/core/theme/tokens.stylex';
|
|
29
|
+
import { fontWeightVars, spacingVars, typeScaleVars, typographyVars } from '@blue-patterns/core/theme/tokens.stylex';
|
|
30
|
+
|
|
31
|
+
/** Where one code block's header goes, in the container's coordinates. */
|
|
32
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
33
|
+
const styles = {
|
|
34
|
+
header: {
|
|
35
|
+
kVAEAm: "x10l6tqk",
|
|
36
|
+
k1xSpc: "x78zum5",
|
|
37
|
+
kGNEyG: "x6s0dn4",
|
|
38
|
+
kjj79g: "x1qughib",
|
|
39
|
+
k8WAf4: "xce4md1",
|
|
40
|
+
kg3NbH: "x1pzlopt",
|
|
41
|
+
kB7OPa: "x9f619",
|
|
42
|
+
$$css: true
|
|
43
|
+
},
|
|
44
|
+
corner: {
|
|
45
|
+
kVAEAm: "x10l6tqk",
|
|
46
|
+
k1xSpc: "x78zum5",
|
|
47
|
+
kjj79g: "x13a6bvl",
|
|
48
|
+
kmVPX3: "xlsj2fj",
|
|
49
|
+
kB7OPa: "x9f619",
|
|
50
|
+
kfzvcC: "x47corl",
|
|
51
|
+
$$css: true
|
|
52
|
+
},
|
|
53
|
+
cornerButton: {
|
|
54
|
+
kfzvcC: "x67bb7w",
|
|
55
|
+
$$css: true
|
|
56
|
+
},
|
|
57
|
+
copyButton: {
|
|
58
|
+
kMwMTN: "x1mjzq1q",
|
|
59
|
+
$$css: true
|
|
60
|
+
},
|
|
61
|
+
placement: (top, left, width) => [{
|
|
62
|
+
k87sOh: top != null ? "xjbys53" : top,
|
|
63
|
+
kbCHJM: left != null ? "xgq9j65" : left,
|
|
64
|
+
kzqmXN: width != null ? "x5lhr3w" : width,
|
|
65
|
+
$$css: true
|
|
66
|
+
}, {
|
|
67
|
+
"--x-top": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(top),
|
|
68
|
+
"--x-left": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(left),
|
|
69
|
+
"--x-width": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(width)
|
|
70
|
+
}]
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The language a header names, as core CodeBlock decides it: none for a fence
|
|
75
|
+
* without one, and none for exactly `plaintext`, which core Markdown gives a
|
|
76
|
+
* fence with no info string.
|
|
77
|
+
*/
|
|
78
|
+
function displayLanguageOf(language) {
|
|
79
|
+
return language == null || language === '' || language === PLAIN_TEXT ? null : language;
|
|
80
|
+
}
|
|
81
|
+
const PLAIN_TEXT = 'plaintext';
|
|
82
|
+
function samePlacements(a, b) {
|
|
83
|
+
return a.length === b.length && a.every((placement, index) => placement.key === b[index].key && placement.language === b[index].language && placement.top === b[index].top && placement.left === b[index].left && placement.width === b[index].width);
|
|
84
|
+
}
|
|
85
|
+
function CodeBlockHeader({
|
|
86
|
+
placement,
|
|
87
|
+
label
|
|
88
|
+
}) {
|
|
89
|
+
const [editor] = useLexicalComposerContext();
|
|
90
|
+
const t = useTranslator();
|
|
91
|
+
const {
|
|
92
|
+
copy,
|
|
93
|
+
isCopied
|
|
94
|
+
} = useClipboard({
|
|
95
|
+
announce: t('@astryx.codeBlock.copied')
|
|
96
|
+
});
|
|
97
|
+
const copyButton = /*#__PURE__*/_jsx(IconButton, {
|
|
98
|
+
variant: "ghost",
|
|
99
|
+
size: "sm",
|
|
100
|
+
icon: /*#__PURE__*/_jsx(Icon, {
|
|
101
|
+
icon: isCopied ? 'check' : 'copy',
|
|
102
|
+
size: "sm",
|
|
103
|
+
color: "inherit"
|
|
104
|
+
}),
|
|
105
|
+
tooltip: t('@astryx.codeBlock.copyCode'),
|
|
106
|
+
label: isCopied ? t('@astryx.codeBlock.copied') : t('@astryx.codeBlock.copyCode'),
|
|
107
|
+
onClick: () => {
|
|
108
|
+
const code = editor.getEditorState().read(() => $getNodeByKey(placement.key)?.getTextContent() ?? '');
|
|
109
|
+
void copy(code);
|
|
110
|
+
},
|
|
111
|
+
xstyle: [styles.copyButton, placement.language == null && styles.cornerButton]
|
|
112
|
+
});
|
|
113
|
+
const position = styles.placement(placement.top, placement.left, placement.width);
|
|
114
|
+
if (placement.language == null) {
|
|
115
|
+
return /*#__PURE__*/_jsx("div", {
|
|
116
|
+
"data-richtext-code-header": "",
|
|
117
|
+
...stylex.props(styles.corner, position),
|
|
118
|
+
children: copyButton
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
122
|
+
"data-richtext-code-header": "",
|
|
123
|
+
...stylex.props(styles.header, position),
|
|
124
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
125
|
+
...{
|
|
126
|
+
className: "x141an7d x9m5x89 x1e4wzip x1ltkj2j x1mjzq1q xeuugli xb3r6kr xlyipyv xuxw1ft"
|
|
127
|
+
},
|
|
128
|
+
children: label ?? placement.language
|
|
129
|
+
}), copyButton]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Draws a header over each top-level code block and keeps it there as the
|
|
135
|
+
* document, the editor's size, or the window changes.
|
|
136
|
+
*/
|
|
137
|
+
export function CodeBlockHeaderPlugin({
|
|
138
|
+
codeLanguages
|
|
139
|
+
} = {}) {
|
|
140
|
+
const [editor] = useLexicalComposerContext();
|
|
141
|
+
const [placements, setPlacements] = useState([]);
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
let frame = 0;
|
|
144
|
+
const measure = () => {
|
|
145
|
+
frame = 0;
|
|
146
|
+
const root = editor.getRootElement();
|
|
147
|
+
const container = root?.parentElement;
|
|
148
|
+
if (root == null || container == null) {
|
|
149
|
+
setPlacements(current => current.length === 0 ? current : []);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const origin = container.getBoundingClientRect();
|
|
153
|
+
const next = editor.getEditorState().read(() => $getRoot().getChildren().filter($isCodeNode).flatMap(node => {
|
|
154
|
+
const element = editor.getElementByKey(node.getKey());
|
|
155
|
+
if (element == null) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
const box = element.getBoundingClientRect();
|
|
159
|
+
// Inside the block's border, so the header lines up with the
|
|
160
|
+
// padding the theme reserves for it.
|
|
161
|
+
return [{
|
|
162
|
+
key: node.getKey(),
|
|
163
|
+
language: displayLanguageOf(node.getLanguage()),
|
|
164
|
+
top: box.top - origin.top + element.clientTop,
|
|
165
|
+
left: box.left - origin.left + element.clientLeft,
|
|
166
|
+
width: element.clientWidth
|
|
167
|
+
}];
|
|
168
|
+
}));
|
|
169
|
+
setPlacements(current => samePlacements(current, next) ? current : next);
|
|
170
|
+
};
|
|
171
|
+
const schedule = () => {
|
|
172
|
+
if (frame === 0) {
|
|
173
|
+
frame = requestAnimationFrame(measure);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(schedule);
|
|
177
|
+
const unregisterRoot = editor.registerRootListener(root => {
|
|
178
|
+
observer?.disconnect();
|
|
179
|
+
if (root != null) {
|
|
180
|
+
observer?.observe(root);
|
|
181
|
+
}
|
|
182
|
+
schedule();
|
|
183
|
+
});
|
|
184
|
+
const unregisterUpdate = editor.registerUpdateListener(schedule);
|
|
185
|
+
window.addEventListener('resize', schedule);
|
|
186
|
+
schedule();
|
|
187
|
+
return () => {
|
|
188
|
+
cancelAnimationFrame(frame);
|
|
189
|
+
observer?.disconnect();
|
|
190
|
+
unregisterRoot();
|
|
191
|
+
unregisterUpdate();
|
|
192
|
+
window.removeEventListener('resize', schedule);
|
|
193
|
+
};
|
|
194
|
+
}, [editor]);
|
|
195
|
+
return /*#__PURE__*/_jsx(_Fragment, {
|
|
196
|
+
children: placements.map(placement => /*#__PURE__*/_jsx(CodeBlockHeader, {
|
|
197
|
+
placement: placement
|
|
198
|
+
// A fence matched in `codeLanguages` shows the value's name, as
|
|
199
|
+
// Markdown does (spec:AST-061).
|
|
200
|
+
,
|
|
201
|
+
label: placement.language == null ? undefined : listedLanguage(codeLanguages, placement.language)?.name
|
|
202
|
+
}, placement.key))
|
|
203
|
+
});
|
|
204
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type BaseSelection, type RangeSelection } from 'lexical';
|
|
2
|
+
/** Whether content inserted at the selection lands in a code block. */
|
|
3
|
+
export declare function $startsInCodeBlock(selection: BaseSelection | null): selection is RangeSelection;
|
|
4
|
+
export declare function CodePastePlugin(): null;
|
|
5
|
+
//# sourceMappingURL=CodePastePlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodePastePlugin.d.ts","sourceRoot":"","sources":["../../src/internal/CodePastePlugin.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAaL,KAAK,aAAa,EAElB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAKjB,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,aAAa,GAAG,IAAI,GAC9B,SAAS,IAAI,cAAc,CAQ7B;AAqCD,wBAAgB,eAAe,IAAI,IAAI,CAuDtC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file CodePastePlugin.tsx
|
|
7
|
+
* @input Uses @lexical/react (composer context), @lexical/code (CodeNode), and
|
|
8
|
+
* lexical.
|
|
9
|
+
* @output Exports CodePastePlugin, which makes rich content pasted or dropped
|
|
10
|
+
* into a code block go in as plain lines of code, and $startsInCodeBlock,
|
|
11
|
+
* which TablePastePlugin uses to leave such a paste to it.
|
|
12
|
+
* @position Rendered by RichTextEditor; always on; not exported. A paste
|
|
13
|
+
* whose selection starts in a code block goes in as plain text: the
|
|
14
|
+
* clipboard's plain text, or else one line per pasted block.
|
|
15
|
+
*/
|
|
16
|
+
import { useEffect } from 'react';
|
|
17
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
18
|
+
import { $isCodeNode } from '@lexical/code';
|
|
19
|
+
import { $addUpdateTag, $findMatchingParent, $getSelection, $isElementNode, $isRangeSelection, $isSelectionCapturedInDecoratorInput, COMMAND_PRIORITY_LOW, isDOMNode, mergeRegister, PASTE_COMMAND, PASTE_TAG, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND } from 'lexical';
|
|
20
|
+
|
|
21
|
+
/** Clipboard types that carry more than plain text. */
|
|
22
|
+
const RICH_TYPES = ['text/html', 'application/x-lexical-editor'];
|
|
23
|
+
|
|
24
|
+
/** Whether content inserted at the selection lands in a code block. */
|
|
25
|
+
export function $startsInCodeBlock(selection) {
|
|
26
|
+
if (!$isRangeSelection(selection)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const start = selection.isBackward() ? selection.focus : selection.anchor;
|
|
30
|
+
const node = start.getNode();
|
|
31
|
+
// A link (an autolinked URL) can sit between the text and its code block.
|
|
32
|
+
return $findMatchingParent(node, $isCodeNode) !== null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Clipboard nodes as plain lines: each block's text on a line of its own (a
|
|
37
|
+
* paragraph, a heading, a list item, a table cell), and inline content
|
|
38
|
+
* between blocks on one line.
|
|
39
|
+
*/
|
|
40
|
+
function $plainLinesOf(nodes) {
|
|
41
|
+
const lines = [];
|
|
42
|
+
let inline = null;
|
|
43
|
+
const endInline = () => {
|
|
44
|
+
if (inline != null) {
|
|
45
|
+
lines.push(inline);
|
|
46
|
+
inline = null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const visit = node => {
|
|
50
|
+
if (node.isInline()) {
|
|
51
|
+
inline = (inline ?? '') + node.getTextContent();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
endInline();
|
|
55
|
+
if ($isElementNode(node) && node.getChildren().some(child => !child.isInline())) {
|
|
56
|
+
node.getChildren().forEach(visit);
|
|
57
|
+
endInline();
|
|
58
|
+
} else {
|
|
59
|
+
lines.push(node.getTextContent());
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
nodes.forEach(visit);
|
|
63
|
+
endInline();
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
export function CodePastePlugin() {
|
|
67
|
+
const [editor] = useLexicalComposerContext();
|
|
68
|
+
useEffect(() => mergeRegister(
|
|
69
|
+
// Ahead of the rich-text paste, which inserts the clipboard's rich
|
|
70
|
+
// content when there is any.
|
|
71
|
+
editor.registerCommand(PASTE_COMMAND, event => {
|
|
72
|
+
// A paste into an input a decorator node renders is that input's.
|
|
73
|
+
if (isDOMNode(event.target) && $isSelectionCapturedInDecoratorInput(event.target)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const data = 'clipboardData' in event ? event.clipboardData : null;
|
|
77
|
+
if (!editor.isEditable() || data == null || !RICH_TYPES.some(type => data.types.includes(type))) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const text = data.getData('text/plain');
|
|
81
|
+
const selection = $getSelection();
|
|
82
|
+
if (text === '' || !$startsInCodeBlock(selection)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
// Its own undo step, as every paste is.
|
|
87
|
+
$addUpdateTag(PASTE_TAG);
|
|
88
|
+
selection.insertRawText(text);
|
|
89
|
+
return true;
|
|
90
|
+
}, COMMAND_PRIORITY_LOW),
|
|
91
|
+
// Every insertion of clipboard nodes, a paste with no plain text or a
|
|
92
|
+
// drop, comes through here; ahead of the table's own handler.
|
|
93
|
+
editor.registerCommand(SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, ({
|
|
94
|
+
nodes,
|
|
95
|
+
selection
|
|
96
|
+
}) => {
|
|
97
|
+
if (!$startsInCodeBlock(selection)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
selection.insertRawText($plainLinesOf(nodes));
|
|
101
|
+
return true;
|
|
102
|
+
}, COMMAND_PRIORITY_LOW)), [editor]);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CodeLanguage } from '@blue-patterns/core/CodeBlock';
|
|
2
|
+
/** The class the theme adds to every code block (editorTheme.ts). */
|
|
3
|
+
export declare const CODE_SYNTAX_CLASS = "astryx-richtext-code";
|
|
4
|
+
export declare function CodeSyntaxPlugin({ codeLanguages, }?: {
|
|
5
|
+
/** Language values a fence may name (component:Markdown FR30). */
|
|
6
|
+
codeLanguages?: ReadonlyArray<CodeLanguage>;
|
|
7
|
+
}): null;
|
|
8
|
+
//# sourceMappingURL=CodeSyntaxPlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeSyntaxPlugin.d.ts","sourceRoot":"","sources":["../../src/internal/CodeSyntaxPlugin.tsx"],"names":[],"mappings":"AAqCA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,+BAA+B,CAAC;AAIvC,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,yBAAyB,CAAC;AAgMxD,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,GACd,GAAE;IACD,kEAAkE;IAClE,aAAa,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACxC,GAAG,IAAI,CAyPZ"}
|