@37signals/lexxy 0.9.17 → 0.9.18
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/dist/lexxy.esm.js +8 -7
- package/package.json +1 -1
- package/dist/lexical.esm.js +0 -344
package/dist/lexxy.esm.js
CHANGED
|
@@ -7898,8 +7898,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
7898
7898
|
return this.#historyState.redo
|
|
7899
7899
|
}
|
|
7900
7900
|
|
|
7901
|
-
#readSanitizedEditorValue(
|
|
7902
|
-
return editor?.read(() => {
|
|
7901
|
+
#readSanitizedEditorValue() {
|
|
7902
|
+
return this.editor?.read(() => {
|
|
7903
7903
|
return sanitize($generateHtmlFromNodes(this.editor, null))
|
|
7904
7904
|
}) ?? null
|
|
7905
7905
|
}
|
|
@@ -7933,7 +7933,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
7933
7933
|
}
|
|
7934
7934
|
|
|
7935
7935
|
#initialize() {
|
|
7936
|
-
this.#synchronizeWithChanges();
|
|
7937
7936
|
this.#registerComponents();
|
|
7938
7937
|
this.#handleEnter();
|
|
7939
7938
|
this.#registerFocusEvents();
|
|
@@ -7942,6 +7941,9 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
7942
7941
|
this.#attachDebugHooks();
|
|
7943
7942
|
this.#attachToolbar();
|
|
7944
7943
|
this.#resetBeforeTurboCaches();
|
|
7944
|
+
|
|
7945
|
+
this.#setInternalFormValue(this.value, { suppressEvent: true });
|
|
7946
|
+
this.#synchronizeWithChanges();
|
|
7945
7947
|
}
|
|
7946
7948
|
|
|
7947
7949
|
#registerFileAcceptFilter() {
|
|
@@ -7969,7 +7971,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
7969
7971
|
$initialEditorState: (editor) => {
|
|
7970
7972
|
this.#configureSanitizer(editor);
|
|
7971
7973
|
this.#loadInitialValue(editor);
|
|
7972
|
-
this.#setInternalFormValue(this.#readSanitizedEditorValue(editor));
|
|
7973
7974
|
},
|
|
7974
7975
|
},
|
|
7975
7976
|
...this.extensions.lexicalExtensions
|
|
@@ -8037,13 +8038,13 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
8037
8038
|
return Array.from(this.attributes).filter(attribute => attribute.name.startsWith("aria-"))
|
|
8038
8039
|
}
|
|
8039
8040
|
|
|
8040
|
-
#setInternalFormValue(html) {
|
|
8041
|
-
const changed =
|
|
8041
|
+
#setInternalFormValue(html, { suppressEvent = false } = {}) {
|
|
8042
|
+
const changed = html !== this.#previousInternalFormValue;
|
|
8042
8043
|
|
|
8043
8044
|
this.internals.setFormValue(html);
|
|
8044
8045
|
this.#previousInternalFormValue = html;
|
|
8045
8046
|
|
|
8046
|
-
if (changed) {
|
|
8047
|
+
if (changed && !suppressEvent) {
|
|
8047
8048
|
dispatch(this, "lexxy:change");
|
|
8048
8049
|
}
|
|
8049
8050
|
}
|
package/package.json
CHANGED
package/dist/lexical.esm.js
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
import { $getRoot, $caretFromPoint, $setSelectionFromCaretRange, $getCaretRange, $normalizeCaret, $getChildCaret, $getCaretInDirection, $isParagraphNode, $isLineBreakNode, $createParagraphNode, $isElementNode, $isRootOrShadowRoot, $isRootNode, $createNodeSelection, $isDecoratorNode, $isTextNode, $getSiblingCaret, $rewindSiblingCaret, $splitAtPointCaretNext, $getSelection, $isChildCaret, $isTextPointCaret, $isExtendableTextPointCaret, $isSiblingCaret, $isRangeSelection, $getCommonAncestor, $findMatchingParent, TextNode } from 'lexical';
|
|
2
|
-
export * from 'lexical';
|
|
3
|
-
import { ListNode } from '@lexical/list';
|
|
4
|
-
import { $getNearestNodeOfType, $wrapNodeInElement, $lastToFirstIterator } from '@lexical/utils';
|
|
5
|
-
import { $ensureForwardRangeSelection, $isAtNodeEnd } from '@lexical/selection';
|
|
6
|
-
|
|
7
|
-
/*** Only import from lexical packages in this file to prevent breaking npm package export chunking ***/
|
|
8
|
-
|
|
9
|
-
function $containsRangeSelection(node, selection = $getSelection()) {
|
|
10
|
-
if ($isRangeSelection(selection)) {
|
|
11
|
-
const { commonAncestor } = $getCommonAncestor(selection.focus.getNode(), selection.anchor.getNode());
|
|
12
|
-
return $findMatchingParent(commonAncestor, parent => parent.is(node))
|
|
13
|
-
} else {
|
|
14
|
-
return false
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function $createNodeSelectionWith(...nodes) {
|
|
19
|
-
const selection = $createNodeSelection();
|
|
20
|
-
nodes.forEach(node => selection.add(node.getKey()));
|
|
21
|
-
return selection
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function $isShadowRoot(node) {
|
|
25
|
-
return $isElementNode(node) && $isRootOrShadowRoot(node) && !$isRootNode(node)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function $isSafeForRoot(node) {
|
|
29
|
-
return ($isElementNode(node) || $isDecoratorNode(node)) && !node.isParentRequired()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function $makeSafeForRoot(node) {
|
|
33
|
-
if ($isSafeForRoot(node)) {
|
|
34
|
-
return node
|
|
35
|
-
} else {
|
|
36
|
-
return $wrapNodeInElement(node, () => node.createParentElementNode())
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getListType(node) {
|
|
41
|
-
const list = $getNearestNodeOfType(node, ListNode);
|
|
42
|
-
return list?.getListType() ?? null
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function isEditorFocused(editor) {
|
|
46
|
-
const rootElement = editor.getRootElement();
|
|
47
|
-
return rootElement !== null && rootElement.contains(document.activeElement)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function $isAtNodeEdge(point, atStart = null) {
|
|
51
|
-
if (atStart === null) {
|
|
52
|
-
return $isAtNodeEdge(point, true) || $isAtNodeEdge(point, false)
|
|
53
|
-
} else {
|
|
54
|
-
return atStart ? $isAtNodeStart(point) : $isAtNodeEnd(point)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function $isAtNodeStart(point) {
|
|
59
|
-
return point.offset === 0
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function extendTextNodeConversion(conversionName, ...callbacks) {
|
|
63
|
-
return extendConversion(TextNode, conversionName, (conversionOutput, element) => ({
|
|
64
|
-
...conversionOutput,
|
|
65
|
-
forChild: (lexicalNode, parentNode) => {
|
|
66
|
-
const originalForChild = conversionOutput?.forChild ?? (x => x);
|
|
67
|
-
let childNode = originalForChild(lexicalNode, parentNode);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if ($isTextNode(childNode)) {
|
|
71
|
-
childNode = callbacks.reduce(
|
|
72
|
-
(childNode, callback) => callback(childNode, element) ?? childNode,
|
|
73
|
-
childNode
|
|
74
|
-
);
|
|
75
|
-
return childNode
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function extendConversion(nodeKlass, conversionName, callback = (output => output)) {
|
|
82
|
-
return (element) => {
|
|
83
|
-
const converter = nodeKlass.importDOM()?.[conversionName]?.(element);
|
|
84
|
-
if (!converter) return null
|
|
85
|
-
|
|
86
|
-
const conversionOutput = converter.conversion(element);
|
|
87
|
-
if (!conversionOutput) return conversionOutput
|
|
88
|
-
|
|
89
|
-
return callback(conversionOutput, element) ?? conversionOutput
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function $isCursorOnLastLine(selection) {
|
|
94
|
-
const anchorNode = selection.anchor.getNode();
|
|
95
|
-
const elementNode = $isElementNode(anchorNode) ? anchorNode : anchorNode.getParentOrThrow();
|
|
96
|
-
const children = elementNode.getChildren();
|
|
97
|
-
if (children.length === 0) return true
|
|
98
|
-
|
|
99
|
-
const lastChild = children[children.length - 1];
|
|
100
|
-
|
|
101
|
-
if (anchorNode === elementNode.getLatest() && selection.anchor.offset === children.length) return true
|
|
102
|
-
if (anchorNode === lastChild) return true
|
|
103
|
-
|
|
104
|
-
const lastLineBreakIndex = children.findLastIndex(child => $isLineBreakNode(child));
|
|
105
|
-
if (lastLineBreakIndex === -1) return true
|
|
106
|
-
|
|
107
|
-
const anchorIndex = children.indexOf(anchorNode);
|
|
108
|
-
return anchorIndex > lastLineBreakIndex
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function $isBlankNode(node) {
|
|
112
|
-
if (node.getTextContent().trim() !== "") return false
|
|
113
|
-
|
|
114
|
-
const children = node.getChildren?.();
|
|
115
|
-
if (!children || children.length === 0) return true
|
|
116
|
-
|
|
117
|
-
return children.every(child => {
|
|
118
|
-
if ($isLineBreakNode(child)) return true
|
|
119
|
-
return $isBlankNode(child)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function $trimTrailingBlankNodes(parent) {
|
|
124
|
-
for (const child of $lastToFirstIterator(parent)) {
|
|
125
|
-
if ($isBlankNode(child)) {
|
|
126
|
-
child.remove();
|
|
127
|
-
} else {
|
|
128
|
-
break
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// A list item is structurally empty if it contains no meaningful content.
|
|
134
|
-
// Unlike getTextContent().trim() === "", this walks descendants to ensure
|
|
135
|
-
// decorator nodes (mentions, attachments whose getTextContent() may return
|
|
136
|
-
// invisible characters like \ufeff) are treated as non-empty content.
|
|
137
|
-
function $isListItemStructurallyEmpty(listItem) {
|
|
138
|
-
const children = listItem.getChildren();
|
|
139
|
-
for (const child of children) {
|
|
140
|
-
if ($isDecoratorNode(child)) return false
|
|
141
|
-
if ($isLineBreakNode(child)) continue
|
|
142
|
-
if ($isTextNode(child)) {
|
|
143
|
-
if (child.getTextContent().trim() !== "") return false
|
|
144
|
-
} else if ($isElementNode(child)) {
|
|
145
|
-
if (child.getTextContent().trim() !== "") return false
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return true
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Returns the document text up to `offset` inside `targetNode`. Non-inline
|
|
152
|
-
// element siblings are joined with `\n\n`, matching Lexical's own
|
|
153
|
-
// ElementNode.getTextContent behavior.
|
|
154
|
-
function $textBeforeOffset(targetNode, offset) {
|
|
155
|
-
const parts = [];
|
|
156
|
-
let done = false;
|
|
157
|
-
|
|
158
|
-
function visit(node) {
|
|
159
|
-
if (done) return
|
|
160
|
-
if (node === targetNode) {
|
|
161
|
-
parts.push(node.getTextContent().slice(0, offset));
|
|
162
|
-
done = true;
|
|
163
|
-
return
|
|
164
|
-
}
|
|
165
|
-
if ($isElementNode(node)) {
|
|
166
|
-
const children = node.getChildren();
|
|
167
|
-
for (let i = 0; i < children.length; i++) {
|
|
168
|
-
visit(children[i]);
|
|
169
|
-
if (done) return
|
|
170
|
-
const child = children[i];
|
|
171
|
-
if ($isElementNode(child) && !child.isInline() && i < children.length - 1) {
|
|
172
|
-
parts.push("\n\n");
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
} else {
|
|
176
|
-
parts.push(node.getTextContent());
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
visit($getRoot());
|
|
181
|
-
return parts.join("")
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function $splitSelectedParagraphsAtInnerLineBreaks(selection) {
|
|
185
|
-
const topLevelElements = new Set();
|
|
186
|
-
for (const node of selection.getNodes()) {
|
|
187
|
-
const topLevel = node.getTopLevelElement();
|
|
188
|
-
if (topLevel) topLevelElements.add(topLevel);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
for (const element of topLevelElements) {
|
|
192
|
-
if (!$isParagraphNode(element)) continue
|
|
193
|
-
|
|
194
|
-
const children = element.getChildren();
|
|
195
|
-
if (!children.some($isLineBreakNode)) continue
|
|
196
|
-
|
|
197
|
-
const groups = [ [] ];
|
|
198
|
-
for (const child of children) {
|
|
199
|
-
if ($isLineBreakNode(child)) {
|
|
200
|
-
groups.push([]);
|
|
201
|
-
child.remove();
|
|
202
|
-
} else {
|
|
203
|
-
groups[groups.length - 1].push(child);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
for (const group of groups) {
|
|
208
|
-
if (group.length === 0) continue
|
|
209
|
-
const paragraph = $createParagraphNode();
|
|
210
|
-
group.forEach(child => paragraph.append(child));
|
|
211
|
-
element.insertBefore(paragraph);
|
|
212
|
-
}
|
|
213
|
-
if (groups.some(group => group.length > 0)) element.remove();
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function $expandSelectionToLineBreaksAndSplitAtEdges(selection) {
|
|
218
|
-
$ensureForwardRangeSelection(selection);
|
|
219
|
-
|
|
220
|
-
const focusCaret = $caretFromPoint(selection.focus, "next");
|
|
221
|
-
const anchorCaret = $caretFromPoint(selection.anchor, "previous");
|
|
222
|
-
|
|
223
|
-
// A collapsed cursor adjacent to a <br> would claim it from both sides via
|
|
224
|
-
// inward-edge; force outward-only walks so each side finds its own boundary.
|
|
225
|
-
const skipInwardEdge = selection.isCollapsed();
|
|
226
|
-
const focusBrCaret = $getCaretAtLineBreakBoundary(focusCaret, skipInwardEdge);
|
|
227
|
-
let anchorBrCaret = $getCaretAtLineBreakBoundary(anchorCaret, skipInwardEdge);
|
|
228
|
-
|
|
229
|
-
if (focusBrCaret?.origin.is(anchorBrCaret?.origin)) {
|
|
230
|
-
anchorBrCaret = null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Splitting focus first keeps the anchor <br>'s position stable.
|
|
234
|
-
const focusOuter = focusBrCaret && $splitAroundLineBreak(focusBrCaret);
|
|
235
|
-
const anchorOuter = anchorBrCaret && $splitAroundLineBreak(anchorBrCaret);
|
|
236
|
-
|
|
237
|
-
const innerStart = anchorOuter?.getNextSibling() ?? selection.anchor.getNode().getTopLevelElement();
|
|
238
|
-
const innerEnd = focusOuter?.getPreviousSibling() ?? selection.focus.getNode().getTopLevelElement();
|
|
239
|
-
if (!innerStart || !innerEnd) return
|
|
240
|
-
|
|
241
|
-
$setSelectionFromCaretRange($getCaretRange(
|
|
242
|
-
$normalizeCaret($getChildCaret(innerStart, "next")),
|
|
243
|
-
$getCaretInDirection(
|
|
244
|
-
$normalizeCaret($getChildCaret(innerEnd, "previous")),
|
|
245
|
-
"next",
|
|
246
|
-
),
|
|
247
|
-
));
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function $getCaretAtLineBreakBoundary(caret, skipInwardEdge = false) {
|
|
251
|
-
const paragraph = caret.origin.getTopLevelElement();
|
|
252
|
-
if (!paragraph || !$isParagraphNode(paragraph)) return null
|
|
253
|
-
|
|
254
|
-
const lineBreak = (skipInwardEdge ? null : $inwardEdgeLineBreak(caret, paragraph))
|
|
255
|
-
?? $outwardLineBreak(caret, paragraph);
|
|
256
|
-
|
|
257
|
-
return lineBreak ? $getSiblingCaret(lineBreak, caret.direction) : null
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Prefer a <br> the cursor is sitting flush against, except when a further <br>
|
|
261
|
-
// also exists outward — that one is the real paragraph break for this side.
|
|
262
|
-
function $inwardEdgeLineBreak(caret, paragraph) {
|
|
263
|
-
let candidateCaret;
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
($isChildCaret(caret) && caret.origin.is(paragraph)) ||
|
|
267
|
-
($isTextPointCaret(caret) && $isExtendableTextPointCaret(caret.getFlipped()))
|
|
268
|
-
) {
|
|
269
|
-
candidateCaret = null;
|
|
270
|
-
} else if ($isSiblingCaret(caret) && caret.getParentAtCaret().is(paragraph)) {
|
|
271
|
-
candidateCaret = caret;
|
|
272
|
-
} else {
|
|
273
|
-
const childCaret = $paragraphChildCaretAtInwardEdge(caret, paragraph);
|
|
274
|
-
candidateCaret = childCaret ? $rewindSiblingCaret(childCaret) : null;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (candidateCaret && $isLineBreakNode(candidateCaret.origin)) {
|
|
278
|
-
return $candidateUnlessShadowed(candidateCaret)
|
|
279
|
-
} else {
|
|
280
|
-
return null
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function $candidateUnlessShadowed(candidateCaret) {
|
|
285
|
-
const outward = candidateCaret.getNodeAtCaret();
|
|
286
|
-
return $isLineBreakNode(outward) ? null : candidateCaret.origin
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function $outwardLineBreak(caret, paragraph) {
|
|
290
|
-
const startCaret = $outwardWalkStartCaret(caret, paragraph);
|
|
291
|
-
if (!startCaret) return null
|
|
292
|
-
|
|
293
|
-
for (const { origin } of startCaret) {
|
|
294
|
-
if (!origin.getParent().is(paragraph)) break
|
|
295
|
-
if ($isLineBreakNode(origin)) return origin
|
|
296
|
-
}
|
|
297
|
-
return null
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function $outwardWalkStartCaret(caret, paragraph) {
|
|
301
|
-
if (caret.getParentAtCaret().is(paragraph)) {
|
|
302
|
-
return caret
|
|
303
|
-
} else {
|
|
304
|
-
return $paragraphChildCaretContaining(caret, paragraph)
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function $paragraphChildCaretContaining(caret, paragraph) {
|
|
309
|
-
let cursor = caret.getSiblingCaret();
|
|
310
|
-
while (cursor && !cursor.origin.getParent()?.is(paragraph)) {
|
|
311
|
-
cursor = cursor.getParentCaret();
|
|
312
|
-
}
|
|
313
|
-
return cursor?.origin.getParent()?.is(paragraph) ? cursor : null
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Only succeeds when the cursor is flush against the inward edge of every
|
|
317
|
-
// ancestor between itself and the paragraph child.
|
|
318
|
-
function $paragraphChildCaretAtInwardEdge(caret, paragraph) {
|
|
319
|
-
let cursor = caret.getSiblingCaret();
|
|
320
|
-
while (cursor && !cursor.origin.getParent()?.is(paragraph)) {
|
|
321
|
-
if (cursor.getNodeAtCaret()) return null
|
|
322
|
-
cursor = cursor.getParentCaret();
|
|
323
|
-
}
|
|
324
|
-
return cursor?.origin.getParent()?.is(paragraph) ? cursor : null
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
function $splitAroundLineBreak(lineBreakCaret) {
|
|
328
|
-
let outer = null;
|
|
329
|
-
|
|
330
|
-
if (lineBreakCaret.getNodeAtCaret() === null) {
|
|
331
|
-
lineBreakCaret.origin.remove();
|
|
332
|
-
} else {
|
|
333
|
-
const lineBreak = lineBreakCaret.origin;
|
|
334
|
-
const splitCaret = $getCaretInDirection($rewindSiblingCaret(lineBreakCaret), "next");
|
|
335
|
-
|
|
336
|
-
$splitAtPointCaretNext(splitCaret);
|
|
337
|
-
outer = lineBreak.getTopLevelElement();
|
|
338
|
-
lineBreak.remove();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return outer
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
export { $containsRangeSelection, $createNodeSelectionWith, $expandSelectionToLineBreaksAndSplitAtEdges, $isAtNodeEdge, $isAtNodeStart, $isBlankNode, $isCursorOnLastLine, $isListItemStructurallyEmpty, $isSafeForRoot, $isShadowRoot, $makeSafeForRoot, $splitSelectedParagraphsAtInnerLineBreaks, $textBeforeOffset, $trimTrailingBlankNodes, extendConversion, extendTextNodeConversion, getListType, isEditorFocused };
|