lexxy 0.9.13.beta → 0.9.15.alpha.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97cf5bf0fcc025d3ec9064f73665b7127b81daca68c91281c9e365c7fe5742ad
4
- data.tar.gz: dc26d1228a98da394ac222d046a24cf3ffbd71d72f83245311ab9fdd0334a1db
3
+ metadata.gz: 2d9a254f888998ed62a3d7af0cbf6644a0b82fde3ca02d0eefc604c512e12ba9
4
+ data.tar.gz: b0d14ac32727c31eaf0709f16f178ad1056d38e30959141ef3e8b2cbb3f170fe
5
5
  SHA512:
6
- metadata.gz: 103f687cdc45cc9c3b7b6c5454cb044294c3f1f944ce465a54fcf9285b2ce57ce5cf0c0316a68d4b1ee323d796beb4e74d4d366a1138276ab8e63e390b7ce207
7
- data.tar.gz: d4d9b8b61d43b8ab8983547b657e8785907222564913177fbfdd25f77cf2b72fb832c2d4ebe887d38b5df6b5f020680d3eb7c767bf09e1102c70d4ca4567b9d8
6
+ metadata.gz: 3964a7317fdaf7ac44d2c31f546e073a13942bd3a3839be20f9a32a6159e4bc0daf7db35579a21fba551caaa4f136435be392ef70e99c3461eb40ccf3dfdb6c2
7
+ data.tar.gz: f3ea71f58d2a94b6226f9c0e4ace4b08f3ab0fb3d08eaff38159c38f736667661db6e60b0918d3e69a9563c89b244c1d8d3d5b052f8e7f0ea2da298d6011dc1d
data/README.md CHANGED
@@ -2,9 +2,6 @@
2
2
 
3
3
  A modern rich text editor for Rails.
4
4
 
5
- > [!IMPORTANT]
6
- > This is a beta. It hasn't been battle-tested yet. Please try it out and report any issues you find.
7
-
8
5
  **[Try it out!](https://basecamp.github.io/lexxy/try-it)**
9
6
 
10
7
  ## Features
@@ -6795,14 +6795,14 @@ class LexicalToolbarElement extends HTMLElement {
6795
6795
 
6796
6796
  this.#setButtonPressed("bold", isBold);
6797
6797
  this.#setButtonPressed("italic", isItalic);
6798
+ this.#setButtonPressed("strikethrough", isStrikethrough);
6799
+ this.#setButtonPressed("underline", isUnderline);
6798
6800
 
6799
- this.#setButtonPressed("format", isInHeading || isStrikethrough || isUnderline);
6801
+ this.#setButtonPressed("format", isInHeading);
6800
6802
  this.#setButtonPressed("paragraph", !isInHeading);
6801
6803
  this.#setButtonPressed("heading-large", headingTag === "h2");
6802
6804
  this.#setButtonPressed("heading-medium", headingTag === "h3");
6803
6805
  this.#setButtonPressed("heading-small", headingTag === "h4");
6804
- this.#setButtonPressed("strikethrough", isStrikethrough);
6805
- this.#setButtonPressed("underline", isUnderline);
6806
6806
 
6807
6807
  this.#setButtonPressed("lists", isInList);
6808
6808
  this.#setButtonPressed("unordered-list", isInList && listType === "bullet");
@@ -6965,6 +6965,14 @@ class LexicalToolbarElement extends HTMLElement {
6965
6965
  ${ToolbarIcons.italic}
6966
6966
  </button>
6967
6967
 
6968
+ <button class="lexxy-editor__toolbar-button" type="button" name="strikethrough" data-command="strikethrough" title="Strikethrough">
6969
+ ${ToolbarIcons.strikethrough}
6970
+ </button>
6971
+
6972
+ <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="underline" data-command="underline" title="Underline">
6973
+ ${ToolbarIcons.underline}
6974
+ </button>
6975
+
6968
6976
  <lexxy-toolbar-dropdown class="lexxy-editor__toolbar-dropdown">
6969
6977
  <button data-dropdown-trigger class="lexxy-editor__toolbar-button lexxy-editor__toolbar-button--chevron" type="button" name="format" title="Text formatting" aria-haspopup="menu" aria-expanded="false">
6970
6978
  ${ToolbarIcons.heading}
@@ -6983,13 +6991,6 @@ class LexicalToolbarElement extends HTMLElement {
6983
6991
  ${ToolbarIcons.h4} <span>Small Heading</span>
6984
6992
  </button>
6985
6993
  <div class="lexxy-editor__toolbar-separator" role="separator"></div>
6986
- <button type="button" name="strikethrough" data-command="strikethrough" title="Strikethrough" role="menuitem">
6987
- ${ToolbarIcons.strikethrough} <span>Strikethrough</span>
6988
- </button>
6989
- <button type="button" name="underline" data-command="underline" title="Underline" role="menuitem">
6990
- ${ToolbarIcons.underline} <span>Underline</span>
6991
- </button>
6992
- <div class="lexxy-editor__toolbar-separator" role="separator"></div>
6993
6994
  <button type="button" name="clear-formatting" data-command="clearFormatting" title="Clear formatting" role="menuitem">
6994
6995
  ${ToolbarIcons.clearFormatting} <span>Clear formatting</span>
6995
6996
  </button>
@@ -7802,14 +7803,15 @@ function $isShadowRoot(node) {
7802
7803
  return Wi(node) && Is(node) && !Vi(node)
7803
7804
  }
7804
7805
 
7806
+ function $isSafeForRoot(node) {
7807
+ return (Wi(node) || ji(node)) && !node.isParentRequired()
7808
+ }
7809
+
7805
7810
  function $makeSafeForRoot(node) {
7806
- if (Tr(node)) {
7807
- return Tt$4(node, eo)
7808
- } else if (node.isParentRequired()) {
7809
- const parent = node.createRequiredParent();
7810
- return Tt$4(node, parent)
7811
- } else {
7811
+ if ($isSafeForRoot(node)) {
7812
7812
  return node
7813
+ } else {
7814
+ return Tt$4(node, () => node.createParentElementNode())
7813
7815
  }
7814
7816
  }
7815
7817
 
@@ -7924,6 +7926,39 @@ function $isListItemStructurallyEmpty(listItem) {
7924
7926
  return true
7925
7927
  }
7926
7928
 
7929
+ // Returns the document text up to `offset` inside `targetNode`. Non-inline
7930
+ // element siblings are joined with `\n\n`, matching Lexical's own
7931
+ // ElementNode.getTextContent behavior.
7932
+ function $textBeforeOffset(targetNode, offset) {
7933
+ const parts = [];
7934
+ let done = false;
7935
+
7936
+ function visit(node) {
7937
+ if (done) return
7938
+ if (node === targetNode) {
7939
+ parts.push(node.getTextContent().slice(0, offset));
7940
+ done = true;
7941
+ return
7942
+ }
7943
+ if (Wi(node)) {
7944
+ const children = node.getChildren();
7945
+ for (let i = 0; i < children.length; i++) {
7946
+ visit(children[i]);
7947
+ if (done) return
7948
+ const child = children[i];
7949
+ if (Wi(child) && !child.isInline() && i < children.length - 1) {
7950
+ parts.push("\n\n");
7951
+ }
7952
+ }
7953
+ } else {
7954
+ parts.push(node.getTextContent());
7955
+ }
7956
+ }
7957
+
7958
+ visit(Zo());
7959
+ return parts.join("")
7960
+ }
7961
+
7927
7962
  function isAttachmentSpacerTextNode(node, previousNode, index, childCount) {
7928
7963
  return Tr(node)
7929
7964
  && node.getTextContent() === " "
@@ -14949,6 +14984,9 @@ class RemoteFilterSource extends BaseSource {
14949
14984
  const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
14950
14985
  const FILTER_DEBOUNCE_INTERVAL = 50;
14951
14986
 
14987
+ // Start of line, or after a space or newline.
14988
+ const DEFAULT_ONLY_AT_PATTERN = "^|[ \\n]";
14989
+
14952
14990
  class LexicalPromptElement extends HTMLElement {
14953
14991
  #globalListeners = new ListenerBin()
14954
14992
  #popoverListeners = new ListenerBin()
@@ -14994,6 +15032,10 @@ class LexicalPromptElement extends HTMLElement {
14994
15032
  return this.hasAttribute("supports-space-in-searches")
14995
15033
  }
14996
15034
 
15035
+ get onlyAt() {
15036
+ return this.getAttribute("only-at")
15037
+ }
15038
+
14997
15039
  get open() {
14998
15040
  return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
14999
15041
  }
@@ -15037,14 +15079,10 @@ class LexicalPromptElement extends HTMLElement {
15037
15079
  if (offset >= triggerLength) {
15038
15080
  const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
15039
15081
 
15040
- // Check if trigger is at the start of the text node (new line case) or preceded by space or newline
15041
15082
  if (textBeforeCursor === this.trigger) {
15042
- const isAtStart = offset === triggerLength;
15043
-
15044
- const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
15045
- const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
15083
+ const textBeforeTrigger = $textBeforeOffset(node, offset - triggerLength);
15046
15084
 
15047
- if (isAtStart || isPrecededBySpaceOrNewline) {
15085
+ if (this.#onlyAtRegExp.test(textBeforeTrigger)) {
15048
15086
  this.#popoverListeners.dispose();
15049
15087
  this.#showPopover();
15050
15088
  }
@@ -15055,7 +15093,15 @@ class LexicalPromptElement extends HTMLElement {
15055
15093
  }));
15056
15094
  }
15057
15095
 
15096
+ get #onlyAtRegExp() {
15097
+ return new RegExp(`(?:${this.onlyAt ?? DEFAULT_ONLY_AT_PATTERN})$`)
15098
+ }
15099
+
15058
15100
  get #promptContentTypePermitted() {
15101
+ // `insert-editable-text` prompts never create attachments, so the
15102
+ // editor's attachment support and content-type allowlist don't apply.
15103
+ if (this.hasAttribute("insert-editable-text")) return true
15104
+
15059
15105
  const el = this.#editorElement;
15060
15106
  if (!el.supportsAttachments) {
15061
15107
  return false
@@ -15758,6 +15804,8 @@ class TableController {
15758
15804
 
15759
15805
  this.currentCellKey = cellNode?.getKey() ?? null;
15760
15806
  this.currentTableNodeKey = tableNode?.getKey() ?? null;
15807
+
15808
+ return tableNode
15761
15809
  }
15762
15810
 
15763
15811
  executeTableCommand(command, customIndex = null) {
@@ -16260,9 +16308,8 @@ class TableTools extends HTMLElement {
16260
16308
 
16261
16309
  #monitorForTableSelection() {
16262
16310
  this.#listeners.track(this.#editor.registerUpdateListener(() => {
16263
- this.tableController.updateSelectedTable();
16311
+ const tableNode = this.#editor.getRootElement() && this.tableController.updateSelectedTable();
16264
16312
 
16265
- const tableNode = this.tableController.currentTableNode;
16266
16313
  if (tableNode) {
16267
16314
  this.#show();
16268
16315
  } else {
Binary file
Binary file