@37signals/lexxy 0.1.26-beta → 0.1.27-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/README.md CHANGED
@@ -23,7 +23,7 @@ A modern rich text editor for Rails.
23
23
  Add this line to your application's Gemfile:
24
24
 
25
25
  ```ruby
26
- gem 'lexxy', '~> 0.1.23.beta' # Need to specify the version since it's a pre-release
26
+ gem 'lexxy', '~> 0.1.26.beta' # Need to specify the version since it's a pre-release
27
27
  ```
28
28
 
29
29
  And then execute:
package/dist/lexxy.esm.js CHANGED
@@ -15,7 +15,7 @@ import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORD
15
15
  import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
16
16
  import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
17
17
  import { $isLinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
18
- import { $getTableCellNodeFromLexicalNode, INSERT_TABLE_COMMAND, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, TableNode, TableCellNode, TableRowNode, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $getElementForTableNode, $isTableCellNode, TableCellHeaderStates } from '@lexical/table';
18
+ import { $getTableCellNodeFromLexicalNode, INSERT_TABLE_COMMAND, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, TableCellNode, TableNode, TableRowNode, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $getElementForTableNode, $isTableCellNode, TableCellHeaderStates } from '@lexical/table';
19
19
  import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
20
20
  import { registerMarkdownShortcuts, TRANSFORMERS } from '@lexical/markdown';
21
21
  import { createEmptyHistoryState, registerHistory } from '@lexical/history';
@@ -1645,26 +1645,19 @@ class Selection {
1645
1645
  this.#containEditorFocus();
1646
1646
  }
1647
1647
 
1648
- clear() {
1649
- this.current = null;
1650
- }
1651
-
1652
1648
  set current(selection) {
1653
- if ($isNodeSelection(selection)) {
1654
- this.editor.getEditorState().read(() => {
1655
- this._current = $getSelection();
1656
- this.#syncSelectedClasses();
1657
- });
1658
- } else {
1659
- this.editor.update(() => {
1660
- this.#syncSelectedClasses();
1661
- this._current = null;
1662
- });
1663
- }
1649
+ this.editor.update(() => {
1650
+ this.#syncSelectedClasses();
1651
+ });
1664
1652
  }
1665
1653
 
1666
- get current() {
1667
- return this._current
1654
+ get hasNodeSelection() {
1655
+ let result = false;
1656
+ this.editor.getEditorState().read(() => {
1657
+ const selection = $getSelection();
1658
+ result = selection !== null && $isNodeSelection(selection);
1659
+ });
1660
+ return result
1668
1661
  }
1669
1662
 
1670
1663
  get cursorPosition() {
@@ -1857,18 +1850,18 @@ class Selection {
1857
1850
  }
1858
1851
 
1859
1852
  get #currentlySelectedKeys() {
1860
- if (this._currentlySelectedKeys) { return this._currentlySelectedKeys }
1853
+ if (this.currentlySelectedKeys) { return this.currentlySelectedKeys }
1861
1854
 
1862
- this._currentlySelectedKeys = new Set();
1855
+ this.currentlySelectedKeys = new Set();
1863
1856
 
1864
1857
  const selection = $getSelection();
1865
1858
  if (selection && $isNodeSelection(selection)) {
1866
1859
  for (const node of selection.getNodes()) {
1867
- this._currentlySelectedKeys.add(node.getKey());
1860
+ this.currentlySelectedKeys.add(node.getKey());
1868
1861
  }
1869
1862
  }
1870
1863
 
1871
- return this._currentlySelectedKeys
1864
+ return this.currentlySelectedKeys
1872
1865
  }
1873
1866
 
1874
1867
  #processSelectionChangeCommands() {
@@ -1998,7 +1991,7 @@ class Selection {
1998
1991
  this.#highlightNewItems();
1999
1992
 
2000
1993
  this.previouslySelectedKeys = this.#currentlySelectedKeys;
2001
- this._currentlySelectedKeys = null;
1994
+ this.currentlySelectedKeys = null;
2002
1995
  }
2003
1996
 
2004
1997
  #clearPreviouslyHighlightedItems() {
@@ -2020,7 +2013,7 @@ class Selection {
2020
2013
  }
2021
2014
 
2022
2015
  async #selectPreviousNode() {
2023
- if (this.current) {
2016
+ if (this.hasNodeSelection) {
2024
2017
  await this.#withCurrentNode((currentNode) => currentNode.selectPrevious());
2025
2018
  } else {
2026
2019
  this.#selectInLexical(this.nodeBeforeCursor);
@@ -2028,7 +2021,7 @@ class Selection {
2028
2021
  }
2029
2022
 
2030
2023
  async #selectNextNode() {
2031
- if (this.current) {
2024
+ if (this.hasNodeSelection) {
2032
2025
  await this.#withCurrentNode((currentNode) => currentNode.selectNext(0, 0));
2033
2026
  } else {
2034
2027
  this.#selectInLexical(this.nodeAfterCursor);
@@ -2036,7 +2029,7 @@ class Selection {
2036
2029
  }
2037
2030
 
2038
2031
  async #selectPreviousTopLevelNode() {
2039
- if (this.current) {
2032
+ if (this.hasNodeSelection) {
2040
2033
  await this.#withCurrentNode((currentNode) => currentNode.selectPrevious());
2041
2034
  } else {
2042
2035
  this.#selectInLexical(this.topLevelNodeBeforeCursor);
@@ -2044,7 +2037,7 @@ class Selection {
2044
2037
  }
2045
2038
 
2046
2039
  async #selectNextTopLevelNode() {
2047
- if (this.current) {
2040
+ if (this.hasNodeSelection) {
2048
2041
  await this.#withCurrentNode((currentNode) => currentNode.selectNext(0, 0));
2049
2042
  } else {
2050
2043
  this.#selectInLexical(this.topLevelNodeAfterCursor);
@@ -2053,10 +2046,9 @@ class Selection {
2053
2046
 
2054
2047
  async #withCurrentNode(fn) {
2055
2048
  await nextFrame();
2056
- if (this.current) {
2049
+ if (this.hasNodeSelection) {
2057
2050
  this.editor.update(() => {
2058
- this.clear();
2059
- fn(this.current.getNodes()[0]);
2051
+ fn($getSelection().getNodes()[0]);
2060
2052
  this.editor.focus();
2061
2053
  });
2062
2054
  }
@@ -2301,6 +2293,17 @@ class Selection {
2301
2293
  }
2302
2294
  }
2303
2295
 
2296
+ // Prevent the hardcoded background color
2297
+ // A background color value is set by Lexical if background is null:
2298
+ // https://github.com/facebook/lexical/blob/5bbbe849bd229e1db0e7b536e6a919520ada7bb2/packages/lexical-table/src/LexicalTableCellNode.ts#L187
2299
+ function registerHeaderBackgroundTransform(editor) {
2300
+ return editor.registerNodeTransform(TableCellNode, (node) => {
2301
+ if (node.getBackgroundColor() === null) {
2302
+ node.setBackgroundColor("");
2303
+ }
2304
+ })
2305
+ }
2306
+
2304
2307
  class CustomActionTextAttachmentNode extends DecoratorNode {
2305
2308
  static getType() {
2306
2309
  return "custom_action_text_attachment"
@@ -2949,8 +2952,8 @@ class Contents {
2949
2952
  let focusNode = null;
2950
2953
 
2951
2954
  this.editor.update(() => {
2952
- if ($isNodeSelection(this.#selection.current)) {
2953
- const nodesToRemove = this.#selection.current.getNodes();
2955
+ if (this.#selection.hasNodeSelection) {
2956
+ const nodesToRemove = $getSelection().getNodes();
2954
2957
  if (nodesToRemove.length === 0) return
2955
2958
 
2956
2959
  focusNode = this.#findAdjacentNodeTo(nodesToRemove);
@@ -2962,7 +2965,6 @@ class Contents {
2962
2965
 
2963
2966
  this.editor.update(() => {
2964
2967
  this.#selectAfterDeletion(focusNode);
2965
- this.#selection.clear();
2966
2968
  this.editor.focus();
2967
2969
  });
2968
2970
  }
@@ -3988,6 +3990,8 @@ class LexicalEditorElement extends HTMLElement {
3988
3990
  registerTablePlugin(this.editor);
3989
3991
  this.tableHandler = createElement("lexxy-table-handler");
3990
3992
  this.append(this.tableHandler);
3993
+
3994
+ this.#addUnregisterHandler(registerHeaderBackgroundTransform(this.editor));
3991
3995
  }
3992
3996
 
3993
3997
  #registerCodeHiglightingComponents() {
@@ -4556,6 +4560,7 @@ class TableHandler extends HTMLElement {
4556
4560
  const container = createElement("details", {
4557
4561
  className: "lexxy-table-control lexxy-table-control__more-menu"
4558
4562
  });
4563
+ container.setAttribute("name", "lexxy-dropdown");
4559
4564
 
4560
4565
  container.tabIndex = -1;
4561
4566
 
@@ -54,15 +54,17 @@
54
54
  }
55
55
 
56
56
  table {
57
- .table-cell--selected {
58
- background-color: var(--lexxy-color-table-cell-selected-bg) !important;
59
- }
57
+ th, td {
58
+ &.table-cell--selected {
59
+ background-color: var(--lexxy-color-table-cell-selected-bg);
60
+ }
60
61
 
61
- .lexxy-content__table-cell--selected {
62
- background-color: var(--lexxy-color-table-cell-selected-bg) !important;
63
- border-color: var(--lexxy-color-table-cell-selected-border) !important;
62
+ &.lexxy-content__table-cell--selected {
63
+ background-color: var(--lexxy-color-table-cell-selected-bg);
64
+ border-color: var(--lexxy-color-table-cell-selected-border);
65
+ }
64
66
  }
65
-
67
+
66
68
  &.lexxy-content__table--selection {
67
69
  ::selection {
68
70
  background: transparent;
@@ -376,69 +378,6 @@
376
378
  }
377
379
  }
378
380
 
379
- /* Table dropdown
380
- /* -------------------------------------------------------------------------- */
381
-
382
- :where(lexxy-table-dropdown) {
383
- display: flex;
384
- flex-direction: column;
385
- gap: 1ch;
386
-
387
- .lexxy-editor__table-create {
388
- display: flex;
389
- flex-direction: column;
390
- flex-wrap: wrap;
391
- gap: 0;
392
-
393
- .lexxy-editor__table-buttons {
394
- background-color: var(--lexxy-color-ink-lighter);
395
- display: flex;
396
- flex-direction: column;
397
- gap: 1px;
398
- padding: 1px;
399
-
400
- div {
401
- display: flex;
402
- flex-direction: row;
403
- gap: 1px;
404
- }
405
-
406
- button {
407
- aspect-ratio: 1.5 / 1;
408
- border: 0;
409
- border-radius: 0;
410
- color: var(--lexxy-color-ink);
411
- font-family: var(--lexxy-font-base);
412
- font-size: var(--lexxy-text-small);
413
- font-weight: normal;
414
- inline-size: 4ch;
415
- margin: 0;
416
-
417
- &.active {
418
- background-color: var(--lexxy-color-ink-lightest);
419
- }
420
- }
421
- }
422
-
423
- label {
424
- align-items: center;
425
- display: flex;
426
- gap: 0.5ch;
427
- padding: 0.5ch 0;
428
- margin-block-start: 1ch;
429
- }
430
-
431
- &:has(input[type="checkbox"]:checked) {
432
- .lexxy-editor__table-buttons {
433
- div:first-child button,
434
- button:first-child {
435
- filter: brightness(0.95);
436
- }
437
- }
438
- }
439
- }
440
- }
441
-
442
381
  /* Table handle buttons
443
382
  /* -------------------------------------------------------------------------- */
444
383
 
@@ -453,7 +392,7 @@
453
392
  line-height: 1;
454
393
  position: absolute;
455
394
  transform: translate(-50%, -120%);
456
- z-index: 10;
395
+ z-index: 2;
457
396
 
458
397
  .lexxy-table-control {
459
398
  align-items: center;
@@ -465,7 +404,8 @@
465
404
  padding: 2px;
466
405
  white-space: nowrap;
467
406
 
468
- button {
407
+ button,
408
+ summary {
469
409
  aspect-ratio: 1 / 1;
470
410
  align-items: center;
471
411
  background-color: transparent;
@@ -477,14 +417,24 @@
477
417
  font-weight: bold;
478
418
  justify-content: center;
479
419
  line-height: 1;
420
+ list-style: none;
480
421
  min-block-size: var(--button-size);
481
422
  min-inline-size: var(--button-size);
482
423
  padding: 0;
424
+ user-select: none;
425
+ -webkit-user-select: none;
483
426
 
484
427
  &:hover {
485
428
  background-color: var(--lexxy-color-ink-medium);
486
429
  }
487
430
 
431
+ &:focus,
432
+ &:focus-visible {
433
+ background-color: var(--lexxy-color-ink-medium);
434
+ outline: var(--lexxy-focus-ring-size) solid var(--lexxy-focus-ring-color);
435
+ outline-offset: var(--lexxy-focus-ring-offset);
436
+ }
437
+
488
438
  svg {
489
439
  block-size: 1em;
490
440
  inline-size: 1em;
@@ -495,47 +445,17 @@
495
445
  display: none;
496
446
  }
497
447
  }
498
-
499
- button,
500
- summary {
501
- &:focus,
502
- &:focus-visible {
503
- background-color: var(--lexxy-color-ink-medium);
504
- outline: var(--lexxy-focus-ring-size) solid var(--lexxy-focus-ring-color);
505
- outline-offset: var(--lexxy-focus-ring-offset);
506
- }
507
- }
508
448
  }
509
449
 
510
450
  .lexxy-table-control__more-menu {
511
451
  gap: 0;
512
- padding: 2px;
452
+ padding: 0.25ch;
513
453
  position: relative;
514
454
 
515
455
  summary {
516
- aspect-ratio: 1 / 1;
517
- align-items: center;
518
- background: transparent;
519
- border-radius: var(--lexxy-radius);
520
- border: 0;
521
- box-sizing: border-box;
522
- display: flex;
523
- font-size: inherit;
524
- justify-content: center;
525
- list-style: none;
526
- min-block-size: var(--button-size);
527
- min-inline-size: var(--button-size);
528
- padding: 0;
529
- user-select: none;
530
- -webkit-user-select: none;
531
-
532
456
  &::-webkit-details-marker {
533
457
  display: none;
534
458
  }
535
-
536
- &:hover {
537
- background: var(--lexxy-color-ink-medium);
538
- }
539
459
  }
540
460
 
541
461
  .lexxy-table-control__more-menu-details {
@@ -552,7 +472,7 @@
552
472
  border-radius: 0.75ch;
553
473
  display: flex;
554
474
  flex-direction: column;
555
- padding: 2px;
475
+ padding: 0.25ch;
556
476
  }
557
477
 
558
478
  button {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@37signals/lexxy",
3
- "version": "0.1.26-beta",
3
+ "version": "0.1.27-beta",
4
4
  "description": "Lexxy - A modern rich text editor for Rails.",
5
5
  "module": "dist/lexxy.esm.js",
6
6
  "type": "module",