@37signals/lexxy 0.7.2-beta → 0.7.3-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 +819 -544
- package/dist/stylesheets/lexxy-content.css +18 -2
- package/dist/stylesheets/lexxy-editor.css +318 -212
- package/dist/stylesheets/lexxy-variables.css +9 -3
- package/package.json +1 -1
package/dist/lexxy.esm.js
CHANGED
|
@@ -10,12 +10,12 @@ import 'prismjs/components/prism-json';
|
|
|
10
10
|
import 'prismjs/components/prism-diff';
|
|
11
11
|
import DOMPurify from 'dompurify';
|
|
12
12
|
import { getStyleObjectFromCSS, getCSSFromStyleObject, $getSelectionStyleValueForProperty, $patchStyleText } from '@lexical/selection';
|
|
13
|
-
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, $isRootOrShadowRoot, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, COMMAND_PRIORITY_NORMAL, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND,
|
|
13
|
+
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, SELECTION_CHANGE_COMMAND, COMMAND_PRIORITY_HIGH, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, $isRootOrShadowRoot, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, COMMAND_PRIORITY_NORMAL, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $isNodeSelection, $getRoot, $isLineBreakNode, $isElementNode, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_DELETE_COMMAND, KEY_BACKSPACE_COMMAND, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, $isParagraphNode, $insertNodes, $createLineBreakNode, PASTE_TAG, createCommand, createState, defineExtension, $setState, $getState, $hasUpdateTag, CLEAR_HISTORY_COMMAND, $addUpdateTag, KEY_DOWN_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
14
14
|
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, $getListDepth, $createListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
15
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, RichTextExtension, 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, TableNode,
|
|
18
|
+
import { $getTableCellNodeFromLexicalNode, INSERT_TABLE_COMMAND, TableCellNode, TableNode, TableRowNode, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive, TableCellHeaderStates, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $findCellNode, $getElementForTableNode } from '@lexical/table';
|
|
19
19
|
import { createElement, createAttachmentFigure, isPreviewableImage, dispatchCustomEvent, parseHtml, dispatch, generateDomId } from './lexxy_helpers.esm.js';
|
|
20
20
|
export { highlightCode as highlightAll, highlightCode } from './lexxy_helpers.esm.js';
|
|
21
21
|
import { buildEditorFromExtensions } from '@lexical/extension';
|
|
@@ -146,6 +146,7 @@ function buildConfig() {
|
|
|
146
146
|
return {
|
|
147
147
|
ALLOWED_TAGS: ALLOWED_HTML_TAGS.concat(Lexxy.global.get("attachmentTagName")),
|
|
148
148
|
ALLOWED_ATTR: ALLOWED_HTML_ATTRIBUTES,
|
|
149
|
+
ADD_URI_SAFE_ATTR: [ "caption", "filename" ],
|
|
149
150
|
SAFE_FOR_XML: false // So that it does not strip attributes that contains serialized HTML (like content)
|
|
150
151
|
}
|
|
151
152
|
}
|
|
@@ -378,6 +379,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
378
379
|
super();
|
|
379
380
|
this.internals = this.attachInternals();
|
|
380
381
|
this.internals.role = "toolbar";
|
|
382
|
+
|
|
383
|
+
this.#createEditorPromise();
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
connectedCallback() {
|
|
@@ -410,14 +413,26 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
410
413
|
this.#refreshToolbarOverflow();
|
|
411
414
|
this.#bindFocusListeners();
|
|
412
415
|
|
|
416
|
+
this.resolveEditorPromise(editorElement);
|
|
417
|
+
|
|
413
418
|
this.toggleAttribute("connected", true);
|
|
414
419
|
}
|
|
415
420
|
|
|
421
|
+
async getEditorElement() {
|
|
422
|
+
return this.editorElement || await this.editorPromise
|
|
423
|
+
}
|
|
424
|
+
|
|
416
425
|
#reconnect() {
|
|
417
426
|
this.disconnectedCallback();
|
|
418
427
|
this.connectedCallback();
|
|
419
428
|
}
|
|
420
429
|
|
|
430
|
+
#createEditorPromise() {
|
|
431
|
+
this.editorPromise = new Promise((resolve) => {
|
|
432
|
+
this.resolveEditorPromise = resolve;
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
421
436
|
#installResizeObserver() {
|
|
422
437
|
this.resizeObserver = new ResizeObserver(() => this.#refreshToolbarOverflow());
|
|
423
438
|
this.resizeObserver.observe(this);
|
|
@@ -486,28 +501,24 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
486
501
|
}
|
|
487
502
|
|
|
488
503
|
#bindFocusListeners() {
|
|
489
|
-
this.editorElement.addEventListener("lexxy:focus", this.#
|
|
490
|
-
this.editorElement.addEventListener("lexxy:blur", this.#
|
|
491
|
-
this.addEventListener("focusout", this.#handleFocusOut);
|
|
504
|
+
this.editorElement.addEventListener("lexxy:focus", this.#handleEditorFocus);
|
|
505
|
+
this.editorElement.addEventListener("lexxy:blur", this.#handleEditorBlur);
|
|
492
506
|
this.addEventListener("keydown", this.#handleKeydown);
|
|
493
507
|
}
|
|
494
508
|
|
|
495
509
|
#unbindFocusListeners() {
|
|
496
|
-
this.editorElement.removeEventListener("lexxy:focus", this.#
|
|
497
|
-
this.editorElement.removeEventListener("lexxy:blur", this.#
|
|
498
|
-
this.removeEventListener("focusout", this.#handleFocusOut);
|
|
510
|
+
this.editorElement.removeEventListener("lexxy:focus", this.#handleEditorFocus);
|
|
511
|
+
this.editorElement.removeEventListener("lexxy:blur", this.#handleEditorBlur);
|
|
499
512
|
this.removeEventListener("keydown", this.#handleKeydown);
|
|
500
513
|
}
|
|
501
514
|
|
|
502
|
-
#
|
|
503
|
-
this.#resetTabIndexValues();
|
|
515
|
+
#handleEditorFocus = () => {
|
|
504
516
|
this.#focusableItems[0].tabIndex = 0;
|
|
505
517
|
}
|
|
506
518
|
|
|
507
|
-
#
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
519
|
+
#handleEditorBlur = () => {
|
|
520
|
+
this.#resetTabIndexValues();
|
|
521
|
+
this.#closeDropdowns();
|
|
511
522
|
}
|
|
512
523
|
|
|
513
524
|
#handleKeydown = (event) => {
|
|
@@ -521,11 +532,13 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
521
532
|
}
|
|
522
533
|
|
|
523
534
|
#monitorSelectionChanges() {
|
|
524
|
-
this.editor.
|
|
525
|
-
|
|
535
|
+
this.editor.registerCommand(
|
|
536
|
+
SELECTION_CHANGE_COMMAND,
|
|
537
|
+
() => {
|
|
538
|
+
this.#closeDropdowns();
|
|
526
539
|
this.#updateButtonStates();
|
|
527
|
-
|
|
528
|
-
|
|
540
|
+
return false
|
|
541
|
+
}, COMMAND_PRIORITY_HIGH);
|
|
529
542
|
}
|
|
530
543
|
|
|
531
544
|
#monitorHistoryChanges() {
|
|
@@ -614,11 +627,13 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
614
627
|
}
|
|
615
628
|
|
|
616
629
|
#toolbarIsOverflowing() {
|
|
617
|
-
|
|
630
|
+
// Safari can report inconsistent clientWidth values on more than 100% window zoom level,
|
|
631
|
+
// that was affecting the toolbar overflow calculation. We're adding +1 to get around this issue.
|
|
632
|
+
return (this.scrollWidth - this.#overflow.clientWidth) > this.clientWidth + 1
|
|
618
633
|
}
|
|
619
634
|
|
|
620
635
|
#refreshToolbarOverflow = () => {
|
|
621
|
-
this.#
|
|
636
|
+
this.#resetToolbarOverflow();
|
|
622
637
|
this.#compactMenu();
|
|
623
638
|
|
|
624
639
|
this.#overflow.style.display = this.#overflowMenu.children.length ? "block" : "none";
|
|
@@ -644,7 +659,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
644
659
|
}
|
|
645
660
|
}
|
|
646
661
|
|
|
647
|
-
#
|
|
662
|
+
#resetToolbarOverflow() {
|
|
648
663
|
const items = Array.from(this.#overflowMenu.children);
|
|
649
664
|
items.sort((a, b) => this.#itemPosition(b) - this.#itemPosition(a));
|
|
650
665
|
|
|
@@ -666,6 +681,16 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
666
681
|
});
|
|
667
682
|
}
|
|
668
683
|
|
|
684
|
+
#closeDropdowns() {
|
|
685
|
+
this.#dropdowns.forEach((details) => {
|
|
686
|
+
details.open = false;
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
get #dropdowns() {
|
|
691
|
+
return this.querySelectorAll("details")
|
|
692
|
+
}
|
|
693
|
+
|
|
669
694
|
get #overflow() {
|
|
670
695
|
return this.querySelector(".lexxy-editor__toolbar-overflow")
|
|
671
696
|
}
|
|
@@ -707,9 +732,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
707
732
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.65422 0.711575C7.1856 0.242951 6.42579 0.242951 5.95717 0.711575C5.48853 1.18021 5.48853 1.94 5.95717 2.40864L8.70864 5.16011L2.85422 11.0145C1.44834 12.4204 1.44833 14.6998 2.85422 16.1057L7.86011 21.1115C9.26599 22.5174 11.5454 22.5174 12.9513 21.1115L19.6542 14.4087C20.1228 13.94 20.1228 13.1802 19.6542 12.7115L11.8544 4.91171L11.2542 4.31158L7.65422 0.711575ZM4.55127 12.7115L10.4057 6.85716L17.1087 13.56H4.19981C4.19981 13.253 4.31696 12.9459 4.55127 12.7115ZM23.6057 20.76C23.6057 22.0856 22.5311 23.16 21.2057 23.16C19.8802 23.16 18.8057 22.0856 18.8057 20.76C18.8057 19.5408 19.8212 18.5339 20.918 17.4462C21.0135 17.3516 21.1096 17.2563 21.2057 17.16C21.3018 17.2563 21.398 17.3516 21.4935 17.4462C22.5903 18.5339 23.6057 19.5408 23.6057 20.76Z"/></svg>
|
|
708
733
|
</summary>
|
|
709
734
|
<lexxy-highlight-dropdown class="lexxy-editor__toolbar-dropdown-content">
|
|
710
|
-
<div
|
|
711
|
-
<
|
|
712
|
-
<button data-command="removeHighlight" class="lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
|
|
735
|
+
<div class="lexxy-highlight-colors"></div>
|
|
736
|
+
<button data-command="removeHighlight" class="lexxy-editor__toolbar-button lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
|
|
713
737
|
</lexxy-highlight-dropdown>
|
|
714
738
|
</details>
|
|
715
739
|
|
|
@@ -721,8 +745,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
721
745
|
<form method="dialog">
|
|
722
746
|
<input type="url" placeholder="Enter a URL…" class="input">
|
|
723
747
|
<div class="lexxy-editor__toolbar-dropdown-actions">
|
|
724
|
-
<button type="submit" class="
|
|
725
|
-
<button type="button" class="
|
|
748
|
+
<button type="submit" class="lexxy-editor__toolbar-button" value="link">Link</button>
|
|
749
|
+
<button type="button" class="lexxy-editor__toolbar-button" value="unlink">Unlink</button>
|
|
726
750
|
</div>
|
|
727
751
|
</form>
|
|
728
752
|
</lexxy-link-dropdown>
|
|
@@ -770,9 +794,9 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
770
794
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M18.2599 8.26531C15.9672 6.56386 13.1237 5.77629 10.2823 6.05535C7.4408 6.33452 4.80455 7.66079 2.88681 9.77605C1.32245 11.5016 0.326407 13.6516 0.0127834 15.9352C-0.105117 16.7939 0.608975 17.4997 1.47567 17.4997C2.34228 17.4997 3.02969 16.7915 3.19149 15.9401C3.47682 14.4379 4.17156 13.0321 5.212 11.8844C6.60637 10.3464 8.52287 9.38139 10.589 9.17839C12.655 8.97546 14.7227 9.54856 16.3897 10.7858C17.5237 11.6275 18.4165 12.7361 18.9991 13.9997H15.4063C14.578 13.9997 13.9066 14.6714 13.9063 15.4997C13.9063 16.3281 14.5779 16.9997 15.4063 16.9997H22.4063C23.2348 16.9997 23.9063 16.3281 23.9063 15.4997V8.49968C23.9061 7.67144 23.2346 6.99968 22.4063 6.99968C21.578 6.99968 20.9066 7.67144 20.9063 8.49968V11.0212C20.1897 9.9704 19.2984 9.03613 18.2599 8.26531Z"/></svg>
|
|
771
795
|
</button>
|
|
772
796
|
|
|
773
|
-
<details class="lexxy-editor__toolbar-overflow">
|
|
797
|
+
<details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-overflow" name="lexxy-dropdown">
|
|
774
798
|
<summary class="lexxy-editor__toolbar-button" aria-label="Show more toolbar buttons">•••</summary>
|
|
775
|
-
<div class="lexxy-editor__toolbar-overflow-menu" aria-label="More toolbar buttons"></div>
|
|
799
|
+
<div class="lexxy-editor__toolbar-dropdown-content lexxy-editor__toolbar-overflow-menu" aria-label="More toolbar buttons"></div>
|
|
776
800
|
</details>
|
|
777
801
|
`
|
|
778
802
|
}
|
|
@@ -792,6 +816,8 @@ var theme = {
|
|
|
792
816
|
tableCellSelected: "lexxy-content__table-cell--selected",
|
|
793
817
|
tableSelection: "lexxy-content__table--selection",
|
|
794
818
|
tableScrollableWrapper: "lexxy-content__table-wrapper",
|
|
819
|
+
tableCellHighlight: "lexxy-content__table-cell--highlight",
|
|
820
|
+
tableCellFocus: "lexxy-content__table-cell--focus",
|
|
795
821
|
list: {
|
|
796
822
|
nested: {
|
|
797
823
|
listitem: "lexxy-nested-listitem",
|
|
@@ -1373,30 +1399,6 @@ class HorizontalDividerNode extends DecoratorNode {
|
|
|
1373
1399
|
}
|
|
1374
1400
|
}
|
|
1375
1401
|
|
|
1376
|
-
class WrappedTableNode extends TableNode {
|
|
1377
|
-
static clone(node) {
|
|
1378
|
-
return new WrappedTableNode(node.__key)
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
exportDOM(editor) {
|
|
1382
|
-
const superExport = super.exportDOM(editor);
|
|
1383
|
-
|
|
1384
|
-
return {
|
|
1385
|
-
...superExport,
|
|
1386
|
-
after: (tableElement) => {
|
|
1387
|
-
if (superExport.after) {
|
|
1388
|
-
tableElement = superExport.after(tableElement);
|
|
1389
|
-
const clonedTable = tableElement.cloneNode(true);
|
|
1390
|
-
const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML);
|
|
1391
|
-
return wrappedTable
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
return tableElement
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
1402
|
const COMMANDS = [
|
|
1401
1403
|
"bold",
|
|
1402
1404
|
"italic",
|
|
@@ -1414,13 +1416,6 @@ const COMMANDS = [
|
|
|
1414
1416
|
"uploadAttachments",
|
|
1415
1417
|
|
|
1416
1418
|
"insertTable",
|
|
1417
|
-
"insertTableRowAbove",
|
|
1418
|
-
"insertTableRowBelow",
|
|
1419
|
-
"insertTableColumnAfter",
|
|
1420
|
-
"insertTableColumnBefore",
|
|
1421
|
-
"deleteTableRow",
|
|
1422
|
-
"deleteTableColumn",
|
|
1423
|
-
"deleteTable",
|
|
1424
1419
|
|
|
1425
1420
|
"undo",
|
|
1426
1421
|
"redo"
|
|
@@ -1529,9 +1524,7 @@ class CommandDispatcher {
|
|
|
1529
1524
|
}
|
|
1530
1525
|
|
|
1531
1526
|
dispatchInsertHorizontalDivider() {
|
|
1532
|
-
this.
|
|
1533
|
-
this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
|
|
1534
|
-
});
|
|
1527
|
+
this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
|
|
1535
1528
|
|
|
1536
1529
|
this.editor.focus();
|
|
1537
1530
|
}
|
|
@@ -1593,41 +1586,6 @@ class CommandDispatcher {
|
|
|
1593
1586
|
this.editor.dispatchCommand(INSERT_TABLE_COMMAND, { "rows": 3, "columns": 3, "includeHeaders": true });
|
|
1594
1587
|
}
|
|
1595
1588
|
|
|
1596
|
-
dispatchInsertTableRowBelow() {
|
|
1597
|
-
$insertTableRowAtSelection(true);
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
dispatchInsertTableRowAbove() {
|
|
1601
|
-
$insertTableRowAtSelection(false);
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
dispatchInsertTableColumnAfter() {
|
|
1605
|
-
$insertTableColumnAtSelection(true);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
dispatchInsertTableColumnBefore() {
|
|
1609
|
-
$insertTableColumnAtSelection(false);
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
dispatchDeleteTableRow() {
|
|
1613
|
-
$deleteTableRowAtSelection();
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
dispatchDeleteTableColumn() {
|
|
1617
|
-
$deleteTableColumnAtSelection();
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
dispatchDeleteTable() {
|
|
1621
|
-
this.editor.update(() => {
|
|
1622
|
-
const selection = $getSelection();
|
|
1623
|
-
if (!$isRangeSelection(selection)) return
|
|
1624
|
-
|
|
1625
|
-
const anchorNode = selection.anchor.getNode();
|
|
1626
|
-
const tableNode = $findTableNode(anchorNode);
|
|
1627
|
-
tableNode.remove();
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
1589
|
dispatchUndo() {
|
|
1632
1590
|
this.editor.dispatchCommand(UNDO_COMMAND, undefined);
|
|
1633
1591
|
}
|
|
@@ -1921,6 +1879,14 @@ class Selection {
|
|
|
1921
1879
|
return $getNearestNodeOfType(anchorNode, CodeNode) !== null
|
|
1922
1880
|
}
|
|
1923
1881
|
|
|
1882
|
+
get isTableCellSelected() {
|
|
1883
|
+
const selection = $getSelection();
|
|
1884
|
+
if (!$isRangeSelection(selection)) return false
|
|
1885
|
+
|
|
1886
|
+
const anchorNode = selection.anchor.getNode();
|
|
1887
|
+
return $getNearestNodeOfType(anchorNode, TableCellNode) !== null
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1924
1890
|
get nodeAfterCursor() {
|
|
1925
1891
|
const { anchorNode, offset } = this.#getCollapsedSelectionData();
|
|
1926
1892
|
if (!anchorNode) return null
|
|
@@ -2433,17 +2399,6 @@ function sanitize(html) {
|
|
|
2433
2399
|
return DOMPurify.sanitize(html, buildConfig())
|
|
2434
2400
|
}
|
|
2435
2401
|
|
|
2436
|
-
// Prevent the hardcoded background color
|
|
2437
|
-
// A background color value is set by Lexical if background is null:
|
|
2438
|
-
// https://github.com/facebook/lexical/blob/5bbbe849bd229e1db0e7b536e6a919520ada7bb2/packages/lexical-table/src/LexicalTableCellNode.ts#L187
|
|
2439
|
-
function registerHeaderBackgroundTransform(editor) {
|
|
2440
|
-
return editor.registerNodeTransform(TableCellNode, (node) => {
|
|
2441
|
-
if (node.getBackgroundColor() === null) {
|
|
2442
|
-
node.setBackgroundColor("");
|
|
2443
|
-
}
|
|
2444
|
-
})
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
2402
|
function dasherize(value) {
|
|
2448
2403
|
return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`)
|
|
2449
2404
|
}
|
|
@@ -2467,6 +2422,10 @@ function filterMatches(text, potentialMatch) {
|
|
|
2467
2422
|
return normalizeFilteredText(text).includes(normalizeFilteredText(potentialMatch))
|
|
2468
2423
|
}
|
|
2469
2424
|
|
|
2425
|
+
function upcaseFirst(string) {
|
|
2426
|
+
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2470
2429
|
class EditorConfiguration {
|
|
2471
2430
|
#editorElement
|
|
2472
2431
|
#config
|
|
@@ -2908,15 +2867,14 @@ class Contents {
|
|
|
2908
2867
|
new FormatEscaper(editorElement).monitor();
|
|
2909
2868
|
}
|
|
2910
2869
|
|
|
2911
|
-
insertHtml(html) {
|
|
2870
|
+
insertHtml(html, { tag } = {}) {
|
|
2912
2871
|
this.editor.update(() => {
|
|
2913
2872
|
const selection = $getSelection();
|
|
2914
|
-
|
|
2915
2873
|
if (!$isRangeSelection(selection)) return
|
|
2916
2874
|
|
|
2917
2875
|
const nodes = $generateNodesFromDOM(this.editor, parseHtml(html));
|
|
2918
2876
|
selection.insertNodes(nodes);
|
|
2919
|
-
});
|
|
2877
|
+
}, { tag });
|
|
2920
2878
|
}
|
|
2921
2879
|
|
|
2922
2880
|
insertAtCursor(node) {
|
|
@@ -3719,14 +3677,14 @@ class Clipboard {
|
|
|
3719
3677
|
|
|
3720
3678
|
#pasteMarkdown(text) {
|
|
3721
3679
|
const html = marked(text);
|
|
3722
|
-
this.contents.insertHtml(html);
|
|
3680
|
+
this.contents.insertHtml(html, { tag: [ PASTE_TAG ] });
|
|
3723
3681
|
}
|
|
3724
3682
|
|
|
3725
3683
|
#pasteRichText(clipboardData) {
|
|
3726
3684
|
this.editor.update(() => {
|
|
3727
3685
|
const selection = $getSelection();
|
|
3728
3686
|
$insertDataTransferForRichText(clipboardData, selection, this.editor);
|
|
3729
|
-
});
|
|
3687
|
+
}, { tag: PASTE_TAG });
|
|
3730
3688
|
}
|
|
3731
3689
|
|
|
3732
3690
|
#handlePastedFiles(clipboardData) {
|
|
@@ -3960,6 +3918,124 @@ function $applyLanguage(conversionOutput, element) {
|
|
|
3960
3918
|
conversionOutput.node.setLanguage(language);
|
|
3961
3919
|
}
|
|
3962
3920
|
|
|
3921
|
+
class WrappedTableNode extends TableNode {
|
|
3922
|
+
static clone(node) {
|
|
3923
|
+
return new WrappedTableNode(node.__key)
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
exportDOM(editor) {
|
|
3927
|
+
const superExport = super.exportDOM(editor);
|
|
3928
|
+
|
|
3929
|
+
return {
|
|
3930
|
+
...superExport,
|
|
3931
|
+
after: (tableElement) => {
|
|
3932
|
+
if (superExport.after) {
|
|
3933
|
+
tableElement = superExport.after(tableElement);
|
|
3934
|
+
const clonedTable = tableElement.cloneNode(true);
|
|
3935
|
+
const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML);
|
|
3936
|
+
return wrappedTable
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
return tableElement
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
|
|
3945
|
+
const TablesLexicalExtension = defineExtension({
|
|
3946
|
+
name: "lexxy/tables",
|
|
3947
|
+
nodes: [
|
|
3948
|
+
WrappedTableNode,
|
|
3949
|
+
{
|
|
3950
|
+
replace: TableNode,
|
|
3951
|
+
with: () => new WrappedTableNode()
|
|
3952
|
+
},
|
|
3953
|
+
TableCellNode,
|
|
3954
|
+
TableRowNode
|
|
3955
|
+
],
|
|
3956
|
+
register(editor) {
|
|
3957
|
+
// Register Lexical table plugins
|
|
3958
|
+
registerTablePlugin(editor);
|
|
3959
|
+
registerTableSelectionObserver(editor, true);
|
|
3960
|
+
setScrollableTablesActive(editor, true);
|
|
3961
|
+
|
|
3962
|
+
// Bug fix: Prevent hardcoded background color (Lexical #8089)
|
|
3963
|
+
editor.registerNodeTransform(TableCellNode, (node) => {
|
|
3964
|
+
if (node.getBackgroundColor() === null) {
|
|
3965
|
+
node.setBackgroundColor("");
|
|
3966
|
+
}
|
|
3967
|
+
});
|
|
3968
|
+
|
|
3969
|
+
// Bug fix: Fix column header states (Lexical #8090)
|
|
3970
|
+
editor.registerNodeTransform(TableCellNode, (node) => {
|
|
3971
|
+
const headerState = node.getHeaderStyles();
|
|
3972
|
+
|
|
3973
|
+
if (headerState !== TableCellHeaderStates.ROW) return
|
|
3974
|
+
|
|
3975
|
+
const rowParent = node.getParent();
|
|
3976
|
+
const tableNode = rowParent?.getParent();
|
|
3977
|
+
if (!tableNode) return
|
|
3978
|
+
|
|
3979
|
+
const rows = tableNode.getChildren();
|
|
3980
|
+
const cellIndex = rowParent.getChildren().indexOf(node);
|
|
3981
|
+
|
|
3982
|
+
const cellsInRow = rowParent.getChildren();
|
|
3983
|
+
const isHeaderRow = cellsInRow.every(cell =>
|
|
3984
|
+
cell.getHeaderStyles() !== TableCellHeaderStates.NO_STATUS
|
|
3985
|
+
);
|
|
3986
|
+
|
|
3987
|
+
const isHeaderColumn = rows.every(row => {
|
|
3988
|
+
const cell = row.getChildren()[cellIndex];
|
|
3989
|
+
return cell && cell.getHeaderStyles() !== TableCellHeaderStates.NO_STATUS
|
|
3990
|
+
});
|
|
3991
|
+
|
|
3992
|
+
let newHeaderState = TableCellHeaderStates.NO_STATUS;
|
|
3993
|
+
|
|
3994
|
+
if (isHeaderRow) {
|
|
3995
|
+
newHeaderState |= TableCellHeaderStates.ROW;
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
if (isHeaderColumn) {
|
|
3999
|
+
newHeaderState |= TableCellHeaderStates.COLUMN;
|
|
4000
|
+
}
|
|
4001
|
+
|
|
4002
|
+
if (newHeaderState !== headerState) {
|
|
4003
|
+
node.setHeaderStyles(newHeaderState, TableCellHeaderStates.BOTH);
|
|
4004
|
+
}
|
|
4005
|
+
});
|
|
4006
|
+
|
|
4007
|
+
editor.registerCommand("insertTableRowAfter", () => {
|
|
4008
|
+
$insertTableRowAtSelection(true);
|
|
4009
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4010
|
+
|
|
4011
|
+
editor.registerCommand("insertTableRowBefore", () => {
|
|
4012
|
+
$insertTableRowAtSelection(false);
|
|
4013
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4014
|
+
|
|
4015
|
+
editor.registerCommand("insertTableColumnAfter", () => {
|
|
4016
|
+
$insertTableColumnAtSelection(true);
|
|
4017
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4018
|
+
|
|
4019
|
+
editor.registerCommand("insertTableColumnBefore", () => {
|
|
4020
|
+
$insertTableColumnAtSelection(false);
|
|
4021
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4022
|
+
|
|
4023
|
+
editor.registerCommand("deleteTableRow", () => {
|
|
4024
|
+
$deleteTableRowAtSelection();
|
|
4025
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4026
|
+
|
|
4027
|
+
editor.registerCommand("deleteTableColumn", () => {
|
|
4028
|
+
$deleteTableColumnAtSelection();
|
|
4029
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4030
|
+
|
|
4031
|
+
editor.registerCommand("deleteTable", () => {
|
|
4032
|
+
const selection = $getSelection();
|
|
4033
|
+
if (!$isRangeSelection(selection)) return false
|
|
4034
|
+
$findTableNode(selection.anchor.getNode())?.remove();
|
|
4035
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4036
|
+
}
|
|
4037
|
+
});
|
|
4038
|
+
|
|
3963
4039
|
class LexicalEditorElement extends HTMLElement {
|
|
3964
4040
|
static formAssociated = true
|
|
3965
4041
|
static debug = false
|
|
@@ -4151,8 +4227,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4151
4227
|
this.#registerComponents();
|
|
4152
4228
|
this.#listenForInvalidatedNodes();
|
|
4153
4229
|
this.#handleEnter();
|
|
4154
|
-
this.#
|
|
4155
|
-
this.#handleTables();
|
|
4230
|
+
this.#registerFocusEvents();
|
|
4156
4231
|
this.#attachDebugHooks();
|
|
4157
4232
|
this.#attachToolbar();
|
|
4158
4233
|
this.#loadInitialValue();
|
|
@@ -4180,7 +4255,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4180
4255
|
const extensions = [ ];
|
|
4181
4256
|
const richTextExtensions = [
|
|
4182
4257
|
this.highlighter.lexicalExtension,
|
|
4183
|
-
TrixContentExtension
|
|
4258
|
+
TrixContentExtension,
|
|
4259
|
+
TablesLexicalExtension
|
|
4184
4260
|
];
|
|
4185
4261
|
|
|
4186
4262
|
if (this.supportsRichText) {
|
|
@@ -4205,14 +4281,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4205
4281
|
CodeHighlightNode,
|
|
4206
4282
|
LinkNode,
|
|
4207
4283
|
AutoLinkNode,
|
|
4208
|
-
HorizontalDividerNode
|
|
4209
|
-
WrappedTableNode,
|
|
4210
|
-
{
|
|
4211
|
-
replace: TableNode,
|
|
4212
|
-
with: () => { return new WrappedTableNode() }
|
|
4213
|
-
},
|
|
4214
|
-
TableCellNode,
|
|
4215
|
-
TableRowNode,
|
|
4284
|
+
HorizontalDividerNode
|
|
4216
4285
|
);
|
|
4217
4286
|
}
|
|
4218
4287
|
|
|
@@ -4326,11 +4395,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4326
4395
|
}
|
|
4327
4396
|
|
|
4328
4397
|
#registerTableComponents() {
|
|
4329
|
-
|
|
4330
|
-
this.
|
|
4331
|
-
this.append(this.tableHandler);
|
|
4332
|
-
|
|
4333
|
-
this.#addUnregisterHandler(registerHeaderBackgroundTransform(this.editor));
|
|
4398
|
+
this.tableTools = createElement("lexxy-table-tools");
|
|
4399
|
+
this.append(this.tableTools);
|
|
4334
4400
|
}
|
|
4335
4401
|
|
|
4336
4402
|
#registerCodeHiglightingComponents() {
|
|
@@ -4377,12 +4443,27 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4377
4443
|
);
|
|
4378
4444
|
}
|
|
4379
4445
|
|
|
4380
|
-
#
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4446
|
+
#registerFocusEvents() {
|
|
4447
|
+
this.addEventListener("focusin", this.#handleFocusIn);
|
|
4448
|
+
this.addEventListener("focusout", this.#handleFocusOut);
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
#handleFocusIn(event) {
|
|
4452
|
+
if (this.#elementInEditorOrToolbar(event.target) && !this.currentlyFocused) {
|
|
4453
|
+
dispatch(this, "lexxy:focus");
|
|
4454
|
+
this.currentlyFocused = true;
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
|
|
4458
|
+
#handleFocusOut(event) {
|
|
4459
|
+
if (!this.#elementInEditorOrToolbar(event.relatedTarget)) {
|
|
4460
|
+
dispatch(this, "lexxy:blur");
|
|
4461
|
+
this.currentlyFocused = false;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
#elementInEditorOrToolbar(element) {
|
|
4466
|
+
return this.contains(element) || this.toolbarElement?.contains(element)
|
|
4386
4467
|
}
|
|
4387
4468
|
|
|
4388
4469
|
#onFocus() {
|
|
@@ -4399,12 +4480,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4399
4480
|
}
|
|
4400
4481
|
}
|
|
4401
4482
|
|
|
4402
|
-
#handleTables() {
|
|
4403
|
-
if (this.supportsRichText) {
|
|
4404
|
-
this.removeTableSelectionObserver = registerTableSelectionObserver(this.editor, true);
|
|
4405
|
-
setScrollableTablesActive(this.editor, true);
|
|
4406
|
-
}
|
|
4407
|
-
}
|
|
4408
4483
|
|
|
4409
4484
|
#attachDebugHooks() {
|
|
4410
4485
|
if (!LexicalEditorElement.debug) return
|
|
@@ -4502,10 +4577,11 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4502
4577
|
|
|
4503
4578
|
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
4504
4579
|
this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
4580
|
+
|
|
4581
|
+
this.#onToolbarEditor(this.initialize.bind(this));
|
|
4505
4582
|
}
|
|
4506
4583
|
|
|
4507
4584
|
disconnectedCallback() {
|
|
4508
|
-
this.#removeClickOutsideHandler();
|
|
4509
4585
|
this.container.removeEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
4510
4586
|
}
|
|
4511
4587
|
|
|
@@ -4521,46 +4597,29 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4521
4597
|
return this.toolbar.editor
|
|
4522
4598
|
}
|
|
4523
4599
|
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
}
|
|
4527
|
-
|
|
4528
|
-
#handleToggle(event) {
|
|
4529
|
-
if (this.container.open) {
|
|
4530
|
-
this.#handleOpen(event.target);
|
|
4531
|
-
} else {
|
|
4532
|
-
this.#handleClose();
|
|
4533
|
-
}
|
|
4600
|
+
initialize() {
|
|
4601
|
+
// Any post-editor initialization
|
|
4534
4602
|
}
|
|
4535
4603
|
|
|
4536
|
-
|
|
4537
|
-
this.#interactiveElements[0].focus();
|
|
4538
|
-
this.#setupClickOutsideHandler();
|
|
4539
|
-
|
|
4540
|
-
this.#resetTabIndexValues();
|
|
4541
|
-
}
|
|
4542
|
-
|
|
4543
|
-
#handleClose() {
|
|
4544
|
-
this.#removeClickOutsideHandler();
|
|
4604
|
+
close() {
|
|
4545
4605
|
this.editor.focus();
|
|
4606
|
+
this.container.open = false;
|
|
4546
4607
|
}
|
|
4547
4608
|
|
|
4548
|
-
#
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
this.clickOutsideHandler = this.#handleClickOutside.bind(this);
|
|
4552
|
-
document.addEventListener("click", this.clickOutsideHandler, true);
|
|
4609
|
+
async #onToolbarEditor(callback) {
|
|
4610
|
+
await this.toolbar.editorConnected;
|
|
4611
|
+
callback();
|
|
4553
4612
|
}
|
|
4554
4613
|
|
|
4555
|
-
#
|
|
4556
|
-
if (
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
this.clickOutsideHandler = null;
|
|
4614
|
+
#handleToggle() {
|
|
4615
|
+
if (this.container.open) {
|
|
4616
|
+
this.#handleOpen();
|
|
4617
|
+
}
|
|
4560
4618
|
}
|
|
4561
4619
|
|
|
4562
|
-
#
|
|
4563
|
-
|
|
4620
|
+
async #handleOpen() {
|
|
4621
|
+
this.#interactiveElements[0].focus();
|
|
4622
|
+
this.#resetTabIndexValues();
|
|
4564
4623
|
}
|
|
4565
4624
|
|
|
4566
4625
|
#handleKeyDown(event) {
|
|
@@ -4648,19 +4707,14 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
|
4648
4707
|
const NO_STYLE = Symbol("no_style");
|
|
4649
4708
|
|
|
4650
4709
|
class HighlightDropdown extends ToolbarDropdown {
|
|
4651
|
-
#initialized = false
|
|
4652
|
-
|
|
4653
4710
|
connectedCallback() {
|
|
4654
4711
|
super.connectedCallback();
|
|
4655
4712
|
this.#registerToggleHandler();
|
|
4656
4713
|
}
|
|
4657
4714
|
|
|
4658
|
-
|
|
4659
|
-
if (this.#initialized) return
|
|
4660
|
-
|
|
4715
|
+
initialize() {
|
|
4661
4716
|
this.#setUpButtons();
|
|
4662
4717
|
this.#registerButtonHandlers();
|
|
4663
|
-
this.#initialized = true;
|
|
4664
4718
|
}
|
|
4665
4719
|
|
|
4666
4720
|
#registerToggleHandler() {
|
|
@@ -4673,16 +4727,18 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4673
4727
|
}
|
|
4674
4728
|
|
|
4675
4729
|
#setUpButtons() {
|
|
4676
|
-
this
|
|
4677
|
-
|
|
4678
|
-
|
|
4730
|
+
const colorGroups = this.editorElement.config.get("highlight.buttons");
|
|
4731
|
+
|
|
4732
|
+
this.#populateButtonGroup("color", colorGroups.color);
|
|
4733
|
+
this.#populateButtonGroup("background-color", colorGroups["background-color"]);
|
|
4734
|
+
|
|
4735
|
+
const maxNumberOfColors = Math.max(colorGroups.color.length, colorGroups["background-color"].length);
|
|
4736
|
+
this.style.setProperty("--max-colors", maxNumberOfColors);
|
|
4679
4737
|
}
|
|
4680
4738
|
|
|
4681
|
-
#populateButtonGroup(
|
|
4682
|
-
const attribute = buttonGroup.dataset.buttonGroup;
|
|
4683
|
-
const values = this.editorElement.config.get(`highlight.buttons.${attribute}`) || [];
|
|
4739
|
+
#populateButtonGroup(attribute, values) {
|
|
4684
4740
|
values.forEach((value, index) => {
|
|
4685
|
-
|
|
4741
|
+
this.#buttonContainer.appendChild(this.#createButton(attribute, value, index));
|
|
4686
4742
|
});
|
|
4687
4743
|
}
|
|
4688
4744
|
|
|
@@ -4691,15 +4747,13 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4691
4747
|
button.dataset.style = attribute;
|
|
4692
4748
|
button.style.setProperty(attribute, value);
|
|
4693
4749
|
button.dataset.value = value;
|
|
4694
|
-
button.classList.add("lexxy-highlight-button");
|
|
4750
|
+
button.classList.add("lexxy-editor__toolbar-button", "lexxy-highlight-button");
|
|
4695
4751
|
button.name = attribute + "-" + index;
|
|
4696
4752
|
return button
|
|
4697
4753
|
}
|
|
4698
4754
|
|
|
4699
4755
|
#handleToggle({ newState }) {
|
|
4700
4756
|
if (newState === "open") {
|
|
4701
|
-
this.#ensureInitialized();
|
|
4702
|
-
|
|
4703
4757
|
this.editor.getEditorState().read(() => {
|
|
4704
4758
|
this.#updateColorButtonStates($getSelection());
|
|
4705
4759
|
});
|
|
@@ -4742,8 +4796,8 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4742
4796
|
this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).disabled = !hasHighlight;
|
|
4743
4797
|
}
|
|
4744
4798
|
|
|
4745
|
-
get #
|
|
4746
|
-
return this.
|
|
4799
|
+
get #buttonContainer() {
|
|
4800
|
+
return this.querySelector(".lexxy-highlight-colors")
|
|
4747
4801
|
}
|
|
4748
4802
|
|
|
4749
4803
|
get #colorButtons() {
|
|
@@ -4753,501 +4807,714 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4753
4807
|
|
|
4754
4808
|
customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
|
|
4755
4809
|
|
|
4756
|
-
class
|
|
4757
|
-
|
|
4758
|
-
this
|
|
4759
|
-
this
|
|
4760
|
-
this
|
|
4810
|
+
class TableController {
|
|
4811
|
+
constructor(editorElement) {
|
|
4812
|
+
this.editor = editorElement.editor;
|
|
4813
|
+
this.contents = editorElement.contents;
|
|
4814
|
+
this.selection = editorElement.selection;
|
|
4815
|
+
|
|
4816
|
+
this.currentTableNodeKey = null;
|
|
4817
|
+
this.currentCellKey = null;
|
|
4818
|
+
|
|
4819
|
+
this.#registerKeyHandlers();
|
|
4761
4820
|
}
|
|
4762
4821
|
|
|
4763
|
-
|
|
4764
|
-
this
|
|
4822
|
+
destroy() {
|
|
4823
|
+
this.currentTableNodeKey = null;
|
|
4824
|
+
this.currentCellKey = null;
|
|
4825
|
+
|
|
4826
|
+
this.#unregisterKeyHandlers();
|
|
4765
4827
|
}
|
|
4766
4828
|
|
|
4767
|
-
get
|
|
4768
|
-
|
|
4829
|
+
get currentCell() {
|
|
4830
|
+
if (!this.currentCellKey) return null
|
|
4831
|
+
|
|
4832
|
+
return this.editor.getEditorState().read(() => {
|
|
4833
|
+
const cell = $getNodeByKey(this.currentCellKey);
|
|
4834
|
+
return (cell instanceof TableCellNode) ? cell : null
|
|
4835
|
+
})
|
|
4769
4836
|
}
|
|
4770
4837
|
|
|
4771
|
-
get
|
|
4772
|
-
|
|
4838
|
+
get currentTableNode() {
|
|
4839
|
+
if (!this.currentTableNodeKey) return null
|
|
4840
|
+
|
|
4841
|
+
return this.editor.getEditorState().read(() => {
|
|
4842
|
+
const tableNode = $getNodeByKey(this.currentTableNodeKey);
|
|
4843
|
+
return (tableNode instanceof TableNode) ? tableNode : null
|
|
4844
|
+
})
|
|
4773
4845
|
}
|
|
4774
4846
|
|
|
4775
|
-
get
|
|
4776
|
-
const
|
|
4777
|
-
if (!$isRangeSelection(selection)) return null
|
|
4847
|
+
get currentRowCells() {
|
|
4848
|
+
const currentRowIndex = this.currentRowIndex;
|
|
4778
4849
|
|
|
4779
|
-
const
|
|
4780
|
-
|
|
4850
|
+
const rows = this.tableRows;
|
|
4851
|
+
if (!rows) return null
|
|
4852
|
+
|
|
4853
|
+
return this.editor.getEditorState().read(() => {
|
|
4854
|
+
return rows[currentRowIndex]?.getChildren() ?? null
|
|
4855
|
+
}) ?? null
|
|
4781
4856
|
}
|
|
4782
4857
|
|
|
4783
|
-
get
|
|
4784
|
-
const currentCell = this
|
|
4858
|
+
get currentRowIndex() {
|
|
4859
|
+
const currentCell = this.currentCell;
|
|
4785
4860
|
if (!currentCell) return 0
|
|
4786
|
-
|
|
4861
|
+
|
|
4862
|
+
return this.editor.getEditorState().read(() => {
|
|
4863
|
+
return $getTableRowIndexFromTableCellNode(currentCell)
|
|
4864
|
+
}) ?? 0
|
|
4787
4865
|
}
|
|
4788
4866
|
|
|
4789
|
-
get
|
|
4790
|
-
const
|
|
4791
|
-
|
|
4792
|
-
|
|
4867
|
+
get currentColumnCells() {
|
|
4868
|
+
const columnIndex = this.currentColumnIndex;
|
|
4869
|
+
|
|
4870
|
+
const rows = this.tableRows;
|
|
4871
|
+
if (!rows) return null
|
|
4872
|
+
|
|
4873
|
+
return this.editor.getEditorState().read(() => {
|
|
4874
|
+
return rows.map(row => row.getChildAtIndex(columnIndex))
|
|
4875
|
+
}) ?? null
|
|
4793
4876
|
}
|
|
4794
4877
|
|
|
4795
|
-
get
|
|
4796
|
-
|
|
4878
|
+
get currentColumnIndex() {
|
|
4879
|
+
const currentCell = this.currentCell;
|
|
4880
|
+
if (!currentCell) return 0
|
|
4881
|
+
|
|
4882
|
+
return this.editor.getEditorState().read(() => {
|
|
4883
|
+
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
4884
|
+
}) ?? 0
|
|
4797
4885
|
}
|
|
4798
4886
|
|
|
4799
|
-
|
|
4800
|
-
this.
|
|
4887
|
+
get tableRows() {
|
|
4888
|
+
return this.editor.getEditorState().read(() => {
|
|
4889
|
+
return this.currentTableNode?.getChildren()
|
|
4890
|
+
}) ?? null
|
|
4801
4891
|
}
|
|
4802
4892
|
|
|
4803
|
-
|
|
4804
|
-
|
|
4893
|
+
updateSelectedTable() {
|
|
4894
|
+
let cellNode = null;
|
|
4895
|
+
let tableNode = null;
|
|
4896
|
+
|
|
4897
|
+
this.editor.getEditorState().read(() => {
|
|
4898
|
+
const selection = $getSelection();
|
|
4899
|
+
if (!selection || !this.selection.isTableCellSelected) return
|
|
4900
|
+
|
|
4901
|
+
const node = selection.getNodes()[0];
|
|
4902
|
+
|
|
4903
|
+
cellNode = $findCellNode(node);
|
|
4904
|
+
tableNode = $findTableNode(node);
|
|
4905
|
+
});
|
|
4906
|
+
|
|
4907
|
+
this.currentCellKey = cellNode?.getKey() ?? null;
|
|
4908
|
+
this.currentTableNodeKey = tableNode?.getKey() ?? null;
|
|
4805
4909
|
}
|
|
4806
4910
|
|
|
4807
|
-
|
|
4808
|
-
if (
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
} else if (event.key === "Escape") {
|
|
4813
|
-
this.#editor.getEditorState().read(() => {
|
|
4814
|
-
const cell = this.#currentCell;
|
|
4815
|
-
if (!cell) return
|
|
4911
|
+
executeTableCommand(command, customIndex = null) {
|
|
4912
|
+
if (command.action === "delete" && command.childType === "table") {
|
|
4913
|
+
this.#deleteTable();
|
|
4914
|
+
return
|
|
4915
|
+
}
|
|
4816
4916
|
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
});
|
|
4821
|
-
this.#closeMoreMenu();
|
|
4917
|
+
if (command.action === "toggle") {
|
|
4918
|
+
this.#executeToggleStyle(command);
|
|
4919
|
+
return
|
|
4822
4920
|
}
|
|
4921
|
+
|
|
4922
|
+
this.#executeCommand(command, customIndex);
|
|
4823
4923
|
}
|
|
4824
4924
|
|
|
4825
|
-
#
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
handleRollingTabIndex(this.#tableHandlerButtons, event);
|
|
4830
|
-
}
|
|
4925
|
+
#executeCommand(command, customIndex = null) {
|
|
4926
|
+
this.#selectCellAtSelection();
|
|
4927
|
+
this.editor.dispatchCommand(this.#commandName(command));
|
|
4928
|
+
this.#selectNextBestCell(command, customIndex);
|
|
4831
4929
|
}
|
|
4832
4930
|
|
|
4833
|
-
#
|
|
4834
|
-
|
|
4835
|
-
this.appendChild(this.#createColumnButtonsContainer());
|
|
4931
|
+
#executeToggleStyle(command) {
|
|
4932
|
+
const childType = command.childType;
|
|
4836
4933
|
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
this.addEventListener("keydown", this.#handleTableHandlerKeydown);
|
|
4840
|
-
}
|
|
4934
|
+
let cells = null;
|
|
4935
|
+
let headerState = null;
|
|
4841
4936
|
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4937
|
+
if (childType === "row") {
|
|
4938
|
+
cells = this.currentRowCells;
|
|
4939
|
+
headerState = TableCellHeaderStates.ROW;
|
|
4940
|
+
} else if (childType === "column") {
|
|
4941
|
+
cells = this.currentColumnCells;
|
|
4942
|
+
headerState = TableCellHeaderStates.COLUMN;
|
|
4943
|
+
}
|
|
4845
4944
|
|
|
4846
|
-
|
|
4847
|
-
|
|
4945
|
+
if (!cells || cells.length === 0) return
|
|
4946
|
+
|
|
4947
|
+
this.editor.update(() => {
|
|
4948
|
+
const firstCell = $getTableCellNodeFromLexicalNode(cells[0]);
|
|
4949
|
+
if (!firstCell) return
|
|
4950
|
+
|
|
4951
|
+
const currentStyle = firstCell.getHeaderStyles();
|
|
4952
|
+
const newStyle = currentStyle ^ headerState;
|
|
4953
|
+
|
|
4954
|
+
cells.forEach(cell => {
|
|
4955
|
+
this.#setHeaderStyle(cell, newStyle, headerState);
|
|
4956
|
+
});
|
|
4957
|
+
});
|
|
4848
4958
|
}
|
|
4849
4959
|
|
|
4850
|
-
#
|
|
4851
|
-
this
|
|
4852
|
-
this
|
|
4960
|
+
#deleteTable() {
|
|
4961
|
+
this.#selectCellAtSelection();
|
|
4962
|
+
this.editor.dispatchCommand("deleteTable");
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
#selectCellAtSelection() {
|
|
4966
|
+
this.editor.update(() => {
|
|
4967
|
+
const selection = $getSelection();
|
|
4968
|
+
if (!selection) return
|
|
4969
|
+
|
|
4970
|
+
const node = selection.getNodes()[0];
|
|
4853
4971
|
|
|
4854
|
-
|
|
4855
|
-
|
|
4972
|
+
$findCellNode(node)?.selectEnd();
|
|
4973
|
+
});
|
|
4856
4974
|
}
|
|
4857
4975
|
|
|
4858
|
-
#
|
|
4859
|
-
const
|
|
4860
|
-
if (!tableElement) return
|
|
4976
|
+
#commandName(command) {
|
|
4977
|
+
const { action, childType, direction } = command;
|
|
4861
4978
|
|
|
4862
|
-
const
|
|
4863
|
-
const
|
|
4979
|
+
const childTypeSuffix = upcaseFirst(childType);
|
|
4980
|
+
const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
|
|
4981
|
+
return `${action}Table${childTypeSuffix}${directionSuffix}`
|
|
4982
|
+
}
|
|
4864
4983
|
|
|
4865
|
-
|
|
4866
|
-
const
|
|
4867
|
-
|
|
4868
|
-
this.style.left = `${relativeCenter}px`;
|
|
4984
|
+
#setHeaderStyle(cell, newStyle, headerState) {
|
|
4985
|
+
const tableCellNode = $getTableCellNodeFromLexicalNode(cell);
|
|
4986
|
+
tableCellNode?.setHeaderStyles(newStyle, headerState);
|
|
4869
4987
|
}
|
|
4870
4988
|
|
|
4871
|
-
#
|
|
4872
|
-
|
|
4989
|
+
async #selectCellAtIndex(rowIndex, columnIndex) {
|
|
4990
|
+
// We wait for next frame, otherwise table operations might not have completed yet.
|
|
4991
|
+
await nextFrame();
|
|
4873
4992
|
|
|
4874
|
-
|
|
4875
|
-
if (!tableElement) return
|
|
4993
|
+
if (!this.currentTableNode) return
|
|
4876
4994
|
|
|
4877
|
-
const
|
|
4878
|
-
|
|
4995
|
+
const rows = this.tableRows;
|
|
4996
|
+
if (!rows) return
|
|
4879
4997
|
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
}
|
|
4998
|
+
const row = rows[rowIndex];
|
|
4999
|
+
if (!row) return
|
|
4883
5000
|
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
"aria-label": label,
|
|
4888
|
-
type: "button"
|
|
5001
|
+
this.editor.update(() => {
|
|
5002
|
+
const cell = $getTableCellNodeFromLexicalNode(row.getChildAtIndex(columnIndex));
|
|
5003
|
+
cell?.selectEnd();
|
|
4889
5004
|
});
|
|
4890
|
-
button.tabIndex = -1;
|
|
4891
|
-
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
4892
|
-
button.addEventListener("click", onClick.bind(this));
|
|
4893
|
-
|
|
4894
|
-
return button
|
|
4895
5005
|
}
|
|
4896
5006
|
|
|
4897
|
-
#
|
|
4898
|
-
const
|
|
5007
|
+
#selectNextBestCell(command, customIndex = null) {
|
|
5008
|
+
const { childType, direction } = command;
|
|
4899
5009
|
|
|
4900
|
-
|
|
4901
|
-
|
|
5010
|
+
let rowIndex = this.currentRowIndex;
|
|
5011
|
+
let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
|
|
4902
5012
|
|
|
4903
|
-
|
|
4904
|
-
|
|
5013
|
+
const deleteOffset = command.action === "delete" ? -1 : 0;
|
|
5014
|
+
const offset = direction === "after" ? 1 : deleteOffset;
|
|
4905
5015
|
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
5016
|
+
if (childType === "row") {
|
|
5017
|
+
rowIndex += offset;
|
|
5018
|
+
} else if (childType === "column") {
|
|
5019
|
+
columnIndex += offset;
|
|
5020
|
+
}
|
|
4909
5021
|
|
|
4910
|
-
|
|
5022
|
+
this.#selectCellAtIndex(rowIndex, columnIndex);
|
|
4911
5023
|
}
|
|
4912
5024
|
|
|
4913
|
-
#
|
|
4914
|
-
const
|
|
5025
|
+
#selectNextRow() {
|
|
5026
|
+
const rows = this.tableRows;
|
|
5027
|
+
if (!rows) return
|
|
4915
5028
|
|
|
4916
|
-
const
|
|
4917
|
-
|
|
5029
|
+
const nextRow = rows.at(this.currentRowIndex + 1);
|
|
5030
|
+
if (!nextRow) return
|
|
4918
5031
|
|
|
4919
|
-
this.
|
|
4920
|
-
|
|
5032
|
+
this.editor.update(() => {
|
|
5033
|
+
nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
|
|
5034
|
+
});
|
|
5035
|
+
}
|
|
4921
5036
|
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
5037
|
+
#selectPreviousCell() {
|
|
5038
|
+
const cell = this.currentCell;
|
|
5039
|
+
if (!cell) return
|
|
4925
5040
|
|
|
4926
|
-
|
|
5041
|
+
this.editor.update(() => {
|
|
5042
|
+
cell.selectPrevious();
|
|
5043
|
+
});
|
|
4927
5044
|
}
|
|
4928
5045
|
|
|
4929
|
-
#
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
});
|
|
4933
|
-
container.setAttribute("name", "lexxy-dropdown");
|
|
5046
|
+
#insertRowAndSelectFirstCell() {
|
|
5047
|
+
this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
|
|
5048
|
+
}
|
|
4934
5049
|
|
|
4935
|
-
|
|
5050
|
+
#deleteRowAndSelectLastCell() {
|
|
5051
|
+
this.executeTableCommand({ action: "delete", childType: "row" }, -1);
|
|
5052
|
+
}
|
|
4936
5053
|
|
|
4937
|
-
|
|
4938
|
-
|
|
5054
|
+
#deleteRowAndSelectNextNode() {
|
|
5055
|
+
const tableNode = this.currentTableNode;
|
|
5056
|
+
this.executeTableCommand({ action: "delete", childType: "row" });
|
|
4939
5057
|
|
|
4940
|
-
|
|
4941
|
-
|
|
5058
|
+
this.editor.update(() => {
|
|
5059
|
+
const next = tableNode?.getNextSibling();
|
|
5060
|
+
if ($isParagraphNode(next)) {
|
|
5061
|
+
next.selectStart();
|
|
5062
|
+
} else {
|
|
5063
|
+
const newParagraph = $createParagraphNode();
|
|
5064
|
+
this.currentTableNode.insertAfter(newParagraph);
|
|
5065
|
+
newParagraph.selectStart();
|
|
5066
|
+
}
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
4942
5069
|
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
details.appendChild(this.#createDeleteTableSection());
|
|
5070
|
+
#isCurrentCellEmpty() {
|
|
5071
|
+
if (!this.currentTableNode) return false
|
|
4946
5072
|
|
|
4947
|
-
|
|
5073
|
+
const cell = this.currentCell;
|
|
5074
|
+
if (!cell) return false
|
|
4948
5075
|
|
|
4949
|
-
return
|
|
5076
|
+
return cell.getTextContent().trim() === ""
|
|
4950
5077
|
}
|
|
4951
5078
|
|
|
4952
|
-
#
|
|
4953
|
-
|
|
5079
|
+
#isCurrentRowLast() {
|
|
5080
|
+
if (!this.currentTableNode) return false
|
|
4954
5081
|
|
|
4955
|
-
const
|
|
4956
|
-
|
|
4957
|
-
{ icon: this.#icon("add-column-after"), label: "Add column after", onClick: () => this.#insertTableColumn("right") },
|
|
4958
|
-
{ icon: this.#icon("remove-column"), label: "Remove column", onClick: this.#deleteTableColumn },
|
|
4959
|
-
{ icon: this.#icon("toggle-column-style"), label: "Toggle column style", onClick: this.#toggleColumnHeaderStyle },
|
|
4960
|
-
];
|
|
5082
|
+
const rows = this.tableRows;
|
|
5083
|
+
if (!rows) return false
|
|
4961
5084
|
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
columnSection.appendChild(buttonElement);
|
|
4965
|
-
});
|
|
5085
|
+
return rows.length === this.currentRowIndex + 1
|
|
5086
|
+
}
|
|
4966
5087
|
|
|
4967
|
-
|
|
5088
|
+
#isCurrentRowEmpty() {
|
|
5089
|
+
if (!this.currentTableNode) return false
|
|
5090
|
+
|
|
5091
|
+
const cells = this.currentRowCells;
|
|
5092
|
+
if (!cells) return false
|
|
5093
|
+
|
|
5094
|
+
return cells.every(cell => cell.getTextContent().trim() === "")
|
|
4968
5095
|
}
|
|
4969
5096
|
|
|
4970
|
-
#
|
|
4971
|
-
|
|
5097
|
+
#isFirstCellInRow() {
|
|
5098
|
+
if (!this.currentTableNode) return false
|
|
4972
5099
|
|
|
4973
|
-
const
|
|
4974
|
-
|
|
4975
|
-
{ icon: this.#icon("add-row-below"), label: "Add row below", onClick: () => this.#insertTableRow("below") },
|
|
4976
|
-
{ icon: this.#icon("remove-row"), label: "Remove row", onClick: this.#deleteTableRow },
|
|
4977
|
-
{ icon: this.#icon("toggle-row-style"), label: "Toggle row style", onClick: this.#toggleRowHeaderStyle }
|
|
4978
|
-
];
|
|
5100
|
+
const cells = this.currentRowCells;
|
|
5101
|
+
if (!cells) return false
|
|
4979
5102
|
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
rowSection.appendChild(buttonElement);
|
|
4983
|
-
});
|
|
5103
|
+
return cells.indexOf(this.currentCell) === 0
|
|
5104
|
+
}
|
|
4984
5105
|
|
|
4985
|
-
|
|
5106
|
+
#registerKeyHandlers() {
|
|
5107
|
+
// We can't prevent these externally using regular keydown because Lexical handles it first.
|
|
5108
|
+
this.unregisterBackspaceKeyHandler = this.editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => this.#handleBackspaceKey(event), COMMAND_PRIORITY_HIGH);
|
|
5109
|
+
this.unregisterEnterKeyHandler = this.editor.registerCommand(KEY_ENTER_COMMAND, (event) => this.#handleEnterKey(event), COMMAND_PRIORITY_HIGH);
|
|
4986
5110
|
}
|
|
4987
5111
|
|
|
4988
|
-
#
|
|
4989
|
-
|
|
5112
|
+
#unregisterKeyHandlers() {
|
|
5113
|
+
this.unregisterBackspaceKeyHandler?.();
|
|
5114
|
+
this.unregisterEnterKeyHandler?.();
|
|
4990
5115
|
|
|
4991
|
-
|
|
5116
|
+
this.unregisterBackspaceKeyHandler = null;
|
|
5117
|
+
this.unregisterEnterKeyHandler = null;
|
|
5118
|
+
}
|
|
4992
5119
|
|
|
4993
|
-
|
|
4994
|
-
|
|
5120
|
+
#handleBackspaceKey(event) {
|
|
5121
|
+
if (!this.currentTableNode) return false
|
|
4995
5122
|
|
|
4996
|
-
|
|
5123
|
+
if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
|
|
5124
|
+
event.preventDefault();
|
|
5125
|
+
this.#deleteRowAndSelectLastCell();
|
|
5126
|
+
return true
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
|
|
5130
|
+
event.preventDefault();
|
|
5131
|
+
this.#selectPreviousCell();
|
|
5132
|
+
return true
|
|
5133
|
+
}
|
|
5134
|
+
|
|
5135
|
+
return false
|
|
4997
5136
|
}
|
|
4998
5137
|
|
|
4999
|
-
#
|
|
5000
|
-
if (
|
|
5001
|
-
|
|
5138
|
+
#handleEnterKey(event) {
|
|
5139
|
+
if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
|
|
5140
|
+
|
|
5141
|
+
if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
|
|
5142
|
+
|
|
5143
|
+
event.preventDefault();
|
|
5144
|
+
|
|
5145
|
+
if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
|
|
5146
|
+
this.#deleteRowAndSelectNextNode();
|
|
5147
|
+
} else if (this.#isCurrentRowLast()) {
|
|
5148
|
+
this.#insertRowAndSelectFirstCell();
|
|
5002
5149
|
} else {
|
|
5003
|
-
this.#
|
|
5150
|
+
this.#selectNextRow();
|
|
5004
5151
|
}
|
|
5152
|
+
|
|
5153
|
+
return true
|
|
5005
5154
|
}
|
|
5155
|
+
}
|
|
5006
5156
|
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5157
|
+
var TableIcons = {
|
|
5158
|
+
"insert-row-before":
|
|
5159
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5160
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 15C8.29055e-07 15.8284 0.671574 16.5 1.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V4.5L16.4922 4.34668C16.4154 3.59028 15.7767 3 15 3H13.5L13.5 4.5H15V9H1.5L1.5 4.5L3 4.5V3H1.5C0.671574 3 1.20956e-06 3.67157 1.24577e-06 4.5L7.86804e-07 15ZM15 10.5V15H1.5L1.5 10.5H15Z"/>
|
|
5161
|
+
<path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
|
|
5162
|
+
</svg>`,
|
|
5163
|
+
|
|
5164
|
+
"insert-row-after":
|
|
5165
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5166
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.86804e-07 13.5C7.50592e-07 14.3284 0.671574 15 1.5 15H3V13.5H1.5L1.5 9L15 9V13.5H13.5V15H15C15.7767 15 16.4154 14.4097 16.4922 13.6533L16.5 13.5V3L16.4922 2.84668C16.4205 2.14069 15.8593 1.57949 15.1533 1.50781L15 1.5L1.5 1.5C0.671574 1.5 1.28803e-06 2.17157 1.24577e-06 3L7.86804e-07 13.5ZM15 3V7.5L1.5 7.5L1.5 3L15 3Z"/>
|
|
5167
|
+
<path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
|
|
5168
|
+
</svg>`,
|
|
5169
|
+
|
|
5170
|
+
"delete-row":
|
|
5171
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5172
|
+
<path d="M16.4922 12.1533C16.4154 12.9097 15.7767 13.5 15 13.5L12 13.5V12H15V6L1.5 6L1.5 12H4.5V13.5H1.5C0.723337 13.5 0.0846104 12.9097 0.00781328 12.1533L7.86804e-07 12L1.04907e-06 6C1.17362e-06 5.22334 0.590278 4.58461 1.34668 4.50781L1.5 4.5L15 4.5C15.8284 4.5 16.5 5.17157 16.5 6V12L16.4922 12.1533Z"/>
|
|
5173
|
+
<path d="M10.3711 15.9316L8.25 13.8096L6.12793 15.9316L5.06738 14.8711L7.18945 12.75L5.06738 10.6289L6.12793 9.56836L8.25 11.6895L10.3711 9.56836L11.4316 10.6289L9.31055 12.75L11.4316 14.8711L10.3711 15.9316Z"/>
|
|
5174
|
+
</svg>`,
|
|
5175
|
+
|
|
5176
|
+
"toggle-row":
|
|
5177
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5178
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.00781328 13.6533C0.0846108 14.4097 0.723337 15 1.5 15L15 15L15.1533 14.9922C15.8593 14.9205 16.4205 14.3593 16.4922 13.6533L16.5 13.5V4.5L16.4922 4.34668C16.4205 3.64069 15.8593 3.07949 15.1533 3.00781L15 3L1.5 3C0.671574 3 1.24863e-06 3.67157 1.18021e-06 4.5L7.86804e-07 13.5L0.00781328 13.6533ZM15 9V13.5L1.5 13.5L1.5 9L15 9Z"/>
|
|
5179
|
+
</svg>`,
|
|
5180
|
+
|
|
5181
|
+
"insert-column-before":
|
|
5182
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5183
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 0C3.67157 0 3 0.671573 3 1.5V3H4.5V1.5H9V15H4.5V13.5H3V15C3 15.7767 3.59028 16.4154 4.34668 16.4922L4.5 16.5H15L15.1533 16.4922C15.8593 16.4205 16.4205 15.8593 16.4922 15.1533L16.5 15V1.5C16.5 0.671573 15.8284 6.03989e-09 15 0H4.5ZM15 15H10.5V1.5H15V15Z"/>
|
|
5184
|
+
<path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
|
|
5185
|
+
</svg>`,
|
|
5186
|
+
|
|
5187
|
+
"insert-column-after":
|
|
5188
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5189
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5 0C14.3284 0 15 0.671573 15 1.5V3H13.5V1.5H9V15H13.5V13.5H15V15C15 15.7767 14.4097 16.4154 13.6533 16.4922L13.5 16.5H3L2.84668 16.4922C2.14069 16.4205 1.57949 15.8593 1.50781 15.1533L1.5 15V1.5C1.5 0.671573 2.17157 6.03989e-09 3 0H13.5ZM3 15H7.5V1.5H3V15Z"/>
|
|
5190
|
+
<path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
|
|
5191
|
+
</svg>`,
|
|
5192
|
+
|
|
5193
|
+
"delete-column":
|
|
5194
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5195
|
+
<path d="M12.1533 0.0078125C12.9097 0.0846097 13.5 0.723336 13.5 1.5V4.5H12V1.5H6V15H12V12H13.5V15C13.5 15.7767 12.9097 16.4154 12.1533 16.4922L12 16.5H6C5.22334 16.5 4.58461 15.9097 4.50781 15.1533L4.5 15V1.5C4.5 0.671573 5.17157 2.41596e-08 6 0H12L12.1533 0.0078125Z"/>
|
|
5196
|
+
<path d="M15.9316 6.12891L13.8105 8.24902L15.9326 10.3711L14.8711 11.4316L12.75 9.31055L10.6289 11.4316L9.56738 10.3711L11.6885 8.24902L9.56836 6.12891L10.6289 5.06836L12.75 7.18848L14.8711 5.06836L15.9316 6.12891Z"/>
|
|
5197
|
+
</svg>`,
|
|
5198
|
+
|
|
5199
|
+
"toggle-column":
|
|
5200
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5201
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.6533 17.9922C14.4097 17.9154 15 17.2767 15 16.5L15 3L14.9922 2.84668C14.9205 2.14069 14.3593 1.57949 13.6533 1.50781L13.5 1.5L4.5 1.5L4.34668 1.50781C3.59028 1.58461 3 2.22334 3 3L3 16.5C3 17.2767 3.59028 17.9154 4.34668 17.9922L4.5 18L13.5 18L13.6533 17.9922ZM9 3L13.5 3L13.5 16.5L9 16.5L9 3Z" />
|
|
5202
|
+
</svg>`,
|
|
5203
|
+
|
|
5204
|
+
"delete-table":
|
|
5205
|
+
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
5206
|
+
<path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
|
|
5207
|
+
</svg>`
|
|
5208
|
+
};
|
|
5209
|
+
|
|
5210
|
+
class TableTools extends HTMLElement {
|
|
5211
|
+
connectedCallback() {
|
|
5212
|
+
this.tableController = new TableController(this.#editorElement);
|
|
5213
|
+
|
|
5214
|
+
this.#setUpButtons();
|
|
5215
|
+
this.#monitorForTableSelection();
|
|
5216
|
+
this.#registerKeyboardShortcuts();
|
|
5010
5217
|
}
|
|
5011
5218
|
|
|
5012
|
-
|
|
5013
|
-
this.#
|
|
5014
|
-
this.#editor.getEditorState().read(() => {
|
|
5015
|
-
const selection = $getSelection();
|
|
5016
|
-
if (!$isRangeSelection(selection)) return
|
|
5219
|
+
disconnectedCallback() {
|
|
5220
|
+
this.#unregisterKeyboardShortcuts();
|
|
5017
5221
|
|
|
5018
|
-
|
|
5019
|
-
|
|
5222
|
+
this.unregisterUpdateListener?.();
|
|
5223
|
+
this.unregisterUpdateListener = null;
|
|
5020
5224
|
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
}
|
|
5026
|
-
});
|
|
5027
|
-
});
|
|
5225
|
+
this.removeEventListener("keydown", this.#handleToolsKeydown);
|
|
5226
|
+
|
|
5227
|
+
this.tableController?.destroy();
|
|
5228
|
+
this.tableController = null;
|
|
5028
5229
|
}
|
|
5029
5230
|
|
|
5030
|
-
#
|
|
5031
|
-
this.#editorElement.
|
|
5231
|
+
get #editor() {
|
|
5232
|
+
return this.#editorElement.editor
|
|
5233
|
+
}
|
|
5032
5234
|
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
if (!tableParent) return
|
|
5036
|
-
tableParent.classList.add("node--selected");
|
|
5037
|
-
}
|
|
5235
|
+
get #editorElement() {
|
|
5236
|
+
return this.closest("lexxy-editor")
|
|
5038
5237
|
}
|
|
5039
5238
|
|
|
5040
|
-
#
|
|
5041
|
-
this.
|
|
5042
|
-
this.#updateButtonsPosition(tableNode);
|
|
5043
|
-
this.#showTableHandlerButtons();
|
|
5239
|
+
get #tableToolsButtons() {
|
|
5240
|
+
return Array.from(this.querySelectorAll("button, details > summary"))
|
|
5044
5241
|
}
|
|
5045
5242
|
|
|
5046
|
-
#
|
|
5047
|
-
this
|
|
5048
|
-
|
|
5049
|
-
if (!currentCell) return
|
|
5243
|
+
#setUpButtons() {
|
|
5244
|
+
this.appendChild(this.#createRowButtonsContainer());
|
|
5245
|
+
this.appendChild(this.#createColumnButtonsContainer());
|
|
5050
5246
|
|
|
5051
|
-
|
|
5052
|
-
|
|
5247
|
+
this.appendChild(this.#createDeleteTableButton());
|
|
5248
|
+
this.addEventListener("keydown", this.#handleToolsKeydown);
|
|
5249
|
+
}
|
|
5053
5250
|
|
|
5054
|
-
|
|
5055
|
-
});
|
|
5251
|
+
#createButtonsContainer(childType, setCountProperty, moreMenu) {
|
|
5252
|
+
const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
|
|
5253
|
+
|
|
5254
|
+
const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
|
|
5255
|
+
const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
|
|
5256
|
+
|
|
5257
|
+
const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
|
|
5258
|
+
dropdown.setAttribute("name", "lexxy-dropdown");
|
|
5259
|
+
dropdown.tabIndex = -1;
|
|
5260
|
+
|
|
5261
|
+
const count = createElement("summary", {}, `_ ${childType}s`);
|
|
5262
|
+
setCountProperty(count);
|
|
5263
|
+
dropdown.appendChild(count);
|
|
5264
|
+
|
|
5265
|
+
dropdown.appendChild(moreMenu);
|
|
5266
|
+
|
|
5267
|
+
container.appendChild(minusButton);
|
|
5268
|
+
container.appendChild(dropdown);
|
|
5269
|
+
container.appendChild(plusButton);
|
|
5270
|
+
|
|
5271
|
+
return container
|
|
5056
5272
|
}
|
|
5057
5273
|
|
|
5058
|
-
#
|
|
5059
|
-
this.#
|
|
5274
|
+
#createRowButtonsContainer() {
|
|
5275
|
+
return this.#createButtonsContainer(
|
|
5276
|
+
"row",
|
|
5277
|
+
(count) => { this.rowCount = count; },
|
|
5278
|
+
this.#createMoreMenuSection("row")
|
|
5279
|
+
)
|
|
5060
5280
|
}
|
|
5061
5281
|
|
|
5062
|
-
#
|
|
5063
|
-
|
|
5282
|
+
#createColumnButtonsContainer() {
|
|
5283
|
+
return this.#createButtonsContainer(
|
|
5284
|
+
"column",
|
|
5285
|
+
(count) => { this.columnCount = count; },
|
|
5286
|
+
this.#createMoreMenuSection("column")
|
|
5287
|
+
)
|
|
5288
|
+
}
|
|
5064
5289
|
|
|
5065
|
-
|
|
5066
|
-
|
|
5290
|
+
#createMoreMenuSection(childType) {
|
|
5291
|
+
const section = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
5292
|
+
const addBeforeButton = this.#createButton(`Add ${childType} before`, { action: "insert", childType, direction: "before" });
|
|
5293
|
+
const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
|
|
5294
|
+
const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
|
|
5295
|
+
const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
|
|
5067
5296
|
|
|
5068
|
-
|
|
5297
|
+
section.appendChild(addBeforeButton);
|
|
5298
|
+
section.appendChild(addAfterButton);
|
|
5299
|
+
section.appendChild(toggleStyleButton);
|
|
5300
|
+
section.appendChild(deleteButton);
|
|
5301
|
+
|
|
5302
|
+
return section
|
|
5069
5303
|
}
|
|
5070
5304
|
|
|
5071
|
-
#
|
|
5072
|
-
|
|
5305
|
+
#createDeleteTableButton() {
|
|
5306
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
5073
5307
|
|
|
5074
|
-
this.#
|
|
5075
|
-
|
|
5076
|
-
|
|
5308
|
+
const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
|
|
5309
|
+
deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
|
|
5310
|
+
|
|
5311
|
+
container.appendChild(deleteTableButton);
|
|
5312
|
+
|
|
5313
|
+
this.deleteContainer = container;
|
|
5077
5314
|
|
|
5078
|
-
|
|
5079
|
-
this.#executeTableCommand("insert", "row", direction);
|
|
5315
|
+
return container
|
|
5080
5316
|
}
|
|
5081
5317
|
|
|
5082
|
-
#
|
|
5083
|
-
|
|
5318
|
+
#createButton(label, command = {}, icon = this.#icon(command)) {
|
|
5319
|
+
const button = createElement("button", {
|
|
5320
|
+
className: "lexxy-table-control__button",
|
|
5321
|
+
"aria-label": label,
|
|
5322
|
+
type: "button"
|
|
5323
|
+
});
|
|
5324
|
+
button.tabIndex = -1;
|
|
5325
|
+
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
5326
|
+
|
|
5327
|
+
button.dataset.action = command.action;
|
|
5328
|
+
button.dataset.childType = command.childType;
|
|
5329
|
+
button.dataset.direction = command.direction;
|
|
5330
|
+
|
|
5331
|
+
button.addEventListener("click", () => this.#executeTableCommand(command));
|
|
5332
|
+
|
|
5333
|
+
button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
|
|
5334
|
+
button.addEventListener("focus", () => this.#handleCommandButtonHover());
|
|
5335
|
+
button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
|
|
5336
|
+
|
|
5337
|
+
return button
|
|
5084
5338
|
}
|
|
5085
5339
|
|
|
5086
|
-
#
|
|
5087
|
-
this.#
|
|
5340
|
+
#registerKeyboardShortcuts() {
|
|
5341
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleAccessibilityShortcutKey, COMMAND_PRIORITY_HIGH);
|
|
5088
5342
|
}
|
|
5089
5343
|
|
|
5090
|
-
#
|
|
5091
|
-
this
|
|
5344
|
+
#unregisterKeyboardShortcuts() {
|
|
5345
|
+
this.unregisterKeyboardShortcuts?.();
|
|
5346
|
+
this.unregisterKeyboardShortcuts = null;
|
|
5092
5347
|
}
|
|
5093
5348
|
|
|
5094
|
-
#
|
|
5095
|
-
|
|
5096
|
-
const
|
|
5097
|
-
|
|
5349
|
+
#handleAccessibilityShortcutKey = (event) => {
|
|
5350
|
+
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
5351
|
+
const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
5352
|
+
firstButton?.focus();
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5098
5355
|
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5356
|
+
#handleToolsKeydown = (event) => {
|
|
5357
|
+
if (event.key === "Escape") {
|
|
5358
|
+
this.#handleEscapeKey();
|
|
5359
|
+
} else {
|
|
5360
|
+
handleRollingTabIndex(this.#tableToolsButtons, event);
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5102
5363
|
|
|
5103
|
-
|
|
5364
|
+
#handleEscapeKey() {
|
|
5365
|
+
const cell = this.tableController.currentCell;
|
|
5366
|
+
if (!cell) return
|
|
5104
5367
|
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5368
|
+
this.#editor.update(() => {
|
|
5369
|
+
cell.select();
|
|
5370
|
+
this.#editor.focus();
|
|
5108
5371
|
});
|
|
5109
5372
|
|
|
5110
|
-
this.#
|
|
5111
|
-
this.#updateRowColumnCount();
|
|
5373
|
+
this.#update();
|
|
5112
5374
|
}
|
|
5113
5375
|
|
|
5114
|
-
#
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5376
|
+
async #handleCommandButtonHover() {
|
|
5377
|
+
await nextFrame();
|
|
5378
|
+
|
|
5379
|
+
this.#clearCellStyles();
|
|
5380
|
+
|
|
5381
|
+
const activeElement = this.querySelector("button:hover, button:focus");
|
|
5382
|
+
if (!activeElement) return
|
|
5383
|
+
|
|
5384
|
+
const command = {
|
|
5385
|
+
action: activeElement.dataset.action,
|
|
5386
|
+
childType: activeElement.dataset.childType,
|
|
5387
|
+
direction: activeElement.dataset.direction
|
|
5388
|
+
};
|
|
5389
|
+
|
|
5390
|
+
let cellsToHighlight = null;
|
|
5391
|
+
|
|
5392
|
+
switch (command.childType) {
|
|
5393
|
+
case "row":
|
|
5394
|
+
cellsToHighlight = this.tableController.currentRowCells;
|
|
5133
5395
|
break
|
|
5134
|
-
case "
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
case "column":
|
|
5140
|
-
this.#editor.dispatchCommand("deleteTableColumn");
|
|
5141
|
-
break
|
|
5142
|
-
}
|
|
5396
|
+
case "column":
|
|
5397
|
+
cellsToHighlight = this.tableController.currentColumnCells;
|
|
5398
|
+
break
|
|
5399
|
+
case "table":
|
|
5400
|
+
cellsToHighlight = this.tableController.tableRows;
|
|
5143
5401
|
break
|
|
5144
5402
|
}
|
|
5145
|
-
}
|
|
5146
5403
|
|
|
5147
|
-
|
|
5148
|
-
this.#editor.update(() => {
|
|
5149
|
-
const rows = this.currentTableNode.getChildren();
|
|
5404
|
+
if (!cellsToHighlight) return
|
|
5150
5405
|
|
|
5151
|
-
|
|
5152
|
-
|
|
5406
|
+
cellsToHighlight.forEach(cell => {
|
|
5407
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
5408
|
+
if (!cellElement) return
|
|
5153
5409
|
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5410
|
+
cellElement.classList.toggle(theme.tableCellHighlight, true);
|
|
5411
|
+
Object.assign(cellElement.dataset, command);
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5157
5414
|
|
|
5158
|
-
|
|
5159
|
-
|
|
5415
|
+
#monitorForTableSelection() {
|
|
5416
|
+
this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
|
|
5417
|
+
this.tableController.updateSelectedTable();
|
|
5160
5418
|
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5419
|
+
const tableNode = this.tableController.currentTableNode;
|
|
5420
|
+
if (tableNode) {
|
|
5421
|
+
this.#show();
|
|
5422
|
+
} else {
|
|
5423
|
+
this.#hide();
|
|
5424
|
+
}
|
|
5164
5425
|
});
|
|
5165
5426
|
}
|
|
5166
5427
|
|
|
5167
|
-
#
|
|
5168
|
-
this
|
|
5169
|
-
|
|
5428
|
+
#executeTableCommand(command) {
|
|
5429
|
+
this.tableController.executeTableCommand(command);
|
|
5430
|
+
this.#update();
|
|
5431
|
+
}
|
|
5170
5432
|
|
|
5171
|
-
|
|
5172
|
-
|
|
5433
|
+
#show() {
|
|
5434
|
+
this.style.display = "flex";
|
|
5435
|
+
this.#update();
|
|
5436
|
+
}
|
|
5173
5437
|
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5438
|
+
#hide() {
|
|
5439
|
+
this.style.display = "none";
|
|
5440
|
+
this.#clearCellStyles();
|
|
5441
|
+
}
|
|
5177
5442
|
|
|
5178
|
-
|
|
5179
|
-
|
|
5443
|
+
#update() {
|
|
5444
|
+
this.#updateButtonsPosition();
|
|
5445
|
+
this.#updateRowColumnCount();
|
|
5446
|
+
this.#closeMoreMenu();
|
|
5447
|
+
this.#handleCommandButtonHover();
|
|
5448
|
+
}
|
|
5180
5449
|
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
if (!cell) return
|
|
5184
|
-
this.#setHeaderStyle(cell, newStyle, TableCellHeaderStates.COLUMN);
|
|
5185
|
-
});
|
|
5186
|
-
});
|
|
5450
|
+
#closeMoreMenu() {
|
|
5451
|
+
this.querySelector("details[open]")?.removeAttribute("open");
|
|
5187
5452
|
}
|
|
5188
5453
|
|
|
5189
|
-
#
|
|
5190
|
-
const
|
|
5454
|
+
#updateButtonsPosition() {
|
|
5455
|
+
const tableNode = this.tableController.currentTableNode;
|
|
5456
|
+
if (!tableNode) return
|
|
5191
5457
|
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5458
|
+
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
5459
|
+
if (!tableElement) return
|
|
5460
|
+
|
|
5461
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
5462
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5463
|
+
|
|
5464
|
+
const relativeTop = tableRect.top - editorRect.top;
|
|
5465
|
+
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
5466
|
+
this.style.top = `${relativeTop}px`;
|
|
5467
|
+
this.style.left = `${relativeCenter}px`;
|
|
5195
5468
|
}
|
|
5196
5469
|
|
|
5197
|
-
#
|
|
5198
|
-
const
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
</svg>`,
|
|
5470
|
+
#updateRowColumnCount() {
|
|
5471
|
+
const tableNode = this.tableController.currentTableNode;
|
|
5472
|
+
if (!tableNode) return
|
|
5473
|
+
|
|
5474
|
+
const tableElement = $getElementForTableNode(this.#editor, tableNode);
|
|
5475
|
+
if (!tableElement) return
|
|
5204
5476
|
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
<path d="M4 11L0 8V14L4 11ZM6.5 10.5H16.5V11.5H6.5V10.5ZM18 10C18 9.44772 17.5523 9 17 9H6C5.44772 9 5 9.44772 5 10V12C5 12.5523 5.44772 13 6 13H17C17.5523 13 18 12.5523 18 12V10Z"/><path d="M2 16C2 16.5523 2.44772 17 3 17H15C15.5523 17 16 16.5523 16 16C16 15.4477 15.5523 15 15 15H3C2.44772 15 2 15.4477 2 16Z"/><path d="M2 6C2 6.55228 2.44772 7 3 7H15C15.5523 7 16 6.55228 16 6C16 5.44772 15.5523 5 15 5H3C2.44772 5 2 5.44772 2 6Z"/><path d="M2 2C2 2.55228 2.44772 3 3 3H15C15.5523 3 16 2.55228 16 2C16 1.44772 15.5523 1 15 1H3C2.44772 1 2 1.44772 2 2Z"/>
|
|
5208
|
-
</svg>`,
|
|
5477
|
+
const rowCount = tableElement.rows;
|
|
5478
|
+
const columnCount = tableElement.columns;
|
|
5209
5479
|
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
</svg>`,
|
|
5480
|
+
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
5481
|
+
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
5482
|
+
}
|
|
5214
5483
|
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
</svg>`,
|
|
5484
|
+
#setTableCellFocus() {
|
|
5485
|
+
const cell = this.tableController.currentCell;
|
|
5486
|
+
if (!cell) return
|
|
5219
5487
|
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
<path d="M7 4L10 2.62268e-07L4 0L7 4ZM7.5 6.5L7.5 16.5H6.5L6.5 6.5H7.5ZM8 18C8.55228 18 9 17.5523 9 17V6C9 5.44772 8.55229 5 8 5H6C5.44772 5 5 5.44772 5 6L5 17C5 17.5523 5.44772 18 6 18H8Z"/><path d="M2 2C1.44772 2 1 2.44772 1 3L1 15C1 15.5523 1.44772 16 2 16C2.55228 16 3 15.5523 3 15L3 3C3 2.44772 2.55229 2 2 2Z"/><path d="M12 2C11.4477 2 11 2.44772 11 3L11 15C11 15.5523 11.4477 16 12 16C12.5523 16 13 15.5523 13 15L13 3C13 2.44772 12.5523 2 12 2Z"/><path d="M16 2C15.4477 2 15 2.44772 15 3L15 15C15 15.5523 15.4477 16 16 16C16.5523 16 17 15.5523 17 15V3C17 2.44772 16.5523 2 16 2Z"/>
|
|
5223
|
-
</svg>`,
|
|
5488
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
5489
|
+
if (!cellElement) return
|
|
5224
5490
|
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
<path d="M11 4L8 2.62268e-07L14 0L11 4ZM10.5 6.5V16.5H11.5V6.5H10.5ZM10 18C9.44772 18 9 17.5523 9 17V6C9 5.44772 9.44772 5 10 5H12C12.5523 5 13 5.44772 13 6V17C13 17.5523 12.5523 18 12 18H10Z"/><path d="M16 2C16.5523 2 17 2.44772 17 3L17 15C17 15.5523 16.5523 16 16 16C15.4477 16 15 15.5523 15 15V3C15 2.44772 15.4477 2 16 2Z"/><path d="M6 2C6.55228 2 7 2.44772 7 3L7 15C7 15.5523 6.55228 16 6 16C5.44772 16 5 15.5523 5 15L5 3C5 2.44772 5.44771 2 6 2Z"/><path d="M2 2C2.55228 2 3 2.44772 3 3L3 15C3 15.5523 2.55228 16 2 16C1.44772 16 1 15.5523 1 15V3C1 2.44772 1.44771 2 2 2Z"/>
|
|
5228
|
-
</svg>`,
|
|
5491
|
+
cellElement.classList.add(theme.tableCellFocus);
|
|
5492
|
+
}
|
|
5229
5493
|
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5494
|
+
#clearCellStyles() {
|
|
5495
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellFocus}`)?.forEach(cell => {
|
|
5496
|
+
cell.classList.remove(theme.tableCellFocus);
|
|
5497
|
+
});
|
|
5234
5498
|
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5499
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
|
|
5500
|
+
cell.classList.remove(theme.tableCellHighlight);
|
|
5501
|
+
cell.removeAttribute("data-action");
|
|
5502
|
+
cell.removeAttribute("data-child-type");
|
|
5503
|
+
cell.removeAttribute("data-direction");
|
|
5504
|
+
});
|
|
5239
5505
|
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
<path d="M18.2129 19.2305C18.0925 20.7933 16.7892 22 15.2217 22H7.77832C6.21084 22 4.90753 20.7933 4.78711 19.2305L4 9H19L18.2129 19.2305Z"/><path d="M13 2C14.1046 2 15 2.89543 15 4H19C19.5523 4 20 4.44772 20 5V6C20 6.55228 19.5523 7 19 7H4C3.44772 7 3 6.55228 3 6V5C3 4.44772 3.44772 4 4 4H8C8 2.89543 8.89543 2 10 2H13Z"/>
|
|
5243
|
-
</svg>`
|
|
5244
|
-
};
|
|
5506
|
+
this.#setTableCellFocus();
|
|
5507
|
+
}
|
|
5245
5508
|
|
|
5246
|
-
|
|
5509
|
+
#icon(command) {
|
|
5510
|
+
const { action, childType } = command;
|
|
5511
|
+
const direction = (action == "insert" ? command.direction : null);
|
|
5512
|
+
const iconId = [ action, childType, direction ].filter(Boolean).join("-");
|
|
5513
|
+
return TableIcons[iconId]
|
|
5247
5514
|
}
|
|
5248
5515
|
}
|
|
5249
5516
|
|
|
5250
|
-
customElements.define("lexxy-table-
|
|
5517
|
+
customElements.define("lexxy-table-tools", TableTools);
|
|
5251
5518
|
|
|
5252
5519
|
class BaseSource {
|
|
5253
5520
|
// Template method to override
|
|
@@ -5451,25 +5718,30 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
5451
5718
|
}
|
|
5452
5719
|
|
|
5453
5720
|
#addTriggerListener() {
|
|
5454
|
-
const unregister = this.#editor.registerUpdateListener(() => {
|
|
5455
|
-
|
|
5721
|
+
const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
|
|
5722
|
+
editorState.read(() => {
|
|
5456
5723
|
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5457
5724
|
if (!node) return
|
|
5458
5725
|
|
|
5459
|
-
if ($isTextNode(node)
|
|
5726
|
+
if ($isTextNode(node)) {
|
|
5460
5727
|
const fullText = node.getTextContent();
|
|
5461
|
-
const
|
|
5728
|
+
const triggerLength = this.trigger.length;
|
|
5729
|
+
|
|
5730
|
+
// Check if we have enough characters for the trigger
|
|
5731
|
+
if (offset >= triggerLength) {
|
|
5732
|
+
const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
|
|
5462
5733
|
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5734
|
+
// Check if trigger is at the start of the text node (new line case) or preceded by space or newline
|
|
5735
|
+
if (textBeforeCursor === this.trigger) {
|
|
5736
|
+
const isAtStart = offset === triggerLength;
|
|
5466
5737
|
|
|
5467
|
-
|
|
5468
|
-
|
|
5738
|
+
const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
|
|
5739
|
+
const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
|
|
5469
5740
|
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5741
|
+
if (isAtStart || isPrecededBySpaceOrNewline) {
|
|
5742
|
+
unregister();
|
|
5743
|
+
this.#showPopover();
|
|
5744
|
+
}
|
|
5473
5745
|
}
|
|
5474
5746
|
}
|
|
5475
5747
|
}
|
|
@@ -5489,9 +5761,10 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
5489
5761
|
const fullText = node.getTextContent();
|
|
5490
5762
|
const textBeforeCursor = fullText.slice(0, offset);
|
|
5491
5763
|
const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
|
|
5764
|
+
const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
|
|
5492
5765
|
|
|
5493
|
-
// If trigger is not found, or cursor is at or before the trigger position, hide popover
|
|
5494
|
-
if (lastTriggerIndex === -1 || offset <=
|
|
5766
|
+
// If trigger is not found, or cursor is at or before the trigger end position, hide popover
|
|
5767
|
+
if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
|
|
5495
5768
|
this.#hidePopover();
|
|
5496
5769
|
}
|
|
5497
5770
|
} else {
|
|
@@ -5935,8 +6208,10 @@ class CodeLanguagePicker extends HTMLElement {
|
|
|
5935
6208
|
const codeRect = codeElement.getBoundingClientRect();
|
|
5936
6209
|
const editorRect = this.editorElement.getBoundingClientRect();
|
|
5937
6210
|
const relativeTop = codeRect.top - editorRect.top;
|
|
6211
|
+
const relativeRight = editorRect.right - codeRect.right;
|
|
5938
6212
|
|
|
5939
6213
|
this.style.top = `${relativeTop}px`;
|
|
6214
|
+
this.style.right = `${relativeRight}px`;
|
|
5940
6215
|
}
|
|
5941
6216
|
|
|
5942
6217
|
#showLanguagePicker() {
|