@lexical/text 0.1.16 → 0.1.19

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Dominic Gannaway
3
+ Copyright (c) Meta Platforms, Inc. and affiliates.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type {ElementNode, LexicalEditor, RootNode, TextNode} from 'lexical';
9
+ export type TextNodeWithOffset = {
10
+ node: TextNode;
11
+ offset: number;
12
+ };
13
+ export function $findTextIntersectionFromCharacters(
14
+ root: RootNode,
15
+ targetCharacters: number,
16
+ ): null | {
17
+ node: TextNode;
18
+ offset: number;
19
+ };
20
+ export function $joinTextNodesInElementNode(
21
+ elementNode: ElementNode,
22
+ separator: string,
23
+ stopAt: TextNodeWithOffset,
24
+ ): string;
25
+ export function $findNodeWithOffsetFromJoinedText(
26
+ offsetInJoinedText: number,
27
+ joinedTextLength: number,
28
+ separatorLength: number,
29
+ elementNode: ElementNode,
30
+ ): ?TextNodeWithOffset;
31
+ export function $isRootTextContentEmpty(
32
+ isEditorComposing: boolean,
33
+ trim?: boolean,
34
+ ): boolean;
35
+ export function $isRootTextContentEmptyCurry(
36
+ isEditorComposing: boolean,
37
+ trim?: boolean,
38
+ ): () => boolean;
39
+ export function $rootTextContentCurry(): string;
40
+ export function $canShowPlaceholder(isComposing: boolean): boolean;
41
+ export function $canShowPlaceholderCurry(
42
+ isEditorComposing: boolean,
43
+ ): () => boolean;
44
+ export type EntityMatch = {
45
+ end: number;
46
+ start: number;
47
+ };
48
+ export function registerLexicalTextEntity<N extends TextNode>(
49
+ editor: LexicalEditor,
50
+ getMatch: (text: string) => null | EntityMatch,
51
+ targetNode: Class<N>,
52
+ createNode: (textNode: TextNode) => N,
53
+ ): Array<() => void>;
@@ -212,6 +212,158 @@ function $canShowPlaceholder(isComposing) {
212
212
  function $canShowPlaceholderCurry(isEditorComposing) {
213
213
  return () => $canShowPlaceholder(isEditorComposing);
214
214
  }
215
+ function registerLexicalTextEntity(editor, getMatch, targetNode, createNode) {
216
+ const isTargetNode = node => {
217
+ return node instanceof targetNode;
218
+ };
219
+
220
+ const replaceWithSimpleText = node => {
221
+ const textNode = lexical.$createTextNode(node.getTextContent());
222
+ textNode.setFormat(node.getFormat());
223
+ node.replace(textNode);
224
+ };
225
+
226
+ const getMode = node => {
227
+ return node.getLatest().__mode;
228
+ };
229
+
230
+ const textNodeTransform = node => {
231
+ if (!node.isSimpleText()) {
232
+ return;
233
+ }
234
+
235
+ const prevSibling = node.getPreviousSibling();
236
+ let text = node.getTextContent();
237
+ let currentNode = node;
238
+ let match;
239
+
240
+ if (lexical.$isTextNode(prevSibling)) {
241
+ const previousText = prevSibling.getTextContent();
242
+ const combinedText = previousText + text;
243
+ const prevMatch = getMatch(combinedText);
244
+
245
+ if (isTargetNode(prevSibling)) {
246
+ if (prevMatch === null || getMode(prevSibling) !== 0) {
247
+ replaceWithSimpleText(prevSibling);
248
+ return;
249
+ } else {
250
+ const diff = prevMatch.end - previousText.length;
251
+
252
+ if (diff > 0) {
253
+ const concatText = text.slice(0, diff);
254
+ const newTextContent = previousText + concatText;
255
+ prevSibling.select();
256
+ prevSibling.setTextContent(newTextContent);
257
+
258
+ if (diff === text.length) {
259
+ node.remove();
260
+ } else {
261
+ const remainingText = text.slice(diff);
262
+ node.setTextContent(remainingText);
263
+ }
264
+
265
+ return;
266
+ }
267
+ }
268
+ } else if (prevMatch === null || prevMatch.start < previousText.length) {
269
+ return;
270
+ }
271
+ }
272
+
273
+ while (true) {
274
+ match = getMatch(text);
275
+ let nextText = match === null ? '' : text.slice(match.end);
276
+ text = nextText;
277
+
278
+ if (nextText === '') {
279
+ const nextSibling = currentNode.getNextSibling();
280
+
281
+ if (lexical.$isTextNode(nextSibling)) {
282
+ nextText = currentNode.getTextContent() + nextSibling.getTextContent();
283
+ const nextMatch = getMatch(nextText);
284
+
285
+ if (nextMatch === null) {
286
+ if (isTargetNode(nextSibling)) {
287
+ replaceWithSimpleText(nextSibling);
288
+ } else {
289
+ nextSibling.markDirty();
290
+ }
291
+
292
+ return;
293
+ } else if (nextMatch.start !== 0) {
294
+ return;
295
+ }
296
+ }
297
+ } else {
298
+ const nextMatch = getMatch(nextText);
299
+
300
+ if (nextMatch !== null && nextMatch.start === 0) {
301
+ return;
302
+ }
303
+ }
304
+
305
+ if (match === null) {
306
+ return;
307
+ }
308
+
309
+ if (match.start === 0 && lexical.$isTextNode(prevSibling) && prevSibling.isTextEntity()) {
310
+ continue;
311
+ }
312
+
313
+ let nodeToReplace;
314
+
315
+ if (match.start === 0) {
316
+ [nodeToReplace, currentNode] = currentNode.splitText(match.end);
317
+ } else {
318
+ [, nodeToReplace, currentNode] = currentNode.splitText(match.start, match.end);
319
+ }
320
+
321
+ const replacementNode = createNode(nodeToReplace);
322
+ nodeToReplace.replace(replacementNode);
323
+
324
+ if (currentNode == null) {
325
+ return;
326
+ }
327
+ }
328
+ };
329
+
330
+ const reverseNodeTransform = node => {
331
+ const text = node.getTextContent();
332
+ const match = getMatch(text);
333
+
334
+ if (match === null || match.start !== 0) {
335
+ replaceWithSimpleText(node);
336
+ return;
337
+ }
338
+
339
+ if (text.length > match.end) {
340
+ // This will split out the rest of the text as simple text
341
+ node.splitText(match.end);
342
+ return;
343
+ }
344
+
345
+ const prevSibling = node.getPreviousSibling();
346
+
347
+ if (lexical.$isTextNode(prevSibling) && prevSibling.isTextEntity()) {
348
+ replaceWithSimpleText(prevSibling);
349
+ replaceWithSimpleText(node);
350
+ }
351
+
352
+ const nextSibling = node.getNextSibling();
353
+
354
+ if (lexical.$isTextNode(nextSibling) && nextSibling.isTextEntity()) {
355
+ replaceWithSimpleText(nextSibling); // This may have already been converted in the previous block
356
+
357
+ if (isTargetNode(node)) {
358
+ replaceWithSimpleText(node);
359
+ }
360
+ }
361
+ };
362
+
363
+ const removePlainTextTransform = editor.registerNodeTransform(lexical.TextNode, textNodeTransform);
364
+ const removeReverseNodeTransform = editor.registerNodeTransform(targetNode, reverseNodeTransform);
365
+ return [removePlainTextTransform, removeReverseNodeTransform];
366
+ }
215
367
 
216
368
  exports.$canShowPlaceholder = $canShowPlaceholder;
217
369
  exports.$canShowPlaceholderCurry = $canShowPlaceholderCurry;
@@ -221,3 +373,4 @@ exports.$isRootTextContentEmpty = $isRootTextContentEmpty;
221
373
  exports.$isRootTextContentEmptyCurry = $isRootTextContentEmptyCurry;
222
374
  exports.$joinTextNodesInElementNode = $joinTextNodesInElementNode;
223
375
  exports.$rootTextContentCurry = $rootTextContentCurry;
376
+ exports.registerLexicalTextEntity = registerLexicalTextEntity;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict
8
+ */
9
+ import type {ElementNode, LexicalEditor, RootNode, TextNode} from 'lexical';
10
+ export type TextNodeWithOffset = {
11
+ node: TextNode,
12
+ offset: number,
13
+ };
14
+ declare export function $findTextIntersectionFromCharacters(
15
+ root: RootNode,
16
+ targetCharacters: number,
17
+ ): null | {
18
+ node: TextNode,
19
+ offset: number,
20
+ };
21
+ declare export function $joinTextNodesInElementNode(
22
+ elementNode: ElementNode,
23
+ separator: string,
24
+ stopAt: TextNodeWithOffset,
25
+ ): string;
26
+ declare export function $findNodeWithOffsetFromJoinedText(
27
+ offsetInJoinedText: number,
28
+ joinedTextLength: number,
29
+ separatorLength: number,
30
+ elementNode: ElementNode,
31
+ ): ?TextNodeWithOffset;
32
+ declare export function $isRootTextContentEmpty(
33
+ isEditorComposing: boolean,
34
+ trim?: boolean,
35
+ ): boolean;
36
+ declare export function $isRootTextContentEmptyCurry(
37
+ isEditorComposing: boolean,
38
+ trim?: boolean,
39
+ ): () => boolean;
40
+ declare export function $rootTextContentCurry(): string;
41
+ declare export function $canShowPlaceholder(isComposing: boolean): boolean;
42
+ declare export function $canShowPlaceholderCurry(
43
+ isEditorComposing: boolean,
44
+ ): () => boolean;
45
+ export type EntityMatch = {
46
+ end: number,
47
+ start: number,
48
+ };
49
+ declare export function registerLexicalTextEntity<N: TextNode>(
50
+ editor: LexicalEditor,
51
+ getMatch: (text: string) => null | EntityMatch,
52
+ targetNode: Class<N>,
53
+ createNode: (textNode: TextNode) => N,
54
+ ): Array<() => void>;
@@ -4,8 +4,11 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- var f=require("lexical");function m(a,e=!0){if(a)return!1;a=p();e&&(a=a.trim());return""===a}function p(){return f.$getRoot().getTextContent()}function q(a){if(!m(a,!1))return!1;a=f.$getRoot().getChildren();const e=a.length;if(1<e)return!1;for(let c=0;c<e;c++){var b=a[c];if(f.$isElementNode(b)){if("paragraph"!==b.__type||0!==b.__indent)return!1;b=b.getChildren();const k=b.length;for(let d=0;d<k;d++)if(!f.$isTextNode(b[c]))return!1}}return!0}exports.$canShowPlaceholder=q;
8
- exports.$canShowPlaceholderCurry=function(a){return()=>q(a)};exports.$findNodeWithOffsetFromJoinedText=function(a,e,b,c){c=c.getChildren();const k=c.length;let d=0,g=!1;for(let n=0;n<k&&!(d>e);++n){const l=c[n],r=f.$isTextNode(l);var h=r?l.getTextContent().length:b;h=d+h;if((!1===g&&d===a||0===d&&d===a||d<a&&a<=h)&&f.$isTextNode(l))return{node:l,offset:a-d};d=h;g=r}return null};
9
- exports.$findTextIntersectionFromCharacters=function(a,e){var b=a.getFirstChild();a=0;a:for(;null!==b;){if(f.$isElementNode(b)){var c=b.getFirstChild();if(null!==c){b=c;continue}}else if(f.$isTextNode(b)){c=b.getTextContentSize();if(a+c>e)return{node:b,offset:e-a};a+=c}c=b.getNextSibling();if(null!==c)b=c;else{for(b=b.getParent();null!==b;){c=b.getNextSibling();if(null!==c){b=c;continue a}b=b.getParent()}break}}return null};exports.$isRootTextContentEmpty=m;
10
- exports.$isRootTextContentEmptyCurry=function(a,e){return()=>m(a,e)};
11
- exports.$joinTextNodesInElementNode=function(a,e,b){let c="";a=a.getChildren();const k=a.length;for(let d=0;d<k;++d){const g=a[d];if(f.$isTextNode(g)){const h=g.getTextContent();if(g.is(b.node)){if(b.offset>h.length)throw Error("Minified Lexical error #50; see codes.json for the full message or use the non-minified dev environment for full errors and additional helpful warnings.");c+=g.getTextContent().substr(0,b.offset);break}else c+=h}else c+=e}return c};exports.$rootTextContentCurry=p;
7
+ var k=require("lexical");function r(b,f=!0){if(b)return!1;b=t();f&&(b=b.trim());return""===b}function t(){return k.$getRoot().getTextContent()}function u(b){if(!r(b,!1))return!1;b=k.$getRoot().getChildren();const f=b.length;if(1<f)return!1;for(let e=0;e<f;e++){var c=b[e];if(k.$isElementNode(c)){if("paragraph"!==c.__type||0!==c.__indent)return!1;c=c.getChildren();const p=c.length;for(let g=0;g<p;g++)if(!k.$isTextNode(c[e]))return!1}}return!0}exports.$canShowPlaceholder=u;
8
+ exports.$canShowPlaceholderCurry=function(b){return()=>u(b)};exports.$findNodeWithOffsetFromJoinedText=function(b,f,c,e){e=e.getChildren();const p=e.length;let g=0,a=!1;for(let l=0;l<p&&!(g>f);++l){const m=e[l],n=k.$isTextNode(m);var d=n?m.getTextContent().length:c;d=g+d;if((!1===a&&g===b||0===g&&g===b||g<b&&b<=d)&&k.$isTextNode(m))return{node:m,offset:b-g};g=d;a=n}return null};
9
+ exports.$findTextIntersectionFromCharacters=function(b,f){var c=b.getFirstChild();b=0;a:for(;null!==c;){if(k.$isElementNode(c)){var e=c.getFirstChild();if(null!==e){c=e;continue}}else if(k.$isTextNode(c)){e=c.getTextContentSize();if(b+e>f)return{node:c,offset:f-b};b+=e}e=c.getNextSibling();if(null!==e)c=e;else{for(c=c.getParent();null!==c;){e=c.getNextSibling();if(null!==e){c=e;continue a}c=c.getParent()}break}}return null};exports.$isRootTextContentEmpty=r;
10
+ exports.$isRootTextContentEmptyCurry=function(b,f){return()=>r(b,f)};
11
+ exports.$joinTextNodesInElementNode=function(b,f,c){let e="";b=b.getChildren();const p=b.length;for(let g=0;g<p;++g){const a=b[g];if(k.$isTextNode(a)){const d=a.getTextContent();if(a.is(c.node)){if(c.offset>d.length)throw Error("Minified Lexical error #50; see codes.json for the full message or use the non-minified dev environment for full errors and additional helpful warnings.");e+=a.getTextContent().substr(0,c.offset);break}else e+=d}else e+=f}return e};exports.$rootTextContentCurry=t;
12
+ exports.registerLexicalTextEntity=function(b,f,c,e){const p=a=>{const d=k.$createTextNode(a.getTextContent());d.setFormat(a.getFormat());a.replace(d)},g=b.registerNodeTransform(k.TextNode,a=>{if(a.isSimpleText()){var d=a.getPreviousSibling(),l=a.getTextContent(),m=a;if(k.$isTextNode(d)){var n=d.getTextContent(),h=f(n+l);if(d instanceof c){if(null===h||0!==d.getLatest().__mode){p(d);return}h=h.end-n.length;if(0<h){m=l.slice(0,h);m=n+m;d.select();d.setTextContent(m);h===l.length?a.remove():(d=l.slice(h),
13
+ a.setTextContent(d));return}}else if(null===h||h.start<n.length)return}for(;;){a=f(l);l=h=null===a?"":l.slice(a.end);if(""===h){if(n=m.getNextSibling(),k.$isTextNode(n))if(h=m.getTextContent()+n.getTextContent(),h=f(h),null===h){n instanceof c?p(n):n.markDirty();break}else if(0!==h.start)break}else if(n=f(h),null!==n&&0===n.start)break;if(null===a)break;if(0===a.start&&k.$isTextNode(d)&&d.isTextEntity())continue;let q;0===a.start?[q,m]=m.splitText(a.end):[,q,m]=m.splitText(a.start,a.end);a=e(q);q.replace(a);
14
+ if(null==m)break}}});b=b.registerNodeTransform(c,a=>{var d=a.getTextContent();const l=f(d);null===l||0!==l.start?p(a):d.length>l.end?a.splitText(l.end):(d=a.getPreviousSibling(),k.$isTextNode(d)&&d.isTextEntity()&&(p(d),p(a)),d=a.getNextSibling(),k.$isTextNode(d)&&d.isTextEntity()&&(p(d),a instanceof c&&p(a)))});return[g,b]};
package/package.json CHANGED
@@ -1,9 +1,5 @@
1
1
  {
2
2
  "name": "@lexical/text",
3
- "author": {
4
- "name": "Dominic Gannaway",
5
- "email": "dg@domgan.com"
6
- },
7
3
  "description": "This package contains utilities and helpers for handling Lexical text.",
8
4
  "keywords": [
9
5
  "lexical",
@@ -13,10 +9,10 @@
13
9
  "text"
14
10
  ],
15
11
  "license": "MIT",
16
- "version": "0.1.16",
12
+ "version": "0.1.19",
17
13
  "main": "LexicalText.js",
18
14
  "peerDependencies": {
19
- "lexical": "0.1.16"
15
+ "lexical": "0.1.19"
20
16
  },
21
17
  "repository": {
22
18
  "type": "git",