@lexical/utils 0.8.1 → 0.9.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/LexicalUtils.dev.js +182 -4
- package/index.d.ts +113 -0
- package/package.json +5 -5
package/LexicalUtils.dev.js
CHANGED
|
@@ -10,6 +10,15 @@ var selection = require('@lexical/selection');
|
|
|
10
10
|
var lexical = require('lexical');
|
|
11
11
|
|
|
12
12
|
/** @module @lexical/utils */
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Takes an HTML element and adds the classNames passed within an array,
|
|
16
|
+
* ignoring any non-string types. A space can be used to add multiple classes
|
|
17
|
+
* eg. addClassNamesToElement(element, ['element-inner active', true, null])
|
|
18
|
+
* will add both 'element-inner' and 'active' as classes to that element.
|
|
19
|
+
* @param element - The element in which the classes are added
|
|
20
|
+
* @param classNames - An array defining the class names to add to the element
|
|
21
|
+
*/
|
|
13
22
|
function addClassNamesToElement(element, ...classNames) {
|
|
14
23
|
classNames.forEach(className => {
|
|
15
24
|
if (typeof className === 'string') {
|
|
@@ -18,6 +27,15 @@ function addClassNamesToElement(element, ...classNames) {
|
|
|
18
27
|
}
|
|
19
28
|
});
|
|
20
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Takes an HTML element and removes the classNames passed within an array,
|
|
32
|
+
* ignoring any non-string types. A space can be used to remove multiple classes
|
|
33
|
+
* eg. removeClassNamesFromElement(element, ['active small', true, null])
|
|
34
|
+
* will remove both the 'active' and 'small' classes from that element.
|
|
35
|
+
* @param element - The element in which the classes are removed
|
|
36
|
+
* @param classNames - An array defining the class names to remove from the element
|
|
37
|
+
*/
|
|
38
|
+
|
|
21
39
|
function removeClassNamesFromElement(element, ...classNames) {
|
|
22
40
|
classNames.forEach(className => {
|
|
23
41
|
if (typeof className === 'string') {
|
|
@@ -25,15 +43,24 @@ function removeClassNamesFromElement(element, ...classNames) {
|
|
|
25
43
|
}
|
|
26
44
|
});
|
|
27
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if the file type matches the types passed within the acceptableMimeTypes array, false otherwise.
|
|
48
|
+
* The types passed must be strings and are CASE-SENSITIVE.
|
|
49
|
+
* eg. if file is of type 'text' and acceptableMimeTypes = ['TEXT', 'IMAGE'] the function will return false.
|
|
50
|
+
* @param file - The file you want to type check.
|
|
51
|
+
* @param acceptableMimeTypes - An array of strings of types which the file is checked against.
|
|
52
|
+
* @returns true if the file is an acceptable mime type, false otherwise.
|
|
53
|
+
*/
|
|
54
|
+
|
|
28
55
|
function isMimeType(file, acceptableMimeTypes) {
|
|
29
56
|
for (const acceptableType of acceptableMimeTypes) {
|
|
30
57
|
if (file.type.startsWith(acceptableType)) {
|
|
31
58
|
return true;
|
|
32
59
|
}
|
|
33
60
|
}
|
|
61
|
+
|
|
34
62
|
return false;
|
|
35
63
|
}
|
|
36
|
-
|
|
37
64
|
/**
|
|
38
65
|
* Lexical File Reader with:
|
|
39
66
|
* 1. MIME type support
|
|
@@ -45,58 +72,81 @@ function isMimeType(file, acceptableMimeTypes) {
|
|
|
45
72
|
* src: file.result,
|
|
46
73
|
* }));
|
|
47
74
|
*/
|
|
75
|
+
|
|
48
76
|
function mediaFileReader(files, acceptableMimeTypes) {
|
|
49
77
|
const filesIterator = files[Symbol.iterator]();
|
|
50
78
|
return new Promise((resolve, reject) => {
|
|
51
79
|
const processed = [];
|
|
80
|
+
|
|
52
81
|
const handleNextFile = () => {
|
|
53
82
|
const {
|
|
54
83
|
done,
|
|
55
84
|
value: file
|
|
56
85
|
} = filesIterator.next();
|
|
86
|
+
|
|
57
87
|
if (done) {
|
|
58
88
|
return resolve(processed);
|
|
59
89
|
}
|
|
90
|
+
|
|
60
91
|
const fileReader = new FileReader();
|
|
61
92
|
fileReader.addEventListener('error', reject);
|
|
62
93
|
fileReader.addEventListener('load', () => {
|
|
63
94
|
const result = fileReader.result;
|
|
95
|
+
|
|
64
96
|
if (typeof result === 'string') {
|
|
65
97
|
processed.push({
|
|
66
98
|
file,
|
|
67
99
|
result
|
|
68
100
|
});
|
|
69
101
|
}
|
|
102
|
+
|
|
70
103
|
handleNextFile();
|
|
71
104
|
});
|
|
105
|
+
|
|
72
106
|
if (isMimeType(file, acceptableMimeTypes)) {
|
|
73
107
|
fileReader.readAsDataURL(file);
|
|
74
108
|
} else {
|
|
75
109
|
handleNextFile();
|
|
76
110
|
}
|
|
77
111
|
};
|
|
112
|
+
|
|
78
113
|
handleNextFile();
|
|
79
114
|
});
|
|
80
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end
|
|
118
|
+
* before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a
|
|
119
|
+
* branch until you hit a dead-end (leaf) and backtracking to find the nearest branching path and repeat.
|
|
120
|
+
* It will then return all the nodes found in the search in an array of objects.
|
|
121
|
+
* @param startingNode - The node to start the search, if ommitted, it will start at the root node.
|
|
122
|
+
* @param endingNode - The node to end the search, if ommitted, it will find all descendants of the startingNode.
|
|
123
|
+
* @returns An array of objects of all the nodes found by the search, including their depth into the tree.
|
|
124
|
+
* {depth: number, node: LexicalNode} It will always return at least 1 node (the ending node) so long as it exists
|
|
125
|
+
*/
|
|
126
|
+
|
|
81
127
|
function $dfs(startingNode, endingNode) {
|
|
82
128
|
const nodes = [];
|
|
83
129
|
const start = (startingNode || lexical.$getRoot()).getLatest();
|
|
84
130
|
const end = endingNode || (lexical.$isElementNode(start) ? start.getLastDescendant() : start);
|
|
85
131
|
let node = start;
|
|
86
132
|
let depth = $getDepth(node);
|
|
133
|
+
|
|
87
134
|
while (node !== null && !node.is(end)) {
|
|
88
135
|
nodes.push({
|
|
89
136
|
depth,
|
|
90
137
|
node
|
|
91
138
|
});
|
|
139
|
+
|
|
92
140
|
if (lexical.$isElementNode(node) && node.getChildrenSize() > 0) {
|
|
93
141
|
node = node.getFirstChild();
|
|
94
142
|
depth++;
|
|
95
143
|
} else {
|
|
96
144
|
// Find immediate sibling or nearest parent sibling
|
|
97
145
|
let sibling = null;
|
|
146
|
+
|
|
98
147
|
while (sibling === null && node !== null) {
|
|
99
148
|
sibling = node.getNextSibling();
|
|
149
|
+
|
|
100
150
|
if (sibling === null) {
|
|
101
151
|
node = node.getParent();
|
|
102
152
|
depth--;
|
|
@@ -106,75 +156,151 @@ function $dfs(startingNode, endingNode) {
|
|
|
106
156
|
}
|
|
107
157
|
}
|
|
108
158
|
}
|
|
159
|
+
|
|
109
160
|
if (node !== null && node.is(end)) {
|
|
110
161
|
nodes.push({
|
|
111
162
|
depth,
|
|
112
163
|
node
|
|
113
164
|
});
|
|
114
165
|
}
|
|
166
|
+
|
|
115
167
|
return nodes;
|
|
116
168
|
}
|
|
169
|
+
|
|
117
170
|
function $getDepth(node) {
|
|
118
171
|
let innerNode = node;
|
|
119
172
|
let depth = 0;
|
|
173
|
+
|
|
120
174
|
while ((innerNode = innerNode.getParent()) !== null) {
|
|
121
175
|
depth++;
|
|
122
176
|
}
|
|
177
|
+
|
|
123
178
|
return depth;
|
|
124
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Takes a node and traverses up its ancestors (toward the root node)
|
|
182
|
+
* in order to find a specific type of node.
|
|
183
|
+
* @param node - the node to begin searching.
|
|
184
|
+
* @param klass - an instance of the type of node to look for.
|
|
185
|
+
* @returns the node of type klass that was passed, or null if none exist.
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
|
|
125
189
|
function $getNearestNodeOfType(node, klass) {
|
|
126
190
|
let parent = node;
|
|
191
|
+
|
|
127
192
|
while (parent != null) {
|
|
128
193
|
if (parent instanceof klass) {
|
|
129
194
|
return parent;
|
|
130
195
|
}
|
|
196
|
+
|
|
131
197
|
parent = parent.getParent();
|
|
132
198
|
}
|
|
199
|
+
|
|
133
200
|
return null;
|
|
134
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
*Returns the element node of the nearest ancestor, otherwise throws an error.
|
|
204
|
+
* @param startNode - The starting node of the search
|
|
205
|
+
* @returns The ancestor node found
|
|
206
|
+
*/
|
|
207
|
+
|
|
135
208
|
function $getNearestBlockElementAncestorOrThrow(startNode) {
|
|
136
209
|
const blockNode = $findMatchingParent(startNode, node => lexical.$isElementNode(node) && !node.isInline());
|
|
210
|
+
|
|
137
211
|
if (!lexical.$isElementNode(blockNode)) {
|
|
138
212
|
{
|
|
139
213
|
throw Error(`Expected node ${startNode.__key} to have closest block element node.`);
|
|
140
214
|
}
|
|
141
215
|
}
|
|
216
|
+
|
|
142
217
|
return blockNode;
|
|
143
218
|
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Starts with a node and moves up the tree (toward the root node) to find a matching node based on
|
|
222
|
+
* the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
|
|
223
|
+
* passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false
|
|
224
|
+
* @param startingNode - The node where the search starts.
|
|
225
|
+
* @param findFn - A testing function that returns true if the current node satisfies the testing parameters.
|
|
226
|
+
* @returns A parent node that matches the findFn parameters, or null if one wasn't found.
|
|
227
|
+
*/
|
|
144
228
|
function $findMatchingParent(startingNode, findFn) {
|
|
145
229
|
let curr = startingNode;
|
|
230
|
+
|
|
146
231
|
while (curr !== lexical.$getRoot() && curr != null) {
|
|
147
232
|
if (findFn(curr)) {
|
|
148
233
|
return curr;
|
|
149
234
|
}
|
|
235
|
+
|
|
150
236
|
curr = curr.getParent();
|
|
151
237
|
}
|
|
238
|
+
|
|
152
239
|
return null;
|
|
153
240
|
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Returns a function that will execute all functions passed when called. It is generally used
|
|
244
|
+
* to register multiple lexical listeners and then tear them down with a single function call, such
|
|
245
|
+
* as React's useEffect hook.
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* useEffect(() => {
|
|
249
|
+
* return mergeRegister(
|
|
250
|
+
* editor.registerCommand(...registerCommand1 logic),
|
|
251
|
+
* editor.registerCommand(...registerCommand2 logic),
|
|
252
|
+
* editor.registerCommand(...registerCommand3 logic)
|
|
253
|
+
* )
|
|
254
|
+
* }, [editor])
|
|
255
|
+
* ```
|
|
256
|
+
* In this case, useEffect is returning the function returned by mergeRegister as a cleanup
|
|
257
|
+
* function to be executed after either the useEffect runs again (due to one of its dependencies
|
|
258
|
+
* updating) or the compenent it resides in unmounts.
|
|
259
|
+
* Note the functions don't neccesarily need to be in an array as all arguements
|
|
260
|
+
* are considered to be the func argument and spread from there.
|
|
261
|
+
* @param func - An array of functions meant to be executed by the returned function.
|
|
262
|
+
* @returns the function which executes all the passed register command functions.
|
|
263
|
+
*/
|
|
154
264
|
function mergeRegister(...func) {
|
|
155
265
|
return () => {
|
|
156
266
|
func.forEach(f => f());
|
|
157
267
|
};
|
|
158
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Attempts to resolve nested element nodes of the same type into a single node of that type.
|
|
271
|
+
* It is generally used for marks/commenting
|
|
272
|
+
* @param editor - The lexical editor
|
|
273
|
+
* @param targetNode - The target for the nested element to be extracted from.
|
|
274
|
+
* @param cloneNode - See {@link $createMarkNode}
|
|
275
|
+
* @param handleOverlap - Handles any overlap between the node to extract and the targetNode
|
|
276
|
+
* @returns The lexical editor
|
|
277
|
+
*/
|
|
278
|
+
|
|
159
279
|
function registerNestedElementResolver(editor, targetNode, cloneNode, handleOverlap) {
|
|
160
280
|
const $isTargetNode = node => {
|
|
161
281
|
return node instanceof targetNode;
|
|
162
282
|
};
|
|
283
|
+
|
|
163
284
|
const $findMatch = node => {
|
|
164
285
|
// First validate we don't have any children that are of the target,
|
|
165
286
|
// as we need to handle them first.
|
|
166
287
|
const children = node.getChildren();
|
|
288
|
+
|
|
167
289
|
for (let i = 0; i < children.length; i++) {
|
|
168
290
|
const child = children[i];
|
|
291
|
+
|
|
169
292
|
if ($isTargetNode(child)) {
|
|
170
293
|
return null;
|
|
171
294
|
}
|
|
172
295
|
}
|
|
296
|
+
|
|
173
297
|
let parentNode = node;
|
|
174
298
|
let childNode = node;
|
|
299
|
+
|
|
175
300
|
while (parentNode !== null) {
|
|
176
301
|
childNode = parentNode;
|
|
177
302
|
parentNode = parentNode.getParent();
|
|
303
|
+
|
|
178
304
|
if ($isTargetNode(parentNode)) {
|
|
179
305
|
return {
|
|
180
306
|
child: childNode,
|
|
@@ -182,78 +308,110 @@ function registerNestedElementResolver(editor, targetNode, cloneNode, handleOver
|
|
|
182
308
|
};
|
|
183
309
|
}
|
|
184
310
|
}
|
|
311
|
+
|
|
185
312
|
return null;
|
|
186
313
|
};
|
|
314
|
+
|
|
187
315
|
const elementNodeTransform = node => {
|
|
188
316
|
const match = $findMatch(node);
|
|
317
|
+
|
|
189
318
|
if (match !== null) {
|
|
190
319
|
const {
|
|
191
320
|
child,
|
|
192
321
|
parent
|
|
193
|
-
} = match;
|
|
194
|
-
|
|
195
|
-
// Simple path, we can move child out and siblings into a new parent.
|
|
322
|
+
} = match; // Simple path, we can move child out and siblings into a new parent.
|
|
196
323
|
|
|
197
324
|
if (child.is(node)) {
|
|
198
325
|
handleOverlap(parent, node);
|
|
199
326
|
const nextSiblings = child.getNextSiblings();
|
|
200
327
|
const nextSiblingsLength = nextSiblings.length;
|
|
201
328
|
parent.insertAfter(child);
|
|
329
|
+
|
|
202
330
|
if (nextSiblingsLength !== 0) {
|
|
203
331
|
const newParent = cloneNode(parent);
|
|
204
332
|
child.insertAfter(newParent);
|
|
333
|
+
|
|
205
334
|
for (let i = 0; i < nextSiblingsLength; i++) {
|
|
206
335
|
newParent.append(nextSiblings[i]);
|
|
207
336
|
}
|
|
208
337
|
}
|
|
338
|
+
|
|
209
339
|
if (!parent.canBeEmpty() && parent.getChildrenSize() === 0) {
|
|
210
340
|
parent.remove();
|
|
211
341
|
}
|
|
212
342
|
}
|
|
213
343
|
}
|
|
214
344
|
};
|
|
345
|
+
|
|
215
346
|
return editor.registerNodeTransform(targetNode, elementNodeTransform);
|
|
216
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Clones the editor and marks it as dirty to be reconciled. If there was a selection,
|
|
350
|
+
* it would be set back to its previous state, or null otherwise.
|
|
351
|
+
* @param editor - The lexical editor
|
|
352
|
+
* @param editorState - The editor's state
|
|
353
|
+
*/
|
|
354
|
+
|
|
217
355
|
function $restoreEditorState(editor, editorState) {
|
|
218
356
|
const FULL_RECONCILE = 2;
|
|
219
357
|
const nodeMap = new Map();
|
|
220
358
|
const activeEditorState = editor._pendingEditorState;
|
|
359
|
+
|
|
221
360
|
for (const [key, node] of editorState._nodeMap) {
|
|
222
361
|
const clone = selection.$cloneWithProperties(node);
|
|
362
|
+
|
|
223
363
|
if (lexical.$isTextNode(clone)) {
|
|
224
364
|
clone.__text = node.__text;
|
|
225
365
|
}
|
|
366
|
+
|
|
226
367
|
nodeMap.set(key, clone);
|
|
227
368
|
}
|
|
369
|
+
|
|
228
370
|
if (activeEditorState) {
|
|
229
371
|
activeEditorState._nodeMap = nodeMap;
|
|
230
372
|
}
|
|
373
|
+
|
|
231
374
|
editor._dirtyType = FULL_RECONCILE;
|
|
232
375
|
const selection$1 = editorState._selection;
|
|
233
376
|
lexical.$setSelection(selection$1 === null ? null : selection$1.clone());
|
|
234
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* If the selected insertion area is the root/shadow root node (see {@link lexical!$isRootOrShadowRoot}),
|
|
380
|
+
* the node will be appended there, otherwise, it will be inserted before the insertion area.
|
|
381
|
+
* If there is no selection where the node is to be inserted, it will be appended after any current nodes
|
|
382
|
+
* within the tree, as a child of the root node. A paragraph node will then be added after the inserted node and selected.
|
|
383
|
+
* @param node - The node to be inserted
|
|
384
|
+
* @returns The node after its insertion
|
|
385
|
+
*/
|
|
386
|
+
|
|
235
387
|
function $insertNodeToNearestRoot(node) {
|
|
236
388
|
const selection = lexical.$getSelection();
|
|
389
|
+
|
|
237
390
|
if (lexical.$isRangeSelection(selection)) {
|
|
238
391
|
const {
|
|
239
392
|
focus
|
|
240
393
|
} = selection;
|
|
241
394
|
const focusNode = focus.getNode();
|
|
242
395
|
const focusOffset = focus.offset;
|
|
396
|
+
|
|
243
397
|
if (lexical.$isRootOrShadowRoot(focusNode)) {
|
|
244
398
|
const focusChild = focusNode.getChildAtIndex(focusOffset);
|
|
399
|
+
|
|
245
400
|
if (focusChild == null) {
|
|
246
401
|
focusNode.append(node);
|
|
247
402
|
} else {
|
|
248
403
|
focusChild.insertBefore(node);
|
|
249
404
|
}
|
|
405
|
+
|
|
250
406
|
node.selectNext();
|
|
251
407
|
} else {
|
|
252
408
|
let splitNode;
|
|
253
409
|
let splitOffset;
|
|
410
|
+
|
|
254
411
|
if (lexical.$isTextNode(focusNode)) {
|
|
255
412
|
splitNode = focusNode.getParentOrThrow();
|
|
256
413
|
splitOffset = focusNode.getIndexWithinParent();
|
|
414
|
+
|
|
257
415
|
if (focusOffset > 0) {
|
|
258
416
|
splitOffset += 1;
|
|
259
417
|
focusNode.splitText(focusOffset);
|
|
@@ -262,6 +420,7 @@ function $insertNodeToNearestRoot(node) {
|
|
|
262
420
|
splitNode = focusNode;
|
|
263
421
|
splitOffset = focusOffset;
|
|
264
422
|
}
|
|
423
|
+
|
|
265
424
|
const [, rightTree] = lexical.$splitNode(splitNode, splitOffset);
|
|
266
425
|
rightTree.insertBefore(node);
|
|
267
426
|
rightTree.selectStart();
|
|
@@ -274,21 +433,40 @@ function $insertNodeToNearestRoot(node) {
|
|
|
274
433
|
const root = lexical.$getRoot();
|
|
275
434
|
root.append(node);
|
|
276
435
|
}
|
|
436
|
+
|
|
277
437
|
const paragraphNode = lexical.$createParagraphNode();
|
|
278
438
|
node.insertAfter(paragraphNode);
|
|
279
439
|
paragraphNode.select();
|
|
280
440
|
}
|
|
441
|
+
|
|
281
442
|
return node.getLatest();
|
|
282
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* Wraps the node into another node created from a createElementNode function, eg. $createParagraphNode
|
|
446
|
+
* @param node - Node to be wrapped.
|
|
447
|
+
* @param createElementNode - Creates a new lexcial element to wrap the to-be-wrapped node and returns it.
|
|
448
|
+
* @returns A new lexcial element with the previous node appended within (as a child, including its children).
|
|
449
|
+
*/
|
|
450
|
+
|
|
283
451
|
function $wrapNodeInElement(node, createElementNode) {
|
|
284
452
|
const elementNode = createElementNode();
|
|
285
453
|
node.replace(elementNode);
|
|
286
454
|
elementNode.append(node);
|
|
287
455
|
return elementNode;
|
|
288
456
|
}
|
|
457
|
+
/**
|
|
458
|
+
* @param x - The element being tested
|
|
459
|
+
* @returns Returns true if x is an HTML anchor tag, false otherwise
|
|
460
|
+
*/
|
|
461
|
+
|
|
289
462
|
function isHTMLAnchorElement(x) {
|
|
290
463
|
return isHTMLElement(x) && x.tagName === 'A';
|
|
291
464
|
}
|
|
465
|
+
/**
|
|
466
|
+
* @param x - The element being testing
|
|
467
|
+
* @returns Returns true if x is an HTML element, false otherwise.
|
|
468
|
+
*/
|
|
469
|
+
|
|
292
470
|
function isHTMLElement(x) {
|
|
293
471
|
// @ts-ignore-next-line - strict check on nodeType here should filter out non-Element EventTarget implementors
|
|
294
472
|
return x.nodeType === 1;
|
package/index.d.ts
CHANGED
|
@@ -12,8 +12,32 @@ export declare type DFSNode = Readonly<{
|
|
|
12
12
|
depth: number;
|
|
13
13
|
node: LexicalNode;
|
|
14
14
|
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Takes an HTML element and adds the classNames passed within an array,
|
|
17
|
+
* ignoring any non-string types. A space can be used to add multiple classes
|
|
18
|
+
* eg. addClassNamesToElement(element, ['element-inner active', true, null])
|
|
19
|
+
* will add both 'element-inner' and 'active' as classes to that element.
|
|
20
|
+
* @param element - The element in which the classes are added
|
|
21
|
+
* @param classNames - An array defining the class names to add to the element
|
|
22
|
+
*/
|
|
15
23
|
export declare function addClassNamesToElement(element: HTMLElement, ...classNames: Array<typeof undefined | boolean | null | string>): void;
|
|
24
|
+
/**
|
|
25
|
+
* Takes an HTML element and removes the classNames passed within an array,
|
|
26
|
+
* ignoring any non-string types. A space can be used to remove multiple classes
|
|
27
|
+
* eg. removeClassNamesFromElement(element, ['active small', true, null])
|
|
28
|
+
* will remove both the 'active' and 'small' classes from that element.
|
|
29
|
+
* @param element - The element in which the classes are removed
|
|
30
|
+
* @param classNames - An array defining the class names to remove from the element
|
|
31
|
+
*/
|
|
16
32
|
export declare function removeClassNamesFromElement(element: HTMLElement, ...classNames: Array<typeof undefined | boolean | null | string>): void;
|
|
33
|
+
/**
|
|
34
|
+
* Returns true if the file type matches the types passed within the acceptableMimeTypes array, false otherwise.
|
|
35
|
+
* The types passed must be strings and are CASE-SENSITIVE.
|
|
36
|
+
* eg. if file is of type 'text' and acceptableMimeTypes = ['TEXT', 'IMAGE'] the function will return false.
|
|
37
|
+
* @param file - The file you want to type check.
|
|
38
|
+
* @param acceptableMimeTypes - An array of strings of types which the file is checked against.
|
|
39
|
+
* @returns true if the file is an acceptable mime type, false otherwise.
|
|
40
|
+
*/
|
|
17
41
|
export declare function isMimeType(file: File, acceptableMimeTypes: Array<string>): boolean;
|
|
18
42
|
/**
|
|
19
43
|
* Lexical File Reader with:
|
|
@@ -30,17 +54,106 @@ export declare function mediaFileReader(files: Array<File>, acceptableMimeTypes:
|
|
|
30
54
|
file: File;
|
|
31
55
|
result: string;
|
|
32
56
|
}>>;
|
|
57
|
+
/**
|
|
58
|
+
* "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end
|
|
59
|
+
* before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a
|
|
60
|
+
* branch until you hit a dead-end (leaf) and backtracking to find the nearest branching path and repeat.
|
|
61
|
+
* It will then return all the nodes found in the search in an array of objects.
|
|
62
|
+
* @param startingNode - The node to start the search, if ommitted, it will start at the root node.
|
|
63
|
+
* @param endingNode - The node to end the search, if ommitted, it will find all descendants of the startingNode.
|
|
64
|
+
* @returns An array of objects of all the nodes found by the search, including their depth into the tree.
|
|
65
|
+
* {depth: number, node: LexicalNode} It will always return at least 1 node (the ending node) so long as it exists
|
|
66
|
+
*/
|
|
33
67
|
export declare function $dfs(startingNode?: LexicalNode, endingNode?: LexicalNode): Array<DFSNode>;
|
|
68
|
+
/**
|
|
69
|
+
* Takes a node and traverses up its ancestors (toward the root node)
|
|
70
|
+
* in order to find a specific type of node.
|
|
71
|
+
* @param node - the node to begin searching.
|
|
72
|
+
* @param klass - an instance of the type of node to look for.
|
|
73
|
+
* @returns the node of type klass that was passed, or null if none exist.
|
|
74
|
+
*/
|
|
34
75
|
export declare function $getNearestNodeOfType<T extends ElementNode>(node: LexicalNode, klass: Klass<T>): T | null;
|
|
76
|
+
/**
|
|
77
|
+
*Returns the element node of the nearest ancestor, otherwise throws an error.
|
|
78
|
+
* @param startNode - The starting node of the search
|
|
79
|
+
* @returns The ancestor node found
|
|
80
|
+
*/
|
|
35
81
|
export declare function $getNearestBlockElementAncestorOrThrow(startNode: LexicalNode): ElementNode;
|
|
36
82
|
export declare type DOMNodeToLexicalConversion = (element: Node) => LexicalNode;
|
|
37
83
|
export declare type DOMNodeToLexicalConversionMap = Record<string, DOMNodeToLexicalConversion>;
|
|
84
|
+
/**
|
|
85
|
+
* Starts with a node and moves up the tree (toward the root node) to find a matching node based on
|
|
86
|
+
* the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be
|
|
87
|
+
* passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false
|
|
88
|
+
* @param startingNode - The node where the search starts.
|
|
89
|
+
* @param findFn - A testing function that returns true if the current node satisfies the testing parameters.
|
|
90
|
+
* @returns A parent node that matches the findFn parameters, or null if one wasn't found.
|
|
91
|
+
*/
|
|
38
92
|
export declare function $findMatchingParent(startingNode: LexicalNode, findFn: (node: LexicalNode) => boolean): LexicalNode | null;
|
|
39
93
|
declare type Func = () => void;
|
|
94
|
+
/**
|
|
95
|
+
* Returns a function that will execute all functions passed when called. It is generally used
|
|
96
|
+
* to register multiple lexical listeners and then tear them down with a single function call, such
|
|
97
|
+
* as React's useEffect hook.
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* useEffect(() => {
|
|
101
|
+
* return mergeRegister(
|
|
102
|
+
* editor.registerCommand(...registerCommand1 logic),
|
|
103
|
+
* editor.registerCommand(...registerCommand2 logic),
|
|
104
|
+
* editor.registerCommand(...registerCommand3 logic)
|
|
105
|
+
* )
|
|
106
|
+
* }, [editor])
|
|
107
|
+
* ```
|
|
108
|
+
* In this case, useEffect is returning the function returned by mergeRegister as a cleanup
|
|
109
|
+
* function to be executed after either the useEffect runs again (due to one of its dependencies
|
|
110
|
+
* updating) or the compenent it resides in unmounts.
|
|
111
|
+
* Note the functions don't neccesarily need to be in an array as all arguements
|
|
112
|
+
* are considered to be the func argument and spread from there.
|
|
113
|
+
* @param func - An array of functions meant to be executed by the returned function.
|
|
114
|
+
* @returns the function which executes all the passed register command functions.
|
|
115
|
+
*/
|
|
40
116
|
export declare function mergeRegister(...func: Array<Func>): () => void;
|
|
117
|
+
/**
|
|
118
|
+
* Attempts to resolve nested element nodes of the same type into a single node of that type.
|
|
119
|
+
* It is generally used for marks/commenting
|
|
120
|
+
* @param editor - The lexical editor
|
|
121
|
+
* @param targetNode - The target for the nested element to be extracted from.
|
|
122
|
+
* @param cloneNode - See {@link $createMarkNode}
|
|
123
|
+
* @param handleOverlap - Handles any overlap between the node to extract and the targetNode
|
|
124
|
+
* @returns The lexical editor
|
|
125
|
+
*/
|
|
41
126
|
export declare function registerNestedElementResolver<N extends ElementNode>(editor: LexicalEditor, targetNode: Klass<N>, cloneNode: (from: N) => N, handleOverlap: (from: N, to: N) => void): () => void;
|
|
127
|
+
/**
|
|
128
|
+
* Clones the editor and marks it as dirty to be reconciled. If there was a selection,
|
|
129
|
+
* it would be set back to its previous state, or null otherwise.
|
|
130
|
+
* @param editor - The lexical editor
|
|
131
|
+
* @param editorState - The editor's state
|
|
132
|
+
*/
|
|
42
133
|
export declare function $restoreEditorState(editor: LexicalEditor, editorState: EditorState): void;
|
|
134
|
+
/**
|
|
135
|
+
* If the selected insertion area is the root/shadow root node (see {@link lexical!$isRootOrShadowRoot}),
|
|
136
|
+
* the node will be appended there, otherwise, it will be inserted before the insertion area.
|
|
137
|
+
* If there is no selection where the node is to be inserted, it will be appended after any current nodes
|
|
138
|
+
* within the tree, as a child of the root node. A paragraph node will then be added after the inserted node and selected.
|
|
139
|
+
* @param node - The node to be inserted
|
|
140
|
+
* @returns The node after its insertion
|
|
141
|
+
*/
|
|
43
142
|
export declare function $insertNodeToNearestRoot<T extends LexicalNode>(node: T): T;
|
|
143
|
+
/**
|
|
144
|
+
* Wraps the node into another node created from a createElementNode function, eg. $createParagraphNode
|
|
145
|
+
* @param node - Node to be wrapped.
|
|
146
|
+
* @param createElementNode - Creates a new lexcial element to wrap the to-be-wrapped node and returns it.
|
|
147
|
+
* @returns A new lexcial element with the previous node appended within (as a child, including its children).
|
|
148
|
+
*/
|
|
44
149
|
export declare function $wrapNodeInElement(node: LexicalNode, createElementNode: () => ElementNode): ElementNode;
|
|
150
|
+
/**
|
|
151
|
+
* @param x - The element being tested
|
|
152
|
+
* @returns Returns true if x is an HTML anchor tag, false otherwise
|
|
153
|
+
*/
|
|
45
154
|
export declare function isHTMLAnchorElement(x: Node): x is HTMLAnchorElement;
|
|
155
|
+
/**
|
|
156
|
+
* @param x - The element being testing
|
|
157
|
+
* @returns Returns true if x is an HTML element, false otherwise.
|
|
158
|
+
*/
|
|
46
159
|
export declare function isHTMLElement(x: Node | EventTarget): x is HTMLElement;
|
package/package.json
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"utils"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.9.1",
|
|
12
12
|
"main": "LexicalUtils.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.
|
|
14
|
+
"lexical": "0.9.1"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/list": "0.
|
|
18
|
-
"@lexical/table": "0.
|
|
19
|
-
"@lexical/selection": "0.
|
|
17
|
+
"@lexical/list": "0.9.1",
|
|
18
|
+
"@lexical/table": "0.9.1",
|
|
19
|
+
"@lexical/selection": "0.9.1"
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|