@lexical/utils 0.9.0 → 0.9.2

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.
@@ -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,6 +43,15 @@ 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)) {
@@ -86,6 +113,17 @@ function mediaFileReader(files, acceptableMimeTypes) {
86
113
  handleNextFile();
87
114
  });
88
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
+
89
127
  function $dfs(startingNode, endingNode) {
90
128
  const nodes = [];
91
129
  const start = (startingNode || lexical.$getRoot()).getLatest();
@@ -139,6 +177,14 @@ function $getDepth(node) {
139
177
 
140
178
  return depth;
141
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
+
142
188
 
143
189
  function $getNearestNodeOfType(node, klass) {
144
190
  let parent = node;
@@ -153,6 +199,12 @@ function $getNearestNodeOfType(node, klass) {
153
199
 
154
200
  return null;
155
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
+
156
208
  function $getNearestBlockElementAncestorOrThrow(startNode) {
157
209
  const blockNode = $findMatchingParent(startNode, node => lexical.$isElementNode(node) && !node.isInline());
158
210
 
@@ -164,6 +216,15 @@ function $getNearestBlockElementAncestorOrThrow(startNode) {
164
216
 
165
217
  return blockNode;
166
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
+ */
167
228
  function $findMatchingParent(startingNode, findFn) {
168
229
  let curr = startingNode;
169
230
 
@@ -177,11 +238,44 @@ function $findMatchingParent(startingNode, findFn) {
177
238
 
178
239
  return null;
179
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
+ */
180
264
  function mergeRegister(...func) {
181
265
  return () => {
182
266
  func.forEach(f => f());
183
267
  };
184
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
+
185
279
  function registerNestedElementResolver(editor, targetNode, cloneNode, handleOverlap) {
186
280
  const $isTargetNode = node => {
187
281
  return node instanceof targetNode;
@@ -251,6 +345,13 @@ function registerNestedElementResolver(editor, targetNode, cloneNode, handleOver
251
345
 
252
346
  return editor.registerNodeTransform(targetNode, elementNodeTransform);
253
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
+
254
355
  function $restoreEditorState(editor, editorState) {
255
356
  const FULL_RECONCILE = 2;
256
357
  const nodeMap = new Map();
@@ -274,6 +375,15 @@ function $restoreEditorState(editor, editorState) {
274
375
  const selection$1 = editorState._selection;
275
376
  lexical.$setSelection(selection$1 === null ? null : selection$1.clone());
276
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
+
277
387
  function $insertNodeToNearestRoot(node) {
278
388
  const selection = lexical.$getSelection();
279
389
 
@@ -331,15 +441,32 @@ function $insertNodeToNearestRoot(node) {
331
441
 
332
442
  return node.getLatest();
333
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
+
334
451
  function $wrapNodeInElement(node, createElementNode) {
335
452
  const elementNode = createElementNode();
336
453
  node.replace(elementNode);
337
454
  elementNode.append(node);
338
455
  return elementNode;
339
456
  }
457
+ /**
458
+ * @param x - The element being tested
459
+ * @returns Returns true if x is an HTML anchor tag, false otherwise
460
+ */
461
+
340
462
  function isHTMLAnchorElement(x) {
341
463
  return isHTMLElement(x) && x.tagName === 'A';
342
464
  }
465
+ /**
466
+ * @param x - The element being testing
467
+ * @returns Returns true if x is an HTML element, false otherwise.
468
+ */
469
+
343
470
  function isHTMLElement(x) {
344
471
  // @ts-ignore-next-line - strict check on nodeType here should filter out non-Element EventTarget implementors
345
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.9.0",
11
+ "version": "0.9.2",
12
12
  "main": "LexicalUtils.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.9.0"
14
+ "lexical": "0.9.2"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/list": "0.9.0",
18
- "@lexical/table": "0.9.0",
19
- "@lexical/selection": "0.9.0"
17
+ "@lexical/list": "0.9.2",
18
+ "@lexical/table": "0.9.2",
19
+ "@lexical/selection": "0.9.2"
20
20
  },
21
21
  "repository": {
22
22
  "type": "git",