@lexical/html 0.43.1-nightly.20260417.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ContextRecord.d.ts +93 -0
- package/DOMRenderExtension.d.ts +16 -0
- package/LexicalHtml.dev.js +650 -50
- package/LexicalHtml.dev.mjs +638 -49
- package/LexicalHtml.js.flow +178 -11
- package/LexicalHtml.mjs +12 -1
- package/LexicalHtml.node.mjs +12 -1
- package/LexicalHtml.prod.js +1 -1
- package/LexicalHtml.prod.mjs +1 -1
- package/RenderContext.d.ts +32 -0
- package/compileDOMRenderConfigOverrides.d.ts +22 -0
- package/constants.d.ts +10 -0
- package/domOverride.d.ts +18 -0
- package/index.d.ts +17 -0
- package/package.json +5 -4
- package/types.d.ts +293 -0
package/LexicalHtml.dev.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
11
|
var selection = require('@lexical/selection');
|
|
12
|
-
var utils = require('@lexical/utils');
|
|
13
12
|
var lexical = require('lexical');
|
|
13
|
+
var extension = require('@lexical/extension');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
@@ -20,6 +20,565 @@ var lexical = require('lexical');
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
// Do not require this module directly! Use normal `invariant` calls.
|
|
24
|
+
|
|
25
|
+
function formatDevErrorMessage(message) {
|
|
26
|
+
throw new Error(message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
31
|
+
*
|
|
32
|
+
* This source code is licensed under the MIT license found in the
|
|
33
|
+
* LICENSE file in the root directory of this source tree.
|
|
34
|
+
*
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
let activeContext;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @experimental
|
|
41
|
+
*
|
|
42
|
+
* The LexicalEditor with context
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @experimental
|
|
47
|
+
*
|
|
48
|
+
* @param contextRecord The ContextRecord
|
|
49
|
+
* @param cfg The configuration
|
|
50
|
+
* @returns The value or defaultValue of cfg
|
|
51
|
+
*/
|
|
52
|
+
function getContextValue(contextRecord, cfg) {
|
|
53
|
+
const {
|
|
54
|
+
key
|
|
55
|
+
} = cfg;
|
|
56
|
+
return contextRecord && key in contextRecord ? contextRecord[key] : cfg.defaultValue;
|
|
57
|
+
}
|
|
58
|
+
function getEditorContext(editor) {
|
|
59
|
+
return activeContext && activeContext.editor === editor ? activeContext : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @experimental
|
|
64
|
+
*
|
|
65
|
+
* @param sym The symbol for this ContextRecord (e.g. DOMRenderContextSymbol)
|
|
66
|
+
* @param editor The editor
|
|
67
|
+
* @returns The current context or undefined
|
|
68
|
+
*/
|
|
69
|
+
function getContextRecord(sym, editor) {
|
|
70
|
+
const editorContext = getEditorContext(editor);
|
|
71
|
+
return editorContext && editorContext[sym];
|
|
72
|
+
}
|
|
73
|
+
function toPair(contextRecord, pairOrUpdater) {
|
|
74
|
+
if ('cfg' in pairOrUpdater) {
|
|
75
|
+
const {
|
|
76
|
+
cfg,
|
|
77
|
+
updater
|
|
78
|
+
} = pairOrUpdater;
|
|
79
|
+
return [cfg, updater(getContextValue(contextRecord, cfg))];
|
|
80
|
+
}
|
|
81
|
+
return pairOrUpdater;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Construct a new context from a parent context and pairs
|
|
86
|
+
*
|
|
87
|
+
* @param pairs The pairs and updaters to build the context from
|
|
88
|
+
* @param parent The parent context
|
|
89
|
+
* @returns The new context
|
|
90
|
+
*/
|
|
91
|
+
function contextFromPairs(pairs, parent) {
|
|
92
|
+
let rval = parent;
|
|
93
|
+
for (const pairOrUpdater of pairs) {
|
|
94
|
+
const [k, v] = toPair(rval, pairOrUpdater);
|
|
95
|
+
const key = k.key;
|
|
96
|
+
if (rval === parent && getContextValue(rval, k) === v) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const ctx = rval || createChildContext(parent);
|
|
100
|
+
ctx[key] = v;
|
|
101
|
+
rval = ctx;
|
|
102
|
+
}
|
|
103
|
+
return rval;
|
|
104
|
+
}
|
|
105
|
+
function createChildContext(parent) {
|
|
106
|
+
return Object.create(parent || null);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a context config pair that sets a value in the render context.
|
|
111
|
+
* @experimental
|
|
112
|
+
*/
|
|
113
|
+
function contextValue(cfg, value) {
|
|
114
|
+
return [cfg, value];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a context config updater that transforms a value in the render context.
|
|
119
|
+
* @experimental
|
|
120
|
+
*/
|
|
121
|
+
function contextUpdater(cfg, updater) {
|
|
122
|
+
return {
|
|
123
|
+
cfg,
|
|
124
|
+
updater
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @internal
|
|
130
|
+
* @experimental
|
|
131
|
+
* @__NO_SIDE_EFFECTS__
|
|
132
|
+
*/
|
|
133
|
+
function $withFullContext(sym, contextRecord, f, editor = lexical.$getEditor()) {
|
|
134
|
+
const prevDOMContext = activeContext;
|
|
135
|
+
const parentEditorContext = getEditorContext(editor);
|
|
136
|
+
try {
|
|
137
|
+
activeContext = {
|
|
138
|
+
...parentEditorContext,
|
|
139
|
+
editor,
|
|
140
|
+
[sym]: contextRecord
|
|
141
|
+
};
|
|
142
|
+
return f();
|
|
143
|
+
} finally {
|
|
144
|
+
activeContext = prevDOMContext;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @internal
|
|
150
|
+
* @experimental
|
|
151
|
+
* @__NO_SIDE_EFFECTS__
|
|
152
|
+
*/
|
|
153
|
+
function $withContext(sym, $defaults = () => undefined) {
|
|
154
|
+
return (cfg, editor = lexical.$getEditor()) => {
|
|
155
|
+
return f => {
|
|
156
|
+
const parentEditorContext = getEditorContext(editor);
|
|
157
|
+
const parentContextRecord = parentEditorContext && parentEditorContext[sym];
|
|
158
|
+
const contextRecord = contextFromPairs(cfg, parentContextRecord || $defaults(editor));
|
|
159
|
+
if (!contextRecord || contextRecord === parentContextRecord) {
|
|
160
|
+
return f();
|
|
161
|
+
}
|
|
162
|
+
return $withFullContext(sym, contextRecord, f, editor);
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @experimental
|
|
169
|
+
* @internal
|
|
170
|
+
* @__NO_SIDE_EFFECTS__
|
|
171
|
+
*/
|
|
172
|
+
function createContextState(tag, name, getDefaultValue, isEqual) {
|
|
173
|
+
return Object.assign(lexical.createState(Symbol(name), {
|
|
174
|
+
isEqual,
|
|
175
|
+
parse: getDefaultValue
|
|
176
|
+
}), {
|
|
177
|
+
[tag]: true
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
183
|
+
*
|
|
184
|
+
* This source code is licensed under the MIT license found in the
|
|
185
|
+
* LICENSE file in the root directory of this source tree.
|
|
186
|
+
*
|
|
187
|
+
*/
|
|
188
|
+
const DOMRenderExtensionName = '@lexical/html/DOM';
|
|
189
|
+
const DOMRenderContextSymbol = Symbol.for('@lexical/html/DOMExportContext');
|
|
190
|
+
const ALWAYS_TRUE = () => true;
|
|
191
|
+
|
|
192
|
+
function buildTypeTree(editorConfig) {
|
|
193
|
+
const t = {};
|
|
194
|
+
const {
|
|
195
|
+
nodes
|
|
196
|
+
} = extension.getKnownTypesAndNodes(editorConfig);
|
|
197
|
+
for (const klass of nodes) {
|
|
198
|
+
const type = klass.getType();
|
|
199
|
+
t[type] = {
|
|
200
|
+
klass,
|
|
201
|
+
types: {}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
for (const baseRec of Object.values(t)) {
|
|
205
|
+
if (baseRec) {
|
|
206
|
+
const baseType = baseRec.klass.getType();
|
|
207
|
+
for (let {
|
|
208
|
+
klass
|
|
209
|
+
} = baseRec; lexical.$isLexicalNode(klass.prototype); klass = Object.getPrototypeOf(klass)) {
|
|
210
|
+
const {
|
|
211
|
+
ownNodeType
|
|
212
|
+
} = lexical.getStaticNodeConfig(klass);
|
|
213
|
+
const superRec = ownNodeType && t[ownNodeType];
|
|
214
|
+
if (superRec) {
|
|
215
|
+
superRec.types[baseType] = true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return t;
|
|
221
|
+
}
|
|
222
|
+
function buildNodePredicate(klass) {
|
|
223
|
+
return node => node instanceof klass;
|
|
224
|
+
}
|
|
225
|
+
function getPredicate(typeTree, {
|
|
226
|
+
nodes
|
|
227
|
+
}) {
|
|
228
|
+
if (nodes === '*') {
|
|
229
|
+
return ALWAYS_TRUE;
|
|
230
|
+
}
|
|
231
|
+
let types = {};
|
|
232
|
+
const predicates = [];
|
|
233
|
+
for (const klassOrPredicate of nodes) {
|
|
234
|
+
if ('getType' in klassOrPredicate) {
|
|
235
|
+
const type = klassOrPredicate.getType();
|
|
236
|
+
if (types) {
|
|
237
|
+
const tree = typeTree[type];
|
|
238
|
+
if (!(tree !== undefined)) {
|
|
239
|
+
formatDevErrorMessage(`Node class ${klassOrPredicate.name} with type ${type} not registered in editor`);
|
|
240
|
+
}
|
|
241
|
+
types = Object.assign(types, tree.types);
|
|
242
|
+
}
|
|
243
|
+
predicates.push(buildNodePredicate(klassOrPredicate));
|
|
244
|
+
} else {
|
|
245
|
+
types = undefined;
|
|
246
|
+
predicates.push(klassOrPredicate);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (types) {
|
|
250
|
+
return types;
|
|
251
|
+
} else if (predicates.length === 1) {
|
|
252
|
+
return predicates[0];
|
|
253
|
+
}
|
|
254
|
+
return node => {
|
|
255
|
+
for (const predicate of predicates) {
|
|
256
|
+
if (predicate(node)) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function makePrerender() {
|
|
264
|
+
return {
|
|
265
|
+
$createDOM: [],
|
|
266
|
+
$decorateDOM: [],
|
|
267
|
+
$exportDOM: [],
|
|
268
|
+
$extractWithChild: [],
|
|
269
|
+
$getDOMSlot: [],
|
|
270
|
+
$shouldExclude: [],
|
|
271
|
+
$shouldInclude: [],
|
|
272
|
+
$updateDOM: []
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
277
|
+
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
|
+
|
|
280
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
281
|
+
|
|
282
|
+
function ignoreNext2(acc) {
|
|
283
|
+
return (node, _$next, editor) => acc(node, editor);
|
|
284
|
+
}
|
|
285
|
+
function ignoreNext3(acc) {
|
|
286
|
+
return (node, a, _$next, editor) => acc(node, a, editor);
|
|
287
|
+
}
|
|
288
|
+
function ignoreNext4(acc) {
|
|
289
|
+
return (node, a, b, _$next, editor) => acc(node, a, b, editor);
|
|
290
|
+
}
|
|
291
|
+
function ignoreNext5(acc) {
|
|
292
|
+
return (node, a, b, c, _$next, editor) => acc(node, a, b, c, editor);
|
|
293
|
+
}
|
|
294
|
+
function merge2($acc, $getOverride) {
|
|
295
|
+
return (node, editor) => {
|
|
296
|
+
const $next = () => $acc(node, editor);
|
|
297
|
+
const $override = $getOverride(node);
|
|
298
|
+
return $override ? $override(node, $next, editor) : $next();
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function merge3(acc, $getOverride) {
|
|
302
|
+
return (node, a, editor) => {
|
|
303
|
+
const $next = () => acc(node, a, editor);
|
|
304
|
+
const $override = $getOverride(node);
|
|
305
|
+
return $override ? $override(node, a, $next, editor) : $next();
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function merge4($acc, $getOverride) {
|
|
309
|
+
return (node, a, b, editor) => {
|
|
310
|
+
const $next = () => $acc(node, a, b, editor);
|
|
311
|
+
const $override = $getOverride(node);
|
|
312
|
+
return $override ? $override(node, a, b, $next, editor) : $next();
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function merge5(acc, $getOverride) {
|
|
316
|
+
return (node, a, b, c, editor) => {
|
|
317
|
+
const $next = () => acc(node, a, b, c, editor);
|
|
318
|
+
const $override = $getOverride(node);
|
|
319
|
+
return $override ? $override(node, a, b, c, $next, editor) : $next();
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function sequence4($acc, $getOverride) {
|
|
323
|
+
return (node, a, b, editor) => {
|
|
324
|
+
$acc(node, a, b, editor);
|
|
325
|
+
const $override = $getOverride(node);
|
|
326
|
+
if ($override) {
|
|
327
|
+
$override(node, a, b, editor);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function compilePrerenderKey(prerender, k, defaults, mergeFunction, ignoreNextFunction) {
|
|
332
|
+
let acc = defaults[k];
|
|
333
|
+
for (const pair of prerender[k]) {
|
|
334
|
+
if (typeof pair[0] === 'function') {
|
|
335
|
+
const [$predicate, $override] = pair;
|
|
336
|
+
acc = mergeFunction(acc, node => $predicate(node) && $override || undefined);
|
|
337
|
+
} else {
|
|
338
|
+
const typeOverrides = pair[1];
|
|
339
|
+
const compiled = {};
|
|
340
|
+
for (const type in typeOverrides) {
|
|
341
|
+
const arr = typeOverrides[type];
|
|
342
|
+
if (arr) {
|
|
343
|
+
compiled[type] = arr.reduce(($acc, $override) => mergeFunction($acc, () => $override), acc);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
acc = mergeFunction(acc, node => {
|
|
347
|
+
const f = compiled[node.getType()];
|
|
348
|
+
return f && ignoreNextFunction(f);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
defaults[k] = acc;
|
|
353
|
+
}
|
|
354
|
+
function addOverride(prerender, k, predicateOrTypes, override) {
|
|
355
|
+
if (!override) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const arr = prerender[k];
|
|
359
|
+
if (typeof predicateOrTypes === 'function') {
|
|
360
|
+
arr.push([predicateOrTypes, override]);
|
|
361
|
+
} else {
|
|
362
|
+
const last = arr[arr.length - 1];
|
|
363
|
+
let types;
|
|
364
|
+
if (last && last[0] === 'types') {
|
|
365
|
+
types = last[1];
|
|
366
|
+
} else {
|
|
367
|
+
types = {};
|
|
368
|
+
arr.push(['types', types]);
|
|
369
|
+
}
|
|
370
|
+
for (const type in predicateOrTypes) {
|
|
371
|
+
const typeArr = types[type] || [];
|
|
372
|
+
types[type] = typeArr;
|
|
373
|
+
typeArr.push(override);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function isWildcard(override) {
|
|
378
|
+
return override.nodes === '*';
|
|
379
|
+
}
|
|
380
|
+
function sortedOverrides(overrides) {
|
|
381
|
+
const byWildcard = [];
|
|
382
|
+
const byPredicate = [];
|
|
383
|
+
const byNode = [];
|
|
384
|
+
for (const override of overrides) {
|
|
385
|
+
if (isWildcard(override)) {
|
|
386
|
+
byWildcard.push(override);
|
|
387
|
+
} else if (Array.isArray(override.nodes)) {
|
|
388
|
+
for (const klassOrPredicate of override.nodes) {
|
|
389
|
+
if (lexical.$isLexicalNode(klassOrPredicate.prototype)) {
|
|
390
|
+
byNode.push(override.nodes.length === 1 ? override : {
|
|
391
|
+
...override,
|
|
392
|
+
nodes: [klassOrPredicate]
|
|
393
|
+
});
|
|
394
|
+
} else {
|
|
395
|
+
byPredicate.push(override.nodes.length === 1 ? override : {
|
|
396
|
+
...override,
|
|
397
|
+
nodes: [klassOrPredicate]
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const depths = new Map();
|
|
404
|
+
const depthOf = klass => {
|
|
405
|
+
let depth = depths.get(klass);
|
|
406
|
+
if (depth === undefined) {
|
|
407
|
+
depth = 0;
|
|
408
|
+
for (let k = klass; lexical.$isLexicalNode(k.prototype); k = Object.getPrototypeOf(k)) {
|
|
409
|
+
depth++;
|
|
410
|
+
}
|
|
411
|
+
depths.set(klass, depth);
|
|
412
|
+
}
|
|
413
|
+
return depth;
|
|
414
|
+
};
|
|
415
|
+
byNode.sort((a, b) => depthOf(a.nodes[0]) - depthOf(b.nodes[0]));
|
|
416
|
+
return [...byNode, ...byPredicate, ...byWildcard];
|
|
417
|
+
}
|
|
418
|
+
function precompileDOMRenderConfigOverrides(editorConfig, overrides) {
|
|
419
|
+
const typeTree = buildTypeTree(editorConfig);
|
|
420
|
+
const prerender = makePrerender();
|
|
421
|
+
for (const override of sortedOverrides(overrides)) {
|
|
422
|
+
const predicateOrTypes = getPredicate(typeTree, override);
|
|
423
|
+
for (const k_ in prerender) {
|
|
424
|
+
const k = k_;
|
|
425
|
+
addOverride(prerender, k, predicateOrTypes, override[k]);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return prerender;
|
|
429
|
+
}
|
|
430
|
+
function identity(v) {
|
|
431
|
+
return v;
|
|
432
|
+
}
|
|
433
|
+
function compileDOMRenderConfigOverrides(editorConfig, {
|
|
434
|
+
overrides
|
|
435
|
+
}) {
|
|
436
|
+
const prerender = precompileDOMRenderConfigOverrides(editorConfig, overrides);
|
|
437
|
+
const dom = {
|
|
438
|
+
...lexical.DEFAULT_EDITOR_DOM_CONFIG,
|
|
439
|
+
...editorConfig.dom
|
|
440
|
+
};
|
|
441
|
+
compilePrerenderKey(prerender, '$createDOM', dom, merge2, ignoreNext2);
|
|
442
|
+
compilePrerenderKey(prerender, '$exportDOM', dom, merge2, ignoreNext2);
|
|
443
|
+
compilePrerenderKey(prerender, '$extractWithChild', dom, merge5, ignoreNext5);
|
|
444
|
+
compilePrerenderKey(prerender, '$getDOMSlot', dom, merge3, ignoreNext3);
|
|
445
|
+
compilePrerenderKey(prerender, '$shouldExclude', dom, merge3, ignoreNext3);
|
|
446
|
+
compilePrerenderKey(prerender, '$shouldInclude', dom, merge3, ignoreNext3);
|
|
447
|
+
compilePrerenderKey(prerender, '$updateDOM', dom, merge4, ignoreNext4);
|
|
448
|
+
compilePrerenderKey(prerender, '$decorateDOM', dom, sequence4, identity);
|
|
449
|
+
return dom;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
454
|
+
*
|
|
455
|
+
* This source code is licensed under the MIT license found in the
|
|
456
|
+
* LICENSE file in the root directory of this source tree.
|
|
457
|
+
*
|
|
458
|
+
*/
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* @experimental
|
|
463
|
+
*
|
|
464
|
+
* An extension that allows overriding the render and export behavior for an
|
|
465
|
+
* editor. This is highly experimental and subject to change from one version
|
|
466
|
+
* to the next.
|
|
467
|
+
**/
|
|
468
|
+
const DOMRenderExtension = lexical.defineExtension({
|
|
469
|
+
build(editor, config, state) {
|
|
470
|
+
return {
|
|
471
|
+
defaults: contextFromPairs(config.contextDefaults, undefined)
|
|
472
|
+
};
|
|
473
|
+
},
|
|
474
|
+
config: {
|
|
475
|
+
contextDefaults: [],
|
|
476
|
+
overrides: []
|
|
477
|
+
},
|
|
478
|
+
html: {
|
|
479
|
+
// Define a RootNode export for $generateDOMFromRoot
|
|
480
|
+
export: new Map([[lexical.RootNode, () => {
|
|
481
|
+
const element = document.createElement('div');
|
|
482
|
+
element.role = 'textbox';
|
|
483
|
+
return {
|
|
484
|
+
element
|
|
485
|
+
};
|
|
486
|
+
}]])
|
|
487
|
+
},
|
|
488
|
+
init(editorConfig, config) {
|
|
489
|
+
editorConfig.dom = compileDOMRenderConfigOverrides(editorConfig, config);
|
|
490
|
+
},
|
|
491
|
+
mergeConfig(config, partial) {
|
|
492
|
+
const merged = lexical.shallowMergeConfig(config, partial);
|
|
493
|
+
for (const k of ['overrides', 'contextDefaults']) {
|
|
494
|
+
if (partial[k]) {
|
|
495
|
+
merged[k] = [...config[k], ...partial[k]];
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return merged;
|
|
499
|
+
},
|
|
500
|
+
name: DOMRenderExtensionName
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
505
|
+
*
|
|
506
|
+
* This source code is licensed under the MIT license found in the
|
|
507
|
+
* LICENSE file in the root directory of this source tree.
|
|
508
|
+
*
|
|
509
|
+
*/
|
|
510
|
+
/**
|
|
511
|
+
* Create a context state to be used during render.
|
|
512
|
+
*
|
|
513
|
+
* Note that to support the ValueOrUpdater pattern you can not use a
|
|
514
|
+
* function for V (but you may wrap it in an array or object).
|
|
515
|
+
*
|
|
516
|
+
* @experimental
|
|
517
|
+
* @__NO_SIDE_EFFECTS__
|
|
518
|
+
*/
|
|
519
|
+
function createRenderState(name, getDefaultValue, isEqual) {
|
|
520
|
+
return createContextState(DOMRenderContextSymbol, name, getDefaultValue, isEqual);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Render context state that is true if the export was initiated from the root of the document.
|
|
525
|
+
* @experimental
|
|
526
|
+
*/
|
|
527
|
+
const RenderContextRoot = createRenderState('root', Boolean);
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Render context state that is true if this is an export operation ($generateHtmlFromNodes).
|
|
531
|
+
* @experimental
|
|
532
|
+
*/
|
|
533
|
+
const RenderContextExport = createRenderState('isExport', Boolean);
|
|
534
|
+
function getDefaultRenderContext(editor) {
|
|
535
|
+
const builder = extension.LexicalBuilder.maybeFromEditor(editor);
|
|
536
|
+
return builder && builder.hasExtensionByName(DOMRenderExtensionName) ? extension.getExtensionDependencyFromEditor(editor, DOMRenderExtension).output.defaults : undefined;
|
|
537
|
+
}
|
|
538
|
+
function getRenderContext(editor) {
|
|
539
|
+
return getContextRecord(DOMRenderContextSymbol, editor) || getDefaultRenderContext(editor);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get a render context value during a DOM render or export operation.
|
|
544
|
+
* @experimental
|
|
545
|
+
*/
|
|
546
|
+
function $getRenderContextValue(cfg, editor = lexical.$getEditor()) {
|
|
547
|
+
return getContextValue(getRenderContext(editor), cfg);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Execute a callback within a render context with the given config pairs.
|
|
552
|
+
* @experimental
|
|
553
|
+
*/
|
|
554
|
+
const $withRenderContext = $withContext(DOMRenderContextSymbol, getDefaultRenderContext);
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
558
|
+
*
|
|
559
|
+
* This source code is licensed under the MIT license found in the
|
|
560
|
+
* LICENSE file in the root directory of this source tree.
|
|
561
|
+
*
|
|
562
|
+
*/
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* A convenience function for type inference when constructing DOM overrides for
|
|
566
|
+
* use with {@link DOMRenderExtension}.
|
|
567
|
+
*
|
|
568
|
+
* @experimental
|
|
569
|
+
* @__NO_SIDE_EFFECTS__
|
|
570
|
+
*/
|
|
571
|
+
|
|
572
|
+
function domOverride(nodes, config) {
|
|
573
|
+
return {
|
|
574
|
+
...config,
|
|
575
|
+
nodes
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function isStyleRule(rule) {
|
|
580
|
+
return rule.constructor.name === CSSStyleRule.name;
|
|
581
|
+
}
|
|
23
582
|
|
|
24
583
|
/**
|
|
25
584
|
* Inlines CSS rules from <style> tags onto matching elements as inline styles.
|
|
@@ -56,7 +615,7 @@ function inlineStylesFromStyleSheets(doc) {
|
|
|
56
615
|
continue;
|
|
57
616
|
}
|
|
58
617
|
for (const rule of Array.from(rules)) {
|
|
59
|
-
if (!(rule
|
|
618
|
+
if (!isStyleRule(rule)) {
|
|
60
619
|
continue;
|
|
61
620
|
}
|
|
62
621
|
let elements;
|
|
@@ -66,7 +625,7 @@ function inlineStylesFromStyleSheets(doc) {
|
|
|
66
625
|
continue;
|
|
67
626
|
}
|
|
68
627
|
for (const el of Array.from(elements)) {
|
|
69
|
-
if (!(el
|
|
628
|
+
if (!lexical.isHTMLElement(el)) {
|
|
70
629
|
continue;
|
|
71
630
|
}
|
|
72
631
|
const originalProps = getOriginalInlineProps(el);
|
|
@@ -83,6 +642,7 @@ function inlineStylesFromStyleSheets(doc) {
|
|
|
83
642
|
// styleSheets API not supported in this environment
|
|
84
643
|
}
|
|
85
644
|
}
|
|
645
|
+
const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
|
|
86
646
|
|
|
87
647
|
/**
|
|
88
648
|
* How you parse your html string to get a document is left up to you. In the browser you can use the native
|
|
@@ -94,69 +654,97 @@ function $generateNodesFromDOM(editor, dom) {
|
|
|
94
654
|
inlineStylesFromStyleSheets(dom);
|
|
95
655
|
}
|
|
96
656
|
const elements = lexical.isDOMDocumentNode(dom) ? dom.body.childNodes : dom.childNodes;
|
|
97
|
-
|
|
657
|
+
const lexicalNodes = [];
|
|
98
658
|
const allArtificialNodes = [];
|
|
99
659
|
for (const element of elements) {
|
|
100
660
|
if (!IGNORE_TAGS.has(element.nodeName)) {
|
|
101
661
|
const lexicalNode = $createNodesFromDOM(element, editor, allArtificialNodes, false);
|
|
102
662
|
if (lexicalNode !== null) {
|
|
103
|
-
|
|
663
|
+
for (const node of lexicalNode) {
|
|
664
|
+
lexicalNodes.push(node);
|
|
665
|
+
}
|
|
104
666
|
}
|
|
105
667
|
}
|
|
106
668
|
}
|
|
107
669
|
$unwrapArtificialNodes(allArtificialNodes);
|
|
108
670
|
return lexicalNodes;
|
|
109
671
|
}
|
|
110
|
-
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Generate DOM nodes from the editor state into the given container element,
|
|
675
|
+
* using the editor's {@link EditorDOMRenderConfig}.
|
|
676
|
+
* @experimental
|
|
677
|
+
*/
|
|
678
|
+
function $generateDOMFromNodes(container, selection = null, editor = lexical.$getEditor()) {
|
|
679
|
+
return $withRenderContext([contextValue(RenderContextExport, true)], editor)(() => {
|
|
680
|
+
const root = lexical.$getRoot();
|
|
681
|
+
const domConfig = lexical.$getEditorDOMRenderConfig(editor);
|
|
682
|
+
const parentElementAppend = container.append.bind(container);
|
|
683
|
+
for (const topLevelNode of root.getChildren()) {
|
|
684
|
+
$appendNodesToHTML(editor, topLevelNode, parentElementAppend, selection, domConfig);
|
|
685
|
+
}
|
|
686
|
+
return container;
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Generate DOM nodes from a root node into the given container element,
|
|
692
|
+
* including the root node itself. Uses the editor's {@link EditorDOMRenderConfig}.
|
|
693
|
+
* @experimental
|
|
694
|
+
*/
|
|
695
|
+
function $generateDOMFromRoot(container, root = lexical.$getRoot()) {
|
|
696
|
+
const editor = lexical.$getEditor();
|
|
697
|
+
return $withRenderContext([contextValue(RenderContextExport, true), contextValue(RenderContextRoot, true)], editor)(() => {
|
|
698
|
+
const selection = null;
|
|
699
|
+
const domConfig = lexical.$getEditorDOMRenderConfig(editor);
|
|
700
|
+
const parentElementAppend = container.append.bind(container);
|
|
701
|
+
$appendNodesToHTML(editor, root, parentElementAppend, selection, domConfig);
|
|
702
|
+
return container;
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function $generateHtmlFromNodes(editor, selection = null) {
|
|
111
706
|
if (typeof document === 'undefined' || typeof window === 'undefined' && typeof global.window === 'undefined') {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const root = lexical.$getRoot();
|
|
116
|
-
const topLevelChildren = root.getChildren();
|
|
117
|
-
for (let i = 0; i < topLevelChildren.length; i++) {
|
|
118
|
-
const topLevelNode = topLevelChildren[i];
|
|
119
|
-
$appendNodesToHTML(editor, topLevelNode, container, selection);
|
|
707
|
+
{
|
|
708
|
+
formatDevErrorMessage(`To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom or use withDOM from @lexical/headless/dom before calling this function.`);
|
|
709
|
+
}
|
|
120
710
|
}
|
|
121
|
-
return
|
|
711
|
+
return $generateDOMFromNodes(document.createElement('div'), selection, editor).innerHTML;
|
|
122
712
|
}
|
|
123
|
-
function $appendNodesToHTML(editor, currentNode,
|
|
124
|
-
let shouldInclude = selection$1
|
|
125
|
-
const shouldExclude =
|
|
713
|
+
function $appendNodesToHTML(editor, currentNode, parentElementAppend, selection$1 = null, domConfig = lexical.$getEditorDOMRenderConfig(editor)) {
|
|
714
|
+
let shouldInclude = domConfig.$shouldInclude(currentNode, selection$1, editor);
|
|
715
|
+
const shouldExclude = domConfig.$shouldExclude(currentNode, selection$1, editor);
|
|
126
716
|
let target = currentNode;
|
|
127
717
|
if (selection$1 !== null && lexical.$isTextNode(currentNode)) {
|
|
128
718
|
target = selection.$sliceSelectedTextNodeContent(selection$1, currentNode, 'clone');
|
|
129
719
|
}
|
|
130
|
-
const
|
|
131
|
-
const registeredNode = lexical.getRegisteredNode(editor, target.getType());
|
|
132
|
-
let exportOutput;
|
|
133
|
-
|
|
134
|
-
// Use HTMLConfig overrides, if available.
|
|
135
|
-
if (registeredNode && registeredNode.exportDOM !== undefined) {
|
|
136
|
-
exportOutput = registeredNode.exportDOM(editor, target);
|
|
137
|
-
} else {
|
|
138
|
-
exportOutput = target.exportDOM(editor);
|
|
139
|
-
}
|
|
720
|
+
const exportProps = domConfig.$exportDOM(target, editor);
|
|
140
721
|
const {
|
|
141
722
|
element,
|
|
142
|
-
after
|
|
143
|
-
|
|
723
|
+
after,
|
|
724
|
+
append,
|
|
725
|
+
$getChildNodes
|
|
726
|
+
} = exportProps;
|
|
144
727
|
if (!element) {
|
|
145
728
|
return false;
|
|
146
729
|
}
|
|
147
730
|
const fragment = document.createDocumentFragment();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
731
|
+
const children = $getChildNodes ? $getChildNodes() : lexical.$isElementNode(target) ? target.getChildren() : [];
|
|
732
|
+
const fragmentAppend = fragment.append.bind(fragment);
|
|
733
|
+
for (const childNode of children) {
|
|
734
|
+
const shouldIncludeChild = $appendNodesToHTML(editor, childNode, fragmentAppend, selection$1, domConfig);
|
|
735
|
+
if (!shouldInclude && shouldIncludeChild && domConfig.$extractWithChild(currentNode, childNode, selection$1, 'html', editor)) {
|
|
152
736
|
shouldInclude = true;
|
|
153
737
|
}
|
|
154
738
|
}
|
|
155
739
|
if (shouldInclude && !shouldExclude) {
|
|
156
|
-
if (
|
|
157
|
-
|
|
740
|
+
if (lexical.isHTMLElement(element) || lexical.isDocumentFragment(element)) {
|
|
741
|
+
if (append) {
|
|
742
|
+
append(fragment);
|
|
743
|
+
} else {
|
|
744
|
+
element.append(fragment);
|
|
745
|
+
}
|
|
158
746
|
}
|
|
159
|
-
|
|
747
|
+
parentElementAppend(element);
|
|
160
748
|
if (after) {
|
|
161
749
|
const newElement = after.call(target, element);
|
|
162
750
|
if (newElement) {
|
|
@@ -168,7 +756,7 @@ function $appendNodesToHTML(editor, currentNode, parentElement, selection$1 = nu
|
|
|
168
756
|
}
|
|
169
757
|
}
|
|
170
758
|
} else {
|
|
171
|
-
|
|
759
|
+
parentElementAppend(fragment);
|
|
172
760
|
}
|
|
173
761
|
return shouldInclude;
|
|
174
762
|
}
|
|
@@ -191,9 +779,8 @@ function getConversionFunction(domNode, editor) {
|
|
|
191
779
|
}
|
|
192
780
|
return currentConversion !== null ? currentConversion.conversion : null;
|
|
193
781
|
}
|
|
194
|
-
const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
|
|
195
782
|
function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorLexicalNode, forChildMap = new Map(), parentLexicalNode) {
|
|
196
|
-
|
|
783
|
+
const lexicalNodes = [];
|
|
197
784
|
if (IGNORE_TAGS.has(node.nodeName)) {
|
|
198
785
|
return lexicalNodes;
|
|
199
786
|
}
|
|
@@ -232,7 +819,7 @@ function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorL
|
|
|
232
819
|
if (postTransform != null) {
|
|
233
820
|
childLexicalNodes = postTransform(childLexicalNodes);
|
|
234
821
|
}
|
|
235
|
-
if (
|
|
822
|
+
if (lexical.isBlockDomNode(node)) {
|
|
236
823
|
if (!hasBlockAncestorLexicalNodeForChildren) {
|
|
237
824
|
childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, lexical.$createParagraphNode);
|
|
238
825
|
} else {
|
|
@@ -247,11 +834,13 @@ function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorL
|
|
|
247
834
|
if (childLexicalNodes.length > 0) {
|
|
248
835
|
// If it hasn't been converted to a LexicalNode, we hoist its children
|
|
249
836
|
// up to the same level as it.
|
|
250
|
-
|
|
837
|
+
for (const childNode of childLexicalNodes) {
|
|
838
|
+
lexicalNodes.push(childNode);
|
|
839
|
+
}
|
|
251
840
|
} else {
|
|
252
|
-
if (
|
|
841
|
+
if (lexical.isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) {
|
|
253
842
|
// Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes
|
|
254
|
-
lexicalNodes
|
|
843
|
+
lexicalNodes.push(lexical.$createLineBreakNode());
|
|
255
844
|
}
|
|
256
845
|
}
|
|
257
846
|
} else {
|
|
@@ -289,18 +878,18 @@ function wrapContinuousInlines(domNode, nodes, createWrapperFn) {
|
|
|
289
878
|
return out;
|
|
290
879
|
}
|
|
291
880
|
function $unwrapArtificialNodes(allArtificialNodes) {
|
|
881
|
+
// Replace artificial node with its children, inserting a linebreak
|
|
882
|
+
// between adjacent artificial nodes
|
|
292
883
|
for (const node of allArtificialNodes) {
|
|
293
|
-
if (node.getNextSibling() instanceof lexical.ArtificialNode__DO_NOT_USE) {
|
|
884
|
+
if (node.getParent() && node.getNextSibling() instanceof lexical.ArtificialNode__DO_NOT_USE) {
|
|
294
885
|
node.insertAfter(lexical.$createLineBreakNode());
|
|
295
886
|
}
|
|
296
887
|
}
|
|
297
|
-
// Replace artificial node with it's children
|
|
298
888
|
for (const node of allArtificialNodes) {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
node.
|
|
889
|
+
const parent = node.getParent();
|
|
890
|
+
if (parent) {
|
|
891
|
+
parent.splice(node.getIndexWithinParent(), 1, node.getChildren());
|
|
302
892
|
}
|
|
303
|
-
node.remove();
|
|
304
893
|
}
|
|
305
894
|
}
|
|
306
895
|
function isDomNodeBetweenTwoInlineNodes(node) {
|
|
@@ -310,5 +899,16 @@ function isDomNodeBetweenTwoInlineNodes(node) {
|
|
|
310
899
|
return lexical.isInlineDomNode(node.nextSibling) && lexical.isInlineDomNode(node.previousSibling);
|
|
311
900
|
}
|
|
312
901
|
|
|
902
|
+
exports.$generateDOMFromNodes = $generateDOMFromNodes;
|
|
903
|
+
exports.$generateDOMFromRoot = $generateDOMFromRoot;
|
|
313
904
|
exports.$generateHtmlFromNodes = $generateHtmlFromNodes;
|
|
314
905
|
exports.$generateNodesFromDOM = $generateNodesFromDOM;
|
|
906
|
+
exports.$getRenderContextValue = $getRenderContextValue;
|
|
907
|
+
exports.$withRenderContext = $withRenderContext;
|
|
908
|
+
exports.DOMRenderExtension = DOMRenderExtension;
|
|
909
|
+
exports.RenderContextExport = RenderContextExport;
|
|
910
|
+
exports.RenderContextRoot = RenderContextRoot;
|
|
911
|
+
exports.contextUpdater = contextUpdater;
|
|
912
|
+
exports.contextValue = contextValue;
|
|
913
|
+
exports.createRenderState = createRenderState;
|
|
914
|
+
exports.domOverride = domOverride;
|