@lexical/clipboard 0.8.1 → 0.9.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/LexicalClipboard.dev.js +102 -38
- package/LexicalClipboard.js.flow +1 -1
- package/LexicalClipboard.prod.js +2 -2
- package/clipboard.d.ts +1 -1
- package/index.d.ts +1 -1
- package/package.json +6 -6
package/LexicalClipboard.dev.js
CHANGED
|
@@ -20,75 +20,84 @@ var lexical = require('lexical');
|
|
|
20
20
|
*/
|
|
21
21
|
function $getHtmlContent(editor) {
|
|
22
22
|
const selection = lexical.$getSelection();
|
|
23
|
+
|
|
23
24
|
if (selection == null) {
|
|
24
25
|
{
|
|
25
26
|
throw Error(`Expected valid LexicalSelection`);
|
|
26
27
|
}
|
|
27
|
-
}
|
|
28
|
+
} // If we haven't selected anything
|
|
29
|
+
|
|
28
30
|
|
|
29
|
-
// If we haven't selected anything
|
|
30
31
|
if (lexical.$isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
|
|
31
32
|
return '';
|
|
32
33
|
}
|
|
33
|
-
return html.$generateHtmlFromNodes(editor, selection);
|
|
34
|
-
}
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
return html.$generateHtmlFromNodes(editor, selection);
|
|
36
|
+
} // TODO 0.6.0 Return a blank string instead
|
|
37
37
|
// TODO 0.6.0 Rename to $getJSON
|
|
38
|
+
|
|
38
39
|
function $getLexicalContent(editor) {
|
|
39
40
|
const selection = lexical.$getSelection();
|
|
41
|
+
|
|
40
42
|
if (selection == null) {
|
|
41
43
|
{
|
|
42
44
|
throw Error(`Expected valid LexicalSelection`);
|
|
43
45
|
}
|
|
44
|
-
}
|
|
46
|
+
} // If we haven't selected anything
|
|
47
|
+
|
|
45
48
|
|
|
46
|
-
// If we haven't selected anything
|
|
47
49
|
if (lexical.$isRangeSelection(selection) && selection.isCollapsed() || selection.getNodes().length === 0) {
|
|
48
50
|
return null;
|
|
49
51
|
}
|
|
52
|
+
|
|
50
53
|
return JSON.stringify($generateJSONFromSelectedNodes(editor, selection));
|
|
51
54
|
}
|
|
52
55
|
function $insertDataTransferForPlainText(dataTransfer, selection) {
|
|
53
56
|
const text = dataTransfer.getData('text/plain');
|
|
57
|
+
|
|
54
58
|
if (text != null) {
|
|
55
59
|
selection.insertRawText(text);
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
62
|
function $insertDataTransferForRichText(dataTransfer, selection, editor) {
|
|
59
63
|
const lexicalString = dataTransfer.getData('application/x-lexical-editor');
|
|
64
|
+
|
|
60
65
|
if (lexicalString) {
|
|
61
66
|
try {
|
|
62
67
|
const payload = JSON.parse(lexicalString);
|
|
68
|
+
|
|
63
69
|
if (payload.namespace === editor._config.namespace && Array.isArray(payload.nodes)) {
|
|
64
70
|
const nodes = $generateNodesFromSerializedNodes(payload.nodes);
|
|
65
71
|
return $insertGeneratedNodes(editor, nodes, selection);
|
|
66
72
|
}
|
|
67
|
-
} catch {
|
|
68
|
-
// Fail silently.
|
|
73
|
+
} catch {// Fail silently.
|
|
69
74
|
}
|
|
70
75
|
}
|
|
76
|
+
|
|
71
77
|
const htmlString = dataTransfer.getData('text/html');
|
|
78
|
+
|
|
72
79
|
if (htmlString) {
|
|
73
80
|
try {
|
|
74
81
|
const parser = new DOMParser();
|
|
75
82
|
const dom = parser.parseFromString(htmlString, 'text/html');
|
|
76
83
|
const nodes = html.$generateNodesFromDOM(editor, dom);
|
|
77
84
|
return $insertGeneratedNodes(editor, nodes, selection);
|
|
78
|
-
} catch {
|
|
79
|
-
// Fail silently.
|
|
85
|
+
} catch {// Fail silently.
|
|
80
86
|
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Multi-line plain text in rich text mode pasted as separate paragraphs
|
|
87
|
+
} // Multi-line plain text in rich text mode pasted as separate paragraphs
|
|
84
88
|
// instead of single paragraph with linebreaks.
|
|
89
|
+
|
|
90
|
+
|
|
85
91
|
const text = dataTransfer.getData('text/plain');
|
|
92
|
+
|
|
86
93
|
if (text != null) {
|
|
87
94
|
if (lexical.$isRangeSelection(selection)) {
|
|
88
95
|
const lines = text.split(/\r?\n/);
|
|
89
96
|
const linesLength = lines.length;
|
|
97
|
+
|
|
90
98
|
for (let i = 0; i < linesLength; i++) {
|
|
91
99
|
selection.insertText(lines[i]);
|
|
100
|
+
|
|
92
101
|
if (i < linesLength - 1) {
|
|
93
102
|
selection.insertParagraph();
|
|
94
103
|
}
|
|
@@ -100,30 +109,36 @@ function $insertDataTransferForRichText(dataTransfer, selection, editor) {
|
|
|
100
109
|
}
|
|
101
110
|
function $insertGeneratedNodes(editor, nodes, selection) {
|
|
102
111
|
const isSelectionInsideOfGrid = lexical.DEPRECATED_$isGridSelection(selection) || utils.$findMatchingParent(selection.anchor.getNode(), n => lexical.DEPRECATED_$isGridCellNode(n)) !== null && utils.$findMatchingParent(selection.focus.getNode(), n => lexical.DEPRECATED_$isGridCellNode(n)) !== null;
|
|
112
|
+
|
|
103
113
|
if (isSelectionInsideOfGrid && nodes.length === 1 && lexical.DEPRECATED_$isGridNode(nodes[0])) {
|
|
104
114
|
$mergeGridNodesStrategy(nodes, selection, false, editor);
|
|
105
115
|
return;
|
|
106
116
|
}
|
|
117
|
+
|
|
107
118
|
$basicInsertStrategy(nodes, selection);
|
|
108
119
|
return;
|
|
109
120
|
}
|
|
121
|
+
|
|
110
122
|
function $basicInsertStrategy(nodes, selection) {
|
|
111
123
|
// Wrap text and inline nodes in paragraph nodes so we have all blocks at the top-level
|
|
112
124
|
const topLevelBlocks = [];
|
|
113
125
|
let currentBlock = null;
|
|
126
|
+
|
|
114
127
|
for (let i = 0; i < nodes.length; i++) {
|
|
115
128
|
const node = nodes[i];
|
|
116
129
|
const isLineBreakNode = lexical.$isLineBreakNode(node);
|
|
130
|
+
|
|
117
131
|
if (isLineBreakNode || lexical.$isDecoratorNode(node) && node.isInline() || lexical.$isElementNode(node) && node.isInline() || lexical.$isTextNode(node) || node.isParentRequired()) {
|
|
118
132
|
if (currentBlock === null) {
|
|
119
133
|
currentBlock = node.createParentElementNode();
|
|
120
|
-
topLevelBlocks.push(currentBlock);
|
|
121
|
-
// In the case of LineBreakNode, we just need to
|
|
134
|
+
topLevelBlocks.push(currentBlock); // In the case of LineBreakNode, we just need to
|
|
122
135
|
// add an empty ParagraphNode to the topLevelBlocks.
|
|
136
|
+
|
|
123
137
|
if (isLineBreakNode) {
|
|
124
138
|
continue;
|
|
125
139
|
}
|
|
126
140
|
}
|
|
141
|
+
|
|
127
142
|
if (currentBlock !== null) {
|
|
128
143
|
currentBlock.append(node);
|
|
129
144
|
}
|
|
@@ -132,25 +147,30 @@ function $basicInsertStrategy(nodes, selection) {
|
|
|
132
147
|
currentBlock = null;
|
|
133
148
|
}
|
|
134
149
|
}
|
|
150
|
+
|
|
135
151
|
if (lexical.$isRangeSelection(selection)) {
|
|
136
152
|
selection.insertNodes(topLevelBlocks);
|
|
137
153
|
} else if (lexical.DEPRECATED_$isGridSelection(selection)) {
|
|
138
154
|
// If there's an active grid selection and a non grid is pasted, add to the anchor.
|
|
139
155
|
const anchorCell = selection.anchor.getNode();
|
|
156
|
+
|
|
140
157
|
if (!lexical.DEPRECATED_$isGridCellNode(anchorCell)) {
|
|
141
158
|
{
|
|
142
159
|
throw Error(`Expected Grid Cell in Grid Selection`);
|
|
143
160
|
}
|
|
144
161
|
}
|
|
162
|
+
|
|
145
163
|
anchorCell.append(...topLevelBlocks);
|
|
146
164
|
}
|
|
147
165
|
}
|
|
166
|
+
|
|
148
167
|
function $mergeGridNodesStrategy(nodes, selection, isFromLexical, editor) {
|
|
149
168
|
if (nodes.length !== 1 || !lexical.DEPRECATED_$isGridNode(nodes[0])) {
|
|
150
169
|
{
|
|
151
170
|
throw Error(`$mergeGridNodesStrategy: Expected Grid insertion.`);
|
|
152
171
|
}
|
|
153
172
|
}
|
|
173
|
+
|
|
154
174
|
const newGrid = nodes[0];
|
|
155
175
|
const newGridRows = newGrid.getChildren();
|
|
156
176
|
const newColumnCount = newGrid.getFirstChildOrThrow().getChildrenSize();
|
|
@@ -158,11 +178,13 @@ function $mergeGridNodesStrategy(nodes, selection, isFromLexical, editor) {
|
|
|
158
178
|
const gridCellNode = utils.$findMatchingParent(selection.anchor.getNode(), n => lexical.DEPRECATED_$isGridCellNode(n));
|
|
159
179
|
const gridRowNode = gridCellNode && utils.$findMatchingParent(gridCellNode, n => lexical.DEPRECATED_$isGridRowNode(n));
|
|
160
180
|
const gridNode = gridRowNode && utils.$findMatchingParent(gridRowNode, n => lexical.DEPRECATED_$isGridNode(n));
|
|
181
|
+
|
|
161
182
|
if (!lexical.DEPRECATED_$isGridCellNode(gridCellNode) || !lexical.DEPRECATED_$isGridRowNode(gridRowNode) || !lexical.DEPRECATED_$isGridNode(gridNode)) {
|
|
162
183
|
{
|
|
163
184
|
throw Error(`$mergeGridNodesStrategy: Expected selection to be inside of a Grid.`);
|
|
164
185
|
}
|
|
165
186
|
}
|
|
187
|
+
|
|
166
188
|
const startY = gridRowNode.getIndexWithinParent();
|
|
167
189
|
const stopY = Math.min(gridNode.getChildrenSize() - 1, startY + newRowCount - 1);
|
|
168
190
|
const startX = gridCellNode.getIndexWithinParent();
|
|
@@ -175,40 +197,51 @@ function $mergeGridNodesStrategy(nodes, selection, isFromLexical, editor) {
|
|
|
175
197
|
let newRowIdx = 0;
|
|
176
198
|
let newAnchorCellKey;
|
|
177
199
|
let newFocusCellKey;
|
|
200
|
+
|
|
178
201
|
for (let r = fromY; r <= toY; r++) {
|
|
179
202
|
const currentGridRowNode = gridRowNodes[r];
|
|
203
|
+
|
|
180
204
|
if (!lexical.DEPRECATED_$isGridRowNode(currentGridRowNode)) {
|
|
181
205
|
{
|
|
182
206
|
throw Error(`getNodes: expected to find GridRowNode`);
|
|
183
207
|
}
|
|
184
208
|
}
|
|
209
|
+
|
|
185
210
|
const newGridRowNode = newGridRows[newRowIdx];
|
|
211
|
+
|
|
186
212
|
if (!lexical.DEPRECATED_$isGridRowNode(newGridRowNode)) {
|
|
187
213
|
{
|
|
188
214
|
throw Error(`getNodes: expected to find GridRowNode`);
|
|
189
215
|
}
|
|
190
216
|
}
|
|
217
|
+
|
|
191
218
|
const gridCellNodes = currentGridRowNode.getChildren();
|
|
192
219
|
const newGridCellNodes = newGridRowNode.getChildren();
|
|
193
220
|
let newColumnIdx = 0;
|
|
221
|
+
|
|
194
222
|
for (let c = fromX; c <= toX; c++) {
|
|
195
223
|
const currentGridCellNode = gridCellNodes[c];
|
|
224
|
+
|
|
196
225
|
if (!lexical.DEPRECATED_$isGridCellNode(currentGridCellNode)) {
|
|
197
226
|
{
|
|
198
227
|
throw Error(`getNodes: expected to find GridCellNode`);
|
|
199
228
|
}
|
|
200
229
|
}
|
|
230
|
+
|
|
201
231
|
const newGridCellNode = newGridCellNodes[newColumnIdx];
|
|
232
|
+
|
|
202
233
|
if (!lexical.DEPRECATED_$isGridCellNode(newGridCellNode)) {
|
|
203
234
|
{
|
|
204
235
|
throw Error(`getNodes: expected to find GridCellNode`);
|
|
205
236
|
}
|
|
206
237
|
}
|
|
238
|
+
|
|
207
239
|
if (r === fromY && c === fromX) {
|
|
208
240
|
newAnchorCellKey = currentGridCellNode.getKey();
|
|
209
241
|
} else if (r === toY && c === toX) {
|
|
210
242
|
newFocusCellKey = currentGridCellNode.getKey();
|
|
211
243
|
}
|
|
244
|
+
|
|
212
245
|
const originalChildren = currentGridCellNode.getChildren();
|
|
213
246
|
newGridCellNode.getChildren().forEach(child => {
|
|
214
247
|
if (lexical.$isTextNode(child)) {
|
|
@@ -222,8 +255,10 @@ function $mergeGridNodesStrategy(nodes, selection, isFromLexical, editor) {
|
|
|
222
255
|
originalChildren.forEach(n => n.remove());
|
|
223
256
|
newColumnIdx++;
|
|
224
257
|
}
|
|
258
|
+
|
|
225
259
|
newRowIdx++;
|
|
226
260
|
}
|
|
261
|
+
|
|
227
262
|
if (newAnchorCellKey && newFocusCellKey) {
|
|
228
263
|
const newGridSelection = lexical.DEPRECATED_$createGridSelection();
|
|
229
264
|
newGridSelection.set(gridNode.getKey(), newAnchorCellKey, newFocusCellKey);
|
|
@@ -231,19 +266,20 @@ function $mergeGridNodesStrategy(nodes, selection, isFromLexical, editor) {
|
|
|
231
266
|
editor.dispatchCommand(lexical.SELECTION_CHANGE_COMMAND, undefined);
|
|
232
267
|
}
|
|
233
268
|
}
|
|
269
|
+
|
|
234
270
|
function exportNodeToJSON(node) {
|
|
235
271
|
const serializedNode = node.exportJSON();
|
|
236
|
-
const nodeClass = node.constructor;
|
|
272
|
+
const nodeClass = node.constructor; // @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
237
273
|
|
|
238
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
239
274
|
if (serializedNode.type !== nodeClass.getType()) {
|
|
240
275
|
{
|
|
241
276
|
throw Error(`LexicalNode: Node ${nodeClass.name} does not implement .exportJSON().`);
|
|
242
277
|
}
|
|
243
|
-
}
|
|
278
|
+
} // @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
279
|
+
|
|
244
280
|
|
|
245
|
-
// @ts-expect-error TODO Replace Class utility type with InstanceType
|
|
246
281
|
const serializedChildren = serializedNode.children;
|
|
282
|
+
|
|
247
283
|
if (lexical.$isElementNode(node)) {
|
|
248
284
|
if (!Array.isArray(serializedChildren)) {
|
|
249
285
|
{
|
|
@@ -251,44 +287,50 @@ function exportNodeToJSON(node) {
|
|
|
251
287
|
}
|
|
252
288
|
}
|
|
253
289
|
}
|
|
290
|
+
|
|
254
291
|
return serializedNode;
|
|
255
292
|
}
|
|
293
|
+
|
|
256
294
|
function $appendNodesToJSON(editor, selection$1, currentNode, targetArray = []) {
|
|
257
295
|
let shouldInclude = selection$1 != null ? currentNode.isSelected(selection$1) : true;
|
|
258
296
|
const shouldExclude = lexical.$isElementNode(currentNode) && currentNode.excludeFromCopy('html');
|
|
259
297
|
let target = currentNode;
|
|
298
|
+
|
|
260
299
|
if (selection$1 !== null) {
|
|
261
300
|
let clone = selection.$cloneWithProperties(currentNode);
|
|
262
301
|
clone = lexical.$isTextNode(clone) && selection$1 != null ? selection.$sliceSelectedTextNodeContent(selection$1, clone) : clone;
|
|
263
302
|
target = clone;
|
|
264
303
|
}
|
|
265
|
-
const children = lexical.$isElementNode(target) ? target.getChildren() : [];
|
|
266
|
-
const serializedNode = exportNodeToJSON(target);
|
|
267
304
|
|
|
268
|
-
|
|
305
|
+
const children = lexical.$isElementNode(target) ? target.getChildren() : [];
|
|
306
|
+
const serializedNode = exportNodeToJSON(target); // TODO: TextNode calls getTextContent() (NOT node.__text) within it's exportJSON method
|
|
269
307
|
// which uses getLatest() to get the text from the original node with the same key.
|
|
270
308
|
// This is a deeper issue with the word "clone" here, it's still a reference to the
|
|
271
309
|
// same node as far as the LexicalEditor is concerned since it shares a key.
|
|
272
310
|
// We need a way to create a clone of a Node in memory with it's own key, but
|
|
273
311
|
// until then this hack will work for the selected text extract use case.
|
|
312
|
+
|
|
274
313
|
if (lexical.$isTextNode(target)) {
|
|
275
|
-
const text = target.__text;
|
|
276
|
-
// If an uncollapsed selection ends or starts at the end of a line of specialized,
|
|
314
|
+
const text = target.__text; // If an uncollapsed selection ends or starts at the end of a line of specialized,
|
|
277
315
|
// TextNodes, such as code tokens, we will get a 'blank' TextNode here, i.e., one
|
|
278
316
|
// with text of length 0. We don't want this, it makes a confusing mess. Reset!
|
|
317
|
+
|
|
279
318
|
if (text.length > 0) {
|
|
280
319
|
serializedNode.text = text;
|
|
281
320
|
} else {
|
|
282
321
|
shouldInclude = false;
|
|
283
322
|
}
|
|
284
323
|
}
|
|
324
|
+
|
|
285
325
|
for (let i = 0; i < children.length; i++) {
|
|
286
326
|
const childNode = children[i];
|
|
287
327
|
const shouldIncludeChild = $appendNodesToJSON(editor, selection$1, childNode, serializedNode.children);
|
|
328
|
+
|
|
288
329
|
if (!shouldInclude && lexical.$isElementNode(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection$1, 'clone')) {
|
|
289
330
|
shouldInclude = true;
|
|
290
331
|
}
|
|
291
332
|
}
|
|
333
|
+
|
|
292
334
|
if (shouldInclude && !shouldExclude) {
|
|
293
335
|
targetArray.push(serializedNode);
|
|
294
336
|
} else if (Array.isArray(serializedNode.children)) {
|
|
@@ -297,18 +339,21 @@ function $appendNodesToJSON(editor, selection$1, currentNode, targetArray = [])
|
|
|
297
339
|
targetArray.push(serializedChildNode);
|
|
298
340
|
}
|
|
299
341
|
}
|
|
342
|
+
|
|
300
343
|
return shouldInclude;
|
|
301
|
-
}
|
|
344
|
+
} // TODO why $ function with Editor instance?
|
|
345
|
+
|
|
302
346
|
|
|
303
|
-
// TODO why $ function with Editor instance?
|
|
304
347
|
function $generateJSONFromSelectedNodes(editor, selection) {
|
|
305
348
|
const nodes = [];
|
|
306
349
|
const root = lexical.$getRoot();
|
|
307
350
|
const topLevelChildren = root.getChildren();
|
|
351
|
+
|
|
308
352
|
for (let i = 0; i < topLevelChildren.length; i++) {
|
|
309
353
|
const topLevelNode = topLevelChildren[i];
|
|
310
354
|
$appendNodesToJSON(editor, selection, topLevelNode, nodes);
|
|
311
355
|
}
|
|
356
|
+
|
|
312
357
|
return {
|
|
313
358
|
namespace: editor._config.namespace,
|
|
314
359
|
nodes
|
|
@@ -316,27 +361,31 @@ function $generateJSONFromSelectedNodes(editor, selection) {
|
|
|
316
361
|
}
|
|
317
362
|
function $generateNodesFromSerializedNodes(serializedNodes) {
|
|
318
363
|
const nodes = [];
|
|
364
|
+
|
|
319
365
|
for (let i = 0; i < serializedNodes.length; i++) {
|
|
320
366
|
const serializedNode = serializedNodes[i];
|
|
321
367
|
const node = lexical.$parseSerializedNode(serializedNode);
|
|
368
|
+
|
|
322
369
|
if (lexical.$isTextNode(node)) {
|
|
323
370
|
selection.$addNodeStyle(node);
|
|
324
371
|
}
|
|
372
|
+
|
|
325
373
|
nodes.push(node);
|
|
326
374
|
}
|
|
375
|
+
|
|
327
376
|
return nodes;
|
|
328
377
|
}
|
|
329
378
|
const EVENT_LATENCY = 50;
|
|
330
|
-
let clipboardEventTimeout = null;
|
|
331
|
-
|
|
332
|
-
// TODO custom selection
|
|
379
|
+
let clipboardEventTimeout = null; // TODO custom selection
|
|
333
380
|
// TODO potentially have a node customizable version for plain text
|
|
334
|
-
|
|
381
|
+
|
|
382
|
+
async function copyToClipboard(editor, event) {
|
|
335
383
|
if (clipboardEventTimeout !== null) {
|
|
336
384
|
// Prevent weird race conditions that can happen when this function is run multiple times
|
|
337
385
|
// synchronously. In the future, we can do better, we can cancel/override the previously running job.
|
|
338
386
|
return false;
|
|
339
387
|
}
|
|
388
|
+
|
|
340
389
|
if (event !== null) {
|
|
341
390
|
return new Promise((resolve, reject) => {
|
|
342
391
|
editor.update(() => {
|
|
@@ -344,11 +393,14 @@ async function copyToClipboard__EXPERIMENTAL(editor, event) {
|
|
|
344
393
|
});
|
|
345
394
|
});
|
|
346
395
|
}
|
|
396
|
+
|
|
347
397
|
const rootElement = editor.getRootElement();
|
|
348
398
|
const domSelection = document.getSelection();
|
|
399
|
+
|
|
349
400
|
if (rootElement === null || domSelection === null) {
|
|
350
401
|
return false;
|
|
351
402
|
}
|
|
403
|
+
|
|
352
404
|
const element = document.createElement('span');
|
|
353
405
|
element.style.cssText = 'position: fixed; top: -1000px;';
|
|
354
406
|
element.append(document.createTextNode('#'));
|
|
@@ -362,17 +414,20 @@ async function copyToClipboard__EXPERIMENTAL(editor, event) {
|
|
|
362
414
|
const removeListener = editor.registerCommand(lexical.COPY_COMMAND, secondEvent => {
|
|
363
415
|
if (secondEvent instanceof ClipboardEvent) {
|
|
364
416
|
removeListener();
|
|
417
|
+
|
|
365
418
|
if (clipboardEventTimeout !== null) {
|
|
366
419
|
window.clearTimeout(clipboardEventTimeout);
|
|
367
420
|
clipboardEventTimeout = null;
|
|
368
421
|
}
|
|
422
|
+
|
|
369
423
|
resolve($copyToClipboardEvent(editor, secondEvent));
|
|
370
|
-
}
|
|
371
|
-
|
|
424
|
+
} // Block the entire copy flow while we wait for the next ClipboardEvent
|
|
425
|
+
|
|
426
|
+
|
|
372
427
|
return true;
|
|
373
|
-
}, lexical.COMMAND_PRIORITY_CRITICAL);
|
|
374
|
-
// If the above hack execCommand hack works, this timeout code should never fire. Otherwise,
|
|
428
|
+
}, lexical.COMMAND_PRIORITY_CRITICAL); // If the above hack execCommand hack works, this timeout code should never fire. Otherwise,
|
|
375
429
|
// the listener will be quickly freed so that the user can reuse it again
|
|
430
|
+
|
|
376
431
|
clipboardEventTimeout = window.setTimeout(() => {
|
|
377
432
|
removeListener();
|
|
378
433
|
clipboardEventTimeout = null;
|
|
@@ -381,37 +436,46 @@ async function copyToClipboard__EXPERIMENTAL(editor, event) {
|
|
|
381
436
|
document.execCommand('copy');
|
|
382
437
|
element.remove();
|
|
383
438
|
});
|
|
384
|
-
}
|
|
439
|
+
} // TODO shouldn't pass editor (pass namespace directly)
|
|
385
440
|
|
|
386
|
-
// TODO shouldn't pass editor (pass namespace directly)
|
|
387
441
|
function $copyToClipboardEvent(editor, event) {
|
|
388
442
|
const domSelection = window.getSelection();
|
|
443
|
+
|
|
389
444
|
if (!domSelection) {
|
|
390
445
|
return false;
|
|
391
446
|
}
|
|
447
|
+
|
|
392
448
|
const anchorDOM = domSelection.anchorNode;
|
|
393
449
|
const focusDOM = domSelection.focusNode;
|
|
450
|
+
|
|
394
451
|
if (anchorDOM !== null && focusDOM !== null && !lexical.isSelectionWithinEditor(editor, anchorDOM, focusDOM)) {
|
|
395
452
|
return false;
|
|
396
453
|
}
|
|
454
|
+
|
|
397
455
|
event.preventDefault();
|
|
398
456
|
const clipboardData = event.clipboardData;
|
|
399
457
|
const selection = lexical.$getSelection();
|
|
458
|
+
|
|
400
459
|
if (clipboardData === null || selection === null) {
|
|
401
460
|
return false;
|
|
402
461
|
}
|
|
462
|
+
|
|
403
463
|
const htmlString = $getHtmlContent(editor);
|
|
404
464
|
const lexicalString = $getLexicalContent(editor);
|
|
405
465
|
let plainString = '';
|
|
466
|
+
|
|
406
467
|
if (selection !== null) {
|
|
407
468
|
plainString = selection.getTextContent();
|
|
408
469
|
}
|
|
470
|
+
|
|
409
471
|
if (htmlString !== null) {
|
|
410
472
|
clipboardData.setData('text/html', htmlString);
|
|
411
473
|
}
|
|
474
|
+
|
|
412
475
|
if (lexicalString !== null) {
|
|
413
476
|
clipboardData.setData('application/x-lexical-editor', lexicalString);
|
|
414
477
|
}
|
|
478
|
+
|
|
415
479
|
clipboardData.setData('text/plain', plainString);
|
|
416
480
|
return true;
|
|
417
481
|
}
|
|
@@ -423,4 +487,4 @@ exports.$getLexicalContent = $getLexicalContent;
|
|
|
423
487
|
exports.$insertDataTransferForPlainText = $insertDataTransferForPlainText;
|
|
424
488
|
exports.$insertDataTransferForRichText = $insertDataTransferForRichText;
|
|
425
489
|
exports.$insertGeneratedNodes = $insertGeneratedNodes;
|
|
426
|
-
exports.
|
|
490
|
+
exports.copyToClipboard = copyToClipboard;
|
package/LexicalClipboard.js.flow
CHANGED
|
@@ -68,7 +68,7 @@ declare export function $generateNodesFromSerializedNodes(
|
|
|
68
68
|
serializedNodes: Array<BaseSerializedNode>,
|
|
69
69
|
): Array<LexicalNode>;
|
|
70
70
|
|
|
71
|
-
declare export function
|
|
71
|
+
declare export function copyToClipboard(
|
|
72
72
|
editor: LexicalEditor,
|
|
73
73
|
event: null | ClipboardEvent,
|
|
74
74
|
): Promise<boolean>;
|
package/LexicalClipboard.prod.js
CHANGED
|
@@ -17,5 +17,5 @@ function L(a,b){var c=window.getSelection();if(!c)return!1;var f=c.anchorNode;c=
|
|
|
17
17
|
exports.$generateNodesFromSerializedNodes=J;exports.$getHtmlContent=A;exports.$getLexicalContent=B;exports.$insertDataTransferForPlainText=function(a,b){a=a.getData("text/plain");null!=a&&b.insertRawText(a)};
|
|
18
18
|
exports.$insertDataTransferForRichText=function(a,b,c){var f=a.getData("application/x-lexical-editor");if(f)try{let g=JSON.parse(f);if(g.namespace===c._config.namespace&&Array.isArray(g.nodes)){let h=J(g.nodes);return D(c,h,b)}}catch{}if(f=a.getData("text/html"))try{var e=(new DOMParser).parseFromString(f,"text/html");let g=d.$generateNodesFromDOM(c,e);return D(c,g,b)}catch{}a=a.getData("text/plain");if(null!=a)if(t.$isRangeSelection(b))for(a=a.split(/\r?\n/),c=a.length,e=0;e<c;e++)b.insertText(a[e]),
|
|
19
19
|
e<c-1&&b.insertParagraph();else b.insertRawText(a)};exports.$insertGeneratedNodes=D;
|
|
20
|
-
exports.
|
|
21
|
-
|
|
20
|
+
exports.copyToClipboard=async function(a,b){if(null!==K)return!1;if(null!==b)return new Promise(g=>{a.update(()=>{g(L(a,b))})});var c=a.getRootElement();let f=document.getSelection();if(null===c||null===f)return!1;let e=document.createElement("span");e.style.cssText="position: fixed; top: -1000px;";e.append(document.createTextNode("#"));c.append(e);c=new Range;c.setStart(e,0);c.setEnd(e,1);f.removeAllRanges();f.addRange(c);return new Promise(g=>{let h=a.registerCommand(t.COPY_COMMAND,k=>{k instanceof
|
|
21
|
+
ClipboardEvent&&(h(),null!==K&&(window.clearTimeout(K),K=null),g(L(a,k)));return!0},t.COMMAND_PRIORITY_CRITICAL);K=window.setTimeout(()=>{h();K=null;g(!1)},50);document.execCommand("copy");e.remove()})}
|
package/clipboard.d.ts
CHANGED
|
@@ -21,4 +21,4 @@ export declare function $generateJSONFromSelectedNodes<SerializedNode extends Ba
|
|
|
21
21
|
nodes: Array<SerializedNode>;
|
|
22
22
|
};
|
|
23
23
|
export declare function $generateNodesFromSerializedNodes(serializedNodes: Array<BaseSerializedNode>): Array<LexicalNode>;
|
|
24
|
-
export declare function
|
|
24
|
+
export declare function copyToClipboard(editor: LexicalEditor, event: null | ClipboardEvent): Promise<boolean>;
|
package/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
|
-
export { $generateJSONFromSelectedNodes, $generateNodesFromSerializedNodes, $getHtmlContent, $getLexicalContent, $insertDataTransferForPlainText, $insertDataTransferForRichText, $insertGeneratedNodes,
|
|
9
|
+
export { $generateJSONFromSelectedNodes, $generateNodesFromSerializedNodes, $getHtmlContent, $getLexicalContent, $insertDataTransferForPlainText, $insertDataTransferForRichText, $insertGeneratedNodes, copyToClipboard, } from './clipboard';
|
package/package.json
CHANGED
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"paste"
|
|
10
10
|
],
|
|
11
11
|
"license": "MIT",
|
|
12
|
-
"version": "0.
|
|
12
|
+
"version": "0.9.0",
|
|
13
13
|
"main": "LexicalClipboard.js",
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"lexical": "0.
|
|
15
|
+
"lexical": "0.9.0"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@lexical/utils": "0.
|
|
19
|
-
"@lexical/list": "0.
|
|
20
|
-
"@lexical/selection": "0.
|
|
21
|
-
"@lexical/html": "0.
|
|
18
|
+
"@lexical/utils": "0.9.0",
|
|
19
|
+
"@lexical/list": "0.9.0",
|
|
20
|
+
"@lexical/selection": "0.9.0",
|
|
21
|
+
"@lexical/html": "0.9.0"
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|