@37signals/lexxy 0.8.6-beta → 0.9.1-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.
package/dist/lexxy.esm.js CHANGED
@@ -696,13 +696,13 @@ class LexicalToolbarElement extends HTMLElement {
696
696
 
697
697
  static get defaultTemplate() {
698
698
  return `
699
- <button class="lexxy-editor__toolbar-button" type="button" name="image" data-command="uploadAttachments" data-prevent-overflow="true" title="Add images">
700
- ${ToolbarIcons.image}
701
- </button>
699
+ <button class="lexxy-editor__toolbar-button" type="button" name="image" data-command="uploadImage" data-prevent-overflow="true" title="Add images and video">
700
+ ${ToolbarIcons.image}
701
+ </button>
702
702
 
703
- <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="file" data-command="uploadAttachments" title="Upload files">
704
- ${ToolbarIcons.attachment}
705
- </button>
703
+ <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="file" data-command="uploadFile" title="Upload files">
704
+ ${ToolbarIcons.attachment}
705
+ </button>
706
706
 
707
707
  <button class="lexxy-editor__toolbar-button" type="button" name="bold" data-command="bold" title="Bold">
708
708
  ${ToolbarIcons.bold}
@@ -726,10 +726,10 @@ class LexicalToolbarElement extends HTMLElement {
726
726
  <button type="button" name="heading-medium" data-command="setFormatHeadingMedium" title="Medium heading">
727
727
  ${ToolbarIcons.h3} <span>Medium Heading</span>
728
728
  </button>
729
- <button type="button" name="heading-small" data-command="setFormatHeadingSmall" title="Small heading">
729
+ <button class="lexxy-editor__toolbar-group-end" type="button" name="heading-small" data-command="setFormatHeadingSmall" title="Small heading">
730
730
  ${ToolbarIcons.h4} <span>Small Heading</span>
731
731
  </button>
732
- <div class="separator" role="separator"></div>
732
+ <div class="lexxy-editor__toolbar-separator" role="separator"></div>
733
733
  <button type="button" name="strikethrough" data-command="strikethrough" title="Strikethrough">
734
734
  ${ToolbarIcons.strikethrough} <span>Strikethrough</span>
735
735
  </button>
@@ -739,21 +739,6 @@ class LexicalToolbarElement extends HTMLElement {
739
739
  </div>
740
740
  </details>
741
741
 
742
-
743
- <details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-dropdown--chevron" name="lexxy-dropdown">
744
- <summary class="lexxy-editor__toolbar-button" name="lists" title="Lists">
745
- ${ToolbarIcons.ul}
746
- </summary>
747
- <div class="lexxy-editor__toolbar-dropdown-list">
748
- <button type="button" name="unordered-list" data-command="insertUnorderedList" title="Bullet list">
749
- ${ToolbarIcons.ul} <span>Bullets</span>
750
- </button>
751
- <button type="button" name="ordered-list" data-command="insertOrderedList" title="Numbered list">
752
- ${ToolbarIcons.ol} <span>Numbers</span>
753
- </button>
754
- </div>
755
- </details>
756
-
757
742
  <details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-dropdown--chevron" name="lexxy-dropdown">
758
743
  <summary class="lexxy-editor__toolbar-button" name="highlight" title="Color highlight">
759
744
  ${ToolbarIcons.highlight}
@@ -765,7 +750,7 @@ class LexicalToolbarElement extends HTMLElement {
765
750
  </details>
766
751
 
767
752
  <details class="lexxy-editor__toolbar-dropdown" name="lexxy-dropdown">
768
- <summary class="lexxy-editor__toolbar-button" name="link" title="Link" data-hotkey="cmd+k ctrl+k">
753
+ <summary class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" name="link" title="Link" data-hotkey="cmd+k ctrl+k">
769
754
  ${ToolbarIcons.link}
770
755
  </summary>
771
756
  <lexxy-link-dropdown class="lexxy-editor__toolbar-dropdown-content">
@@ -783,10 +768,17 @@ class LexicalToolbarElement extends HTMLElement {
783
768
  ${ToolbarIcons.quote}
784
769
  </button>
785
770
 
786
- <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="code" data-command="insertCodeBlock" title="Code">
771
+ <button class="lexxy-editor__toolbar-button" type="button" name="code" data-command="insertCodeBlock" title="Code">
787
772
  ${ToolbarIcons.code}
788
773
  </button>
789
774
 
775
+ <button class="lexxy-editor__toolbar-button" type="button" name="unordered-list" data-command="insertUnorderedList" title="Bullet list">
776
+ ${ToolbarIcons.ul}
777
+ </button>
778
+ <button class="lexxy-editor__toolbar-button lexxy-editor__toolbar-group-end" type="button" name="ordered-list" data-command="insertOrderedList" title="Numbered list">
779
+ ${ToolbarIcons.ol}
780
+ </button>
781
+
790
782
  <button class="lexxy-editor__toolbar-button" type="button" name="table" data-command="insertTable" title="Insert a table">
791
783
  ${ToolbarIcons.table}
792
784
  </button>
@@ -1961,7 +1953,8 @@ const COMMANDS = [
1961
1953
  "insertQuoteBlock",
1962
1954
  "insertCodeBlock",
1963
1955
  "insertHorizontalDivider",
1964
- "uploadAttachments",
1956
+ "uploadImage",
1957
+ "uploadFile",
1965
1958
 
1966
1959
  "insertTable",
1967
1960
 
@@ -2145,15 +2138,27 @@ class CommandDispatcher {
2145
2138
  this.contents.applyParagraphFormat();
2146
2139
  }
2147
2140
 
2148
- dispatchUploadAttachments() {
2149
- const input = createElement("input", {
2141
+ dispatchUploadImage() {
2142
+ this.#dispatchUploadAttachment("image/*,video/*");
2143
+ }
2144
+
2145
+ dispatchUploadFile() {
2146
+ this.#dispatchUploadAttachment();
2147
+ }
2148
+
2149
+ #dispatchUploadAttachment(accept = null) {
2150
+ const attributes = {
2150
2151
  type: "file",
2151
2152
  multiple: true,
2152
2153
  style: "display: none;",
2153
2154
  onchange: ({ target: { files } }) => {
2154
2155
  this.contents.uploadFiles(files, { selectLast: true });
2155
2156
  }
2156
- });
2157
+ };
2158
+
2159
+ if (accept) attributes.accept = accept;
2160
+
2161
+ const input = createElement("input", attributes);
2157
2162
 
2158
2163
  // Append and remove to make testable
2159
2164
  this.editorElement.appendChild(input);
@@ -4432,7 +4437,8 @@ class Contents {
4432
4437
  }
4433
4438
 
4434
4439
  #splitParagraphsAtLineBreaks(selection) {
4435
- const selectedNodeKeys = new Set(selection.getNodes().map(n => n.getKey()));
4440
+ const anchorKey = selection.anchor.getNode().getKey();
4441
+ const focusKey = selection.focus.getNode().getKey();
4436
4442
  const topLevelElements = this.#topLevelElementsInSelection(selection);
4437
4443
 
4438
4444
  for (const element of topLevelElements) {
@@ -4441,6 +4447,14 @@ class Contents {
4441
4447
  const children = element.getChildren();
4442
4448
  if (!children.some($isLineBreakNode)) continue
4443
4449
 
4450
+ // Check whether this paragraph needs splitting: skip only if neither
4451
+ // selection endpoint is inside it (meaning it's a middle paragraph
4452
+ // fully between anchor and focus with no partial lines to split off).
4453
+ const hasEndpoint = children.some(child =>
4454
+ child.getKey() === anchorKey || child.getKey() === focusKey
4455
+ );
4456
+ if (!hasEndpoint) continue
4457
+
4444
4458
  const groups = [ [] ];
4445
4459
  for (const child of children) {
4446
4460
  if ($isLineBreakNode(child)) {
@@ -4451,8 +4465,6 @@ class Contents {
4451
4465
  }
4452
4466
  }
4453
4467
 
4454
- if (groups.every(group => group.some(child => selectedNodeKeys.has(child.getKey())))) continue
4455
-
4456
4468
  for (const group of groups) {
4457
4469
  if (group.length === 0) continue
4458
4470
  const paragraph = $createParagraphNode();
@@ -4668,7 +4680,9 @@ class Clipboard {
4668
4680
  return true
4669
4681
  }
4670
4682
 
4671
- return this.#handlePastedFiles(clipboardData)
4683
+ const handled = this.#handlePastedFiles(clipboardData);
4684
+ if (handled) event.preventDefault();
4685
+ return handled
4672
4686
  }
4673
4687
 
4674
4688
  #isPlainTextOrURLPasted(clipboardData) {
@@ -4766,14 +4780,21 @@ class Clipboard {
4766
4780
  return true
4767
4781
  }
4768
4782
 
4769
- if (html) {
4783
+ if (html && !this.#isLexicalClipboardData(clipboardData)) {
4770
4784
  this.contents.insertHtml(html, { tag: PASTE_TAG });
4771
4785
  return true
4772
4786
  }
4773
4787
 
4774
- this.#uploadFilesPreservingScroll(files);
4788
+ if (files.length) {
4789
+ this.#uploadFilesPreservingScroll(files);
4790
+ return true
4791
+ }
4792
+
4793
+ return false
4794
+ }
4775
4795
 
4776
- return true
4796
+ #isLexicalClipboardData(clipboardData) {
4797
+ return Array.from(clipboardData.types).includes("application/x-lexical-editor")
4777
4798
  }
4778
4799
 
4779
4800
  #isCopiedImageHTML(html) {
@@ -488,14 +488,15 @@
488
488
 
489
489
  &:after {
490
490
  background-color: var(--lexxy-color-ink-lighter);
491
+ block-size: var(--lexxy-toolbar-icon-size);
491
492
  content: "";
492
493
  display: block;
493
- width: 1px;
494
- height: 60%;
494
+ inline-size: 1px;
495
495
  inset-inline-end: calc(-1 * var(--lexxy-toolbar-spacing));
496
- inset-block-start: 20%;
497
- position: absolute;
496
+ inset-block: 0;
497
+ margin: auto;
498
498
  pointer-events: none;
499
+ position: absolute;
499
500
  }
500
501
  }
501
502
  }
@@ -556,11 +557,6 @@
556
557
  }
557
558
  }
558
559
 
559
- [overflowing] &:not(.lexxy-editor__toolbar-overflow) summary ~ * {
560
- inset-inline-end: var(--lexxy-toolbar-spacing);
561
- inset-inline-start: var(--lexxy-toolbar-spacing);
562
- }
563
-
564
560
  button {
565
561
  color: var(--lexxy-color-text);
566
562
 
@@ -572,7 +568,6 @@
572
568
  .lexxy-editor__toolbar-dropdown-list {
573
569
  border-start-start-radius: 0;
574
570
  flex-direction: column;
575
- gap: 0.1ch;
576
571
  padding: 0.1ch;
577
572
 
578
573
  button {
@@ -581,6 +576,7 @@
581
576
  flex-direction: row;
582
577
  gap: 1ch;
583
578
  padding: 1ch;
579
+ position: relative;
584
580
 
585
581
  &[aria-pressed="true"] {
586
582
  background-color: var(--lexxy-color-selected);
@@ -600,11 +596,22 @@
600
596
  }
601
597
  }
602
598
 
603
- .separator {
599
+ .lexxy-editor__toolbar-separator {
604
600
  background: var(--lexxy-color-ink-lighter);
605
601
  block-size: 1px;
606
602
  inline-size: 100%;
607
603
  }
604
+
605
+ [overflowing] & {
606
+ display: grid;
607
+ grid-template-columns: repeat(4, 1fr);
608
+
609
+ button span { display: none; }
610
+
611
+ .lexxy-editor__toolbar-separator {
612
+ grid-column: 1 / -1;
613
+ }
614
+ }
608
615
  }
609
616
 
610
617
 
@@ -706,6 +713,11 @@
706
713
  inset-inline-start: 0;
707
714
  max-inline-size: var(--max-inline-size);
708
715
 
716
+ [overflowing] & {
717
+ inset-inline-end: var(--lexxy-toolbar-spacing);
718
+ inset-inline-start: var(--lexxy-toolbar-spacing);
719
+ }
720
+
709
721
  button {
710
722
  position: relative;
711
723
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@37signals/lexxy",
3
- "version": "0.8.6-beta",
3
+ "version": "0.9.1-beta",
4
4
  "description": "Lexxy - A modern rich text editor for Rails.",
5
5
  "module": "dist/lexxy.esm.js",
6
6
  "type": "module",