lexxy 0.1.9.beta → 0.1.11.beta

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.
@@ -5091,6 +5091,17 @@ function getListType(node) {
5091
5091
  return null
5092
5092
  }
5093
5093
 
5094
+ function isPrintableCharacter(event) {
5095
+ // Ignore if modifier keys are pressed (except Shift for uppercase)
5096
+ if (event.ctrlKey || event.metaKey || event.altKey) return false
5097
+
5098
+ // Ignore special keys
5099
+ if (event.key.length > 1 && event.key !== 'Enter' && event.key !== 'Space') return false
5100
+
5101
+ // Accept single character keys (letters, numbers, punctuation)
5102
+ return event.key.length === 1
5103
+ }
5104
+
5094
5105
  class LexicalToolbarElement extends HTMLElement {
5095
5106
  constructor() {
5096
5107
  super();
@@ -5950,6 +5961,8 @@ class CommandDispatcher {
5950
5961
 
5951
5962
  dispatchInsertUnorderedList() {
5952
5963
  const selection = Nr();
5964
+ if (!selection) return;
5965
+
5953
5966
  const anchorNode = selection.anchor.getNode();
5954
5967
 
5955
5968
  if (this.selection.isInsideList && anchorNode && getListType(anchorNode) === "bullet") {
@@ -5961,6 +5974,8 @@ class CommandDispatcher {
5961
5974
 
5962
5975
  dispatchInsertOrderedList() {
5963
5976
  const selection = Nr();
5977
+ if (!selection) return;
5978
+
5964
5979
  const anchorNode = selection.anchor.getNode();
5965
5980
 
5966
5981
  if (this.selection.isInsideList && anchorNode && getListType(anchorNode) === "number") {
@@ -6133,11 +6148,14 @@ function nextFrame() {
6133
6148
  class Selection {
6134
6149
  constructor(editorElement) {
6135
6150
  this.editorElement = editorElement;
6151
+ this.editorContentElement = editorElement.editorContentElement;
6136
6152
  this.editor = this.editorElement.editor;
6137
6153
  this.previouslySelectedKeys = new Set();
6138
6154
 
6139
6155
  this.#listenForNodeSelections();
6140
6156
  this.#processSelectionChangeCommands();
6157
+ this.#handleInputWhenDecoratorNodesSelected();
6158
+ this.#containEditorFocus();
6141
6159
  }
6142
6160
 
6143
6161
  clear() {
@@ -6231,6 +6249,21 @@ class Selection {
6231
6249
  return this.#findNextSiblingUp(anchorNode)
6232
6250
  }
6233
6251
 
6252
+ get topLevelNodeAfterCursor() {
6253
+ const { anchorNode, offset } = this.#getCollapsedSelectionData();
6254
+ if (!anchorNode) return null
6255
+
6256
+ if (Qn(anchorNode)) {
6257
+ return this.#getNextNodeFromTextEnd(anchorNode)
6258
+ }
6259
+
6260
+ if (di(anchorNode)) {
6261
+ return this.#getNodeAfterElementNode(anchorNode, offset)
6262
+ }
6263
+
6264
+ return this.#findNextSiblingUp(anchorNode)
6265
+ }
6266
+
6234
6267
  get nodeBeforeCursor() {
6235
6268
  const { anchorNode, offset } = this.#getCollapsedSelectionData();
6236
6269
  if (!anchorNode) return null
@@ -6246,6 +6279,21 @@ class Selection {
6246
6279
  return this.#findPreviousSiblingUp(anchorNode)
6247
6280
  }
6248
6281
 
6282
+ get topLevelNodeBeforeCursor() {
6283
+ const { anchorNode, offset } = this.#getCollapsedSelectionData();
6284
+ if (!anchorNode) return null
6285
+
6286
+ if (Qn(anchorNode)) {
6287
+ return this.#getPreviousNodeFromTextStart(anchorNode)
6288
+ }
6289
+
6290
+ if (di(anchorNode)) {
6291
+ return this.#getNodeBeforeElementNode(anchorNode, offset)
6292
+ }
6293
+
6294
+ return this.#findPreviousSiblingUp(anchorNode)
6295
+ }
6296
+
6249
6297
  get #contents() {
6250
6298
  return this.editorElement.contents
6251
6299
  }
@@ -6266,9 +6314,9 @@ class Selection {
6266
6314
 
6267
6315
  #processSelectionChangeCommands() {
6268
6316
  this.editor.registerCommand(Te$1, this.#selectPreviousNode.bind(this), Ii);
6269
- this.editor.registerCommand(Ne$1, this.#selectPreviousNode.bind(this), Ii);
6270
6317
  this.editor.registerCommand(ve$1, this.#selectNextNode.bind(this), Ii);
6271
- this.editor.registerCommand(we$1, this.#selectNextNode.bind(this), Ii);
6318
+ this.editor.registerCommand(Ne$1, this.#selectPreviousTopLevelNode.bind(this), Ii);
6319
+ this.editor.registerCommand(we$1, this.#selectNextTopLevelNode.bind(this), Ii);
6272
6320
 
6273
6321
  this.editor.registerCommand(De$1, this.#deleteSelectedOrNext.bind(this), Ii);
6274
6322
  this.editor.registerCommand(Ae$1, this.#deletePreviousOrNext.bind(this), Ii);
@@ -6299,6 +6347,93 @@ class Selection {
6299
6347
  });
6300
6348
  }
6301
6349
 
6350
+ // In Safari, when the only node in the document is an attachment, it won't let you enter text
6351
+ // before/below it. There is probably a better fix here, but this workaround solves the problem until
6352
+ // we find it.
6353
+ #handleInputWhenDecoratorNodesSelected() {
6354
+ this.editor.getRootElement().addEventListener("keydown", (event) => {
6355
+ if (isPrintableCharacter(event)) {
6356
+ this.editor.update(() => {
6357
+ const selection = Nr();
6358
+
6359
+ if (cr(selection) && selection.isCollapsed()) {
6360
+ const anchorNode = selection.anchor.getNode();
6361
+ const offset = selection.anchor.offset;
6362
+
6363
+ const nodeBefore = this.#getNodeBeforePosition(anchorNode, offset);
6364
+ const nodeAfter = this.#getNodeAfterPosition(anchorNode, offset);
6365
+
6366
+ if (nodeBefore instanceof gi && !nodeBefore.isInline()) {
6367
+ event.preventDefault();
6368
+ this.#contents.createParagraphAfterNode(nodeBefore, event.key);
6369
+ return
6370
+ } else if (nodeAfter instanceof gi && !nodeAfter.isInline()) {
6371
+ event.preventDefault();
6372
+ this.#contents.createParagraphBeforeNode(nodeAfter, event.key);
6373
+ return
6374
+ }
6375
+ }
6376
+ });
6377
+ }
6378
+ }, true);
6379
+ }
6380
+
6381
+ #getNodeBeforePosition(node, offset) {
6382
+ if (Qn(node) && offset === 0) {
6383
+ return node.getPreviousSibling()
6384
+ }
6385
+ if (di(node) && offset > 0) {
6386
+ return node.getChildAtIndex(offset - 1)
6387
+ }
6388
+ return null
6389
+ }
6390
+
6391
+ #getNodeAfterPosition(node, offset) {
6392
+ if (Qn(node) && offset === node.getTextContentSize()) {
6393
+ return node.getNextSibling()
6394
+ }
6395
+ if (di(node)) {
6396
+ return node.getChildAtIndex(offset)
6397
+ }
6398
+ return null
6399
+ }
6400
+
6401
+ #containEditorFocus() {
6402
+ // Workaround for a bizarre Chrome bug where the cursor abandons the editor to focus on not-focusable elements
6403
+ // above when navigating UP/DOWN when Lexical shows its fake cursor on custom decorator nodes.
6404
+ this.editorContentElement.addEventListener("keydown", (event) => {
6405
+ if (event.key === "ArrowUp") {
6406
+ const lexicalCursor = this.editor.getRootElement().querySelector('[data-lexical-cursor]');
6407
+
6408
+ if (lexicalCursor) {
6409
+ let currentElement = lexicalCursor.previousElementSibling;
6410
+ while (currentElement && currentElement.hasAttribute('data-lexical-cursor')) {
6411
+ currentElement = currentElement.previousElementSibling;
6412
+ }
6413
+
6414
+ if (!currentElement) {
6415
+ event.preventDefault();
6416
+ }
6417
+ }
6418
+ }
6419
+
6420
+ if (event.key === "ArrowDown") {
6421
+ const lexicalCursor = this.editor.getRootElement().querySelector('[data-lexical-cursor]');
6422
+
6423
+ if (lexicalCursor) {
6424
+ let currentElement = lexicalCursor.nextElementSibling;
6425
+ while (currentElement && currentElement.hasAttribute('data-lexical-cursor')) {
6426
+ currentElement = currentElement.nextElementSibling;
6427
+ }
6428
+
6429
+ if (!currentElement) {
6430
+ event.preventDefault();
6431
+ }
6432
+ }
6433
+ }
6434
+ }, true);
6435
+ }
6436
+
6302
6437
  #syncSelectedClasses() {
6303
6438
  this.#clearPreviouslyHighlightedItems();
6304
6439
  this.#highlightNewItems();
@@ -6341,6 +6476,22 @@ class Selection {
6341
6476
  }
6342
6477
  }
6343
6478
 
6479
+ async #selectPreviousTopLevelNode() {
6480
+ if (this.current) {
6481
+ await this.#withCurrentNode((currentNode) => currentNode.selectPrevious());
6482
+ } else {
6483
+ this.#selectInLexical(this.topLevelNodeBeforeCursor);
6484
+ }
6485
+ }
6486
+
6487
+ async #selectNextTopLevelNode() {
6488
+ if (this.current) {
6489
+ await this.#withCurrentNode((currentNode) => currentNode.selectNext(0, 0));
6490
+ } else {
6491
+ this.#selectInLexical(this.topLevelNodeAfterCursor);
6492
+ }
6493
+ }
6494
+
6344
6495
  async #withCurrentNode(fn) {
6345
6496
  await nextFrame();
6346
6497
  if (this.current) {
@@ -6589,6 +6740,105 @@ class Selection {
6589
6740
  }
6590
6741
  }
6591
6742
 
6743
+ class CustomActionTextAttachmentNode extends gi {
6744
+ static getType() {
6745
+ return "custom_action_text_attachment"
6746
+ }
6747
+
6748
+ static clone(node) {
6749
+ return new CustomActionTextAttachmentNode({ ...node }, node.__key)
6750
+ }
6751
+
6752
+ static importJSON(serializedNode) {
6753
+ return new CustomActionTextAttachmentNode({ ...serializedNode })
6754
+ }
6755
+
6756
+ static importDOM() {
6757
+ return {
6758
+ "action-text-attachment": (attachment) => {
6759
+ const content = attachment.getAttribute("content");
6760
+ if (!attachment.getAttribute("content")) {
6761
+ return null
6762
+ }
6763
+
6764
+ return {
6765
+ conversion: () => {
6766
+ // Preserve initial space if present since Lexical removes it
6767
+ const nodes = [];
6768
+ const previousSibling = attachment.previousSibling;
6769
+ if (previousSibling && previousSibling.nodeType === Node.TEXT_NODE && /\s$/.test(previousSibling.textContent)) {
6770
+ nodes.push(Xn(" "));
6771
+ }
6772
+
6773
+ nodes.push(new CustomActionTextAttachmentNode({
6774
+ sgid: attachment.getAttribute("sgid"),
6775
+ innerHtml: JSON.parse(content),
6776
+ contentType: attachment.getAttribute("content-type")
6777
+ }));
6778
+
6779
+ nodes.push(Xn(" "));
6780
+
6781
+ return { node: nodes }
6782
+ },
6783
+ priority: 2
6784
+ }
6785
+ }
6786
+ }
6787
+ }
6788
+
6789
+ constructor({ sgid, contentType, innerHtml }, key) {
6790
+ super(key);
6791
+
6792
+ this.sgid = sgid;
6793
+ this.contentType = contentType || "application/vnd.actiontext.unknown";
6794
+ this.innerHtml = innerHtml;
6795
+ }
6796
+
6797
+ createDOM() {
6798
+ const figure = createElement("action-text-attachment", { "content-type": this.contentType, "data-lexxy-decorator": true });
6799
+
6800
+ figure.addEventListener("click", (event) => {
6801
+ dispatchCustomEvent(figure, "lexxy:internal:select-node", { key: this.getKey() });
6802
+ });
6803
+
6804
+ figure.insertAdjacentHTML("beforeend", this.innerHtml);
6805
+
6806
+ return figure
6807
+ }
6808
+
6809
+ updateDOM() {
6810
+ return true
6811
+ }
6812
+
6813
+ isInline() {
6814
+ return true
6815
+ }
6816
+
6817
+ exportDOM() {
6818
+ const attachment = createElement("action-text-attachment", {
6819
+ sgid: this.sgid,
6820
+ content: JSON.stringify(this.innerHtml),
6821
+ "content-type": this.contentType
6822
+ });
6823
+
6824
+ return { element: attachment }
6825
+ }
6826
+
6827
+ exportJSON() {
6828
+ return {
6829
+ type: "custom_action_text_attachment",
6830
+ version: 1,
6831
+ sgid: this.sgid,
6832
+ contentType: this.contentType,
6833
+ innerHtml: this.innerHtml
6834
+ }
6835
+ }
6836
+
6837
+ decorate() {
6838
+ return null
6839
+ }
6840
+ }
6841
+
6592
6842
  class Contents {
6593
6843
  constructor(editorElement) {
6594
6844
  this.editorElement = editorElement;
@@ -6707,6 +6957,24 @@ class Contents {
6707
6957
  });
6708
6958
  }
6709
6959
 
6960
+ createLink(url) {
6961
+ let linkNodeKey = null;
6962
+
6963
+ this.editor.update(() => {
6964
+ const textNode = Xn(url);
6965
+ const linkNode = d$1(url);
6966
+ linkNode.append(textNode);
6967
+
6968
+ const selection = Nr();
6969
+ if (cr(selection)) {
6970
+ selection.insertNodes([linkNode]);
6971
+ linkNodeKey = linkNode.getKey();
6972
+ }
6973
+ });
6974
+
6975
+ return linkNodeKey
6976
+ }
6977
+
6710
6978
  createLinkWithSelectedText(url) {
6711
6979
  if (!this.hasSelectedText()) return
6712
6980
 
@@ -6778,6 +7046,30 @@ class Contents {
6778
7046
  });
6779
7047
  }
6780
7048
 
7049
+ createParagraphAfterNode(node, text) {
7050
+ const newParagraph = Pi();
7051
+ node.insertAfter(newParagraph);
7052
+ newParagraph.selectStart();
7053
+
7054
+ // Insert the typed text
7055
+ if (text) {
7056
+ newParagraph.append(Xn(text));
7057
+ newParagraph.select(1, 1); // Place cursor after the text
7058
+ }
7059
+ }
7060
+
7061
+ createParagraphBeforeNode(node, text) {
7062
+ const newParagraph = Pi();
7063
+ node.insertBefore(newParagraph);
7064
+ newParagraph.selectStart();
7065
+
7066
+ // Insert the typed text
7067
+ if (text) {
7068
+ newParagraph.append(Xn(text));
7069
+ newParagraph.select(1, 1); // Place cursor after the text
7070
+ }
7071
+ }
7072
+
6781
7073
  uploadFile(file) {
6782
7074
  if (!this.editorElement.supportsAttachments) {
6783
7075
  console.warn("This editor does not supports attachments (it's configured with [attachments=false])");
@@ -6812,26 +7104,76 @@ class Contents {
6812
7104
  deleteSelectedNodes() {
6813
7105
  this.editor.update(() => {
6814
7106
  if (ur(this.#selection.current)) {
6815
- let nodesWereRemoved = false;
6816
- this.#selection.current.getNodes().forEach((node) => {
7107
+ const nodesToRemove = this.#selection.current.getNodes();
7108
+ if (nodesToRemove.length === 0) return
7109
+
7110
+ // Use splice() instead of node.remove() for proper removal and
7111
+ // reconciliation. Would have issues with removing unintended decorator nodes
7112
+ // with node.remove()
7113
+ nodesToRemove.forEach((node) => {
6817
7114
  const parent = node.getParent();
7115
+ if (!di(parent)) return
6818
7116
 
6819
- node.remove();
7117
+ const children = parent.getChildren();
7118
+ const index = children.indexOf(node);
6820
7119
 
6821
- if (parent.getType() === "root" && parent.getChildrenSize() === 0) {
6822
- parent.append(Pi());
7120
+ if (index >= 0) {
7121
+ parent.splice(index, 1, []);
6823
7122
  }
6824
-
6825
- nodesWereRemoved = true;
6826
7123
  });
6827
7124
 
6828
- if (nodesWereRemoved) {
6829
- this.#selection.clear();
6830
- this.editor.focus();
7125
+ // Check if root is empty after all removals
7126
+ const root = ps();
7127
+ if (root.getChildrenSize() === 0) {
7128
+ root.append(Pi());
7129
+ }
6831
7130
 
6832
- return true
7131
+ this.#selection.clear();
7132
+ this.editor.focus();
7133
+
7134
+ return true
7135
+ }
7136
+ });
7137
+ }
7138
+
7139
+ replaceNodeWithHTML(nodeKey, html, options = {}) {
7140
+ this.editor.update(() => {
7141
+ const node = us(nodeKey);
7142
+ if (!node) return
7143
+
7144
+ const selection = Nr();
7145
+ let wasSelected = false;
7146
+
7147
+ if (cr(selection)) {
7148
+ const selectedNodes = selection.getNodes();
7149
+ wasSelected = selectedNodes.includes(node) || selectedNodes.some(n => n.getParent() === node);
7150
+
7151
+ if (wasSelected) {
7152
+ ms(null);
6833
7153
  }
6834
7154
  }
7155
+
7156
+ const replacementNode = options.attachment ? this.#createCustomAttachmentNodeWithHtml(html, options.attachment) : this.#createHtmlNodeWith(html);
7157
+ node.replace(replacementNode);
7158
+
7159
+ if (wasSelected) {
7160
+ replacementNode.selectEnd();
7161
+ }
7162
+ });
7163
+ }
7164
+
7165
+ insertHTMLBelowNode(nodeKey, html, options = {}) {
7166
+ this.editor.update(() => {
7167
+ const node = us(nodeKey);
7168
+ if (!node) return
7169
+
7170
+ let previousNode = node;
7171
+ try {
7172
+ previousNode = node.getTopLevelElementOrThrow();
7173
+ } catch {}
7174
+
7175
+ const newNode = options.attachment ? this.#createCustomAttachmentNodeWithHtml(html, options.attachment) : this.#createHtmlNodeWith(html);
7176
+ previousNode.insertAfter(newNode);
6835
7177
  });
6836
7178
  }
6837
7179
 
@@ -7077,6 +7419,21 @@ class Contents {
7077
7419
  }
7078
7420
  }
7079
7421
 
7422
+ #createCustomAttachmentNodeWithHtml(html, options = {}) {
7423
+ const attachmentConfig = typeof options === 'object' ? options : {};
7424
+
7425
+ return new CustomActionTextAttachmentNode({
7426
+ sgid: attachmentConfig.sgid || null,
7427
+ contentType: "text/html",
7428
+ innerHtml: html
7429
+ })
7430
+ }
7431
+
7432
+ #createHtmlNodeWith(html) {
7433
+ const htmlNodes = h$3(this.editor, parseHtml(html));
7434
+ return htmlNodes[0] || Pi()
7435
+ }
7436
+
7080
7437
  #shouldUploadFile(file) {
7081
7438
  return dispatch(this.editorElement, 'lexxy:file-accept', { file }, true)
7082
7439
  }
@@ -7204,12 +7561,27 @@ class Clipboard {
7204
7561
  item.getAsString((text) => {
7205
7562
  if (isUrl(text) && this.contents.hasSelectedText()) {
7206
7563
  this.contents.createLinkWithSelectedText(text);
7564
+ } else if (isUrl(text)) {
7565
+ const nodeKey = this.contents.createLink(text);
7566
+ this.#dispatchLinkInsertEvent(nodeKey, { url: text });
7207
7567
  } else {
7208
7568
  this.#pasteMarkdown(text);
7209
7569
  }
7210
7570
  });
7211
7571
  }
7212
7572
 
7573
+ #dispatchLinkInsertEvent(nodeKey, payload) {
7574
+ const linkManipulationMethods = {
7575
+ replaceLinkWith: (html, options) => this.contents.replaceNodeWithHTML(nodeKey, html, options),
7576
+ insertBelowLink: (html, options) => this.contents.insertHTMLBelowNode(nodeKey, html, options)
7577
+ };
7578
+
7579
+ dispatch(this.editorElement, "lexxy:insert-link", {
7580
+ ...payload,
7581
+ ...linkManipulationMethods
7582
+ });
7583
+ }
7584
+
7213
7585
  #pasteMarkdown(text) {
7214
7586
  const html = d(text);
7215
7587
  this.contents.insertHtml(html);
@@ -7242,113 +7614,15 @@ class Clipboard {
7242
7614
  }
7243
7615
  }
7244
7616
 
7245
- class CustomActionTextAttachmentNode extends gi {
7246
- static getType() {
7247
- return "custom_action_text_attachment"
7248
- }
7249
-
7250
- static clone(node) {
7251
- return new CustomActionTextAttachmentNode({ ...node }, node.__key)
7252
- }
7253
-
7254
- static importJSON(serializedNode) {
7255
- return new CustomActionTextAttachmentNode({ ...serializedNode })
7256
- }
7257
-
7258
- static importDOM() {
7259
- return {
7260
- "action-text-attachment": (attachment) => {
7261
- const content = attachment.getAttribute("content");
7262
- if (!attachment.getAttribute("content")) {
7263
- return null
7264
- }
7265
-
7266
- return {
7267
- conversion: () => {
7268
- // Preserve initial space if present since Lexical removes it
7269
- const nodes = [];
7270
- const previousSibling = attachment.previousSibling;
7271
- if (previousSibling && previousSibling.nodeType === Node.TEXT_NODE && /\s$/.test(previousSibling.textContent)) {
7272
- nodes.push(Xn(" "));
7273
- }
7274
-
7275
- nodes.push(new CustomActionTextAttachmentNode({
7276
- sgid: attachment.getAttribute("sgid"),
7277
- innerHtml: JSON.parse(content),
7278
- contentType: attachment.getAttribute("content-type")
7279
- }));
7280
-
7281
- nodes.push(Xn(" "));
7282
-
7283
- return { node: nodes }
7284
- },
7285
- priority: 2
7286
- }
7287
- }
7288
- }
7289
- }
7290
-
7291
- constructor({ sgid, contentType, innerHtml }, key) {
7292
- super(key);
7293
-
7294
- this.sgid = sgid;
7295
- this.contentType = contentType || "application/vnd.actiontext.unknown";
7296
- this.innerHtml = innerHtml;
7297
- }
7298
-
7299
- createDOM() {
7300
- const figure = createElement("action-text-attachment", { "content-type": this.contentType, "data-lexxy-decorator": true });
7301
-
7302
- figure.addEventListener("click", (event) => {
7303
- dispatchCustomEvent(figure, "lexxy:internal:select-node", { key: this.getKey() });
7304
- });
7305
-
7306
- figure.insertAdjacentHTML("beforeend", this.innerHtml);
7307
-
7308
- return figure
7309
- }
7310
-
7311
- updateDOM() {
7312
- return true
7313
- }
7314
-
7315
- isInline() {
7316
- return true
7317
- }
7318
-
7319
- exportDOM() {
7320
- const attachment = createElement("action-text-attachment", {
7321
- sgid: this.sgid,
7322
- content: JSON.stringify(this.innerHtml),
7323
- "content-type": this.contentType
7324
- });
7325
-
7326
- return { element: attachment }
7327
- }
7328
-
7329
- exportJSON() {
7330
- return {
7331
- type: "custom_action_text_attachment",
7332
- version: 1,
7333
- sgid: this.sgid,
7334
- contentType: this.contentType,
7335
- innerHtml: this.innerHtml
7336
- }
7337
- }
7338
-
7339
- decorate() {
7340
- return null
7341
- }
7342
- }
7343
-
7344
7617
  class LexicalEditorElement extends HTMLElement {
7345
7618
  static formAssociated = true
7346
7619
  static debug = true
7347
7620
  static commands = [ "bold", "italic", "" ]
7348
7621
 
7349
- static observedAttributes = [ "connected" ]
7622
+ static observedAttributes = [ "connected", "required" ]
7350
7623
 
7351
7624
  #initialValue = ""
7625
+ #validationTextArea = document.createElement("textarea")
7352
7626
 
7353
7627
  constructor() {
7354
7628
  super();
@@ -7381,6 +7655,11 @@ class LexicalEditorElement extends HTMLElement {
7381
7655
  if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
7382
7656
  requestAnimationFrame(() => this.#reconnect());
7383
7657
  }
7658
+
7659
+ if (name === "required" && this.isConnected) {
7660
+ this.#validationTextArea.required = this.hasAttribute("required");
7661
+ this.#setValidity();
7662
+ }
7384
7663
  }
7385
7664
 
7386
7665
  formResetCallback() {
@@ -7434,7 +7713,7 @@ class LexicalEditorElement extends HTMLElement {
7434
7713
  Ys(Mi);
7435
7714
  const root = ps();
7436
7715
  root.clear();
7437
- root.append(...this.#parseHtmlIntoLexicalNodes(html));
7716
+ if (html !== "") root.append(...this.#parseHtmlIntoLexicalNodes(html));
7438
7717
  root.select();
7439
7718
 
7440
7719
  this.#toggleEmptyStatus();
@@ -7547,6 +7826,7 @@ class LexicalEditorElement extends HTMLElement {
7547
7826
 
7548
7827
  this.internals.setFormValue(html);
7549
7828
  this._internalFormValue = html;
7829
+ this.#validationTextArea.value = this.#isEmpty ? "" : html;
7550
7830
 
7551
7831
  if (changed) {
7552
7832
  dispatch(this, "lexxy:change");
@@ -7575,6 +7855,7 @@ class LexicalEditorElement extends HTMLElement {
7575
7855
  this.cachedValue = null;
7576
7856
  this.#internalFormValue = this.value;
7577
7857
  this.#toggleEmptyStatus();
7858
+ this.#setValidity();
7578
7859
  }));
7579
7860
  }
7580
7861
 
@@ -7681,6 +7962,14 @@ class LexicalEditorElement extends HTMLElement {
7681
7962
  return !this.editorContentElement.textContent.trim() && !containsVisuallyRelevantChildren(this.editorContentElement)
7682
7963
  }
7683
7964
 
7965
+ #setValidity() {
7966
+ if (this.#validationTextArea.validity.valid) {
7967
+ this.internals.setValidity({});
7968
+ } else {
7969
+ this.internals.setValidity(this.#validationTextArea.validity, this.#validationTextArea.validationMessage, this.editorContentElement);
7970
+ }
7971
+ }
7972
+
7684
7973
  #reset() {
7685
7974
  this.#unregisterHandlers();
7686
7975
 
Binary file
Binary file