@lexical/mark 0.50.1-nightly.20260916.0 → 0.51.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.
@@ -6,7 +6,7 @@
6
6
  *
7
7
  */
8
8
 
9
- import { ElementNode, $getDocument, addClassNamesToElement, removeClassNamesFromElement, $isRangeSelection, $applyNodeReplacement, $isTextNode, $createRangeSelection, $isElementNode, $isDecoratorNode } from 'lexical';
9
+ import { getterTableOf, setterTableOf, setterDefaultOf, ElementNode, $getDocument, addClassNamesToElement, removeClassNamesFromElement, $isRangeSelection, $applyNodeReplacement, nodeSchema, withAccessors, arrayValue, stringValue, $isTextNode, $createRangeSelection, $isElementNode, $isDecoratorNode } from 'lexical';
10
10
 
11
11
  /**
12
12
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -16,30 +16,155 @@ import { ElementNode, $getDocument, addClassNamesToElement, removeClassNamesFrom
16
16
  *
17
17
  */
18
18
 
19
+
20
+ // The JSON number grammar, anchored, matching numberValue: `Number()` alone
21
+ // reads '0x10' as 16 and '' as 0, and neither is a shape a JSON encoder
22
+ // produces. Emitted from the same source the codegen verified against, so the
23
+ // two cannot be different functions.
24
+ const JSON_NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
25
+ function num(v, d) {
26
+ if (typeof v === 'number') {
27
+ return Number.isFinite(v) ? v : d;
28
+ }
29
+ if (typeof v !== 'string' || !JSON_NUMBER.test(v)) {
30
+ return d;
31
+ }
32
+ const n = Number(v);
33
+ return Number.isFinite(n) ? n : d;
34
+ }
35
+ function numC(v, d, min, max, integer) {
36
+ const n = num(v, d);
37
+ return n >= min && n <= max && (false || Number.isInteger(n)) ? n : d;
38
+ }
39
+
40
+ /**
41
+ * MarkNode's schema-declared fields, for a clone. Generated from that
42
+ * schema; do not edit by hand.
43
+ *
44
+ * @internal
45
+ */
46
+ function afterCloneMarkNode(node, prevNode) {
47
+ node.__ids = prevNode.__ids;
48
+ }
49
+
50
+ /** MarkNode's generated implementations, for its `$config`. @internal */
51
+ const GENERATED_MARK = fields => {
52
+ const MARK_FORMAT_GETTER = getterTableOf(fields, 'format');
53
+ const MARK_FORMAT_SETTER = setterTableOf(fields, 'format');
54
+ const MARK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format');
55
+
56
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
57
+ function exportMarkNode(node) {
58
+ const textFormat = node.__textFormat;
59
+ const textStyle = node.__textStyle;
60
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
61
+ return {
62
+ children: [],
63
+ ids: node.getIDs(),
64
+ direction: node.__dir,
65
+ format: MARK_FORMAT_GETTER[node.__format],
66
+ indent: node.__indent,
67
+ textFormat: textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
68
+ textStyle: textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
69
+ type: node.__type,
70
+ version: 1
71
+ };
72
+ }
73
+
74
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
75
+ function exportCompactMarkNode(node) {
76
+ const textFormat = node.__textFormat;
77
+ const textStyle = node.__textStyle;
78
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
79
+ const json = {
80
+ type: node.__type,
81
+ children: []
82
+ };
83
+ const ids = node.getIDs();
84
+ if (ids !== undefined && !(Array.isArray(ids) && ids.length === 0)) {
85
+ json.ids = ids;
86
+ }
87
+ const direction = node.__dir;
88
+ if (direction != null) {
89
+ json.direction = direction;
90
+ }
91
+ const format = MARK_FORMAT_GETTER[node.__format];
92
+ if (format !== undefined && format !== '') {
93
+ json.format = format;
94
+ }
95
+ const indent = node.__indent;
96
+ if (indent !== undefined && indent !== 0) {
97
+ json.indent = indent;
98
+ }
99
+ if (textFormat !== undefined && textFormat !== 0 && shouldSerializeTextStyles) {
100
+ json.textFormat = textFormat;
101
+ }
102
+ if (textStyle !== undefined && textStyle !== '' && shouldSerializeTextStyles) {
103
+ json.textStyle = textStyle;
104
+ }
105
+ return json;
106
+ }
107
+
108
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
109
+ function updateMarkNode(node, json) {
110
+ const direction = json.direction;
111
+ node.__dir = direction === null || direction === 'ltr' || direction === 'rtl' ? direction : null;
112
+ const format = json.format;
113
+ node.__format = typeof format === 'string' && format in MARK_FORMAT_SETTER ? MARK_FORMAT_SETTER[format] : MARK_FORMAT_SETTER_DEFAULT;
114
+ node.__indent = numC(json.indent, 0, 0, Infinity);
115
+ node.__textFormat = num(json.textFormat, 0);
116
+ const textStyle = json.textStyle;
117
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
118
+ const ids = json.ids;
119
+ node.__ids = Array.isArray(ids) ? Array.from(ids, e0 => typeof e0 === 'string' ? e0 : '') : [];
120
+ return node;
121
+ }
122
+ return {
123
+ exportJSON: exportMarkNode,
124
+ exportCompactJSON: exportCompactMarkNode,
125
+ updateFromJSON: updateMarkNode,
126
+ afterCloneFrom: afterCloneMarkNode
127
+ };
128
+ };
129
+
130
+ /**
131
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
132
+ *
133
+ * This source code is licensed under the MIT license found in the
134
+ * LICENSE file in the root directory of this source tree.
135
+ *
136
+ */
137
+
138
+ // Single source of truth for parsing the node-specific properties of a
139
+ // SerializedMarkNode (those it adds over a SerializedElementNode).
140
+ const markNodeSchema = /* @__PURE__ */nodeSchema()({
141
+ // The getter stays a method: getIDs hands out a copy, so the export does not
142
+ // give a caller the node's own array. The setter is the field it writes,
143
+ // which is also what tells the clone where `ids` lives.
144
+ ids: /* @__PURE__ */withAccessors(/* @__PURE__ */arrayValue(/* @__PURE__ */stringValue()), {
145
+ getter: 'getIDs',
146
+ setter: {
147
+ field: '__ids',
148
+ method: 'setIDs'
149
+ }
150
+ })
151
+ });
19
152
  const NO_IDS = [];
20
153
 
154
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
155
+
21
156
  /** @noInheritDoc */
157
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
22
158
  class MarkNode extends ElementNode {
23
159
  /** @internal */
24
160
  __ids;
25
161
  $config() {
26
162
  return this.config('mark', {
27
- extends: ElementNode
163
+ extends: ElementNode,
164
+ generated: GENERATED_MARK,
165
+ json: markNodeSchema
28
166
  });
29
167
  }
30
- afterCloneFrom(prevNode) {
31
- super.afterCloneFrom(prevNode);
32
- this.__ids = prevNode.__ids;
33
- }
34
- updateFromJSON(serializedNode) {
35
- return super.updateFromJSON(serializedNode).setIDs(serializedNode.ids);
36
- }
37
- exportJSON() {
38
- return {
39
- ...super.exportJSON(),
40
- ids: this.getIDs()
41
- };
42
- }
43
168
  constructor(ids = NO_IDS, key) {
44
169
  super(key);
45
170
  this.__ids = ids;
@@ -6,4 +6,4 @@
6
6
  *
7
7
  */
8
8
 
9
- import{ElementNode as e,$getDocument as t,addClassNamesToElement as r,removeClassNamesFromElement as n,$isRangeSelection as s,$applyNodeReplacement as i,$isTextNode as o,$createRangeSelection as c,$isElementNode as f,$isDecoratorNode as u}from"lexical";const l=[];class a extends e{__ids;$config(){return this.config("mark",{extends:e})}afterCloneFrom(e){super.afterCloneFrom(e),this.__ids=e.__ids}updateFromJSON(e){return super.updateFromJSON(e).setIDs(e.ids)}exportJSON(){return{...super.exportJSON(),ids:this.getIDs()}}constructor(e=l,t){super(t),this.__ids=e}createDOM(e){const n=t().createElement("mark");return r(n,e.theme.mark),this.__ids.length>1&&r(n,e.theme.markOverlap),n}updateDOM(e,t,s){const i=e.__ids,o=this.__ids,c=i.length,f=o.length,u=s.theme.markOverlap,l=f>1;return c>1!==l&&(l?r(t,u):n(t,u)),!1}hasID(e){return this.getIDs().includes(e)}getIDs(){return Array.from(this.getLatest().__ids)}setIDs(e){const t=this.getWritable();return t.__ids=e,t}addID(e){const t=this.getWritable();return t.__ids.includes(e)?t:t.setIDs([...t.__ids,e])}deleteID(e){const t=this.getWritable(),r=t.__ids.indexOf(e);if(-1===r)return t;const n=Array.from(t.__ids);return n.splice(r,1),t.setIDs(n)}insertNewAfter(e,t=!0){const r=d(this.__ids);return this.insertAfter(r,t),r}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(e,t,r){if(!s(t)||"html"===r)return!1;const n=t.anchor,i=t.focus,o=n.getNode(),c=i.getNode(),f=t.isBackward()?n.offset-i.offset:i.offset-n.offset;return this.isParentOf(o)&&this.isParentOf(c)&&this.getTextContent().length===f}excludeFromCopy(e){return"clone"!==e}}function d(e=l){return i(new a(e))}function h(e){return e instanceof a}function _(e){const t=e.getChildren();let r=null;for(let n=0;n<t.length;n++){const s=t[n];null===r?e.insertBefore(s):r.insertAfter(s),r=s}e.remove()}function g(e,t,r,n){const s=c(),[i,l]=e.isBackward()?[e.focus,e.anchor]:[e.anchor,e.focus];let a,_;s.anchor.set(i.key,i.offset,i.type),s.focus.set(l.key,l.offset,l.type);const g=s.extract();for(const e of g){if(f(_)&&_.isParentOf(e))continue;let t=null;if(o(e))t=e;else{if(h(e))continue;(f(e)||u(e))&&e.isInline()&&(t=e)}if(null!==t){if(t&&t.is(a))continue;const e=t.getParent();if(null!=e&&e.is(a)||(_=void 0),a=e,void 0===_){_=(n||d)([r]),t.insertBefore(_)}_.append(t)}else a=void 0,_=void 0}f(_)&&(t?_.selectStart():_.selectEnd())}function m(e,t){let r=e;for(;null!==r;){if(h(r))return r.getIDs();if(o(r)&&t===r.getTextContentSize()){const e=r.getNextSibling();if(h(e))return e.getIDs()}r=r.getParent()}return null}const p={name:"@lexical/mark",nodes:()=>[a]};export{d as $createMarkNode,m as $getMarkIDs,h as $isMarkNode,_ as $unwrapMarkNode,g as $wrapSelectionInMarkNode,p as MarkExtension,a as MarkNode};
9
+ import{getterTableOf as t,setterTableOf as e,setterDefaultOf as n,ElementNode as r,$getDocument as i,addClassNamesToElement as s,removeClassNamesFromElement as o,$isRangeSelection as c,$applyNodeReplacement as f,nodeSchema as l,withAccessors as d,arrayValue as u,stringValue as a,$isTextNode as _,$createRangeSelection as m,$isElementNode as h,$isDecoratorNode as g}from"lexical";const y=/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;function x(t,e){if("number"==typeof t)return Number.isFinite(t)?t:e;if("string"!=typeof t||!y.test(t))return e;const n=Number(t);return Number.isFinite(n)?n:e}function p(t,e){t.__ids=e.__ids}const I=r=>{const i=t(r,"format"),s=e(r,"format"),o=n(r,"format");return{exportJSON:function(t){const e=t.__textFormat,n=t.__textStyle,r=(0!==e||""!==n)&&t.shouldSerializeTextStyles();return{children:[],ids:t.getIDs(),direction:t.__dir,format:i[t.__format],indent:t.__indent,textFormat:0!==e&&r?e:void 0,textStyle:""!==n&&r?n:void 0,type:t.__type,version:1}},exportCompactJSON:function(t){const e=t.__textFormat,n=t.__textStyle,r=(0!==e||""!==n)&&t.shouldSerializeTextStyles(),s={type:t.__type,children:[]},o=t.getIDs();void 0===o||Array.isArray(o)&&0===o.length||(s.ids=o);const c=t.__dir;null!=c&&(s.direction=c);const f=i[t.__format];void 0!==f&&""!==f&&(s.format=f);const l=t.__indent;return void 0!==l&&0!==l&&(s.indent=l),void 0!==e&&0!==e&&r&&(s.textFormat=e),void 0!==n&&""!==n&&r&&(s.textStyle=n),s},updateFromJSON:function(t,e){const n=e.direction;t.__dir=null===n||"ltr"===n||"rtl"===n?n:null;const r=e.format;t.__format="string"==typeof r&&r in s?s[r]:o,t.__indent=function(t,e,n,r){const i=x(t,e);return i>=n&&i<=r&&Number.isInteger(i)?i:e}(e.indent,0,0,Infinity),t.__textFormat=x(e.textFormat,0);const i=e.textStyle;t.__textStyle="string"==typeof i?i:"";const c=e.ids;return t.__ids=Array.isArray(c)?Array.from(c,t=>"string"==typeof t?t:""):[],t},afterCloneFrom:p}},D=/* @__PURE__ */l()({ids:/* @__PURE__ */d(/* @__PURE__ */u(/* @__PURE__ */a()),{getter:"getIDs",setter:{field:"__ids",method:"setIDs"}})}),S=[];class v extends r{__ids;$config(){return this.config("mark",{extends:r,generated:I,json:D})}constructor(t=S,e){super(e),this.__ids=t}createDOM(t){const e=i().createElement("mark");return s(e,t.theme.mark),this.__ids.length>1&&s(e,t.theme.markOverlap),e}updateDOM(t,e,n){const r=t.__ids,i=this.__ids,c=r.length,f=i.length,l=n.theme.markOverlap,d=f>1;return c>1!==d&&(d?s(e,l):o(e,l)),!1}hasID(t){return this.getIDs().includes(t)}getIDs(){return Array.from(this.getLatest().__ids)}setIDs(t){const e=this.getWritable();return e.__ids=t,e}addID(t){const e=this.getWritable();return e.__ids.includes(t)?e:e.setIDs([...e.__ids,t])}deleteID(t){const e=this.getWritable(),n=e.__ids.indexOf(t);if(-1===n)return e;const r=Array.from(e.__ids);return r.splice(n,1),e.setIDs(r)}insertNewAfter(t,e=!0){const n=A(this.__ids);return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!c(e)||"html"===n)return!1;const r=e.anchor,i=e.focus,s=r.getNode(),o=i.getNode(),f=e.isBackward()?r.offset-i.offset:i.offset-r.offset;return this.isParentOf(s)&&this.isParentOf(o)&&this.getTextContent().length===f}excludeFromCopy(t){return"clone"!==t}}function A(t=S){return f(new v(t))}function F(t){return t instanceof v}function N(t){const e=t.getChildren();let n=null;for(let r=0;r<e.length;r++){const i=e[r];null===n?t.insertBefore(i):n.insertAfter(i),n=i}t.remove()}function O(t,e,n,r){const i=m(),[s,o]=t.isBackward()?[t.focus,t.anchor]:[t.anchor,t.focus];let c,f;i.anchor.set(s.key,s.offset,s.type),i.focus.set(o.key,o.offset,o.type);const l=i.extract();for(const t of l){if(h(f)&&f.isParentOf(t))continue;let e=null;if(_(t))e=t;else{if(F(t))continue;(h(t)||g(t))&&t.isInline()&&(e=t)}if(null!==e){if(e&&e.is(c))continue;const t=e.getParent();if(null!=t&&t.is(c)||(f=void 0),c=t,void 0===f){f=(r||A)([n]),e.insertBefore(f)}f.append(e)}else c=void 0,f=void 0}h(f)&&(e?f.selectStart():f.selectEnd())}function k(t,e){let n=t;for(;null!==n;){if(F(n))return n.getIDs();if(_(n)&&e===n.getTextContentSize()){const t=n.getNextSibling();if(F(t))return t.getIDs()}n=n.getParent()}return null}const b={name:"@lexical/mark",nodes:()=>[v]};export{A as $createMarkNode,k as $getMarkIDs,F as $isMarkNode,N as $unwrapMarkNode,O as $wrapSelectionInMarkNode,b as MarkExtension,v as MarkNode};
@@ -0,0 +1,18 @@
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 { MarkNode } from './MarkNode.js';
9
+ import { type GeneratedJSONFactory } from 'lexical';
10
+ /**
11
+ * MarkNode's schema-declared fields, for a clone. Generated from that
12
+ * schema; do not edit by hand.
13
+ *
14
+ * @internal
15
+ */
16
+ export declare function afterCloneMarkNode(node: MarkNode, prevNode: MarkNode): void;
17
+ /** MarkNode's generated implementations, for its `$config`. @internal */
18
+ export declare const GENERATED_MARK: GeneratedJSONFactory;
@@ -5,24 +5,45 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
- import { type BaseSelection, type EditorConfig, ElementNode, type LexicalNode, type LexicalUpdateJSON, type NodeKey, type RangeSelection, type SerializedElementNode, type Spread } from 'lexical';
8
+ import { type BaseSelection, type EditorConfig, ElementNode, type LexicalNode, type LexicalParseJSON, type NodeKey, type RangeSelection, type SerializedElementNode, type SerializedPartial, type Spread } from 'lexical';
9
9
  export type SerializedMarkNode = Spread<{
10
10
  ids: string[];
11
11
  }, SerializedElementNode>;
12
+ export interface MarkNode {
13
+ exportJSON(compact?: false): SerializedMarkNode;
14
+ exportJSON(compact: boolean): SerializedPartial<SerializedMarkNode>;
15
+ updateFromJSON(serializedNode: LexicalParseJSON<SerializedMarkNode>): this;
16
+ }
12
17
  /** @noInheritDoc */
13
18
  export declare class MarkNode extends ElementNode {
14
19
  /** @internal */
15
20
  __ids: readonly string[];
16
- $config(): import("lexical").BaseStaticNodeConfig & {
21
+ $config(): import("lexical").BaseStaticNodeConfig & import("lexical").StaticNodeConfigAccessor<{
22
+ readonly $transform: (node: ElementNode) => void;
23
+ readonly extends: typeof LexicalNode;
24
+ readonly generated: import("lexical").GeneratedJSONFactory;
25
+ readonly json: import("lexical").NodeSerializationSchema<ElementNode, {
26
+ readonly direction?: "ltr" | "rtl" | null | undefined;
27
+ readonly format?: "" | "left" | "start" | "center" | "right" | "end" | "justify" | undefined;
28
+ readonly indent?: string | number | undefined;
29
+ readonly textFormat?: string | number | undefined;
30
+ readonly textStyle?: string | undefined;
31
+ }>;
32
+ }> & {
17
33
  readonly mark?: {
18
34
  readonly extends: typeof ElementNode;
35
+ readonly generated: import("lexical").GeneratedJSONFactory;
36
+ readonly json: import("lexical").NodeSerializationSchema<MarkNode, {
37
+ readonly ids?: readonly string[] | undefined;
38
+ }>;
19
39
  } | undefined;
20
40
  } & import("lexical").StaticNodeTypeAccessor<"mark"> & import("lexical").StaticNodeConfigAccessor<{
21
41
  readonly extends: typeof ElementNode;
42
+ readonly generated: import("lexical").GeneratedJSONFactory;
43
+ readonly json: import("lexical").NodeSerializationSchema<MarkNode, {
44
+ readonly ids?: readonly string[] | undefined;
45
+ }>;
22
46
  }>;
23
- afterCloneFrom(prevNode: this): void;
24
- updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedMarkNode>): this;
25
- exportJSON(): SerializedMarkNode;
26
47
  constructor(ids?: readonly string[], key?: NodeKey);
27
48
  createDOM(config: EditorConfig): HTMLElement;
28
49
  updateDOM(prevNode: this, element: HTMLElement, config: EditorConfig): boolean;
package/package.json CHANGED
@@ -8,12 +8,12 @@
8
8
  "mark"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.50.1-nightly.20260916.0",
11
+ "version": "0.51.0",
12
12
  "main": "./dist/LexicalMark.js",
13
13
  "types": "./dist/typescript-too-old.d.ts",
14
14
  "dependencies": {
15
- "@lexical/utils": "0.50.1-nightly.20260916.0",
16
- "lexical": "0.50.1-nightly.20260916.0"
15
+ "@lexical/utils": "0.51.0",
16
+ "lexical": "0.51.0"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
@@ -0,0 +1,184 @@
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
+
9
+ // @generated by scripts/generate-node-json.mjs from each class's `json`
10
+ // schema. Run `pnpm run generate-node-json` to regenerate; do not edit.
11
+
12
+ // Keys are emitted in the order the schema-driven walk writes them, so the two
13
+ // produce byte-identical JSON. That order is the schema's, not alphabetical.
14
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
15
+
16
+ import type {MarkNode} from './MarkNode';
17
+
18
+ import {
19
+ type GeneratedJSONFactory,
20
+ getterTableOf,
21
+ setterDefaultOf,
22
+ setterTableOf,
23
+ } from 'lexical';
24
+
25
+ // The JSON number grammar, anchored, matching numberValue: `Number()` alone
26
+ // reads '0x10' as 16 and '' as 0, and neither is a shape a JSON encoder
27
+ // produces. Emitted from the same source the codegen verified against, so the
28
+ // two cannot be different functions.
29
+ const JSON_NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
30
+
31
+ function num(v: unknown, d: number): number {
32
+ if (typeof v === 'number') {
33
+ return Number.isFinite(v) ? v : d;
34
+ }
35
+ if (typeof v !== 'string' || !JSON_NUMBER.test(v)) {
36
+ return d;
37
+ }
38
+ const n = Number(v);
39
+ return Number.isFinite(n) ? n : d;
40
+ }
41
+
42
+ function numC(
43
+ v: unknown,
44
+ d: number,
45
+ min: number,
46
+ max: number,
47
+ integer: boolean,
48
+ ): number {
49
+ const n = num(v, d);
50
+ return n >= min && n <= max && (!integer || Number.isInteger(n)) ? n : d;
51
+ }
52
+
53
+ /**
54
+ * MarkNode's schema-declared fields, for a clone. Generated from that
55
+ * schema; do not edit by hand.
56
+ *
57
+ * @internal
58
+ */
59
+ export function afterCloneMarkNode(node: MarkNode, prevNode: MarkNode): void {
60
+ node.__ids = prevNode.__ids;
61
+ }
62
+
63
+ /** MarkNode's generated implementations, for its `$config`. @internal */
64
+ export const GENERATED_MARK: GeneratedJSONFactory = fields => {
65
+ const MARK_FORMAT_GETTER = getterTableOf(fields, 'format') as {
66
+ readonly [key: string]:
67
+ | ''
68
+ | 'center'
69
+ | 'end'
70
+ | 'justify'
71
+ | 'left'
72
+ | 'right'
73
+ | 'start';
74
+ };
75
+
76
+ const MARK_FORMAT_SETTER = setterTableOf(fields, 'format') as {
77
+ readonly [key: string]: 0 | 1 | 2 | 3 | 4 | 5 | 6;
78
+ };
79
+
80
+ const MARK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format') as
81
+ | 0
82
+ | 1
83
+ | 2
84
+ | 3
85
+ | 4
86
+ | 5
87
+ | 6;
88
+
89
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
90
+ function exportMarkNode(node: MarkNode): {[key: string]: unknown} {
91
+ const textFormat = node.__textFormat;
92
+ const textStyle = node.__textStyle;
93
+ const shouldSerializeTextStyles =
94
+ (textFormat !== 0 || textStyle !== '') &&
95
+ node.shouldSerializeTextStyles();
96
+ return {
97
+ children: [],
98
+ ids: node.getIDs(),
99
+ direction: node.__dir,
100
+ format: MARK_FORMAT_GETTER[node.__format],
101
+ indent: node.__indent,
102
+ textFormat:
103
+ textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
104
+ textStyle:
105
+ textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
106
+ type: node.__type,
107
+ version: 1,
108
+ };
109
+ }
110
+
111
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
112
+ function exportCompactMarkNode(node: MarkNode): {[key: string]: unknown} {
113
+ const textFormat = node.__textFormat;
114
+ const textStyle = node.__textStyle;
115
+ const shouldSerializeTextStyles =
116
+ (textFormat !== 0 || textStyle !== '') &&
117
+ node.shouldSerializeTextStyles();
118
+ const json: {[key: string]: unknown} = {type: node.__type, children: []};
119
+ const ids = node.getIDs();
120
+ if (ids !== undefined && !(Array.isArray(ids) && ids.length === 0)) {
121
+ json.ids = ids;
122
+ }
123
+ const direction = node.__dir;
124
+ if (direction != null) {
125
+ json.direction = direction;
126
+ }
127
+ const format = MARK_FORMAT_GETTER[node.__format];
128
+ if (format !== undefined && format !== '') {
129
+ json.format = format;
130
+ }
131
+ const indent = node.__indent;
132
+ if (indent !== undefined && indent !== 0) {
133
+ json.indent = indent;
134
+ }
135
+ if (
136
+ textFormat !== undefined &&
137
+ textFormat !== 0 &&
138
+ shouldSerializeTextStyles
139
+ ) {
140
+ json.textFormat = textFormat;
141
+ }
142
+ if (
143
+ textStyle !== undefined &&
144
+ textStyle !== '' &&
145
+ shouldSerializeTextStyles
146
+ ) {
147
+ json.textStyle = textStyle;
148
+ }
149
+ return json;
150
+ }
151
+
152
+ /** Generated from MarkNode's serialization schema. Do not edit by hand. */
153
+ function updateMarkNode(
154
+ node: MarkNode,
155
+ json: {readonly [key: string]: unknown},
156
+ ): MarkNode {
157
+ const direction = json.direction;
158
+ node.__dir =
159
+ direction === null || direction === 'ltr' || direction === 'rtl'
160
+ ? direction
161
+ : null;
162
+ const format = json.format;
163
+ node.__format =
164
+ typeof format === 'string' && format in MARK_FORMAT_SETTER
165
+ ? MARK_FORMAT_SETTER[format]
166
+ : MARK_FORMAT_SETTER_DEFAULT;
167
+ node.__indent = numC(json.indent, 0, 0, Infinity, true);
168
+ node.__textFormat = num(json.textFormat, 0);
169
+ const textStyle = json.textStyle;
170
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
171
+ const ids = json.ids;
172
+ node.__ids = Array.isArray(ids)
173
+ ? Array.from(ids, e0 => (typeof e0 === 'string' ? e0 : ''))
174
+ : [];
175
+ return node;
176
+ }
177
+
178
+ return {
179
+ exportJSON: exportMarkNode,
180
+ exportCompactJSON: exportCompactMarkNode,
181
+ updateFromJSON: updateMarkNode,
182
+ afterCloneFrom: afterCloneMarkNode,
183
+ };
184
+ };
package/src/MarkNode.ts CHANGED
@@ -11,18 +11,25 @@ import {
11
11
  $getDocument,
12
12
  $isRangeSelection,
13
13
  addClassNamesToElement,
14
+ arrayValue,
14
15
  type BaseSelection,
15
16
  type EditorConfig,
16
17
  ElementNode,
17
18
  type LexicalNode,
18
- type LexicalUpdateJSON,
19
+ type LexicalParseJSON,
19
20
  type NodeKey,
21
+ nodeSchema,
20
22
  type RangeSelection,
21
23
  removeClassNamesFromElement,
22
24
  type SerializedElementNode,
25
+ type SerializedPartial,
23
26
  type Spread,
27
+ stringValue,
28
+ withAccessors,
24
29
  } from 'lexical';
25
30
 
31
+ import {GENERATED_MARK} from './LexicalMarkGeneratedJSON';
32
+
26
33
  export type SerializedMarkNode = Spread<
27
34
  {
28
35
  ids: string[];
@@ -30,31 +37,39 @@ export type SerializedMarkNode = Spread<
30
37
  SerializedElementNode
31
38
  >;
32
39
 
40
+ // Single source of truth for parsing the node-specific properties of a
41
+ // SerializedMarkNode (those it adds over a SerializedElementNode).
42
+ const markNodeSchema = nodeSchema<MarkNode>()({
43
+ // The getter stays a method: getIDs hands out a copy, so the export does not
44
+ // give a caller the node's own array. The setter is the field it writes,
45
+ // which is also what tells the clone where `ids` lives.
46
+ ids: withAccessors(arrayValue(stringValue()), {
47
+ getter: 'getIDs',
48
+ setter: {field: '__ids', method: 'setIDs'},
49
+ }),
50
+ });
51
+
33
52
  const NO_IDS: readonly string[] = [];
34
53
 
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
55
+ export interface MarkNode {
56
+ exportJSON(compact?: false): SerializedMarkNode;
57
+ exportJSON(compact: boolean): SerializedPartial<SerializedMarkNode>;
58
+ updateFromJSON(serializedNode: LexicalParseJSON<SerializedMarkNode>): this;
59
+ }
60
+
35
61
  /** @noInheritDoc */
62
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
36
63
  export class MarkNode extends ElementNode {
37
64
  /** @internal */
38
65
  __ids: readonly string[];
39
66
 
40
67
  $config() {
41
- return this.config('mark', {extends: ElementNode});
42
- }
43
-
44
- afterCloneFrom(prevNode: this): void {
45
- super.afterCloneFrom(prevNode);
46
- this.__ids = prevNode.__ids;
47
- }
48
-
49
- updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedMarkNode>): this {
50
- return super.updateFromJSON(serializedNode).setIDs(serializedNode.ids);
51
- }
52
-
53
- exportJSON(): SerializedMarkNode {
54
- return {
55
- ...super.exportJSON(),
56
- ids: this.getIDs(),
57
- };
68
+ return this.config('mark', {
69
+ extends: ElementNode,
70
+ generated: GENERATED_MARK,
71
+ json: markNodeSchema,
72
+ });
58
73
  }
59
74
 
60
75
  constructor(ids: readonly string[] = NO_IDS, key?: NodeKey) {