lexxy 0.9.0.beta → 0.9.2.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.
@@ -4952,9 +4952,22 @@ class LexicalToolbarElement extends HTMLElement {
4952
4952
  }
4953
4953
 
4954
4954
  disconnectedCallback() {
4955
+ this.dispose();
4956
+ }
4957
+
4958
+ dispose() {
4955
4959
  this.#uninstallResizeObserver();
4960
+ this.#unbindButtons();
4956
4961
  this.#unbindHotkeys();
4957
4962
  this.#unbindFocusListeners();
4963
+ this.unregisterSelectionListener?.();
4964
+ this.unregisterHistoryListener?.();
4965
+
4966
+ this.editorElement = null;
4967
+ this.editor = null;
4968
+ this.selection = null;
4969
+
4970
+ this.#createEditorPromise();
4958
4971
  }
4959
4972
 
4960
4973
  attributeChangedCallback(name, oldValue, newValue) {
@@ -4998,10 +5011,12 @@ class LexicalToolbarElement extends HTMLElement {
4998
5011
  this.connectedCallback();
4999
5012
  }
5000
5013
 
5001
- #createEditorPromise() {
5014
+ async #createEditorPromise() {
5002
5015
  this.editorPromise = new Promise((resolve) => {
5003
5016
  this.resolveEditorPromise = resolve;
5004
5017
  });
5018
+
5019
+ this.editorElement = await this.editorPromise;
5005
5020
  }
5006
5021
 
5007
5022
  #installResizeObserver() {
@@ -5017,10 +5032,14 @@ class LexicalToolbarElement extends HTMLElement {
5017
5032
  }
5018
5033
 
5019
5034
  #bindButtons() {
5020
- this.addEventListener("click", this.#handleButtonClicked.bind(this));
5035
+ this.addEventListener("click", this.#handleButtonClicked);
5036
+ }
5037
+
5038
+ #unbindButtons() {
5039
+ this.removeEventListener("click", this.#handleButtonClicked);
5021
5040
  }
5022
5041
 
5023
- #handleButtonClicked(event) {
5042
+ #handleButtonClicked = (event) => {
5024
5043
  this.#handleTargetClicked(event, "[data-command]", this.#dispatchButtonCommand.bind(this));
5025
5044
  }
5026
5045
 
@@ -5080,8 +5099,8 @@ class LexicalToolbarElement extends HTMLElement {
5080
5099
  }
5081
5100
 
5082
5101
  #unbindFocusListeners() {
5083
- this.editorElement.removeEventListener("lexxy:focus", this.#handleEditorFocus);
5084
- this.editorElement.removeEventListener("lexxy:blur", this.#handleEditorBlur);
5102
+ this.editorElement?.removeEventListener("lexxy:focus", this.#handleEditorFocus);
5103
+ this.editorElement?.removeEventListener("lexxy:blur", this.#handleEditorBlur);
5085
5104
  this.removeEventListener("keydown", this.#handleKeydown);
5086
5105
  }
5087
5106
 
@@ -5105,7 +5124,7 @@ class LexicalToolbarElement extends HTMLElement {
5105
5124
  }
5106
5125
 
5107
5126
  #monitorSelectionChanges() {
5108
- this.editor.registerUpdateListener(() => {
5127
+ this.unregisterSelectionListener = this.editor.registerUpdateListener(() => {
5109
5128
  this.editor.getEditorState().read(() => {
5110
5129
  this.#updateButtonStates();
5111
5130
  this.#closeDropdowns();
@@ -5114,7 +5133,7 @@ class LexicalToolbarElement extends HTMLElement {
5114
5133
  }
5115
5134
 
5116
5135
  #monitorHistoryChanges() {
5117
- this.editor.registerUpdateListener(() => {
5136
+ this.unregisterHistoryListener = this.editor.registerUpdateListener(() => {
5118
5137
  this.#updateUndoRedoButtonStates();
5119
5138
  });
5120
5139
  }
@@ -5266,13 +5285,13 @@ class LexicalToolbarElement extends HTMLElement {
5266
5285
 
5267
5286
  static get defaultTemplate() {
5268
5287
  return `
5269
- <button class="lexxy-editor__toolbar-button" type="button" name="image" data-command="uploadAttachments" data-prevent-overflow="true" title="Add images">
5270
- ${ToolbarIcons.image}
5271
- </button>
5288
+ <button class="lexxy-editor__toolbar-button" type="button" name="image" data-command="uploadImage" data-prevent-overflow="true" title="Add images and video">
5289
+ ${ToolbarIcons.image}
5290
+ </button>
5272
5291
 
5273
- <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="file" data-command="uploadAttachments" title="Upload files">
5274
- ${ToolbarIcons.attachment}
5275
- </button>
5292
+ <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="file" data-command="uploadFile" title="Upload files">
5293
+ ${ToolbarIcons.attachment}
5294
+ </button>
5276
5295
 
5277
5296
  <button class="lexxy-editor__toolbar-button" type="button" name="bold" data-command="bold" title="Bold">
5278
5297
  ${ToolbarIcons.bold}
@@ -5296,10 +5315,10 @@ class LexicalToolbarElement extends HTMLElement {
5296
5315
  <button type="button" name="heading-medium" data-command="setFormatHeadingMedium" title="Medium heading">
5297
5316
  ${ToolbarIcons.h3} <span>Medium Heading</span>
5298
5317
  </button>
5299
- <button type="button" name="heading-small" data-command="setFormatHeadingSmall" title="Small heading">
5318
+ <button class="lexxy-editor__toolbar-group-end" type="button" name="heading-small" data-command="setFormatHeadingSmall" title="Small heading">
5300
5319
  ${ToolbarIcons.h4} <span>Small Heading</span>
5301
5320
  </button>
5302
- <div class="separator" role="separator"></div>
5321
+ <div class="lexxy-editor__toolbar-separator" role="separator"></div>
5303
5322
  <button type="button" name="strikethrough" data-command="strikethrough" title="Strikethrough">
5304
5323
  ${ToolbarIcons.strikethrough} <span>Strikethrough</span>
5305
5324
  </button>
@@ -5309,21 +5328,6 @@ class LexicalToolbarElement extends HTMLElement {
5309
5328
  </div>
5310
5329
  </details>
5311
5330
 
5312
-
5313
- <details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-dropdown--chevron" name="lexxy-dropdown">
5314
- <summary class="lexxy-editor__toolbar-button" name="lists" title="Lists">
5315
- ${ToolbarIcons.ul}
5316
- </summary>
5317
- <div class="lexxy-editor__toolbar-dropdown-list">
5318
- <button type="button" name="unordered-list" data-command="insertUnorderedList" title="Bullet list">
5319
- ${ToolbarIcons.ul} <span>Bullets</span>
5320
- </button>
5321
- <button type="button" name="ordered-list" data-command="insertOrderedList" title="Numbered list">
5322
- ${ToolbarIcons.ol} <span>Numbers</span>
5323
- </button>
5324
- </div>
5325
- </details>
5326
-
5327
5331
  <details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-dropdown--chevron" name="lexxy-dropdown">
5328
5332
  <summary class="lexxy-editor__toolbar-button" name="highlight" title="Color highlight">
5329
5333
  ${ToolbarIcons.highlight}
@@ -5335,7 +5339,7 @@ class LexicalToolbarElement extends HTMLElement {
5335
5339
  </details>
5336
5340
 
5337
5341
  <details class="lexxy-editor__toolbar-dropdown" name="lexxy-dropdown">
5338
- <summary class="lexxy-editor__toolbar-button" name="link" title="Link" data-hotkey="cmd+k ctrl+k">
5342
+ <summary class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" name="link" title="Link" data-hotkey="cmd+k ctrl+k">
5339
5343
  ${ToolbarIcons.link}
5340
5344
  </summary>
5341
5345
  <lexxy-link-dropdown class="lexxy-editor__toolbar-dropdown-content">
@@ -5353,10 +5357,17 @@ class LexicalToolbarElement extends HTMLElement {
5353
5357
  ${ToolbarIcons.quote}
5354
5358
  </button>
5355
5359
 
5356
- <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="code" data-command="insertCodeBlock" title="Code">
5360
+ <button class="lexxy-editor__toolbar-button" type="button" name="code" data-command="insertCodeBlock" title="Code">
5357
5361
  ${ToolbarIcons.code}
5358
5362
  </button>
5359
5363
 
5364
+ <button class="lexxy-editor__toolbar-button" type="button" name="unordered-list" data-command="insertUnorderedList" title="Bullet list">
5365
+ ${ToolbarIcons.ul}
5366
+ </button>
5367
+ <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="ordered-list" data-command="insertOrderedList" title="Numbered list">
5368
+ ${ToolbarIcons.ol}
5369
+ </button>
5370
+
5360
5371
  <button class="lexxy-editor__toolbar-button" type="button" name="table" data-command="insertTable" title="Insert a table">
5361
5372
  ${ToolbarIcons.table}
5362
5373
  </button>
@@ -8240,7 +8251,8 @@ const COMMANDS = [
8240
8251
  "insertQuoteBlock",
8241
8252
  "insertCodeBlock",
8242
8253
  "insertHorizontalDivider",
8243
- "uploadAttachments",
8254
+ "uploadImage",
8255
+ "uploadFile",
8244
8256
 
8245
8257
  "insertTable",
8246
8258
 
@@ -8250,9 +8262,10 @@ const COMMANDS = [
8250
8262
 
8251
8263
  class CommandDispatcher {
8252
8264
  #selectionBeforeDrag = null
8265
+ #unregister = []
8253
8266
 
8254
8267
  static configureFor(editorElement) {
8255
- new CommandDispatcher(editorElement);
8268
+ return new CommandDispatcher(editorElement)
8256
8269
  }
8257
8270
 
8258
8271
  constructor(editorElement) {
@@ -8424,15 +8437,27 @@ class CommandDispatcher {
8424
8437
  this.contents.applyParagraphFormat();
8425
8438
  }
8426
8439
 
8427
- dispatchUploadAttachments() {
8428
- const input = createElement("input", {
8440
+ dispatchUploadImage() {
8441
+ this.#dispatchUploadAttachment("image/*,video/*");
8442
+ }
8443
+
8444
+ dispatchUploadFile() {
8445
+ this.#dispatchUploadAttachment();
8446
+ }
8447
+
8448
+ #dispatchUploadAttachment(accept = null) {
8449
+ const attributes = {
8429
8450
  type: "file",
8430
8451
  multiple: true,
8431
8452
  style: "display: none;",
8432
8453
  onchange: ({ target: { files } }) => {
8433
8454
  this.contents.uploadFiles(files, { selectLast: true });
8434
8455
  }
8435
- });
8456
+ };
8457
+
8458
+ if (accept) attributes.accept = accept;
8459
+
8460
+ const input = createElement("input", attributes);
8436
8461
 
8437
8462
  // Append and remove to make testable
8438
8463
  this.editorElement.appendChild(input);
@@ -8452,6 +8477,13 @@ class CommandDispatcher {
8452
8477
  this.editor.dispatchCommand(Ce$1, undefined);
8453
8478
  }
8454
8479
 
8480
+ dispose() {
8481
+ while (this.#unregister.length) {
8482
+ const unregister = this.#unregister.pop();
8483
+ unregister();
8484
+ }
8485
+ }
8486
+
8455
8487
  #registerCommands() {
8456
8488
  for (const command of COMMANDS) {
8457
8489
  const methodName = `dispatch${capitalize(command)}`;
@@ -8462,12 +8494,12 @@ class CommandDispatcher {
8462
8494
  }
8463
8495
 
8464
8496
  #registerCommandHandler(command, priority, handler) {
8465
- this.editor.registerCommand(command, handler, priority);
8497
+ this.#unregister.push(this.editor.registerCommand(command, handler, priority));
8466
8498
  }
8467
8499
 
8468
8500
  #registerKeyboardCommands() {
8469
- this.editor.registerCommand(ve$1, this.#handleArrowRightKey.bind(this), Gi);
8470
- this.editor.registerCommand(De$2, this.#handleTabKey.bind(this), Gi);
8501
+ this.#registerCommandHandler(ve$1, Gi, this.#handleArrowRightKey.bind(this));
8502
+ this.#registerCommandHandler(De$2, Gi, this.#handleTabKey.bind(this));
8471
8503
  }
8472
8504
 
8473
8505
  #handleArrowRightKey(event) {
@@ -8953,6 +8985,8 @@ function $isActionTextAttachmentNode(node) {
8953
8985
  }
8954
8986
 
8955
8987
  class Selection {
8988
+ #unregister = []
8989
+
8956
8990
  constructor(editorElement) {
8957
8991
  this.editorElement = editorElement;
8958
8992
  this.editorContentElement = editorElement.editorContentElement;
@@ -9209,6 +9243,18 @@ class Selection {
9209
9243
  return this.#findPreviousSiblingUp(anchorNode)
9210
9244
  }
9211
9245
 
9246
+ dispose() {
9247
+ this.editorElement = null;
9248
+ this.editorContentElement = null;
9249
+ this.editor = null;
9250
+ this.previouslySelectedKeys = null;
9251
+
9252
+ while (this.#unregister.length) {
9253
+ const unregister = this.#unregister.pop();
9254
+ unregister();
9255
+ }
9256
+ }
9257
+
9212
9258
  // When all inline code text is deleted, Lexical's selection retains the stale
9213
9259
  // code format flag. Verify the flag is backed by actual code-formatted content:
9214
9260
  // a code block ancestor or a text node that carries the code format.
@@ -9224,7 +9270,7 @@ class Selection {
9224
9270
  // detects that stale state and clears it so newly typed text won't be
9225
9271
  // code-formatted.
9226
9272
  #clearStaleInlineCodeFormat() {
9227
- this.editor.registerUpdateListener(({ editorState, tags }) => {
9273
+ this.#unregister.push(this.editor.registerUpdateListener(({ editorState, tags }) => {
9228
9274
  if (tags.has("history-merge") || tags.has("skip-dom-selection")) return
9229
9275
 
9230
9276
  let isStale = false;
@@ -9253,7 +9299,7 @@ class Selection {
9253
9299
  });
9254
9300
  }, 0);
9255
9301
  }
9256
- });
9302
+ }));
9257
9303
  }
9258
9304
 
9259
9305
  get #currentlySelectedKeys() {
@@ -9272,29 +9318,32 @@ class Selection {
9272
9318
  }
9273
9319
 
9274
9320
  #processSelectionChangeCommands() {
9275
- this.editor.registerCommand(ke$3, this.#selectPreviousNode.bind(this), Hi);
9276
- this.editor.registerCommand(ve$1, this.#selectNextNode.bind(this), Hi);
9277
- this.editor.registerCommand(be$2, this.#selectPreviousTopLevelNode.bind(this), Hi);
9278
- this.editor.registerCommand(we$1, this.#selectNextTopLevelNode.bind(this), Hi);
9321
+ this.#unregister.push(ec(
9322
+ this.editor.registerCommand(ke$3, this.#selectPreviousNode.bind(this), Hi),
9323
+ this.editor.registerCommand(ve$1, this.#selectNextNode.bind(this), Hi),
9324
+ this.editor.registerCommand(be$2, this.#selectPreviousTopLevelNode.bind(this), Hi),
9325
+ this.editor.registerCommand(we$1, this.#selectNextTopLevelNode.bind(this), Hi),
9279
9326
 
9280
- this.editor.registerCommand(ue$2, this.#selectDecoratorNodeBeforeDeletion.bind(this), Hi);
9327
+ this.editor.registerCommand(ue$2, this.#selectDecoratorNodeBeforeDeletion.bind(this), Hi),
9281
9328
 
9282
- this.editor.registerCommand(re$2, () => {
9283
- this.current = $r();
9284
- }, Hi);
9329
+ this.editor.registerCommand(re$2, () => {
9330
+ this.current = $r();
9331
+ }, Hi)
9332
+ ));
9285
9333
  }
9286
9334
 
9287
9335
  #listenForNodeSelections() {
9288
- this.editor.registerCommand(oe$4, ({ target }) => {
9336
+ this.#unregister.push(this.editor.registerCommand(oe$4, ({ target }) => {
9289
9337
  if (!As(target)) return false
9290
9338
 
9291
9339
  const targetNode = Do(target);
9292
9340
  return Li(targetNode) && this.#selectInLexical(targetNode)
9293
- }, Hi);
9341
+ }, Hi));
9294
9342
 
9295
- this.editor.getRootElement().addEventListener("lexxy:internal:move-to-next-line", (event) => {
9296
- this.#selectOrAppendNextLine();
9297
- });
9343
+ const moveNextLineHandler = () => this.#selectOrAppendNextLine();
9344
+ const rootElement = this.editor.getRootElement();
9345
+ rootElement.addEventListener("lexxy:internal:move-to-next-line", moveNextLineHandler);
9346
+ this.#unregister.push(() => rootElement.removeEventListener("lexxy:internal:move-to-next-line", moveNextLineHandler));
9298
9347
  }
9299
9348
 
9300
9349
  #containEditorFocus() {
@@ -10429,7 +10478,11 @@ class Contents {
10429
10478
  constructor(editorElement) {
10430
10479
  this.editorElement = editorElement;
10431
10480
  this.editor = editorElement.editor;
10481
+ }
10432
10482
 
10483
+ dispose() {
10484
+ this.editorElement = null;
10485
+ this.editor = null;
10433
10486
  }
10434
10487
 
10435
10488
  insertHtml(html, { tag } = {}) {
@@ -11532,11 +11585,12 @@ class TablesExtension extends LexxyExtension {
11532
11585
  ze$1
11533
11586
  ],
11534
11587
  register(editor) {
11588
+ Cn(editor);
11589
+
11535
11590
  return ec(
11536
11591
  // Register Lexical table plugins
11537
11592
  Kn(editor),
11538
11593
  An(editor, true),
11539
- Cn(editor),
11540
11594
 
11541
11595
  // Bug fix: Prevent hardcoded background color (Lexical #8089)
11542
11596
  editor.registerNodeTransform(Ke$1, (node) => {
@@ -12263,6 +12317,7 @@ class LexicalEditorElement extends HTMLElement {
12263
12317
 
12264
12318
  #initialValue = ""
12265
12319
  #validationTextArea = document.createElement("textarea")
12320
+ #disposables = []
12266
12321
 
12267
12322
  constructor() {
12268
12323
  super();
@@ -12276,12 +12331,19 @@ class LexicalEditorElement extends HTMLElement {
12276
12331
  this.extensions = new Extensions(this);
12277
12332
 
12278
12333
  this.editor = this.#createEditor();
12334
+ this.#disposables.push(this.editor);
12279
12335
 
12280
12336
  this.contents = new Contents(this);
12337
+ this.#disposables.push(this.contents);
12338
+
12281
12339
  this.selection = new Selection(this);
12340
+ this.#disposables.push(this.selection);
12341
+
12282
12342
  this.clipboard = new Clipboard(this);
12283
12343
 
12284
- CommandDispatcher.configureFor(this);
12344
+ const commandDispatcher = CommandDispatcher.configureFor(this);
12345
+ this.#disposables.push(commandDispatcher);
12346
+
12285
12347
  this.#initialize();
12286
12348
 
12287
12349
  requestAnimationFrame(() => dispatch(this, "lexxy:initialize"));
@@ -12334,7 +12396,7 @@ class LexicalEditorElement extends HTMLElement {
12334
12396
  get toolbarElement() {
12335
12397
  if (!this.#hasToolbar) return null
12336
12398
 
12337
- this.toolbar = this.toolbar || this.#findOrCreateDefaultToolbar();
12399
+ this.toolbar ??= this.#findOrCreateDefaultToolbar();
12338
12400
  return this.toolbar
12339
12401
  }
12340
12402
 
@@ -12470,6 +12532,7 @@ class LexicalEditorElement extends HTMLElement {
12470
12532
 
12471
12533
  #createEditor() {
12472
12534
  this.editorContentElement ||= this.#createEditorContentElement();
12535
+ this.appendChild(this.editorContentElement);
12473
12536
 
12474
12537
  const editor = Qt$2({
12475
12538
  name: "lexxy/core",
@@ -12519,7 +12582,6 @@ class LexicalEditorElement extends HTMLElement {
12519
12582
  });
12520
12583
  editorContentElement.id = `${this.id}-content`;
12521
12584
  this.#ariaAttributes.forEach(attribute => editorContentElement.setAttribute(attribute.name, attribute.value));
12522
- this.appendChild(editorContentElement);
12523
12585
 
12524
12586
  if (this.getAttribute("tabindex")) {
12525
12587
  editorContentElement.setAttribute("tabindex", this.getAttribute("tabindex"));
@@ -12595,36 +12657,48 @@ class LexicalEditorElement extends HTMLElement {
12595
12657
  }
12596
12658
 
12597
12659
  #registerComponents() {
12660
+ const registered = [];
12661
+
12598
12662
  if (this.supportsRichText) {
12599
- Jt$2(this.editor);
12600
- Le$2(this.editor);
12663
+ registered.push(
12664
+ Jt$2(this.editor),
12665
+ Le$2(this.editor)
12666
+ );
12601
12667
  this.#registerTableComponents();
12602
12668
  this.#registerCodeHiglightingComponents();
12603
12669
  if (this.supportsMarkdown) {
12604
- Yt$1(this.editor, Jt$1);
12605
- registerMarkdownLeadingTagHandler(this.editor, Jt$1);
12670
+ registered.push(
12671
+ Yt$1(this.editor, Jt$1),
12672
+ registerMarkdownLeadingTagHandler(this.editor, Jt$1)
12673
+ );
12606
12674
  }
12607
12675
  } else {
12608
- z$1(this.editor);
12676
+ registered.push(z$1(this.editor));
12609
12677
  }
12610
12678
  this.historyState = H();
12611
- E$1(this.editor, this.historyState, 20);
12679
+ registered.push(E$1(this.editor, this.historyState, 20));
12680
+
12681
+ this.#addUnregisterHandler(ec(...registered));
12612
12682
  }
12613
12683
 
12614
12684
  #registerTableComponents() {
12615
- this.tableTools = createElement("lexxy-table-tools");
12616
- this.append(this.tableTools);
12685
+ let tableTools = this.querySelector("lexxy-table-tools");
12686
+ tableTools ??= createElement("lexxy-table-tools");
12687
+ this.append(tableTools);
12688
+ this.#disposables.push(tableTools);
12617
12689
  }
12618
12690
 
12619
12691
  #registerCodeHiglightingComponents() {
12620
12692
  zt$2(this.editor);
12621
- this.codeLanguagePicker = createElement("lexxy-code-language-picker");
12622
- this.append(this.codeLanguagePicker);
12693
+ let codeLanguagePicker = this.querySelector("lexxy-code-language-picker");
12694
+ codeLanguagePicker ??= createElement("lexxy-code-language-picker");
12695
+ this.append(codeLanguagePicker);
12696
+ this.#disposables.push(codeLanguagePicker);
12623
12697
  }
12624
12698
 
12625
12699
  #handleEnter() {
12626
12700
  // We can't prevent these externally using regular keydown because Lexical handles it first.
12627
- this.editor.registerCommand(
12701
+ this.#addUnregisterHandler(this.editor.registerCommand(
12628
12702
  Ee$2,
12629
12703
  (event) => {
12630
12704
  // Prevent CTRL+ENTER
@@ -12642,12 +12716,17 @@ class LexicalEditorElement extends HTMLElement {
12642
12716
  return false
12643
12717
  },
12644
12718
  Gi
12645
- );
12719
+ ));
12646
12720
  }
12647
12721
 
12648
12722
  #registerFocusEvents() {
12649
12723
  this.addEventListener("focusin", this.#handleFocusIn);
12650
12724
  this.addEventListener("focusout", this.#handleFocusOut);
12725
+
12726
+ this.#addUnregisterHandler(() => {
12727
+ this.removeEventListener("focusin", this.#handleFocusIn);
12728
+ this.removeEventListener("focusout", this.#handleFocusOut);
12729
+ });
12651
12730
  }
12652
12731
 
12653
12732
  #handleFocusIn(event) {
@@ -12690,6 +12769,10 @@ class LexicalEditorElement extends HTMLElement {
12690
12769
  #attachToolbar() {
12691
12770
  if (this.#hasToolbar) {
12692
12771
  this.toolbarElement.setEditor(this);
12772
+ if (typeof this.toolbarElement.dispose === "function") {
12773
+ this.#disposables.push(this.toolbarElement);
12774
+ }
12775
+
12693
12776
  this.extensions.initializeToolbars();
12694
12777
  }
12695
12778
  }
@@ -12699,7 +12782,7 @@ class LexicalEditorElement extends HTMLElement {
12699
12782
  if (typeof toolbarConfig === "string") {
12700
12783
  return document.getElementById(toolbarConfig)
12701
12784
  } else {
12702
- return this.#createDefaultToolbar()
12785
+ return this.querySelector("lexxy-toolbar") ?? this.#createDefaultToolbar()
12703
12786
  }
12704
12787
  }
12705
12788
 
@@ -12729,34 +12812,22 @@ class LexicalEditorElement extends HTMLElement {
12729
12812
  }
12730
12813
 
12731
12814
  #reset() {
12732
- this.#unregisterHandlers();
12733
-
12734
- if (this.editorContentElement) {
12735
- this.editorContentElement.remove();
12736
- this.editorContentElement = null;
12737
- }
12738
-
12739
- this.contents = null;
12740
- this.editor = null;
12815
+ this.#dispose();
12816
+ this.editorContentElement?.remove();
12817
+ this.editorContentElement = null;
12741
12818
 
12742
- if (this.toolbar) {
12743
- if (!this.getAttribute("toolbar")) { this.toolbar.remove(); }
12744
- this.toolbar = null;
12745
- }
12819
+ // Prevents issues with turbo morphing receiving an empty <lexxy-editor> which wipes
12820
+ // out the DOM for the tools, and the old toolbar reference will cause issues
12821
+ this.toolbar = null;
12822
+ }
12746
12823
 
12747
- if (this.codeLanguagePicker) {
12748
- this.codeLanguagePicker.remove();
12749
- this.codeLanguagePicker = null;
12750
- }
12824
+ #dispose() {
12825
+ this.#unregisterHandlers();
12826
+ document.removeEventListener("turbo:before-cache", this.#handleTurboBeforeCache);
12751
12827
 
12752
- if (this.tableHandler) {
12753
- this.tableHandler.remove();
12754
- this.tableHandler = null;
12828
+ while (this.#disposables.length) {
12829
+ this.#disposables.pop().dispose();
12755
12830
  }
12756
-
12757
- this.selection = null;
12758
-
12759
- document.removeEventListener("turbo:before-cache", this.#handleTurboBeforeCache);
12760
12831
  }
12761
12832
 
12762
12833
  #reconnect() {
@@ -12797,14 +12868,15 @@ class ToolbarDropdown extends HTMLElement {
12797
12868
  connectedCallback() {
12798
12869
  this.container = this.closest("details");
12799
12870
 
12800
- this.container.addEventListener("toggle", this.#handleToggle.bind(this));
12801
- this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
12871
+ this.container.addEventListener("toggle", this.#handleToggle);
12872
+ this.container.addEventListener("keydown", this.#handleKeyDown);
12802
12873
 
12803
12874
  this.#onToolbarEditor(this.initialize.bind(this));
12804
12875
  }
12805
12876
 
12806
12877
  disconnectedCallback() {
12807
- this.container.removeEventListener("keydown", this.#handleKeyDown.bind(this));
12878
+ this.container?.removeEventListener("toggle", this.#handleToggle);
12879
+ this.container?.removeEventListener("keydown", this.#handleKeyDown);
12808
12880
  }
12809
12881
 
12810
12882
  get toolbar() {
@@ -12829,11 +12901,11 @@ class ToolbarDropdown extends HTMLElement {
12829
12901
  }
12830
12902
 
12831
12903
  async #onToolbarEditor(callback) {
12832
- await this.toolbar.editorConnected;
12904
+ await this.toolbar.editorElement;
12833
12905
  callback();
12834
12906
  }
12835
12907
 
12836
- #handleToggle() {
12908
+ #handleToggle = () => {
12837
12909
  if (this.container.open) {
12838
12910
  this.#handleOpen();
12839
12911
  }
@@ -12844,7 +12916,7 @@ class ToolbarDropdown extends HTMLElement {
12844
12916
  this.#resetTabIndexValues();
12845
12917
  }
12846
12918
 
12847
- #handleKeyDown(event) {
12919
+ #handleKeyDown = (event) => {
12848
12920
  if (event.key === "Escape") {
12849
12921
  event.stopPropagation();
12850
12922
  this.close();
@@ -12872,27 +12944,30 @@ class LinkDropdown extends ToolbarDropdown {
12872
12944
  super.connectedCallback();
12873
12945
  this.input = this.querySelector("input");
12874
12946
 
12875
- this.#registerHandlers();
12947
+ this.container.addEventListener("toggle", this.#handleToggle);
12948
+ this.addEventListener("submit", this.#handleSubmit);
12949
+ this.querySelector("[value='unlink']").addEventListener("click", this.#handleUnlink);
12876
12950
  }
12877
12951
 
12878
- #registerHandlers() {
12879
- this.container.addEventListener("toggle", this.#handleToggle.bind(this));
12880
- this.addEventListener("submit", this.#handleSubmit.bind(this));
12881
- this.querySelector("[value='unlink']").addEventListener("click", this.#handleUnlink.bind(this));
12952
+ disconnectedCallback() {
12953
+ this.container?.removeEventListener("toggle", this.#handleToggle);
12954
+ this.removeEventListener("submit", this.#handleSubmit);
12955
+ this.querySelector("[value='unlink']")?.removeEventListener("click", this.#handleUnlink);
12956
+ super.disconnectedCallback();
12882
12957
  }
12883
12958
 
12884
- #handleToggle({ newState }) {
12959
+ #handleToggle = ({ newState }) => {
12885
12960
  this.input.value = this.#selectedLinkUrl;
12886
12961
  this.input.required = newState === "open";
12887
12962
  }
12888
12963
 
12889
- #handleSubmit(event) {
12964
+ #handleSubmit = (event) => {
12890
12965
  const command = event.submitter?.value;
12891
12966
  this.editor.dispatchCommand(command, this.input.value);
12892
12967
  this.close();
12893
12968
  }
12894
12969
 
12895
- #handleUnlink() {
12970
+ #handleUnlink = () => {
12896
12971
  this.editor.dispatchCommand("unlink");
12897
12972
  this.close();
12898
12973
  }
@@ -12927,26 +13002,35 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
12927
13002
  const NO_STYLE = Symbol("no_style");
12928
13003
 
12929
13004
  class HighlightDropdown extends ToolbarDropdown {
12930
- connectedCallback() {
12931
- super.connectedCallback();
12932
- this.#registerToggleHandler();
12933
- }
12934
-
12935
13005
  initialize() {
12936
13006
  this.#setUpButtons();
12937
13007
  this.#registerButtonHandlers();
12938
13008
  }
12939
13009
 
12940
- #registerToggleHandler() {
12941
- this.container.addEventListener("toggle", this.#handleToggle.bind(this));
13010
+ connectedCallback() {
13011
+ super.connectedCallback();
13012
+ this.container.addEventListener("toggle", this.#handleToggle);
13013
+ }
13014
+
13015
+ disconnectedCallback() {
13016
+ this.container?.removeEventListener("toggle", this.#handleToggle);
13017
+ this.#removeButtonHandlers();
13018
+ super.disconnectedCallback();
12942
13019
  }
12943
13020
 
12944
13021
  #registerButtonHandlers() {
12945
- this.#colorButtons.forEach(button => button.addEventListener("click", this.#handleColorButtonClick.bind(this)));
12946
- this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).addEventListener("click", this.#handleRemoveHighlightClick.bind(this));
13022
+ this.#colorButtons.forEach(button => button.addEventListener("click", this.#handleColorButtonClick));
13023
+ this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).addEventListener("click", this.#handleRemoveHighlightClick);
13024
+ }
13025
+
13026
+ #removeButtonHandlers() {
13027
+ this.#colorButtons.forEach(button => button.removeEventListener("click", this.#handleColorButtonClick));
13028
+ this.querySelector(REMOVE_HIGHLIGHT_SELECTOR)?.removeEventListener("click", this.#handleRemoveHighlightClick);
12947
13029
  }
12948
13030
 
12949
13031
  #setUpButtons() {
13032
+ this.#buttonContainer.innerHTML = "";
13033
+
12950
13034
  const colorGroups = this.editorElement.config.get("highlight.buttons");
12951
13035
 
12952
13036
  this.#populateButtonGroup("color", colorGroups.color);
@@ -12972,7 +13056,7 @@ class HighlightDropdown extends ToolbarDropdown {
12972
13056
  return button
12973
13057
  }
12974
13058
 
12975
- #handleToggle({ newState }) {
13059
+ #handleToggle = ({ newState }) => {
12976
13060
  if (newState === "open") {
12977
13061
  this.editor.getEditorState().read(() => {
12978
13062
  this.#updateColorButtonStates($r());
@@ -12980,7 +13064,7 @@ class HighlightDropdown extends ToolbarDropdown {
12980
13064
  }
12981
13065
  }
12982
13066
 
12983
- #handleColorButtonClick(event) {
13067
+ #handleColorButtonClick = (event) => {
12984
13068
  event.preventDefault();
12985
13069
 
12986
13070
  const button = event.target.closest(APPLY_HIGHLIGHT_SELECTOR);
@@ -12993,7 +13077,7 @@ class HighlightDropdown extends ToolbarDropdown {
12993
13077
  this.close();
12994
13078
  }
12995
13079
 
12996
- #handleRemoveHighlightClick(event) {
13080
+ #handleRemoveHighlightClick = (event) => {
12997
13081
  event.preventDefault();
12998
13082
 
12999
13083
  this.editor.dispatchCommand("removeHighlight");
@@ -13655,19 +13739,21 @@ class CodeLanguagePicker extends HTMLElement {
13655
13739
  }
13656
13740
 
13657
13741
  disconnectedCallback() {
13742
+ this.dispose();
13743
+ }
13744
+
13745
+ dispose() {
13658
13746
  this.unregisterUpdateListener?.();
13659
13747
  this.unregisterUpdateListener = null;
13660
13748
  }
13661
13749
 
13662
13750
  #attachLanguagePicker() {
13663
- this.languagePickerElement = this.#createLanguagePicker();
13664
-
13665
- this.languagePickerElement.addEventListener("change", () => {
13666
- this.#updateCodeBlockLanguage(this.languagePickerElement.value);
13667
- });
13751
+ this.languagePickerElement = this.#findLanguagePicker() ?? this.#createLanguagePicker();
13752
+ this.append(this.languagePickerElement);
13753
+ }
13668
13754
 
13669
- this.languagePickerElement.setAttribute("nonce", getNonce());
13670
- this.appendChild(this.languagePickerElement);
13755
+ #findLanguagePicker() {
13756
+ return this.querySelector("select")
13671
13757
  }
13672
13758
 
13673
13759
  #createLanguagePicker() {
@@ -13680,6 +13766,12 @@ class CodeLanguagePicker extends HTMLElement {
13680
13766
  selectElement.appendChild(option);
13681
13767
  }
13682
13768
 
13769
+ selectElement.addEventListener("change", () => {
13770
+ this.#updateCodeBlockLanguage(this.languagePickerElement.value);
13771
+ });
13772
+
13773
+ selectElement.setAttribute("nonce", getNonce());
13774
+
13683
13775
  return selectElement
13684
13776
  }
13685
13777
 
@@ -14240,6 +14332,10 @@ class TableTools extends HTMLElement {
14240
14332
  }
14241
14333
 
14242
14334
  disconnectedCallback() {
14335
+ this.dispose();
14336
+ }
14337
+
14338
+ dispose() {
14243
14339
  this.#unregisterKeyboardShortcuts();
14244
14340
 
14245
14341
  this.unregisterUpdateListener?.();
@@ -14264,6 +14360,8 @@ class TableTools extends HTMLElement {
14264
14360
  }
14265
14361
 
14266
14362
  #setUpButtons() {
14363
+ this.innerHTML = "";
14364
+
14267
14365
  this.appendChild(this.#createRowButtonsContainer());
14268
14366
  this.appendChild(this.#createColumnButtonsContainer());
14269
14367