@37signals/lexxy 0.7.2-beta → 0.7.4-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 +1521 -1194
- 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,
|
|
14
|
-
import { $
|
|
13
|
+
import { HISTORY_MERGE_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG, $isTextNode, TextNode, $isRangeSelection, $getSelection, DecoratorNode, $getEditor, 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, SELECTION_CHANGE_COMMAND, $getNodeByKey, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, PASTE_TAG, createCommand, createState, defineExtension, $setState, $getState, $hasUpdateTag, CLEAR_HISTORY_COMMAND, $addUpdateTag, KEY_SPACE_COMMAND, KEY_DOWN_COMMAND } from 'lexical';
|
|
14
|
+
import { $isListItemNode, $isListNode, 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
|
}
|
|
@@ -155,6 +156,8 @@ function getNonce() {
|
|
|
155
156
|
return element?.content
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
const SILENT_UPDATE_TAGS = [ HISTORY_MERGE_TAG, SKIP_DOM_SELECTION_TAG, SKIP_SCROLL_INTO_VIEW_TAG ];
|
|
160
|
+
|
|
158
161
|
function getNearestListItemNode(node) {
|
|
159
162
|
let current = node;
|
|
160
163
|
while (current !== null) {
|
|
@@ -378,6 +381,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
378
381
|
super();
|
|
379
382
|
this.internals = this.attachInternals();
|
|
380
383
|
this.internals.role = "toolbar";
|
|
384
|
+
|
|
385
|
+
this.#createEditorPromise();
|
|
381
386
|
}
|
|
382
387
|
|
|
383
388
|
connectedCallback() {
|
|
@@ -410,14 +415,26 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
410
415
|
this.#refreshToolbarOverflow();
|
|
411
416
|
this.#bindFocusListeners();
|
|
412
417
|
|
|
418
|
+
this.resolveEditorPromise(editorElement);
|
|
419
|
+
|
|
413
420
|
this.toggleAttribute("connected", true);
|
|
414
421
|
}
|
|
415
422
|
|
|
423
|
+
async getEditorElement() {
|
|
424
|
+
return this.editorElement || await this.editorPromise
|
|
425
|
+
}
|
|
426
|
+
|
|
416
427
|
#reconnect() {
|
|
417
428
|
this.disconnectedCallback();
|
|
418
429
|
this.connectedCallback();
|
|
419
430
|
}
|
|
420
431
|
|
|
432
|
+
#createEditorPromise() {
|
|
433
|
+
this.editorPromise = new Promise((resolve) => {
|
|
434
|
+
this.resolveEditorPromise = resolve;
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
421
438
|
#installResizeObserver() {
|
|
422
439
|
this.resizeObserver = new ResizeObserver(() => this.#refreshToolbarOverflow());
|
|
423
440
|
this.resizeObserver.observe(this);
|
|
@@ -450,7 +467,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
450
467
|
|
|
451
468
|
this.editor.update(() => {
|
|
452
469
|
this.editor.dispatchCommand(command, payload);
|
|
453
|
-
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined }
|
|
470
|
+
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined });
|
|
454
471
|
}
|
|
455
472
|
|
|
456
473
|
#bindHotkeys() {
|
|
@@ -486,28 +503,24 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
486
503
|
}
|
|
487
504
|
|
|
488
505
|
#bindFocusListeners() {
|
|
489
|
-
this.editorElement.addEventListener("lexxy:focus", this.#
|
|
490
|
-
this.editorElement.addEventListener("lexxy:blur", this.#
|
|
491
|
-
this.addEventListener("focusout", this.#handleFocusOut);
|
|
506
|
+
this.editorElement.addEventListener("lexxy:focus", this.#handleEditorFocus);
|
|
507
|
+
this.editorElement.addEventListener("lexxy:blur", this.#handleEditorBlur);
|
|
492
508
|
this.addEventListener("keydown", this.#handleKeydown);
|
|
493
509
|
}
|
|
494
510
|
|
|
495
511
|
#unbindFocusListeners() {
|
|
496
|
-
this.editorElement.removeEventListener("lexxy:focus", this.#
|
|
497
|
-
this.editorElement.removeEventListener("lexxy:blur", this.#
|
|
498
|
-
this.removeEventListener("focusout", this.#handleFocusOut);
|
|
512
|
+
this.editorElement.removeEventListener("lexxy:focus", this.#handleEditorFocus);
|
|
513
|
+
this.editorElement.removeEventListener("lexxy:blur", this.#handleEditorBlur);
|
|
499
514
|
this.removeEventListener("keydown", this.#handleKeydown);
|
|
500
515
|
}
|
|
501
516
|
|
|
502
|
-
#
|
|
503
|
-
this.#resetTabIndexValues();
|
|
517
|
+
#handleEditorFocus = () => {
|
|
504
518
|
this.#focusableItems[0].tabIndex = 0;
|
|
505
519
|
}
|
|
506
520
|
|
|
507
|
-
#
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
521
|
+
#handleEditorBlur = () => {
|
|
522
|
+
this.#resetTabIndexValues();
|
|
523
|
+
this.#closeDropdowns();
|
|
511
524
|
}
|
|
512
525
|
|
|
513
526
|
#handleKeydown = (event) => {
|
|
@@ -524,6 +537,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
524
537
|
this.editor.registerUpdateListener(() => {
|
|
525
538
|
this.editor.getEditorState().read(() => {
|
|
526
539
|
this.#updateButtonStates();
|
|
540
|
+
this.#closeDropdowns();
|
|
527
541
|
});
|
|
528
542
|
});
|
|
529
543
|
}
|
|
@@ -614,11 +628,13 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
614
628
|
}
|
|
615
629
|
|
|
616
630
|
#toolbarIsOverflowing() {
|
|
617
|
-
|
|
631
|
+
// Safari can report inconsistent clientWidth values on more than 100% window zoom level,
|
|
632
|
+
// that was affecting the toolbar overflow calculation. We're adding +1 to get around this issue.
|
|
633
|
+
return (this.scrollWidth - this.#overflow.clientWidth) > this.clientWidth + 1
|
|
618
634
|
}
|
|
619
635
|
|
|
620
636
|
#refreshToolbarOverflow = () => {
|
|
621
|
-
this.#
|
|
637
|
+
this.#resetToolbarOverflow();
|
|
622
638
|
this.#compactMenu();
|
|
623
639
|
|
|
624
640
|
this.#overflow.style.display = this.#overflowMenu.children.length ? "block" : "none";
|
|
@@ -644,7 +660,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
644
660
|
}
|
|
645
661
|
}
|
|
646
662
|
|
|
647
|
-
#
|
|
663
|
+
#resetToolbarOverflow() {
|
|
648
664
|
const items = Array.from(this.#overflowMenu.children);
|
|
649
665
|
items.sort((a, b) => this.#itemPosition(b) - this.#itemPosition(a));
|
|
650
666
|
|
|
@@ -666,6 +682,16 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
666
682
|
});
|
|
667
683
|
}
|
|
668
684
|
|
|
685
|
+
#closeDropdowns() {
|
|
686
|
+
this.#dropdowns.forEach((details) => {
|
|
687
|
+
details.open = false;
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
get #dropdowns() {
|
|
692
|
+
return this.querySelectorAll("details")
|
|
693
|
+
}
|
|
694
|
+
|
|
669
695
|
get #overflow() {
|
|
670
696
|
return this.querySelector(".lexxy-editor__toolbar-overflow")
|
|
671
697
|
}
|
|
@@ -707,9 +733,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
707
733
|
<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
734
|
</summary>
|
|
709
735
|
<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>
|
|
736
|
+
<div class="lexxy-highlight-colors"></div>
|
|
737
|
+
<button data-command="removeHighlight" class="lexxy-editor__toolbar-button lexxy-editor__toolbar-dropdown-reset">Remove all coloring</button>
|
|
713
738
|
</lexxy-highlight-dropdown>
|
|
714
739
|
</details>
|
|
715
740
|
|
|
@@ -721,8 +746,8 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
721
746
|
<form method="dialog">
|
|
722
747
|
<input type="url" placeholder="Enter a URL…" class="input">
|
|
723
748
|
<div class="lexxy-editor__toolbar-dropdown-actions">
|
|
724
|
-
<button type="submit" class="
|
|
725
|
-
<button type="button" class="
|
|
749
|
+
<button type="submit" class="lexxy-editor__toolbar-button" value="link">Link</button>
|
|
750
|
+
<button type="button" class="lexxy-editor__toolbar-button" value="unlink">Unlink</button>
|
|
726
751
|
</div>
|
|
727
752
|
</form>
|
|
728
753
|
</lexxy-link-dropdown>
|
|
@@ -770,16 +795,14 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
770
795
|
<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
796
|
</button>
|
|
772
797
|
|
|
773
|
-
<details class="lexxy-editor__toolbar-overflow">
|
|
798
|
+
<details class="lexxy-editor__toolbar-dropdown lexxy-editor__toolbar-overflow" name="lexxy-dropdown">
|
|
774
799
|
<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>
|
|
800
|
+
<div class="lexxy-editor__toolbar-dropdown-content lexxy-editor__toolbar-overflow-menu" aria-label="More toolbar buttons"></div>
|
|
776
801
|
</details>
|
|
777
802
|
`
|
|
778
803
|
}
|
|
779
804
|
}
|
|
780
805
|
|
|
781
|
-
customElements.define("lexxy-toolbar", LexicalToolbarElement);
|
|
782
|
-
|
|
783
806
|
var theme = {
|
|
784
807
|
text: {
|
|
785
808
|
bold: "lexxy-content__bold",
|
|
@@ -792,6 +815,8 @@ var theme = {
|
|
|
792
815
|
tableCellSelected: "lexxy-content__table-cell--selected",
|
|
793
816
|
tableSelection: "lexxy-content__table--selection",
|
|
794
817
|
tableScrollableWrapper: "lexxy-content__table-wrapper",
|
|
818
|
+
tableCellHighlight: "lexxy-content__table-cell--highlight",
|
|
819
|
+
tableCellFocus: "lexxy-content__table-cell--focus",
|
|
795
820
|
list: {
|
|
796
821
|
nested: {
|
|
797
822
|
listitem: "lexxy-nested-listitem",
|
|
@@ -943,6 +968,8 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
943
968
|
this.fileSize = fileSize;
|
|
944
969
|
this.width = width;
|
|
945
970
|
this.height = height;
|
|
971
|
+
|
|
972
|
+
this.editor = $getEditor();
|
|
946
973
|
}
|
|
947
974
|
|
|
948
975
|
createDOM() {
|
|
@@ -963,8 +990,13 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
963
990
|
return figure
|
|
964
991
|
}
|
|
965
992
|
|
|
966
|
-
updateDOM() {
|
|
967
|
-
|
|
993
|
+
updateDOM(_prevNode, dom) {
|
|
994
|
+
const caption = dom.querySelector("figcaption textarea");
|
|
995
|
+
if (caption && this.caption) {
|
|
996
|
+
caption.value = this.caption;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return false
|
|
968
1000
|
}
|
|
969
1001
|
|
|
970
1002
|
getTextContent() {
|
|
@@ -1072,8 +1104,8 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
1072
1104
|
});
|
|
1073
1105
|
|
|
1074
1106
|
input.addEventListener("focusin", () => input.placeholder = "Add caption...");
|
|
1075
|
-
input.addEventListener("blur", this.#handleCaptionInputBlurred
|
|
1076
|
-
input.addEventListener("keydown", this.#handleCaptionInputKeydown
|
|
1107
|
+
input.addEventListener("blur", (event) => this.#handleCaptionInputBlurred(event));
|
|
1108
|
+
input.addEventListener("keydown", (event) => this.#handleCaptionInputKeydown(event));
|
|
1077
1109
|
|
|
1078
1110
|
caption.appendChild(input);
|
|
1079
1111
|
|
|
@@ -1081,21 +1113,24 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
1081
1113
|
}
|
|
1082
1114
|
|
|
1083
1115
|
#handleCaptionInputBlurred(event) {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
input.placeholder = this.fileName;
|
|
1087
|
-
this.#updateCaptionValueFromInput(input);
|
|
1116
|
+
this.#updateCaptionValueFromInput(event.target);
|
|
1088
1117
|
}
|
|
1089
1118
|
|
|
1090
1119
|
#updateCaptionValueFromInput(input) {
|
|
1091
|
-
|
|
1120
|
+
input.placeholder = this.fileName;
|
|
1121
|
+
this.editor.update(() => {
|
|
1122
|
+
this.getWritable().caption = input.value;
|
|
1123
|
+
});
|
|
1092
1124
|
}
|
|
1093
1125
|
|
|
1094
1126
|
#handleCaptionInputKeydown(event) {
|
|
1095
1127
|
if (event.key === "Enter") {
|
|
1096
1128
|
this.#updateCaptionValueFromInput(event.target);
|
|
1097
|
-
dispatchCustomEvent(event.target, "lexxy:internal:move-to-next-line");
|
|
1098
1129
|
event.preventDefault();
|
|
1130
|
+
|
|
1131
|
+
this.editor.update(() => {
|
|
1132
|
+
this.selectNext();
|
|
1133
|
+
}, { tag: HISTORY_MERGE_TAG });
|
|
1099
1134
|
}
|
|
1100
1135
|
event.stopPropagation();
|
|
1101
1136
|
}
|
|
@@ -1136,56 +1171,83 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1136
1171
|
}
|
|
1137
1172
|
|
|
1138
1173
|
constructor(node, key) {
|
|
1139
|
-
const { file, uploadUrl, blobUrlTemplate,
|
|
1174
|
+
const { file, uploadUrl, blobUrlTemplate, progress, width, height, uploadError } = node;
|
|
1140
1175
|
super({ ...node, contentType: file.type }, key);
|
|
1141
1176
|
this.file = file;
|
|
1142
1177
|
this.uploadUrl = uploadUrl;
|
|
1143
1178
|
this.blobUrlTemplate = blobUrlTemplate;
|
|
1144
|
-
this.
|
|
1145
|
-
this.
|
|
1146
|
-
this.
|
|
1179
|
+
this.progress = progress ?? null;
|
|
1180
|
+
this.width = width;
|
|
1181
|
+
this.height = height;
|
|
1182
|
+
this.uploadError = uploadError;
|
|
1147
1183
|
}
|
|
1148
1184
|
|
|
1149
1185
|
createDOM() {
|
|
1186
|
+
if (this.uploadError) return this.#createDOMForError()
|
|
1187
|
+
|
|
1188
|
+
// This side-effect is trigged on DOM load to fire only once and avoid multiple
|
|
1189
|
+
// uploads through cloning. The upload is guarded from restarting in case the
|
|
1190
|
+
// node is reloaded from saved state such as from history.
|
|
1191
|
+
this.#startUploadIfNeeded();
|
|
1192
|
+
|
|
1150
1193
|
const figure = this.createAttachmentFigure();
|
|
1151
1194
|
|
|
1152
1195
|
if (this.isPreviewableAttachment) {
|
|
1153
|
-
figure.appendChild(this.#createDOMForImage());
|
|
1196
|
+
const img = figure.appendChild(this.#createDOMForImage());
|
|
1197
|
+
|
|
1198
|
+
// load file locally to set dimensions and prevent vertical shifting
|
|
1199
|
+
loadFileIntoImage(this.file, img).then(img => this.#setDimensionsFromImage(img));
|
|
1154
1200
|
} else {
|
|
1155
1201
|
figure.appendChild(this.#createDOMForFile());
|
|
1156
1202
|
}
|
|
1157
1203
|
|
|
1158
1204
|
figure.appendChild(this.#createCaption());
|
|
1205
|
+
figure.appendChild(this.#createProgressBar());
|
|
1206
|
+
|
|
1207
|
+
return figure
|
|
1208
|
+
}
|
|
1159
1209
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1210
|
+
updateDOM(prevNode, dom) {
|
|
1211
|
+
if (this.uploadError !== prevNode.uploadError) return true
|
|
1162
1212
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1213
|
+
if (prevNode.progress !== this.progress) {
|
|
1214
|
+
const progress = dom.querySelector("progress");
|
|
1215
|
+
progress.value = this.progress ?? 0;
|
|
1216
|
+
}
|
|
1166
1217
|
|
|
1167
|
-
return
|
|
1218
|
+
return false
|
|
1168
1219
|
}
|
|
1169
1220
|
|
|
1170
1221
|
exportDOM() {
|
|
1171
1222
|
const img = document.createElement("img");
|
|
1172
|
-
if (this.src) {
|
|
1173
|
-
img.src = this.src;
|
|
1174
|
-
}
|
|
1175
1223
|
return { element: img }
|
|
1176
1224
|
}
|
|
1177
1225
|
|
|
1178
1226
|
exportJSON() {
|
|
1179
1227
|
return {
|
|
1228
|
+
...super.exportJSON(),
|
|
1180
1229
|
type: "action_text_attachment_upload",
|
|
1181
1230
|
version: 1,
|
|
1182
|
-
progress: this.progress,
|
|
1183
1231
|
uploadUrl: this.uploadUrl,
|
|
1184
1232
|
blobUrlTemplate: this.blobUrlTemplate,
|
|
1185
|
-
|
|
1233
|
+
progress: this.progress,
|
|
1234
|
+
width: this.width,
|
|
1235
|
+
height: this.height,
|
|
1236
|
+
uploadError: this.uploadError
|
|
1186
1237
|
}
|
|
1187
1238
|
}
|
|
1188
1239
|
|
|
1240
|
+
get #uploadStarted() {
|
|
1241
|
+
return this.progress !== null
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
#createDOMForError() {
|
|
1245
|
+
const figure = this.createAttachmentFigure();
|
|
1246
|
+
figure.classList.add("attachment--error");
|
|
1247
|
+
figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "file"}` }));
|
|
1248
|
+
return figure
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1189
1251
|
#createDOMForImage() {
|
|
1190
1252
|
return createElement("img")
|
|
1191
1253
|
}
|
|
@@ -1211,94 +1273,126 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
1211
1273
|
return figcaption
|
|
1212
1274
|
}
|
|
1213
1275
|
|
|
1214
|
-
#
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1276
|
+
#createProgressBar() {
|
|
1277
|
+
return createElement("progress", { value: this.progress ?? 0, max: 100 })
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
#setDimensionsFromImage({ width, height }) {
|
|
1281
|
+
if (this.#hasDimensions) return
|
|
1282
|
+
|
|
1283
|
+
this.editor.update(() => {
|
|
1284
|
+
const writable = this.getWritable();
|
|
1285
|
+
writable.width = width;
|
|
1286
|
+
writable.height = height;
|
|
1287
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
get #hasDimensions() {
|
|
1291
|
+
return Boolean(this.width && this.height)
|
|
1221
1292
|
}
|
|
1222
1293
|
|
|
1223
|
-
async #
|
|
1294
|
+
async #startUploadIfNeeded() {
|
|
1295
|
+
if (this.#uploadStarted) return
|
|
1296
|
+
|
|
1297
|
+
this.#setUploadStarted();
|
|
1298
|
+
|
|
1224
1299
|
const { DirectUpload } = await import('@rails/activestorage');
|
|
1225
|
-
const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
|
|
1226
1300
|
|
|
1227
1301
|
const upload = new DirectUpload(this.file, this.uploadUrl, this);
|
|
1302
|
+
upload.delegate = this.#createUploadDelegate();
|
|
1303
|
+
upload.create((error, blob) => {
|
|
1304
|
+
if (error) {
|
|
1305
|
+
this.#handleUploadError(error);
|
|
1306
|
+
} else {
|
|
1307
|
+
this.#showUploadedAttachment(blob);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
#createUploadDelegate() {
|
|
1313
|
+
const shouldAuthenticateUploads = Lexxy.global.get("authenticatedUploads");
|
|
1228
1314
|
|
|
1229
|
-
|
|
1315
|
+
return {
|
|
1230
1316
|
directUploadWillCreateBlobWithXHR: (request) => {
|
|
1231
1317
|
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1232
1318
|
},
|
|
1233
1319
|
directUploadWillStoreFileWithXHR: (request) => {
|
|
1234
1320
|
if (shouldAuthenticateUploads) request.withCredentials = true;
|
|
1235
1321
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
progressBar.value = Math.round(event.loaded / event.total * 100);
|
|
1239
|
-
});
|
|
1240
|
-
});
|
|
1322
|
+
const uploadProgressHandler = (event) => this.#handleUploadProgress(event);
|
|
1323
|
+
request.upload.addEventListener("progress", uploadProgressHandler);
|
|
1241
1324
|
}
|
|
1242
|
-
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1243
1327
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
this.#handleUploadError(figure);
|
|
1247
|
-
} else {
|
|
1248
|
-
this.#loadFigurePreviewFromBlob(blob, figure).then(() => {
|
|
1249
|
-
this.#showUploadedAttachment(figure, blob);
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
});
|
|
1328
|
+
#setUploadStarted() {
|
|
1329
|
+
this.#setProgress(1);
|
|
1253
1330
|
}
|
|
1254
1331
|
|
|
1255
|
-
#
|
|
1256
|
-
|
|
1257
|
-
figure.classList.add("attachment--error");
|
|
1258
|
-
figure.appendChild(createElement("div", { innerText: `Error uploading ${this.file?.name ?? "image"}` }));
|
|
1332
|
+
#handleUploadProgress(event) {
|
|
1333
|
+
this.#setProgress(Math.round(event.loaded / event.total * 100));
|
|
1259
1334
|
}
|
|
1260
1335
|
|
|
1261
|
-
|
|
1336
|
+
#setProgress(progress) {
|
|
1262
1337
|
this.editor.update(() => {
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
const src = this.blobUrlTemplate
|
|
1266
|
-
.replace(":signed_id", blob.signed_id)
|
|
1267
|
-
.replace(":filename", encodeURIComponent(blob.filename));
|
|
1268
|
-
const latest = $getNodeByKey(this.getKey());
|
|
1269
|
-
if (latest) {
|
|
1270
|
-
latest.replace(new ActionTextAttachmentNode({
|
|
1271
|
-
tagName: this.tagName,
|
|
1272
|
-
sgid: blob.attachable_sgid,
|
|
1273
|
-
src: blob.previewable ? blob.url : src,
|
|
1274
|
-
altText: blob.filename,
|
|
1275
|
-
contentType: blob.content_type,
|
|
1276
|
-
fileName: blob.filename,
|
|
1277
|
-
fileSize: blob.byte_size,
|
|
1278
|
-
width: image?.naturalWidth,
|
|
1279
|
-
previewable: blob.previewable,
|
|
1280
|
-
height: image?.naturalHeight
|
|
1281
|
-
}));
|
|
1282
|
-
}
|
|
1283
|
-
}, { tag: HISTORY_MERGE_TAG });
|
|
1338
|
+
this.getWritable().progress = progress;
|
|
1339
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1284
1340
|
}
|
|
1285
1341
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1342
|
+
#handleUploadError(error) {
|
|
1343
|
+
console.warn(`Upload error for ${this.file?.name ?? "file"}: ${error}`);
|
|
1344
|
+
this.editor.update(() => {
|
|
1345
|
+
this.getWritable().uploadError = true;
|
|
1346
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
async #showUploadedAttachment(blob) {
|
|
1350
|
+
this.editor.update(() => {
|
|
1351
|
+
this.replace(this.#toActionTextAttachmentNodeWith(blob));
|
|
1352
|
+
}, { tag: SILENT_UPDATE_TAGS });
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
#toActionTextAttachmentNodeWith(blob) {
|
|
1356
|
+
const conversion = new AttachmentNodeConversion(this, blob);
|
|
1357
|
+
return conversion.toAttachmentNode()
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
class AttachmentNodeConversion {
|
|
1362
|
+
constructor(uploadNode, blob) {
|
|
1363
|
+
this.uploadNode = uploadNode;
|
|
1364
|
+
this.blob = blob;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
toAttachmentNode() {
|
|
1368
|
+
return new ActionTextAttachmentNode({
|
|
1369
|
+
...this.uploadNode,
|
|
1370
|
+
...this.#propertiesFromBlob,
|
|
1371
|
+
src: this.#src
|
|
1372
|
+
})
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
get #propertiesFromBlob() {
|
|
1376
|
+
const { blob } = this;
|
|
1377
|
+
return {
|
|
1378
|
+
sgid: blob.attachable_sgid,
|
|
1379
|
+
altText: blob.filename,
|
|
1380
|
+
contentType: blob.content_type,
|
|
1381
|
+
fileName: blob.filename,
|
|
1382
|
+
fileSize: blob.byte_size,
|
|
1383
|
+
previewable: blob.previewable,
|
|
1300
1384
|
}
|
|
1301
1385
|
}
|
|
1386
|
+
|
|
1387
|
+
get #src() {
|
|
1388
|
+
return this.blob.previewable ? this.blob.url : this.#blobSrc
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
get #blobSrc() {
|
|
1392
|
+
return this.uploadNode.blobUrlTemplate
|
|
1393
|
+
.replace(":signed_id", this.blob.signed_id)
|
|
1394
|
+
.replace(":filename", encodeURIComponent(this.blob.filename))
|
|
1395
|
+
}
|
|
1302
1396
|
}
|
|
1303
1397
|
|
|
1304
1398
|
class HorizontalDividerNode extends DecoratorNode {
|
|
@@ -1373,30 +1467,6 @@ class HorizontalDividerNode extends DecoratorNode {
|
|
|
1373
1467
|
}
|
|
1374
1468
|
}
|
|
1375
1469
|
|
|
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
1470
|
const COMMANDS = [
|
|
1401
1471
|
"bold",
|
|
1402
1472
|
"italic",
|
|
@@ -1414,13 +1484,6 @@ const COMMANDS = [
|
|
|
1414
1484
|
"uploadAttachments",
|
|
1415
1485
|
|
|
1416
1486
|
"insertTable",
|
|
1417
|
-
"insertTableRowAbove",
|
|
1418
|
-
"insertTableRowBelow",
|
|
1419
|
-
"insertTableColumnAfter",
|
|
1420
|
-
"insertTableColumnBefore",
|
|
1421
|
-
"deleteTableRow",
|
|
1422
|
-
"deleteTableColumn",
|
|
1423
|
-
"deleteTable",
|
|
1424
1487
|
|
|
1425
1488
|
"undo",
|
|
1426
1489
|
"redo"
|
|
@@ -1529,9 +1592,7 @@ class CommandDispatcher {
|
|
|
1529
1592
|
}
|
|
1530
1593
|
|
|
1531
1594
|
dispatchInsertHorizontalDivider() {
|
|
1532
|
-
this.
|
|
1533
|
-
this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
|
|
1534
|
-
});
|
|
1595
|
+
this.contents.insertAtCursorEnsuringLineBelow(new HorizontalDividerNode());
|
|
1535
1596
|
|
|
1536
1597
|
this.editor.focus();
|
|
1537
1598
|
}
|
|
@@ -1593,41 +1654,6 @@ class CommandDispatcher {
|
|
|
1593
1654
|
this.editor.dispatchCommand(INSERT_TABLE_COMMAND, { "rows": 3, "columns": 3, "includeHeaders": true });
|
|
1594
1655
|
}
|
|
1595
1656
|
|
|
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
1657
|
dispatchUndo() {
|
|
1632
1658
|
this.editor.dispatchCommand(UNDO_COMMAND, undefined);
|
|
1633
1659
|
}
|
|
@@ -1921,6 +1947,14 @@ class Selection {
|
|
|
1921
1947
|
return $getNearestNodeOfType(anchorNode, CodeNode) !== null
|
|
1922
1948
|
}
|
|
1923
1949
|
|
|
1950
|
+
get isTableCellSelected() {
|
|
1951
|
+
const selection = $getSelection();
|
|
1952
|
+
if (!$isRangeSelection(selection)) return false
|
|
1953
|
+
|
|
1954
|
+
const anchorNode = selection.anchor.getNode();
|
|
1955
|
+
return $getNearestNodeOfType(anchorNode, TableCellNode) !== null
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1924
1958
|
get nodeAfterCursor() {
|
|
1925
1959
|
const { anchorNode, offset } = this.#getCollapsedSelectionData();
|
|
1926
1960
|
if (!anchorNode) return null
|
|
@@ -2433,17 +2467,6 @@ function sanitize(html) {
|
|
|
2433
2467
|
return DOMPurify.sanitize(html, buildConfig())
|
|
2434
2468
|
}
|
|
2435
2469
|
|
|
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
2470
|
function dasherize(value) {
|
|
2448
2471
|
return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`)
|
|
2449
2472
|
}
|
|
@@ -2467,6 +2490,10 @@ function filterMatches(text, potentialMatch) {
|
|
|
2467
2490
|
return normalizeFilteredText(text).includes(normalizeFilteredText(potentialMatch))
|
|
2468
2491
|
}
|
|
2469
2492
|
|
|
2493
|
+
function upcaseFirst(string) {
|
|
2494
|
+
return string.charAt(0).toUpperCase() + string.slice(1)
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2470
2497
|
class EditorConfiguration {
|
|
2471
2498
|
#editorElement
|
|
2472
2499
|
#config
|
|
@@ -2908,15 +2935,14 @@ class Contents {
|
|
|
2908
2935
|
new FormatEscaper(editorElement).monitor();
|
|
2909
2936
|
}
|
|
2910
2937
|
|
|
2911
|
-
insertHtml(html) {
|
|
2938
|
+
insertHtml(html, { tag } = {}) {
|
|
2912
2939
|
this.editor.update(() => {
|
|
2913
2940
|
const selection = $getSelection();
|
|
2914
|
-
|
|
2915
2941
|
if (!$isRangeSelection(selection)) return
|
|
2916
2942
|
|
|
2917
2943
|
const nodes = $generateNodesFromDOM(this.editor, parseHtml(html));
|
|
2918
2944
|
selection.insertNodes(nodes);
|
|
2919
|
-
});
|
|
2945
|
+
}, { tag });
|
|
2920
2946
|
}
|
|
2921
2947
|
|
|
2922
2948
|
insertAtCursor(node) {
|
|
@@ -3156,7 +3182,7 @@ class Contents {
|
|
|
3156
3182
|
const blobUrlTemplate = this.editorElement.blobUrlTemplate;
|
|
3157
3183
|
|
|
3158
3184
|
this.editor.update(() => {
|
|
3159
|
-
const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate
|
|
3185
|
+
const uploadedImageNode = new ActionTextAttachmentUploadNode({ file: file, uploadUrl: uploadUrl, blobUrlTemplate: blobUrlTemplate });
|
|
3160
3186
|
this.insertAtCursor(uploadedImageNode);
|
|
3161
3187
|
}, { tag: HISTORY_MERGE_TAG });
|
|
3162
3188
|
}
|
|
@@ -3719,14 +3745,14 @@ class Clipboard {
|
|
|
3719
3745
|
|
|
3720
3746
|
#pasteMarkdown(text) {
|
|
3721
3747
|
const html = marked(text);
|
|
3722
|
-
this.contents.insertHtml(html);
|
|
3748
|
+
this.contents.insertHtml(html, { tag: [ PASTE_TAG ] });
|
|
3723
3749
|
}
|
|
3724
3750
|
|
|
3725
3751
|
#pasteRichText(clipboardData) {
|
|
3726
3752
|
this.editor.update(() => {
|
|
3727
3753
|
const selection = $getSelection();
|
|
3728
3754
|
$insertDataTransferForRichText(clipboardData, selection, this.editor);
|
|
3729
|
-
});
|
|
3755
|
+
}, { tag: PASTE_TAG });
|
|
3730
3756
|
}
|
|
3731
3757
|
|
|
3732
3758
|
#handlePastedFiles(clipboardData) {
|
|
@@ -3773,7 +3799,7 @@ class Extensions {
|
|
|
3773
3799
|
|
|
3774
3800
|
initializeToolbars() {
|
|
3775
3801
|
if (this.#lexxyToolbar) {
|
|
3776
|
-
this.enabledExtensions.forEach(ext => ext.
|
|
3802
|
+
this.enabledExtensions.forEach(ext => ext.initializeToolbar(this.#lexxyToolbar));
|
|
3777
3803
|
}
|
|
3778
3804
|
}
|
|
3779
3805
|
|
|
@@ -3960,6 +3986,124 @@ function $applyLanguage(conversionOutput, element) {
|
|
|
3960
3986
|
conversionOutput.node.setLanguage(language);
|
|
3961
3987
|
}
|
|
3962
3988
|
|
|
3989
|
+
class WrappedTableNode extends TableNode {
|
|
3990
|
+
static clone(node) {
|
|
3991
|
+
return new WrappedTableNode(node.__key)
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
exportDOM(editor) {
|
|
3995
|
+
const superExport = super.exportDOM(editor);
|
|
3996
|
+
|
|
3997
|
+
return {
|
|
3998
|
+
...superExport,
|
|
3999
|
+
after: (tableElement) => {
|
|
4000
|
+
if (superExport.after) {
|
|
4001
|
+
tableElement = superExport.after(tableElement);
|
|
4002
|
+
const clonedTable = tableElement.cloneNode(true);
|
|
4003
|
+
const wrappedTable = createElement("figure", { className: "lexxy-content__table-wrapper" }, clonedTable.outerHTML);
|
|
4004
|
+
return wrappedTable
|
|
4005
|
+
}
|
|
4006
|
+
|
|
4007
|
+
return tableElement
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
const TablesLexicalExtension = defineExtension({
|
|
4014
|
+
name: "lexxy/tables",
|
|
4015
|
+
nodes: [
|
|
4016
|
+
WrappedTableNode,
|
|
4017
|
+
{
|
|
4018
|
+
replace: TableNode,
|
|
4019
|
+
with: () => new WrappedTableNode()
|
|
4020
|
+
},
|
|
4021
|
+
TableCellNode,
|
|
4022
|
+
TableRowNode
|
|
4023
|
+
],
|
|
4024
|
+
register(editor) {
|
|
4025
|
+
// Register Lexical table plugins
|
|
4026
|
+
registerTablePlugin(editor);
|
|
4027
|
+
registerTableSelectionObserver(editor, true);
|
|
4028
|
+
setScrollableTablesActive(editor, true);
|
|
4029
|
+
|
|
4030
|
+
// Bug fix: Prevent hardcoded background color (Lexical #8089)
|
|
4031
|
+
editor.registerNodeTransform(TableCellNode, (node) => {
|
|
4032
|
+
if (node.getBackgroundColor() === null) {
|
|
4033
|
+
node.setBackgroundColor("");
|
|
4034
|
+
}
|
|
4035
|
+
});
|
|
4036
|
+
|
|
4037
|
+
// Bug fix: Fix column header states (Lexical #8090)
|
|
4038
|
+
editor.registerNodeTransform(TableCellNode, (node) => {
|
|
4039
|
+
const headerState = node.getHeaderStyles();
|
|
4040
|
+
|
|
4041
|
+
if (headerState !== TableCellHeaderStates.ROW) return
|
|
4042
|
+
|
|
4043
|
+
const rowParent = node.getParent();
|
|
4044
|
+
const tableNode = rowParent?.getParent();
|
|
4045
|
+
if (!tableNode) return
|
|
4046
|
+
|
|
4047
|
+
const rows = tableNode.getChildren();
|
|
4048
|
+
const cellIndex = rowParent.getChildren().indexOf(node);
|
|
4049
|
+
|
|
4050
|
+
const cellsInRow = rowParent.getChildren();
|
|
4051
|
+
const isHeaderRow = cellsInRow.every(cell =>
|
|
4052
|
+
cell.getHeaderStyles() !== TableCellHeaderStates.NO_STATUS
|
|
4053
|
+
);
|
|
4054
|
+
|
|
4055
|
+
const isHeaderColumn = rows.every(row => {
|
|
4056
|
+
const cell = row.getChildren()[cellIndex];
|
|
4057
|
+
return cell && cell.getHeaderStyles() !== TableCellHeaderStates.NO_STATUS
|
|
4058
|
+
});
|
|
4059
|
+
|
|
4060
|
+
let newHeaderState = TableCellHeaderStates.NO_STATUS;
|
|
4061
|
+
|
|
4062
|
+
if (isHeaderRow) {
|
|
4063
|
+
newHeaderState |= TableCellHeaderStates.ROW;
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
if (isHeaderColumn) {
|
|
4067
|
+
newHeaderState |= TableCellHeaderStates.COLUMN;
|
|
4068
|
+
}
|
|
4069
|
+
|
|
4070
|
+
if (newHeaderState !== headerState) {
|
|
4071
|
+
node.setHeaderStyles(newHeaderState, TableCellHeaderStates.BOTH);
|
|
4072
|
+
}
|
|
4073
|
+
});
|
|
4074
|
+
|
|
4075
|
+
editor.registerCommand("insertTableRowAfter", () => {
|
|
4076
|
+
$insertTableRowAtSelection(true);
|
|
4077
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4078
|
+
|
|
4079
|
+
editor.registerCommand("insertTableRowBefore", () => {
|
|
4080
|
+
$insertTableRowAtSelection(false);
|
|
4081
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4082
|
+
|
|
4083
|
+
editor.registerCommand("insertTableColumnAfter", () => {
|
|
4084
|
+
$insertTableColumnAtSelection(true);
|
|
4085
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4086
|
+
|
|
4087
|
+
editor.registerCommand("insertTableColumnBefore", () => {
|
|
4088
|
+
$insertTableColumnAtSelection(false);
|
|
4089
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4090
|
+
|
|
4091
|
+
editor.registerCommand("deleteTableRow", () => {
|
|
4092
|
+
$deleteTableRowAtSelection();
|
|
4093
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4094
|
+
|
|
4095
|
+
editor.registerCommand("deleteTableColumn", () => {
|
|
4096
|
+
$deleteTableColumnAtSelection();
|
|
4097
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4098
|
+
|
|
4099
|
+
editor.registerCommand("deleteTable", () => {
|
|
4100
|
+
const selection = $getSelection();
|
|
4101
|
+
if (!$isRangeSelection(selection)) return false
|
|
4102
|
+
$findTableNode(selection.anchor.getNode())?.remove();
|
|
4103
|
+
}, COMMAND_PRIORITY_NORMAL);
|
|
4104
|
+
}
|
|
4105
|
+
});
|
|
4106
|
+
|
|
3963
4107
|
class LexicalEditorElement extends HTMLElement {
|
|
3964
4108
|
static formAssociated = true
|
|
3965
4109
|
static debug = false
|
|
@@ -4149,10 +4293,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4149
4293
|
#initialize() {
|
|
4150
4294
|
this.#synchronizeWithChanges();
|
|
4151
4295
|
this.#registerComponents();
|
|
4152
|
-
this.#listenForInvalidatedNodes();
|
|
4153
4296
|
this.#handleEnter();
|
|
4154
|
-
this.#
|
|
4155
|
-
this.#handleTables();
|
|
4297
|
+
this.#registerFocusEvents();
|
|
4156
4298
|
this.#attachDebugHooks();
|
|
4157
4299
|
this.#attachToolbar();
|
|
4158
4300
|
this.#loadInitialValue();
|
|
@@ -4163,11 +4305,11 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4163
4305
|
this.editorContentElement ||= this.#createEditorContentElement();
|
|
4164
4306
|
|
|
4165
4307
|
const editor = buildEditorFromExtensions({
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4308
|
+
name: "lexxy/core",
|
|
4309
|
+
namespace: "Lexxy",
|
|
4310
|
+
theme: theme,
|
|
4311
|
+
nodes: this.#lexicalNodes
|
|
4312
|
+
},
|
|
4171
4313
|
...this.#lexicalExtensions
|
|
4172
4314
|
);
|
|
4173
4315
|
|
|
@@ -4177,10 +4319,11 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4177
4319
|
}
|
|
4178
4320
|
|
|
4179
4321
|
get #lexicalExtensions() {
|
|
4180
|
-
const extensions = [
|
|
4322
|
+
const extensions = [];
|
|
4181
4323
|
const richTextExtensions = [
|
|
4182
4324
|
this.highlighter.lexicalExtension,
|
|
4183
|
-
TrixContentExtension
|
|
4325
|
+
TrixContentExtension,
|
|
4326
|
+
TablesLexicalExtension
|
|
4184
4327
|
];
|
|
4185
4328
|
|
|
4186
4329
|
if (this.supportsRichText) {
|
|
@@ -4205,14 +4348,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4205
4348
|
CodeHighlightNode,
|
|
4206
4349
|
LinkNode,
|
|
4207
4350
|
AutoLinkNode,
|
|
4208
|
-
HorizontalDividerNode
|
|
4209
|
-
WrappedTableNode,
|
|
4210
|
-
{
|
|
4211
|
-
replace: TableNode,
|
|
4212
|
-
with: () => { return new WrappedTableNode() }
|
|
4213
|
-
},
|
|
4214
|
-
TableCellNode,
|
|
4215
|
-
TableRowNode,
|
|
4351
|
+
HorizontalDividerNode
|
|
4216
4352
|
);
|
|
4217
4353
|
}
|
|
4218
4354
|
|
|
@@ -4326,11 +4462,8 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4326
4462
|
}
|
|
4327
4463
|
|
|
4328
4464
|
#registerTableComponents() {
|
|
4329
|
-
|
|
4330
|
-
this.
|
|
4331
|
-
this.append(this.tableHandler);
|
|
4332
|
-
|
|
4333
|
-
this.#addUnregisterHandler(registerHeaderBackgroundTransform(this.editor));
|
|
4465
|
+
this.tableTools = createElement("lexxy-table-tools");
|
|
4466
|
+
this.append(this.tableTools);
|
|
4334
4467
|
}
|
|
4335
4468
|
|
|
4336
4469
|
#registerCodeHiglightingComponents() {
|
|
@@ -4339,21 +4472,6 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4339
4472
|
this.append(this.codeLanguagePicker);
|
|
4340
4473
|
}
|
|
4341
4474
|
|
|
4342
|
-
#listenForInvalidatedNodes() {
|
|
4343
|
-
this.editor.getRootElement().addEventListener("lexxy:internal:invalidate-node", (event) => {
|
|
4344
|
-
const { key, values } = event.detail;
|
|
4345
|
-
|
|
4346
|
-
this.editor.update(() => {
|
|
4347
|
-
const node = $getNodeByKey(key);
|
|
4348
|
-
|
|
4349
|
-
if (node instanceof ActionTextAttachmentNode) {
|
|
4350
|
-
const updatedNode = node.getWritable();
|
|
4351
|
-
Object.assign(updatedNode, values);
|
|
4352
|
-
}
|
|
4353
|
-
});
|
|
4354
|
-
});
|
|
4355
|
-
}
|
|
4356
|
-
|
|
4357
4475
|
#handleEnter() {
|
|
4358
4476
|
// We can't prevent these externally using regular keydown because Lexical handles it first.
|
|
4359
4477
|
this.editor.registerCommand(
|
|
@@ -4377,12 +4495,27 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4377
4495
|
);
|
|
4378
4496
|
}
|
|
4379
4497
|
|
|
4380
|
-
#
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4498
|
+
#registerFocusEvents() {
|
|
4499
|
+
this.addEventListener("focusin", this.#handleFocusIn);
|
|
4500
|
+
this.addEventListener("focusout", this.#handleFocusOut);
|
|
4501
|
+
}
|
|
4502
|
+
|
|
4503
|
+
#handleFocusIn(event) {
|
|
4504
|
+
if (this.#elementInEditorOrToolbar(event.target) && !this.currentlyFocused) {
|
|
4505
|
+
dispatch(this, "lexxy:focus");
|
|
4506
|
+
this.currentlyFocused = true;
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
#handleFocusOut(event) {
|
|
4511
|
+
if (!this.#elementInEditorOrToolbar(event.relatedTarget)) {
|
|
4512
|
+
dispatch(this, "lexxy:blur");
|
|
4513
|
+
this.currentlyFocused = false;
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
|
|
4517
|
+
#elementInEditorOrToolbar(element) {
|
|
4518
|
+
return this.contains(element) || this.toolbarElement?.contains(element)
|
|
4386
4519
|
}
|
|
4387
4520
|
|
|
4388
4521
|
#onFocus() {
|
|
@@ -4399,22 +4532,9 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4399
4532
|
}
|
|
4400
4533
|
}
|
|
4401
4534
|
|
|
4402
|
-
#handleTables() {
|
|
4403
|
-
if (this.supportsRichText) {
|
|
4404
|
-
this.removeTableSelectionObserver = registerTableSelectionObserver(this.editor, true);
|
|
4405
|
-
setScrollableTablesActive(this.editor, true);
|
|
4406
|
-
}
|
|
4407
|
-
}
|
|
4408
4535
|
|
|
4409
4536
|
#attachDebugHooks() {
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
4413
|
-
editorState.read(() => {
|
|
4414
|
-
console.debug("HTML: ", this.value, "String:", this.toString());
|
|
4415
|
-
console.debug("empty", this.isEmpty, "blank", this.isBlank);
|
|
4416
|
-
});
|
|
4417
|
-
}));
|
|
4537
|
+
return
|
|
4418
4538
|
}
|
|
4419
4539
|
|
|
4420
4540
|
#attachToolbar() {
|
|
@@ -4494,18 +4614,17 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
4494
4614
|
}
|
|
4495
4615
|
}
|
|
4496
4616
|
|
|
4497
|
-
customElements.define("lexxy-editor", LexicalEditorElement);
|
|
4498
|
-
|
|
4499
4617
|
class ToolbarDropdown extends HTMLElement {
|
|
4500
4618
|
connectedCallback() {
|
|
4501
4619
|
this.container = this.closest("details");
|
|
4502
4620
|
|
|
4503
4621
|
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
4504
4622
|
this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
4623
|
+
|
|
4624
|
+
this.#onToolbarEditor(this.initialize.bind(this));
|
|
4505
4625
|
}
|
|
4506
4626
|
|
|
4507
4627
|
disconnectedCallback() {
|
|
4508
|
-
this.#removeClickOutsideHandler();
|
|
4509
4628
|
this.container.removeEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
4510
4629
|
}
|
|
4511
4630
|
|
|
@@ -4521,48 +4640,31 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
4521
4640
|
return this.toolbar.editor
|
|
4522
4641
|
}
|
|
4523
4642
|
|
|
4643
|
+
initialize() {
|
|
4644
|
+
// Any post-editor initialization
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4524
4647
|
close() {
|
|
4525
|
-
this.
|
|
4648
|
+
this.editor.focus();
|
|
4649
|
+
this.container.open = false;
|
|
4526
4650
|
}
|
|
4527
4651
|
|
|
4528
|
-
#
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4652
|
+
async #onToolbarEditor(callback) {
|
|
4653
|
+
await this.toolbar.editorConnected;
|
|
4654
|
+
callback();
|
|
4655
|
+
}
|
|
4656
|
+
|
|
4657
|
+
#handleToggle() {
|
|
4658
|
+
if (this.container.open) {
|
|
4659
|
+
this.#handleOpen();
|
|
4533
4660
|
}
|
|
4534
4661
|
}
|
|
4535
4662
|
|
|
4536
|
-
#handleOpen() {
|
|
4663
|
+
async #handleOpen() {
|
|
4537
4664
|
this.#interactiveElements[0].focus();
|
|
4538
|
-
this.#setupClickOutsideHandler();
|
|
4539
|
-
|
|
4540
4665
|
this.#resetTabIndexValues();
|
|
4541
4666
|
}
|
|
4542
4667
|
|
|
4543
|
-
#handleClose() {
|
|
4544
|
-
this.#removeClickOutsideHandler();
|
|
4545
|
-
this.editor.focus();
|
|
4546
|
-
}
|
|
4547
|
-
|
|
4548
|
-
#setupClickOutsideHandler() {
|
|
4549
|
-
if (this.clickOutsideHandler) return
|
|
4550
|
-
|
|
4551
|
-
this.clickOutsideHandler = this.#handleClickOutside.bind(this);
|
|
4552
|
-
document.addEventListener("click", this.clickOutsideHandler, true);
|
|
4553
|
-
}
|
|
4554
|
-
|
|
4555
|
-
#removeClickOutsideHandler() {
|
|
4556
|
-
if (!this.clickOutsideHandler) return
|
|
4557
|
-
|
|
4558
|
-
document.removeEventListener("click", this.clickOutsideHandler, true);
|
|
4559
|
-
this.clickOutsideHandler = null;
|
|
4560
|
-
}
|
|
4561
|
-
|
|
4562
|
-
#handleClickOutside({ target }) {
|
|
4563
|
-
if (this.container.open && !this.container.contains(target)) this.close();
|
|
4564
|
-
}
|
|
4565
|
-
|
|
4566
4668
|
#handleKeyDown(event) {
|
|
4567
4669
|
if (event.key === "Escape") {
|
|
4568
4670
|
event.stopPropagation();
|
|
@@ -4637,8 +4739,6 @@ class LinkDropdown extends ToolbarDropdown {
|
|
|
4637
4739
|
}
|
|
4638
4740
|
}
|
|
4639
4741
|
|
|
4640
|
-
customElements.define("lexxy-link-dropdown", LinkDropdown);
|
|
4641
|
-
|
|
4642
4742
|
const APPLY_HIGHLIGHT_SELECTOR = "button.lexxy-highlight-button";
|
|
4643
4743
|
const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
4644
4744
|
|
|
@@ -4648,19 +4748,14 @@ const REMOVE_HIGHLIGHT_SELECTOR = "[data-command='removeHighlight']";
|
|
|
4648
4748
|
const NO_STYLE = Symbol("no_style");
|
|
4649
4749
|
|
|
4650
4750
|
class HighlightDropdown extends ToolbarDropdown {
|
|
4651
|
-
#initialized = false
|
|
4652
|
-
|
|
4653
4751
|
connectedCallback() {
|
|
4654
4752
|
super.connectedCallback();
|
|
4655
4753
|
this.#registerToggleHandler();
|
|
4656
4754
|
}
|
|
4657
4755
|
|
|
4658
|
-
|
|
4659
|
-
if (this.#initialized) return
|
|
4660
|
-
|
|
4756
|
+
initialize() {
|
|
4661
4757
|
this.#setUpButtons();
|
|
4662
4758
|
this.#registerButtonHandlers();
|
|
4663
|
-
this.#initialized = true;
|
|
4664
4759
|
}
|
|
4665
4760
|
|
|
4666
4761
|
#registerToggleHandler() {
|
|
@@ -4673,16 +4768,18 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4673
4768
|
}
|
|
4674
4769
|
|
|
4675
4770
|
#setUpButtons() {
|
|
4676
|
-
this
|
|
4677
|
-
|
|
4678
|
-
|
|
4771
|
+
const colorGroups = this.editorElement.config.get("highlight.buttons");
|
|
4772
|
+
|
|
4773
|
+
this.#populateButtonGroup("color", colorGroups.color);
|
|
4774
|
+
this.#populateButtonGroup("background-color", colorGroups["background-color"]);
|
|
4775
|
+
|
|
4776
|
+
const maxNumberOfColors = Math.max(colorGroups.color.length, colorGroups["background-color"].length);
|
|
4777
|
+
this.style.setProperty("--max-colors", maxNumberOfColors);
|
|
4679
4778
|
}
|
|
4680
4779
|
|
|
4681
|
-
#populateButtonGroup(
|
|
4682
|
-
const attribute = buttonGroup.dataset.buttonGroup;
|
|
4683
|
-
const values = this.editorElement.config.get(`highlight.buttons.${attribute}`) || [];
|
|
4780
|
+
#populateButtonGroup(attribute, values) {
|
|
4684
4781
|
values.forEach((value, index) => {
|
|
4685
|
-
|
|
4782
|
+
this.#buttonContainer.appendChild(this.#createButton(attribute, value, index));
|
|
4686
4783
|
});
|
|
4687
4784
|
}
|
|
4688
4785
|
|
|
@@ -4691,15 +4788,13 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4691
4788
|
button.dataset.style = attribute;
|
|
4692
4789
|
button.style.setProperty(attribute, value);
|
|
4693
4790
|
button.dataset.value = value;
|
|
4694
|
-
button.classList.add("lexxy-highlight-button");
|
|
4791
|
+
button.classList.add("lexxy-editor__toolbar-button", "lexxy-highlight-button");
|
|
4695
4792
|
button.name = attribute + "-" + index;
|
|
4696
4793
|
return button
|
|
4697
4794
|
}
|
|
4698
4795
|
|
|
4699
4796
|
#handleToggle({ newState }) {
|
|
4700
4797
|
if (newState === "open") {
|
|
4701
|
-
this.#ensureInitialized();
|
|
4702
|
-
|
|
4703
4798
|
this.editor.getEditorState().read(() => {
|
|
4704
4799
|
this.#updateColorButtonStates($getSelection());
|
|
4705
4800
|
});
|
|
@@ -4742,8 +4837,8 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4742
4837
|
this.querySelector(REMOVE_HIGHLIGHT_SELECTOR).disabled = !hasHighlight;
|
|
4743
4838
|
}
|
|
4744
4839
|
|
|
4745
|
-
get #
|
|
4746
|
-
return this.
|
|
4840
|
+
get #buttonContainer() {
|
|
4841
|
+
return this.querySelector(".lexxy-highlight-colors")
|
|
4747
4842
|
}
|
|
4748
4843
|
|
|
4749
4844
|
get #colorButtons() {
|
|
@@ -4751,1204 +4846,1433 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4751
4846
|
}
|
|
4752
4847
|
}
|
|
4753
4848
|
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
this.#setUpButtons();
|
|
4759
|
-
this.#monitorForTableSelection();
|
|
4760
|
-
this.#registerKeyboardShortcuts();
|
|
4761
|
-
}
|
|
4762
|
-
|
|
4763
|
-
disconnectedCallback() {
|
|
4764
|
-
this.#unregisterKeyboardShortcuts();
|
|
4765
|
-
}
|
|
4766
|
-
|
|
4767
|
-
get #editor() {
|
|
4768
|
-
return this.#editorElement.editor
|
|
4849
|
+
class BaseSource {
|
|
4850
|
+
// Template method to override
|
|
4851
|
+
async buildListItems(filter = "") {
|
|
4852
|
+
return Promise.resolve([])
|
|
4769
4853
|
}
|
|
4770
4854
|
|
|
4771
|
-
|
|
4772
|
-
|
|
4855
|
+
// Template method to override
|
|
4856
|
+
promptItemFor(listItem) {
|
|
4857
|
+
return null
|
|
4773
4858
|
}
|
|
4774
4859
|
|
|
4775
|
-
|
|
4776
|
-
const selection = $getSelection();
|
|
4777
|
-
if (!$isRangeSelection(selection)) return null
|
|
4860
|
+
// Protected
|
|
4778
4861
|
|
|
4779
|
-
|
|
4780
|
-
|
|
4862
|
+
buildListItemElementFor(promptItemElement) {
|
|
4863
|
+
const template = promptItemElement.querySelector("template[type='menu']");
|
|
4864
|
+
const fragment = template.content.cloneNode(true);
|
|
4865
|
+
const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
|
|
4866
|
+
listItemElement.classList.add("lexxy-prompt-menu__item");
|
|
4867
|
+
listItemElement.appendChild(fragment);
|
|
4868
|
+
return listItemElement
|
|
4781
4869
|
}
|
|
4782
4870
|
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4871
|
+
async loadPromptItemsFromUrl(url) {
|
|
4872
|
+
try {
|
|
4873
|
+
const response = await fetch(url);
|
|
4874
|
+
const html = await response.text();
|
|
4875
|
+
const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
|
|
4876
|
+
return Promise.resolve(Array.from(promptItems))
|
|
4877
|
+
} catch (error) {
|
|
4878
|
+
return Promise.reject(error)
|
|
4879
|
+
}
|
|
4787
4880
|
}
|
|
4881
|
+
}
|
|
4788
4882
|
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
return
|
|
4883
|
+
class LocalFilterSource extends BaseSource {
|
|
4884
|
+
async buildListItems(filter = "") {
|
|
4885
|
+
const promptItems = await this.fetchPromptItems();
|
|
4886
|
+
return this.#buildListItemsFromPromptItems(promptItems, filter)
|
|
4793
4887
|
}
|
|
4794
4888
|
|
|
4795
|
-
|
|
4796
|
-
|
|
4889
|
+
// Template method to override
|
|
4890
|
+
async fetchPromptItems(filter) {
|
|
4891
|
+
return Promise.resolve([])
|
|
4797
4892
|
}
|
|
4798
4893
|
|
|
4799
|
-
|
|
4800
|
-
|
|
4894
|
+
promptItemFor(listItem) {
|
|
4895
|
+
return this.promptItemByListItem.get(listItem)
|
|
4801
4896
|
}
|
|
4802
4897
|
|
|
4803
|
-
#
|
|
4804
|
-
|
|
4805
|
-
|
|
4898
|
+
#buildListItemsFromPromptItems(promptItems, filter) {
|
|
4899
|
+
const listItems = [];
|
|
4900
|
+
this.promptItemByListItem = new WeakMap();
|
|
4901
|
+
promptItems.forEach((promptItem) => {
|
|
4902
|
+
const searchableText = promptItem.getAttribute("search");
|
|
4806
4903
|
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
}
|
|
4813
|
-
this.#editor.getEditorState().read(() => {
|
|
4814
|
-
const cell = this.#currentCell;
|
|
4815
|
-
if (!cell) return
|
|
4904
|
+
if (!filter || filterMatches(searchableText, filter)) {
|
|
4905
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
4906
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
4907
|
+
listItems.push(listItem);
|
|
4908
|
+
}
|
|
4909
|
+
});
|
|
4816
4910
|
|
|
4817
|
-
|
|
4818
|
-
cell.select();
|
|
4819
|
-
});
|
|
4820
|
-
});
|
|
4821
|
-
this.#closeMoreMenu();
|
|
4822
|
-
}
|
|
4911
|
+
return listItems
|
|
4823
4912
|
}
|
|
4913
|
+
}
|
|
4824
4914
|
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
handleRollingTabIndex(this.#tableHandlerButtons, event);
|
|
4830
|
-
}
|
|
4915
|
+
class InlinePromptSource extends LocalFilterSource {
|
|
4916
|
+
constructor(inlinePromptItems) {
|
|
4917
|
+
super();
|
|
4918
|
+
this.inlinePromptItemElements = Array.from(inlinePromptItems);
|
|
4831
4919
|
}
|
|
4832
4920
|
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
this.appendChild(this.#createColumnButtonsContainer());
|
|
4836
|
-
|
|
4837
|
-
this.moreMenu = this.#createMoreMenu();
|
|
4838
|
-
this.appendChild(this.moreMenu);
|
|
4839
|
-
this.addEventListener("keydown", this.#handleTableHandlerKeydown);
|
|
4921
|
+
async fetchPromptItems() {
|
|
4922
|
+
return Promise.resolve(this.inlinePromptItemElements)
|
|
4840
4923
|
}
|
|
4924
|
+
}
|
|
4841
4925
|
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4926
|
+
class DeferredPromptSource extends LocalFilterSource {
|
|
4927
|
+
constructor(url) {
|
|
4928
|
+
super();
|
|
4929
|
+
this.url = url;
|
|
4845
4930
|
|
|
4846
|
-
this
|
|
4847
|
-
this.#setTableFocusState(true);
|
|
4931
|
+
this.fetchPromptItems();
|
|
4848
4932
|
}
|
|
4849
4933
|
|
|
4850
|
-
|
|
4851
|
-
this.
|
|
4852
|
-
this.#closeMoreMenu();
|
|
4934
|
+
async fetchPromptItems() {
|
|
4935
|
+
this.promptItems ??= await this.loadPromptItemsFromUrl(this.url);
|
|
4853
4936
|
|
|
4854
|
-
this
|
|
4855
|
-
this.currentTableNode = null;
|
|
4937
|
+
return Promise.resolve(this.promptItems)
|
|
4856
4938
|
}
|
|
4939
|
+
}
|
|
4857
4940
|
|
|
4858
|
-
|
|
4859
|
-
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
4860
|
-
if (!tableElement) return
|
|
4941
|
+
const DEBOUNCE_INTERVAL = 200;
|
|
4861
4942
|
|
|
4862
|
-
|
|
4863
|
-
|
|
4943
|
+
class RemoteFilterSource extends BaseSource {
|
|
4944
|
+
constructor(url) {
|
|
4945
|
+
super();
|
|
4864
4946
|
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
this.style.top = `${relativeTop}px`;
|
|
4868
|
-
this.style.left = `${relativeCenter}px`;
|
|
4947
|
+
this.baseURL = url;
|
|
4948
|
+
this.loadAndFilterListItems = debounceAsync(this.fetchFilteredListItems.bind(this), DEBOUNCE_INTERVAL);
|
|
4869
4949
|
}
|
|
4870
4950
|
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
const tableElement = $getElementForTableNode(this.#editor, this.currentTableNode);
|
|
4875
|
-
if (!tableElement) return
|
|
4876
|
-
|
|
4877
|
-
const rowCount = tableElement.rows;
|
|
4878
|
-
const columnCount = tableElement.columns;
|
|
4879
|
-
|
|
4880
|
-
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
4881
|
-
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
4951
|
+
async buildListItems(filter = "") {
|
|
4952
|
+
return await this.loadAndFilterListItems(filter)
|
|
4882
4953
|
}
|
|
4883
4954
|
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
className: "lexxy-table-control__button",
|
|
4887
|
-
"aria-label": label,
|
|
4888
|
-
type: "button"
|
|
4889
|
-
});
|
|
4890
|
-
button.tabIndex = -1;
|
|
4891
|
-
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
4892
|
-
button.addEventListener("click", onClick.bind(this));
|
|
4893
|
-
|
|
4894
|
-
return button
|
|
4955
|
+
promptItemFor(listItem) {
|
|
4956
|
+
return this.promptItemByListItem.get(listItem)
|
|
4895
4957
|
}
|
|
4896
4958
|
|
|
4897
|
-
|
|
4898
|
-
const
|
|
4899
|
-
|
|
4900
|
-
const plusButton = this.#createButton("+", "Add row", () => this.#insertTableRow("end"));
|
|
4901
|
-
const minusButton = this.#createButton("−", "Remove row", () => this.#deleteTableRow("end"));
|
|
4902
|
-
|
|
4903
|
-
this.rowCount = createElement("span");
|
|
4904
|
-
this.rowCount.textContent = "_ rows";
|
|
4905
|
-
|
|
4906
|
-
container.appendChild(minusButton);
|
|
4907
|
-
container.appendChild(this.rowCount);
|
|
4908
|
-
container.appendChild(plusButton);
|
|
4909
|
-
|
|
4910
|
-
return container
|
|
4959
|
+
async fetchFilteredListItems(filter) {
|
|
4960
|
+
const promptItems = await this.loadPromptItemsFromUrl(this.#urlFor(filter));
|
|
4961
|
+
return this.#buildListItemsFromPromptItems(promptItems)
|
|
4911
4962
|
}
|
|
4912
4963
|
|
|
4913
|
-
#
|
|
4914
|
-
const
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
const minusButton = this.#createButton("−", "Remove column", () => this.#deleteTableColumn("end"));
|
|
4918
|
-
|
|
4919
|
-
this.columnCount = createElement("span");
|
|
4920
|
-
this.columnCount.textContent = "_ columns";
|
|
4921
|
-
|
|
4922
|
-
container.appendChild(minusButton);
|
|
4923
|
-
container.appendChild(this.columnCount);
|
|
4924
|
-
container.appendChild(plusButton);
|
|
4925
|
-
|
|
4926
|
-
return container
|
|
4964
|
+
#urlFor(filter) {
|
|
4965
|
+
const url = new URL(this.baseURL, window.location.origin);
|
|
4966
|
+
url.searchParams.append("filter", filter);
|
|
4967
|
+
return url.toString()
|
|
4927
4968
|
}
|
|
4928
4969
|
|
|
4929
|
-
#
|
|
4930
|
-
const
|
|
4931
|
-
|
|
4932
|
-
});
|
|
4933
|
-
container.setAttribute("name", "lexxy-dropdown");
|
|
4934
|
-
|
|
4935
|
-
container.tabIndex = -1;
|
|
4936
|
-
|
|
4937
|
-
const summary = createElement("summary", {}, "•••");
|
|
4938
|
-
container.appendChild(summary);
|
|
4939
|
-
|
|
4940
|
-
const details = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
4941
|
-
container.appendChild(details);
|
|
4942
|
-
|
|
4943
|
-
details.appendChild(this.#createRowSection());
|
|
4944
|
-
details.appendChild(this.#createColumnSection());
|
|
4945
|
-
details.appendChild(this.#createDeleteTableSection());
|
|
4970
|
+
#buildListItemsFromPromptItems(promptItems) {
|
|
4971
|
+
const listItems = [];
|
|
4972
|
+
this.promptItemByListItem = new WeakMap();
|
|
4946
4973
|
|
|
4947
|
-
|
|
4974
|
+
for (const promptItem of promptItems) {
|
|
4975
|
+
const listItem = this.buildListItemElementFor(promptItem);
|
|
4976
|
+
this.promptItemByListItem.set(listItem, promptItem);
|
|
4977
|
+
listItems.push(listItem);
|
|
4978
|
+
}
|
|
4948
4979
|
|
|
4949
|
-
return
|
|
4980
|
+
return listItems
|
|
4950
4981
|
}
|
|
4982
|
+
}
|
|
4951
4983
|
|
|
4952
|
-
|
|
4953
|
-
const columnSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4954
|
-
|
|
4955
|
-
const columnButtons = [
|
|
4956
|
-
{ icon: this.#icon("add-column-before"), label: "Add column before", onClick: () => this.#insertTableColumn("left") },
|
|
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
|
-
];
|
|
4961
|
-
|
|
4962
|
-
columnButtons.forEach(button => {
|
|
4963
|
-
const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
|
|
4964
|
-
columnSection.appendChild(buttonElement);
|
|
4965
|
-
});
|
|
4984
|
+
const NOTHING_FOUND_DEFAULT_MESSAGE = "Nothing found";
|
|
4966
4985
|
|
|
4967
|
-
|
|
4986
|
+
class LexicalPromptElement extends HTMLElement {
|
|
4987
|
+
constructor() {
|
|
4988
|
+
super();
|
|
4989
|
+
this.keyListeners = [];
|
|
4968
4990
|
}
|
|
4969
4991
|
|
|
4970
|
-
|
|
4971
|
-
const rowSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4972
|
-
|
|
4973
|
-
const rowButtons = [
|
|
4974
|
-
{ icon: this.#icon("add-row-above"), label: "Add row above", onClick: () => this.#insertTableRow("above") },
|
|
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
|
-
];
|
|
4992
|
+
static observedAttributes = [ "connected" ]
|
|
4979
4993
|
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
rowSection.appendChild(buttonElement);
|
|
4983
|
-
});
|
|
4994
|
+
connectedCallback() {
|
|
4995
|
+
this.source = this.#createSource();
|
|
4984
4996
|
|
|
4985
|
-
|
|
4997
|
+
this.#addTriggerListener();
|
|
4998
|
+
this.toggleAttribute("connected", true);
|
|
4986
4999
|
}
|
|
4987
5000
|
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
const deleteButton = { icon: this.#icon("delete-table"), label: "Delete table", onClick: this.#deleteTable };
|
|
4992
|
-
|
|
4993
|
-
const buttonElement = this.#createButton(deleteButton.icon, deleteButton.label, deleteButton.onClick);
|
|
4994
|
-
deleteSection.appendChild(buttonElement);
|
|
4995
|
-
|
|
4996
|
-
return deleteSection
|
|
5001
|
+
disconnectedCallback() {
|
|
5002
|
+
this.source = null;
|
|
5003
|
+
this.popoverElement = null;
|
|
4997
5004
|
}
|
|
4998
5005
|
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
this.#removeFocusStateFromSelectedCell();
|
|
5006
|
+
|
|
5007
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
5008
|
+
if (name === "connected" && this.isConnected && oldValue != null && oldValue !== newValue) {
|
|
5009
|
+
requestAnimationFrame(() => this.#reconnect());
|
|
5004
5010
|
}
|
|
5005
5011
|
}
|
|
5006
5012
|
|
|
5007
|
-
|
|
5008
|
-
this
|
|
5009
|
-
this.moreMenu.removeAttribute("open");
|
|
5013
|
+
get name() {
|
|
5014
|
+
return this.getAttribute("name")
|
|
5010
5015
|
}
|
|
5011
5016
|
|
|
5012
|
-
|
|
5013
|
-
this
|
|
5014
|
-
this.#editor.getEditorState().read(() => {
|
|
5015
|
-
const selection = $getSelection();
|
|
5016
|
-
if (!$isRangeSelection(selection)) return
|
|
5017
|
-
|
|
5018
|
-
const anchorNode = selection.anchor.getNode();
|
|
5019
|
-
const tableNode = $findTableNode(anchorNode);
|
|
5020
|
-
|
|
5021
|
-
if (tableNode) {
|
|
5022
|
-
this.#tableCellWasSelected(tableNode);
|
|
5023
|
-
} else {
|
|
5024
|
-
this.#hideTableHandlerButtons();
|
|
5025
|
-
}
|
|
5026
|
-
});
|
|
5027
|
-
});
|
|
5017
|
+
get trigger() {
|
|
5018
|
+
return this.getAttribute("trigger")
|
|
5028
5019
|
}
|
|
5029
5020
|
|
|
5030
|
-
|
|
5031
|
-
this
|
|
5021
|
+
get supportsSpaceInSearches() {
|
|
5022
|
+
return this.hasAttribute("supports-space-in-searches")
|
|
5023
|
+
}
|
|
5032
5024
|
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
if (!tableParent) return
|
|
5036
|
-
tableParent.classList.add("node--selected");
|
|
5037
|
-
}
|
|
5025
|
+
get open() {
|
|
5026
|
+
return this.popoverElement?.classList?.contains("lexxy-prompt-menu--visible")
|
|
5038
5027
|
}
|
|
5039
5028
|
|
|
5040
|
-
|
|
5041
|
-
this.
|
|
5042
|
-
this.#updateButtonsPosition(tableNode);
|
|
5043
|
-
this.#showTableHandlerButtons();
|
|
5029
|
+
get closed() {
|
|
5030
|
+
return !this.open
|
|
5044
5031
|
}
|
|
5045
5032
|
|
|
5046
|
-
#
|
|
5047
|
-
this
|
|
5048
|
-
|
|
5049
|
-
if (!currentCell) return
|
|
5033
|
+
get #doesSpaceSelect() {
|
|
5034
|
+
return !this.supportsSpaceInSearches
|
|
5035
|
+
}
|
|
5050
5036
|
|
|
5051
|
-
|
|
5052
|
-
|
|
5037
|
+
#createSource() {
|
|
5038
|
+
const src = this.getAttribute("src");
|
|
5039
|
+
if (src) {
|
|
5040
|
+
if (this.hasAttribute("remote-filtering")) {
|
|
5041
|
+
return new RemoteFilterSource(src)
|
|
5042
|
+
} else {
|
|
5043
|
+
return new DeferredPromptSource(src)
|
|
5044
|
+
}
|
|
5045
|
+
} else {
|
|
5046
|
+
return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
|
|
5050
|
+
#addTriggerListener() {
|
|
5051
|
+
const unregister = this.#editor.registerUpdateListener(({ editorState }) => {
|
|
5052
|
+
editorState.read(() => {
|
|
5053
|
+
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5054
|
+
if (!node) return
|
|
5055
|
+
|
|
5056
|
+
if ($isTextNode(node)) {
|
|
5057
|
+
const fullText = node.getTextContent();
|
|
5058
|
+
const triggerLength = this.trigger.length;
|
|
5053
5059
|
|
|
5054
|
-
|
|
5060
|
+
// Check if we have enough characters for the trigger
|
|
5061
|
+
if (offset >= triggerLength) {
|
|
5062
|
+
const textBeforeCursor = fullText.slice(offset - triggerLength, offset);
|
|
5063
|
+
|
|
5064
|
+
// Check if trigger is at the start of the text node (new line case) or preceded by space or newline
|
|
5065
|
+
if (textBeforeCursor === this.trigger) {
|
|
5066
|
+
const isAtStart = offset === triggerLength;
|
|
5067
|
+
|
|
5068
|
+
const charBeforeTrigger = offset > triggerLength ? fullText[offset - triggerLength - 1] : null;
|
|
5069
|
+
const isPrecededBySpaceOrNewline = charBeforeTrigger === " " || charBeforeTrigger === "\n";
|
|
5070
|
+
|
|
5071
|
+
if (isAtStart || isPrecededBySpaceOrNewline) {
|
|
5072
|
+
unregister();
|
|
5073
|
+
this.#showPopover();
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
});
|
|
5055
5079
|
});
|
|
5056
5080
|
}
|
|
5057
5081
|
|
|
5058
|
-
#
|
|
5059
|
-
this.#
|
|
5082
|
+
#addCursorPositionListener() {
|
|
5083
|
+
this.cursorPositionListener = this.#editor.registerUpdateListener(() => {
|
|
5084
|
+
if (this.closed) return
|
|
5085
|
+
|
|
5086
|
+
this.#editor.read(() => {
|
|
5087
|
+
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5088
|
+
if (!node) return
|
|
5089
|
+
|
|
5090
|
+
if ($isTextNode(node) && offset > 0) {
|
|
5091
|
+
const fullText = node.getTextContent();
|
|
5092
|
+
const textBeforeCursor = fullText.slice(0, offset);
|
|
5093
|
+
const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
|
|
5094
|
+
const triggerEndIndex = lastTriggerIndex + this.trigger.length - 1;
|
|
5095
|
+
|
|
5096
|
+
// If trigger is not found, or cursor is at or before the trigger end position, hide popover
|
|
5097
|
+
if (lastTriggerIndex === -1 || offset <= triggerEndIndex) {
|
|
5098
|
+
this.#hidePopover();
|
|
5099
|
+
}
|
|
5100
|
+
} else {
|
|
5101
|
+
// Cursor is not in a text node or at offset 0, hide popover
|
|
5102
|
+
this.#hidePopover();
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
5105
|
+
});
|
|
5060
5106
|
}
|
|
5061
5107
|
|
|
5062
|
-
#
|
|
5063
|
-
if (
|
|
5108
|
+
#removeCursorPositionListener() {
|
|
5109
|
+
if (this.cursorPositionListener) {
|
|
5110
|
+
this.cursorPositionListener();
|
|
5111
|
+
this.cursorPositionListener = null;
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5064
5114
|
|
|
5065
|
-
|
|
5066
|
-
|
|
5115
|
+
get #editor() {
|
|
5116
|
+
return this.#editorElement.editor
|
|
5117
|
+
}
|
|
5067
5118
|
|
|
5068
|
-
|
|
5119
|
+
get #editorElement() {
|
|
5120
|
+
return this.closest("lexxy-editor")
|
|
5069
5121
|
}
|
|
5070
5122
|
|
|
5071
|
-
#
|
|
5072
|
-
this.#
|
|
5123
|
+
get #selection() {
|
|
5124
|
+
return this.#editorElement.selection
|
|
5125
|
+
}
|
|
5073
5126
|
|
|
5074
|
-
|
|
5075
|
-
this.#
|
|
5127
|
+
async #showPopover() {
|
|
5128
|
+
this.popoverElement ??= await this.#buildPopover();
|
|
5129
|
+
this.#resetPopoverPosition();
|
|
5130
|
+
await this.#filterOptions();
|
|
5131
|
+
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
|
|
5132
|
+
this.#selectFirstOption();
|
|
5133
|
+
|
|
5134
|
+
this.#editorElement.addEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5135
|
+
this.#editorElement.addEventListener("lexxy:change", this.#filterOptions);
|
|
5136
|
+
|
|
5137
|
+
this.#registerKeyListeners();
|
|
5138
|
+
this.#addCursorPositionListener();
|
|
5076
5139
|
}
|
|
5077
5140
|
|
|
5078
|
-
#
|
|
5079
|
-
|
|
5141
|
+
#registerKeyListeners() {
|
|
5142
|
+
// We can't use a regular keydown for Enter as Lexical handles it first
|
|
5143
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ENTER_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5144
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_TAB_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5145
|
+
|
|
5146
|
+
if (this.#doesSpaceSelect) {
|
|
5147
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_SPACE_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// Register arrow keys with HIGH priority to prevent Lexical's selection handlers from running
|
|
5151
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_UP_COMMAND, this.#handleArrowUp.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5152
|
+
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_DOWN_COMMAND, this.#handleArrowDown.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5155
|
+
#handleArrowUp(event) {
|
|
5156
|
+
this.#moveSelectionUp();
|
|
5157
|
+
event.preventDefault();
|
|
5158
|
+
return true
|
|
5159
|
+
}
|
|
5160
|
+
|
|
5161
|
+
#handleArrowDown(event) {
|
|
5162
|
+
this.#moveSelectionDown();
|
|
5163
|
+
event.preventDefault();
|
|
5164
|
+
return true
|
|
5165
|
+
}
|
|
5166
|
+
|
|
5167
|
+
#selectFirstOption() {
|
|
5168
|
+
const firstOption = this.#listItemElements[0];
|
|
5169
|
+
|
|
5170
|
+
if (firstOption) {
|
|
5171
|
+
this.#selectOption(firstOption);
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
|
|
5175
|
+
get #listItemElements() {
|
|
5176
|
+
return Array.from(this.popoverElement.querySelectorAll(".lexxy-prompt-menu__item"))
|
|
5177
|
+
}
|
|
5178
|
+
|
|
5179
|
+
#selectOption(listItem) {
|
|
5180
|
+
this.#clearSelection();
|
|
5181
|
+
listItem.toggleAttribute("aria-selected", true);
|
|
5182
|
+
listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
5183
|
+
listItem.focus();
|
|
5184
|
+
|
|
5185
|
+
// Preserve selection to prevent cursor jump
|
|
5186
|
+
this.#selection.preservingSelection(() => {
|
|
5187
|
+
this.#editorElement.focus();
|
|
5188
|
+
});
|
|
5189
|
+
|
|
5190
|
+
this.#editorContentElement.setAttribute("aria-controls", this.popoverElement.id);
|
|
5191
|
+
this.#editorContentElement.setAttribute("aria-activedescendant", listItem.id);
|
|
5192
|
+
this.#editorContentElement.setAttribute("aria-haspopup", "listbox");
|
|
5080
5193
|
}
|
|
5081
5194
|
|
|
5082
|
-
#
|
|
5083
|
-
this.#
|
|
5195
|
+
#clearSelection() {
|
|
5196
|
+
this.#listItemElements.forEach((item) => { item.toggleAttribute("aria-selected", false); });
|
|
5197
|
+
this.#editorContentElement.removeAttribute("aria-controls");
|
|
5198
|
+
this.#editorContentElement.removeAttribute("aria-activedescendant");
|
|
5199
|
+
this.#editorContentElement.removeAttribute("aria-haspopup");
|
|
5200
|
+
}
|
|
5201
|
+
|
|
5202
|
+
#positionPopover() {
|
|
5203
|
+
const { x, y, fontSize } = this.#selection.cursorPosition;
|
|
5204
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5205
|
+
const contentRect = this.#editorContentElement.getBoundingClientRect();
|
|
5206
|
+
const verticalOffset = contentRect.top - editorRect.top;
|
|
5207
|
+
|
|
5208
|
+
if (!this.popoverElement.hasAttribute("data-anchored")) {
|
|
5209
|
+
this.popoverElement.style.left = `${x}px`;
|
|
5210
|
+
this.popoverElement.toggleAttribute("data-anchored", true);
|
|
5211
|
+
}
|
|
5212
|
+
|
|
5213
|
+
this.popoverElement.style.top = `${y + verticalOffset}px`;
|
|
5214
|
+
this.popoverElement.style.bottom = "auto";
|
|
5215
|
+
|
|
5216
|
+
const popoverRect = this.popoverElement.getBoundingClientRect();
|
|
5217
|
+
const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
|
|
5218
|
+
|
|
5219
|
+
if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
|
|
5220
|
+
this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
|
|
5221
|
+
this.popoverElement.style.bottom = "auto";
|
|
5222
|
+
this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
|
|
5223
|
+
}
|
|
5084
5224
|
}
|
|
5085
5225
|
|
|
5086
|
-
#
|
|
5087
|
-
this
|
|
5226
|
+
#resetPopoverPosition() {
|
|
5227
|
+
this.popoverElement.removeAttribute("data-clipped-at-bottom");
|
|
5228
|
+
this.popoverElement.removeAttribute("data-anchored");
|
|
5088
5229
|
}
|
|
5089
5230
|
|
|
5090
|
-
#
|
|
5091
|
-
this.#
|
|
5231
|
+
async #hidePopover() {
|
|
5232
|
+
this.#clearSelection();
|
|
5233
|
+
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
|
|
5234
|
+
this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
|
|
5235
|
+
this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5236
|
+
|
|
5237
|
+
this.#unregisterKeyListeners();
|
|
5238
|
+
this.#removeCursorPositionListener();
|
|
5239
|
+
|
|
5240
|
+
await nextFrame();
|
|
5241
|
+
this.#addTriggerListener();
|
|
5242
|
+
}
|
|
5243
|
+
|
|
5244
|
+
#unregisterKeyListeners() {
|
|
5245
|
+
this.keyListeners.forEach((unregister) => unregister());
|
|
5246
|
+
this.keyListeners = [];
|
|
5247
|
+
}
|
|
5248
|
+
|
|
5249
|
+
#filterOptions = async () => {
|
|
5250
|
+
if (this.initialPrompt) {
|
|
5251
|
+
this.initialPrompt = false;
|
|
5252
|
+
return
|
|
5253
|
+
}
|
|
5254
|
+
|
|
5255
|
+
if (this.#editorContents.containsTextBackUntil(this.trigger)) {
|
|
5256
|
+
await this.#showFilteredOptions();
|
|
5257
|
+
await nextFrame();
|
|
5258
|
+
this.#positionPopover();
|
|
5259
|
+
} else {
|
|
5260
|
+
this.#hidePopover();
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
|
|
5264
|
+
async #showFilteredOptions() {
|
|
5265
|
+
const filter = this.#editorContents.textBackUntil(this.trigger);
|
|
5266
|
+
const filteredListItems = await this.source.buildListItems(filter);
|
|
5267
|
+
this.popoverElement.innerHTML = "";
|
|
5268
|
+
|
|
5269
|
+
if (filteredListItems.length > 0) {
|
|
5270
|
+
this.#showResults(filteredListItems);
|
|
5271
|
+
} else {
|
|
5272
|
+
this.#showEmptyResults();
|
|
5273
|
+
}
|
|
5274
|
+
this.#selectFirstOption();
|
|
5275
|
+
}
|
|
5276
|
+
|
|
5277
|
+
#showResults(filteredListItems) {
|
|
5278
|
+
this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
|
|
5279
|
+
this.popoverElement.append(...filteredListItems);
|
|
5280
|
+
}
|
|
5281
|
+
|
|
5282
|
+
#showEmptyResults() {
|
|
5283
|
+
this.popoverElement.classList.add("lexxy-prompt-menu--empty");
|
|
5284
|
+
const el = createElement("li", { innerHTML: this.#emptyResultsMessage });
|
|
5285
|
+
el.classList.add("lexxy-prompt-menu__item--empty");
|
|
5286
|
+
this.popoverElement.append(el);
|
|
5287
|
+
}
|
|
5288
|
+
|
|
5289
|
+
get #emptyResultsMessage() {
|
|
5290
|
+
return this.getAttribute("empty-results") || NOTHING_FOUND_DEFAULT_MESSAGE
|
|
5291
|
+
}
|
|
5292
|
+
|
|
5293
|
+
#handleKeydownOnPopover = (event) => {
|
|
5294
|
+
if (event.key === "Escape") {
|
|
5295
|
+
this.#hidePopover();
|
|
5296
|
+
this.#editorElement.focus();
|
|
5297
|
+
event.stopPropagation();
|
|
5298
|
+
}
|
|
5299
|
+
// Arrow keys are now handled via Lexical commands with HIGH priority
|
|
5300
|
+
}
|
|
5301
|
+
|
|
5302
|
+
#moveSelectionDown() {
|
|
5303
|
+
const nextIndex = this.#selectedIndex + 1;
|
|
5304
|
+
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
|
|
5305
|
+
}
|
|
5306
|
+
|
|
5307
|
+
#moveSelectionUp() {
|
|
5308
|
+
const previousIndex = this.#selectedIndex - 1;
|
|
5309
|
+
if (previousIndex >= 0) this.#selectOption(this.#listItemElements[previousIndex]);
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
get #selectedIndex() {
|
|
5313
|
+
return this.#listItemElements.findIndex((item) => item.hasAttribute("aria-selected"))
|
|
5314
|
+
}
|
|
5315
|
+
|
|
5316
|
+
get #selectedListItem() {
|
|
5317
|
+
return this.#listItemElements[this.#selectedIndex]
|
|
5318
|
+
}
|
|
5319
|
+
|
|
5320
|
+
#handleSelectedOption(event) {
|
|
5321
|
+
event.preventDefault();
|
|
5322
|
+
event.stopPropagation();
|
|
5323
|
+
this.#optionWasSelected();
|
|
5324
|
+
return true
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
#optionWasSelected() {
|
|
5328
|
+
this.#replaceTriggerWithSelectedItem();
|
|
5329
|
+
this.#hidePopover();
|
|
5330
|
+
this.#editorElement.focus();
|
|
5331
|
+
}
|
|
5332
|
+
|
|
5333
|
+
#replaceTriggerWithSelectedItem() {
|
|
5334
|
+
const promptItem = this.source.promptItemFor(this.#selectedListItem);
|
|
5335
|
+
|
|
5336
|
+
if (!promptItem) { return }
|
|
5337
|
+
|
|
5338
|
+
const templates = Array.from(promptItem.querySelectorAll("template[type='editor']"));
|
|
5339
|
+
const stringToReplace = `${this.trigger}${this.#editorContents.textBackUntil(this.trigger)}`;
|
|
5340
|
+
|
|
5341
|
+
if (this.hasAttribute("insert-editable-text")) {
|
|
5342
|
+
this.#insertTemplatesAsEditableText(templates, stringToReplace);
|
|
5343
|
+
} else {
|
|
5344
|
+
this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
|
|
5348
|
+
#insertTemplatesAsEditableText(templates, stringToReplace) {
|
|
5349
|
+
this.#editor.update(() => {
|
|
5350
|
+
const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
|
|
5351
|
+
this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5354
|
+
|
|
5355
|
+
#buildEditableTextNodes(template) {
|
|
5356
|
+
return $generateNodesFromDOM(this.#editor, parseHtml(`${template.innerHTML}`))
|
|
5357
|
+
}
|
|
5358
|
+
|
|
5359
|
+
#insertTemplatesAsAttachments(templates, stringToReplace, fallbackSgid = null) {
|
|
5360
|
+
this.#editor.update(() => {
|
|
5361
|
+
const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
|
|
5362
|
+
const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
|
|
5363
|
+
this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
|
|
5364
|
+
});
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
#buildAttachmentNodes(templates, fallbackSgid = null) {
|
|
5368
|
+
return templates.map(
|
|
5369
|
+
template => this.#buildAttachmentNode(
|
|
5370
|
+
template.innerHTML,
|
|
5371
|
+
template.getAttribute("content-type") || this.#defaultPromptContentType,
|
|
5372
|
+
template.getAttribute("sgid") || fallbackSgid
|
|
5373
|
+
))
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
#getSpacerTextNode() {
|
|
5377
|
+
return $createTextNode(" ")
|
|
5378
|
+
}
|
|
5379
|
+
|
|
5380
|
+
get #defaultPromptContentType() {
|
|
5381
|
+
const attachmentContentTypeNamespace = Lexxy.global.get("attachmentContentTypeNamespace");
|
|
5382
|
+
return `application/vnd.${attachmentContentTypeNamespace}.${this.name}`
|
|
5383
|
+
}
|
|
5384
|
+
|
|
5385
|
+
#buildAttachmentNode(innerHtml, contentType, sgid) {
|
|
5386
|
+
return new CustomActionTextAttachmentNode({ sgid, contentType, innerHtml })
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
get #editorContents() {
|
|
5390
|
+
return this.#editorElement.contents
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
get #editorContentElement() {
|
|
5394
|
+
return this.#editorElement.editorContentElement
|
|
5395
|
+
}
|
|
5396
|
+
|
|
5397
|
+
async #buildPopover() {
|
|
5398
|
+
const popoverContainer = createElement("ul", { role: "listbox", id: generateDomId("prompt-popover") }); // Avoiding [popover] due to not being able to position at an arbitrary X, Y position.
|
|
5399
|
+
popoverContainer.classList.add("lexxy-prompt-menu");
|
|
5400
|
+
popoverContainer.style.position = "absolute";
|
|
5401
|
+
popoverContainer.setAttribute("nonce", getNonce());
|
|
5402
|
+
popoverContainer.append(...await this.source.buildListItems());
|
|
5403
|
+
popoverContainer.addEventListener("click", this.#handlePopoverClick);
|
|
5404
|
+
this.#editorElement.appendChild(popoverContainer);
|
|
5405
|
+
return popoverContainer
|
|
5406
|
+
}
|
|
5407
|
+
|
|
5408
|
+
#handlePopoverClick = (event) => {
|
|
5409
|
+
const listItem = event.target.closest(".lexxy-prompt-menu__item");
|
|
5410
|
+
if (listItem) {
|
|
5411
|
+
this.#selectOption(listItem);
|
|
5412
|
+
this.#optionWasSelected();
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5416
|
+
#reconnect() {
|
|
5417
|
+
this.disconnectedCallback();
|
|
5418
|
+
this.connectedCallback();
|
|
5092
5419
|
}
|
|
5420
|
+
}
|
|
5093
5421
|
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5422
|
+
class CodeLanguagePicker extends HTMLElement {
|
|
5423
|
+
connectedCallback() {
|
|
5424
|
+
this.editorElement = this.closest("lexxy-editor");
|
|
5425
|
+
this.editor = this.editorElement.editor;
|
|
5098
5426
|
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5427
|
+
this.#attachLanguagePicker();
|
|
5428
|
+
this.#monitorForCodeBlockSelection();
|
|
5429
|
+
}
|
|
5102
5430
|
|
|
5103
|
-
|
|
5431
|
+
#attachLanguagePicker() {
|
|
5432
|
+
this.languagePickerElement = this.#createLanguagePicker();
|
|
5104
5433
|
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
}
|
|
5434
|
+
this.languagePickerElement.addEventListener("change", () => {
|
|
5435
|
+
this.#updateCodeBlockLanguage(this.languagePickerElement.value);
|
|
5108
5436
|
});
|
|
5109
5437
|
|
|
5110
|
-
this
|
|
5111
|
-
this
|
|
5438
|
+
this.languagePickerElement.setAttribute("nonce", getNonce());
|
|
5439
|
+
this.appendChild(this.languagePickerElement);
|
|
5112
5440
|
}
|
|
5113
5441
|
|
|
5114
|
-
#
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
this.#editor.dispatchCommand("insertTableRowBelow");
|
|
5123
|
-
}
|
|
5124
|
-
break
|
|
5125
|
-
case "column":
|
|
5126
|
-
if (direction === "left") {
|
|
5127
|
-
this.#editor.dispatchCommand("insertTableColumnBefore");
|
|
5128
|
-
} else {
|
|
5129
|
-
this.#editor.dispatchCommand("insertTableColumnAfter");
|
|
5130
|
-
}
|
|
5131
|
-
break
|
|
5132
|
-
}
|
|
5133
|
-
break
|
|
5134
|
-
case "delete":
|
|
5135
|
-
switch (childType) {
|
|
5136
|
-
case "row":
|
|
5137
|
-
this.#editor.dispatchCommand("deleteTableRow");
|
|
5138
|
-
break
|
|
5139
|
-
case "column":
|
|
5140
|
-
this.#editor.dispatchCommand("deleteTableColumn");
|
|
5141
|
-
break
|
|
5142
|
-
}
|
|
5143
|
-
break
|
|
5442
|
+
#createLanguagePicker() {
|
|
5443
|
+
const selectElement = createElement("select", { className: "lexxy-code-language-picker", "aria-label": "Pick a language…", name: "lexxy-code-language" });
|
|
5444
|
+
|
|
5445
|
+
for (const [ value, label ] of Object.entries(this.#languages)) {
|
|
5446
|
+
const option = document.createElement("option");
|
|
5447
|
+
option.value = value;
|
|
5448
|
+
option.textContent = label;
|
|
5449
|
+
selectElement.appendChild(option);
|
|
5144
5450
|
}
|
|
5145
|
-
}
|
|
5146
5451
|
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
const rows = this.currentTableNode.getChildren();
|
|
5452
|
+
return selectElement
|
|
5453
|
+
}
|
|
5150
5454
|
|
|
5151
|
-
|
|
5152
|
-
|
|
5455
|
+
get #languages() {
|
|
5456
|
+
const languages = { ...CODE_LANGUAGE_FRIENDLY_NAME_MAP };
|
|
5153
5457
|
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5458
|
+
if (!languages.ruby) languages.ruby = "Ruby";
|
|
5459
|
+
if (!languages.php) languages.php = "PHP";
|
|
5460
|
+
if (!languages.go) languages.go = "Go";
|
|
5461
|
+
if (!languages.bash) languages.bash = "Bash";
|
|
5462
|
+
if (!languages.json) languages.json = "JSON";
|
|
5463
|
+
if (!languages.diff) languages.diff = "Diff";
|
|
5157
5464
|
|
|
5158
|
-
|
|
5159
|
-
|
|
5465
|
+
const sortedEntries = Object.entries(languages)
|
|
5466
|
+
.sort(([ , a ], [ , b ]) => a.localeCompare(b));
|
|
5160
5467
|
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5468
|
+
// Place the "plain" entry first, then the rest of language sorted alphabetically
|
|
5469
|
+
const plainIndex = sortedEntries.findIndex(([ key ]) => key === "plain");
|
|
5470
|
+
const plainEntry = sortedEntries.splice(plainIndex, 1)[0];
|
|
5471
|
+
return Object.fromEntries([ plainEntry, ...sortedEntries ])
|
|
5165
5472
|
}
|
|
5166
5473
|
|
|
5167
|
-
#
|
|
5168
|
-
this
|
|
5169
|
-
const
|
|
5170
|
-
|
|
5171
|
-
const row = rows[this.#currentRow];
|
|
5172
|
-
if (!row) return
|
|
5474
|
+
#updateCodeBlockLanguage(language) {
|
|
5475
|
+
this.editor.update(() => {
|
|
5476
|
+
const codeNode = this.#getCurrentCodeNode();
|
|
5173
5477
|
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5478
|
+
if (codeNode) {
|
|
5479
|
+
codeNode.setLanguage(language);
|
|
5480
|
+
}
|
|
5481
|
+
});
|
|
5482
|
+
}
|
|
5177
5483
|
|
|
5178
|
-
|
|
5179
|
-
|
|
5484
|
+
#monitorForCodeBlockSelection() {
|
|
5485
|
+
this.editor.registerUpdateListener(() => {
|
|
5486
|
+
this.editor.getEditorState().read(() => {
|
|
5487
|
+
const codeNode = this.#getCurrentCodeNode();
|
|
5180
5488
|
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5489
|
+
if (codeNode) {
|
|
5490
|
+
this.#codeNodeWasSelected(codeNode);
|
|
5491
|
+
} else {
|
|
5492
|
+
this.#hideLanguagePicker();
|
|
5493
|
+
}
|
|
5185
5494
|
});
|
|
5186
5495
|
});
|
|
5187
5496
|
}
|
|
5188
5497
|
|
|
5189
|
-
#
|
|
5190
|
-
const
|
|
5498
|
+
#getCurrentCodeNode() {
|
|
5499
|
+
const selection = $getSelection();
|
|
5191
5500
|
|
|
5192
|
-
if (
|
|
5193
|
-
|
|
5501
|
+
if (!$isRangeSelection(selection)) {
|
|
5502
|
+
return null
|
|
5194
5503
|
}
|
|
5195
|
-
}
|
|
5196
|
-
|
|
5197
|
-
#icon(name) {
|
|
5198
|
-
const icons =
|
|
5199
|
-
{
|
|
5200
|
-
"add-row-above":
|
|
5201
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5202
|
-
<path d="M4 7L0 10V4L4 7ZM6.5 7.5H16.5V6.5H6.5V7.5ZM18 8C18 8.55228 17.5523 9 17 9H6C5.44772 9 5 8.55228 5 8V6C5 5.44772 5.44772 5 6 5H17C17.5523 5 18 5.44772 18 6V8Z"/><path d="M2 2C2 1.44772 2.44772 1 3 1H15C15.5523 1 16 1.44772 16 2C16 2.55228 15.5523 3 15 3H3C2.44772 3 2 2.55228 2 2Z"/><path d="M2 12C2 11.4477 2.44772 11 3 11H15C15.5523 11 16 11.4477 16 12C16 12.5523 15.5523 13 15 13H3C2.44772 13 2 12.5523 2 12Z"/><path d="M2 16C2 15.4477 2.44772 15 3 15H15C15.5523 15 16 15.4477 16 16C16 16.5523 15.5523 17 15 17H3C2.44772 17 2 16.5523 2 16Z"/>
|
|
5203
|
-
</svg>`,
|
|
5204
|
-
|
|
5205
|
-
"add-row-below":
|
|
5206
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
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>`,
|
|
5209
|
-
|
|
5210
|
-
"remove-row":
|
|
5211
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5212
|
-
<path d="M17.9951 10.1025C17.9438 10.6067 17.5177 11 17 11H12.4922L13.9922 9.5H16.5V5.5L1.5 5.5L1.5 9.5H4.00586L5.50586 11H1L0.897461 10.9951C0.427034 10.9472 0.0527828 10.573 0.00488281 10.1025L0 10L1.78814e-07 5C2.61831e-07 4.48232 0.393332 4.05621 0.897461 4.00488L1 4L17 4C17.5523 4 18 4.44772 18 5V10L17.9951 10.1025Z"/><path d="M11.2969 15.0146L8.99902 12.7168L6.7002 15.0146L5.63965 13.9541L7.93848 11.6562L5.63965 9.3584L6.7002 8.29785L8.99902 10.5957L11.2969 8.29785L12.3574 9.3584L10.0596 11.6562L12.3574 13.9541L11.2969 15.0146Z"/>
|
|
5213
|
-
</svg>`,
|
|
5214
|
-
|
|
5215
|
-
"toggle-row-style":
|
|
5216
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5217
|
-
<path d="M1 2C1 1.44772 1.44772 1 2 1H7C7.55228 1 8 1.44772 8 2V7C8 7.55228 7.55228 8 7 8H2C1.44772 8 1 7.55228 1 7V2Z"/><path d="M2.5 15.5H6.5V11.5H2.5V15.5ZM8 16C8 16.5177 7.60667 16.9438 7.10254 16.9951L7 17H2L1.89746 16.9951C1.42703 16.9472 1.05278 16.573 1.00488 16.1025L1 16V11C1 10.4477 1.44772 10 2 10H7C7.55228 10 8 10.4477 8 11V16Z"/><path d="M10 2C10 1.44772 10.4477 1 11 1H16C16.5523 1 17 1.44772 17 2V7C17 7.55228 16.5523 8 16 8H11C10.4477 8 10 7.55228 10 7V2Z"/><path d="M11.5 15.5H15.5V11.5H11.5V15.5ZM17 16C17 16.5177 16.6067 16.9438 16.1025 16.9951L16 17H11L10.8975 16.9951C10.427 16.9472 10.0528 16.573 10.0049 16.1025L10 16V11C10 10.4477 10.4477 10 11 10H16C16.5523 10 17 10.4477 17 11V16Z"/>
|
|
5218
|
-
</svg>`,
|
|
5219
|
-
|
|
5220
|
-
"add-column-before":
|
|
5221
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
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>`,
|
|
5224
5504
|
|
|
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>`,
|
|
5229
|
-
|
|
5230
|
-
"remove-column":
|
|
5231
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5232
|
-
<path d="M10.1025 0.00488281C10.6067 0.0562145 11 0.482323 11 1V5.50781L9.5 4.00781V1.5H5.5V16.5H9.5V13.9941L11 12.4941V17L10.9951 17.1025C10.9472 17.573 10.573 17.9472 10.1025 17.9951L10 18H5C4.48232 18 4.05621 17.6067 4.00488 17.1025L4 17V1C4 0.447715 4.44772 1.61064e-08 5 0H10L10.1025 0.00488281Z"/><path d="M12.7169 8.99999L15.015 11.2981L13.9543 12.3588L11.6562 10.0607L9.35815 12.3588L8.29749 11.2981L10.5956 8.99999L8.29749 6.7019L9.35815 5.64124L11.6562 7.93933L13.9543 5.64124L15.015 6.7019L12.7169 8.99999Z"/>
|
|
5233
|
-
</svg>`,
|
|
5234
|
-
|
|
5235
|
-
"toggle-column-style":
|
|
5236
|
-
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5237
|
-
<path d="M1 2C1 1.44772 1.44772 1 2 1H7C7.55228 1 8 1.44772 8 2V7C8 7.55228 7.55228 8 7 8H2C1.44772 8 1 7.55228 1 7V2Z"/><path d="M1 11C1 10.4477 1.44772 10 2 10H7C7.55228 10 8 10.4477 8 11V16C8 16.5523 7.55228 17 7 17H2C1.44772 17 1 16.5523 1 16V11Z"/><path d="M11.5 6.5H15.5V2.5H11.5V6.5ZM17 7C17 7.51768 16.6067 7.94379 16.1025 7.99512L16 8H11L10.8975 7.99512C10.427 7.94722 10.0528 7.57297 10.0049 7.10254L10 7V2C10 1.44772 10.4477 1 11 1H16C16.5523 1 17 1.44772 17 2V7Z"/><path d="M11.5 15.5H15.5V11.5H11.5V15.5ZM17 16C17 16.5177 16.6067 16.9438 16.1025 16.9951L16 17H11L10.8975 16.9951C10.427 16.9472 10.0528 16.573 10.0049 16.1025L10 16V11C10 10.4477 10.4477 10 11 10H16C16.5523 10 17 10.4477 17 11V16Z"/>
|
|
5238
|
-
</svg>`,
|
|
5239
|
-
|
|
5240
|
-
"delete-table":
|
|
5241
|
-
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
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
|
-
};
|
|
5245
|
-
|
|
5246
|
-
return icons[name]
|
|
5247
|
-
}
|
|
5248
|
-
}
|
|
5249
|
-
|
|
5250
|
-
customElements.define("lexxy-table-handler", TableHandler);
|
|
5505
|
+
const anchorNode = selection.anchor.getNode();
|
|
5506
|
+
const parentNode = anchorNode.getParent();
|
|
5251
5507
|
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5508
|
+
if ($isCodeNode(anchorNode)) {
|
|
5509
|
+
return anchorNode
|
|
5510
|
+
} else if ($isCodeNode(parentNode)) {
|
|
5511
|
+
return parentNode
|
|
5512
|
+
}
|
|
5257
5513
|
|
|
5258
|
-
// Template method to override
|
|
5259
|
-
promptItemFor(listItem) {
|
|
5260
5514
|
return null
|
|
5261
5515
|
}
|
|
5262
5516
|
|
|
5263
|
-
|
|
5517
|
+
#codeNodeWasSelected(codeNode) {
|
|
5518
|
+
const language = codeNode.getLanguage();
|
|
5264
5519
|
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
const listItemElement = createElement("li", { role: "option", id: generateDomId("prompt-item"), tabindex: "0" });
|
|
5269
|
-
listItemElement.classList.add("lexxy-prompt-menu__item");
|
|
5270
|
-
listItemElement.appendChild(fragment);
|
|
5271
|
-
return listItemElement
|
|
5520
|
+
this.#updateLanguagePickerWith(language);
|
|
5521
|
+
this.#showLanguagePicker();
|
|
5522
|
+
this.#positionLanguagePicker(codeNode);
|
|
5272
5523
|
}
|
|
5273
5524
|
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
const
|
|
5277
|
-
|
|
5278
|
-
const promptItems = parseHtml(html).querySelectorAll("lexxy-prompt-item");
|
|
5279
|
-
return Promise.resolve(Array.from(promptItems))
|
|
5280
|
-
} catch (error) {
|
|
5281
|
-
return Promise.reject(error)
|
|
5525
|
+
#updateLanguagePickerWith(language) {
|
|
5526
|
+
if (this.languagePickerElement && language) {
|
|
5527
|
+
const normalizedLanguage = normalizeCodeLang(language);
|
|
5528
|
+
this.languagePickerElement.value = normalizedLanguage;
|
|
5282
5529
|
}
|
|
5283
5530
|
}
|
|
5284
|
-
}
|
|
5285
5531
|
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
return this.#buildListItemsFromPromptItems(promptItems, filter)
|
|
5290
|
-
}
|
|
5532
|
+
#positionLanguagePicker(codeNode) {
|
|
5533
|
+
const codeElement = this.editor.getElementByKey(codeNode.getKey());
|
|
5534
|
+
if (!codeElement) return
|
|
5291
5535
|
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5536
|
+
const codeRect = codeElement.getBoundingClientRect();
|
|
5537
|
+
const editorRect = this.editorElement.getBoundingClientRect();
|
|
5538
|
+
const relativeTop = codeRect.top - editorRect.top;
|
|
5539
|
+
const relativeRight = editorRect.right - codeRect.right;
|
|
5296
5540
|
|
|
5297
|
-
|
|
5298
|
-
|
|
5541
|
+
this.style.top = `${relativeTop}px`;
|
|
5542
|
+
this.style.right = `${relativeRight}px`;
|
|
5299
5543
|
}
|
|
5300
5544
|
|
|
5301
|
-
#
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
promptItems.forEach((promptItem) => {
|
|
5305
|
-
const searchableText = promptItem.getAttribute("search");
|
|
5306
|
-
|
|
5307
|
-
if (!filter || filterMatches(searchableText, filter)) {
|
|
5308
|
-
const listItem = this.buildListItemElementFor(promptItem);
|
|
5309
|
-
this.promptItemByListItem.set(listItem, promptItem);
|
|
5310
|
-
listItems.push(listItem);
|
|
5311
|
-
}
|
|
5312
|
-
});
|
|
5545
|
+
#showLanguagePicker() {
|
|
5546
|
+
this.hidden = false;
|
|
5547
|
+
}
|
|
5313
5548
|
|
|
5314
|
-
|
|
5549
|
+
#hideLanguagePicker() {
|
|
5550
|
+
this.hidden = true;
|
|
5315
5551
|
}
|
|
5316
5552
|
}
|
|
5317
5553
|
|
|
5318
|
-
class
|
|
5319
|
-
constructor(
|
|
5320
|
-
|
|
5321
|
-
this.
|
|
5322
|
-
|
|
5554
|
+
class TableController {
|
|
5555
|
+
constructor(editorElement) {
|
|
5556
|
+
this.editor = editorElement.editor;
|
|
5557
|
+
this.contents = editorElement.contents;
|
|
5558
|
+
this.selection = editorElement.selection;
|
|
5323
5559
|
|
|
5324
|
-
|
|
5325
|
-
|
|
5560
|
+
this.currentTableNodeKey = null;
|
|
5561
|
+
this.currentCellKey = null;
|
|
5562
|
+
|
|
5563
|
+
this.#registerKeyHandlers();
|
|
5326
5564
|
}
|
|
5327
|
-
}
|
|
5328
5565
|
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
this.url = url;
|
|
5566
|
+
destroy() {
|
|
5567
|
+
this.currentTableNodeKey = null;
|
|
5568
|
+
this.currentCellKey = null;
|
|
5333
5569
|
|
|
5334
|
-
this
|
|
5570
|
+
this.#unregisterKeyHandlers();
|
|
5335
5571
|
}
|
|
5336
5572
|
|
|
5337
|
-
|
|
5338
|
-
|
|
5573
|
+
get currentCell() {
|
|
5574
|
+
if (!this.currentCellKey) return null
|
|
5339
5575
|
|
|
5340
|
-
return
|
|
5576
|
+
return this.editor.getEditorState().read(() => {
|
|
5577
|
+
const cell = $getNodeByKey(this.currentCellKey);
|
|
5578
|
+
return (cell instanceof TableCellNode) ? cell : null
|
|
5579
|
+
})
|
|
5341
5580
|
}
|
|
5342
|
-
}
|
|
5343
|
-
|
|
5344
|
-
const DEBOUNCE_INTERVAL = 200;
|
|
5345
5581
|
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
super();
|
|
5582
|
+
get currentTableNode() {
|
|
5583
|
+
if (!this.currentTableNodeKey) return null
|
|
5349
5584
|
|
|
5350
|
-
this.
|
|
5351
|
-
|
|
5585
|
+
return this.editor.getEditorState().read(() => {
|
|
5586
|
+
const tableNode = $getNodeByKey(this.currentTableNodeKey);
|
|
5587
|
+
return (tableNode instanceof TableNode) ? tableNode : null
|
|
5588
|
+
})
|
|
5352
5589
|
}
|
|
5353
5590
|
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
}
|
|
5591
|
+
get currentRowCells() {
|
|
5592
|
+
const currentRowIndex = this.currentRowIndex;
|
|
5357
5593
|
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
}
|
|
5594
|
+
const rows = this.tableRows;
|
|
5595
|
+
if (!rows) return null
|
|
5361
5596
|
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5597
|
+
return this.editor.getEditorState().read(() => {
|
|
5598
|
+
return rows[currentRowIndex]?.getChildren() ?? null
|
|
5599
|
+
}) ?? null
|
|
5365
5600
|
}
|
|
5366
5601
|
|
|
5367
|
-
|
|
5368
|
-
const
|
|
5369
|
-
|
|
5370
|
-
|
|
5602
|
+
get currentRowIndex() {
|
|
5603
|
+
const currentCell = this.currentCell;
|
|
5604
|
+
if (!currentCell) return 0
|
|
5605
|
+
|
|
5606
|
+
return this.editor.getEditorState().read(() => {
|
|
5607
|
+
return $getTableRowIndexFromTableCellNode(currentCell)
|
|
5608
|
+
}) ?? 0
|
|
5371
5609
|
}
|
|
5372
5610
|
|
|
5373
|
-
|
|
5374
|
-
const
|
|
5375
|
-
this.promptItemByListItem = new WeakMap();
|
|
5611
|
+
get currentColumnCells() {
|
|
5612
|
+
const columnIndex = this.currentColumnIndex;
|
|
5376
5613
|
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
this.promptItemByListItem.set(listItem, promptItem);
|
|
5380
|
-
listItems.push(listItem);
|
|
5381
|
-
}
|
|
5614
|
+
const rows = this.tableRows;
|
|
5615
|
+
if (!rows) return null
|
|
5382
5616
|
|
|
5383
|
-
return
|
|
5617
|
+
return this.editor.getEditorState().read(() => {
|
|
5618
|
+
return rows.map(row => row.getChildAtIndex(columnIndex))
|
|
5619
|
+
}) ?? null
|
|
5384
5620
|
}
|
|
5385
|
-
}
|
|
5386
5621
|
|
|
5387
|
-
|
|
5622
|
+
get currentColumnIndex() {
|
|
5623
|
+
const currentCell = this.currentCell;
|
|
5624
|
+
if (!currentCell) return 0
|
|
5388
5625
|
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
this.keyListeners = [];
|
|
5626
|
+
return this.editor.getEditorState().read(() => {
|
|
5627
|
+
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
5628
|
+
}) ?? 0
|
|
5393
5629
|
}
|
|
5394
5630
|
|
|
5395
|
-
|
|
5631
|
+
get tableRows() {
|
|
5632
|
+
return this.editor.getEditorState().read(() => {
|
|
5633
|
+
return this.currentTableNode?.getChildren()
|
|
5634
|
+
}) ?? null
|
|
5635
|
+
}
|
|
5396
5636
|
|
|
5397
|
-
|
|
5398
|
-
|
|
5637
|
+
updateSelectedTable() {
|
|
5638
|
+
let cellNode = null;
|
|
5639
|
+
let tableNode = null;
|
|
5399
5640
|
|
|
5400
|
-
this
|
|
5401
|
-
|
|
5402
|
-
|
|
5641
|
+
this.editor.getEditorState().read(() => {
|
|
5642
|
+
const selection = $getSelection();
|
|
5643
|
+
if (!selection || !this.selection.isTableCellSelected) return
|
|
5403
5644
|
|
|
5404
|
-
|
|
5405
|
-
this.source = null;
|
|
5406
|
-
this.popoverElement = null;
|
|
5407
|
-
}
|
|
5645
|
+
const node = selection.getNodes()[0];
|
|
5408
5646
|
|
|
5647
|
+
cellNode = $findCellNode(node);
|
|
5648
|
+
tableNode = $findTableNode(node);
|
|
5649
|
+
});
|
|
5409
5650
|
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
requestAnimationFrame(() => this.#reconnect());
|
|
5413
|
-
}
|
|
5651
|
+
this.currentCellKey = cellNode?.getKey() ?? null;
|
|
5652
|
+
this.currentTableNodeKey = tableNode?.getKey() ?? null;
|
|
5414
5653
|
}
|
|
5415
5654
|
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5655
|
+
executeTableCommand(command, customIndex = null) {
|
|
5656
|
+
if (command.action === "delete" && command.childType === "table") {
|
|
5657
|
+
this.#deleteTable();
|
|
5658
|
+
return
|
|
5659
|
+
}
|
|
5419
5660
|
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5661
|
+
if (command.action === "toggle") {
|
|
5662
|
+
this.#executeToggleStyle(command);
|
|
5663
|
+
return
|
|
5664
|
+
}
|
|
5423
5665
|
|
|
5424
|
-
|
|
5425
|
-
return this.hasAttribute("supports-space-in-searches")
|
|
5666
|
+
this.#executeCommand(command, customIndex);
|
|
5426
5667
|
}
|
|
5427
5668
|
|
|
5428
|
-
|
|
5429
|
-
|
|
5669
|
+
#executeCommand(command, customIndex = null) {
|
|
5670
|
+
this.#selectCellAtSelection();
|
|
5671
|
+
this.editor.dispatchCommand(this.#commandName(command));
|
|
5672
|
+
this.#selectNextBestCell(command, customIndex);
|
|
5430
5673
|
}
|
|
5431
5674
|
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
}
|
|
5675
|
+
#executeToggleStyle(command) {
|
|
5676
|
+
const childType = command.childType;
|
|
5435
5677
|
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
}
|
|
5678
|
+
let cells = null;
|
|
5679
|
+
let headerState = null;
|
|
5439
5680
|
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
return new DeferredPromptSource(src)
|
|
5447
|
-
}
|
|
5448
|
-
} else {
|
|
5449
|
-
return new InlinePromptSource(this.querySelectorAll("lexxy-prompt-item"))
|
|
5681
|
+
if (childType === "row") {
|
|
5682
|
+
cells = this.currentRowCells;
|
|
5683
|
+
headerState = TableCellHeaderStates.ROW;
|
|
5684
|
+
} else if (childType === "column") {
|
|
5685
|
+
cells = this.currentColumnCells;
|
|
5686
|
+
headerState = TableCellHeaderStates.COLUMN;
|
|
5450
5687
|
}
|
|
5451
|
-
}
|
|
5452
|
-
|
|
5453
|
-
#addTriggerListener() {
|
|
5454
|
-
const unregister = this.#editor.registerUpdateListener(() => {
|
|
5455
|
-
this.#editor.read(() => {
|
|
5456
|
-
const { node, offset } = this.#selection.selectedNodeWithOffset();
|
|
5457
|
-
if (!node) return
|
|
5458
5688
|
|
|
5459
|
-
|
|
5460
|
-
const fullText = node.getTextContent();
|
|
5461
|
-
const charBeforeCursor = fullText[offset - 1];
|
|
5689
|
+
if (!cells || cells.length === 0) return
|
|
5462
5690
|
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5691
|
+
this.editor.update(() => {
|
|
5692
|
+
const firstCell = $getTableCellNodeFromLexicalNode(cells[0]);
|
|
5693
|
+
if (!firstCell) return
|
|
5466
5694
|
|
|
5467
|
-
|
|
5468
|
-
|
|
5695
|
+
const currentStyle = firstCell.getHeaderStyles();
|
|
5696
|
+
const newStyle = currentStyle ^ headerState;
|
|
5469
5697
|
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
this.#showPopover();
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
}
|
|
5698
|
+
cells.forEach(cell => {
|
|
5699
|
+
this.#setHeaderStyle(cell, newStyle, headerState);
|
|
5476
5700
|
});
|
|
5477
5701
|
});
|
|
5478
5702
|
}
|
|
5479
5703
|
|
|
5480
|
-
#
|
|
5481
|
-
this
|
|
5482
|
-
|
|
5704
|
+
#deleteTable() {
|
|
5705
|
+
this.#selectCellAtSelection();
|
|
5706
|
+
this.editor.dispatchCommand("deleteTable");
|
|
5707
|
+
}
|
|
5483
5708
|
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5709
|
+
#selectCellAtSelection() {
|
|
5710
|
+
this.editor.update(() => {
|
|
5711
|
+
const selection = $getSelection();
|
|
5712
|
+
if (!selection) return
|
|
5487
5713
|
|
|
5488
|
-
|
|
5489
|
-
const fullText = node.getTextContent();
|
|
5490
|
-
const textBeforeCursor = fullText.slice(0, offset);
|
|
5491
|
-
const lastTriggerIndex = textBeforeCursor.lastIndexOf(this.trigger);
|
|
5714
|
+
const node = selection.getNodes()[0];
|
|
5492
5715
|
|
|
5493
|
-
|
|
5494
|
-
if (lastTriggerIndex === -1 || offset <= lastTriggerIndex) {
|
|
5495
|
-
this.#hidePopover();
|
|
5496
|
-
}
|
|
5497
|
-
} else {
|
|
5498
|
-
// Cursor is not in a text node or at offset 0, hide popover
|
|
5499
|
-
this.#hidePopover();
|
|
5500
|
-
}
|
|
5501
|
-
});
|
|
5716
|
+
$findCellNode(node)?.selectEnd();
|
|
5502
5717
|
});
|
|
5503
5718
|
}
|
|
5504
5719
|
|
|
5505
|
-
#
|
|
5506
|
-
|
|
5507
|
-
this.cursorPositionListener();
|
|
5508
|
-
this.cursorPositionListener = null;
|
|
5509
|
-
}
|
|
5510
|
-
}
|
|
5720
|
+
#commandName(command) {
|
|
5721
|
+
const { action, childType, direction } = command;
|
|
5511
5722
|
|
|
5512
|
-
|
|
5513
|
-
|
|
5723
|
+
const childTypeSuffix = upcaseFirst(childType);
|
|
5724
|
+
const directionSuffix = action == "insert" ? upcaseFirst(direction) : "";
|
|
5725
|
+
return `${action}Table${childTypeSuffix}${directionSuffix}`
|
|
5514
5726
|
}
|
|
5515
5727
|
|
|
5516
|
-
|
|
5517
|
-
|
|
5728
|
+
#setHeaderStyle(cell, newStyle, headerState) {
|
|
5729
|
+
const tableCellNode = $getTableCellNodeFromLexicalNode(cell);
|
|
5730
|
+
tableCellNode?.setHeaderStyles(newStyle, headerState);
|
|
5518
5731
|
}
|
|
5519
5732
|
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5733
|
+
async #selectCellAtIndex(rowIndex, columnIndex) {
|
|
5734
|
+
// We wait for next frame, otherwise table operations might not have completed yet.
|
|
5735
|
+
await nextFrame();
|
|
5523
5736
|
|
|
5524
|
-
|
|
5525
|
-
this.popoverElement ??= await this.#buildPopover();
|
|
5526
|
-
this.#resetPopoverPosition();
|
|
5527
|
-
await this.#filterOptions();
|
|
5528
|
-
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
|
|
5529
|
-
this.#selectFirstOption();
|
|
5737
|
+
if (!this.currentTableNode) return
|
|
5530
5738
|
|
|
5531
|
-
this
|
|
5532
|
-
|
|
5739
|
+
const rows = this.tableRows;
|
|
5740
|
+
if (!rows) return
|
|
5533
5741
|
|
|
5534
|
-
|
|
5535
|
-
|
|
5742
|
+
const row = rows[rowIndex];
|
|
5743
|
+
if (!row) return
|
|
5744
|
+
|
|
5745
|
+
this.editor.update(() => {
|
|
5746
|
+
const cell = $getTableCellNodeFromLexicalNode(row.getChildAtIndex(columnIndex));
|
|
5747
|
+
cell?.selectEnd();
|
|
5748
|
+
});
|
|
5536
5749
|
}
|
|
5537
5750
|
|
|
5538
|
-
#
|
|
5539
|
-
|
|
5540
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_ENTER_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5541
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_TAB_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5751
|
+
#selectNextBestCell(command, customIndex = null) {
|
|
5752
|
+
const { childType, direction } = command;
|
|
5542
5753
|
|
|
5543
|
-
|
|
5544
|
-
|
|
5754
|
+
let rowIndex = this.currentRowIndex;
|
|
5755
|
+
let columnIndex = customIndex !== null ? customIndex : this.currentColumnIndex;
|
|
5756
|
+
|
|
5757
|
+
const deleteOffset = command.action === "delete" ? -1 : 0;
|
|
5758
|
+
const offset = direction === "after" ? 1 : deleteOffset;
|
|
5759
|
+
|
|
5760
|
+
if (childType === "row") {
|
|
5761
|
+
rowIndex += offset;
|
|
5762
|
+
} else if (childType === "column") {
|
|
5763
|
+
columnIndex += offset;
|
|
5545
5764
|
}
|
|
5546
5765
|
|
|
5547
|
-
|
|
5548
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_UP_COMMAND, this.#handleArrowUp.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5549
|
-
this.keyListeners.push(this.#editor.registerCommand(KEY_ARROW_DOWN_COMMAND, this.#handleArrowDown.bind(this), COMMAND_PRIORITY_HIGH));
|
|
5766
|
+
this.#selectCellAtIndex(rowIndex, columnIndex);
|
|
5550
5767
|
}
|
|
5551
5768
|
|
|
5552
|
-
#
|
|
5553
|
-
this
|
|
5554
|
-
|
|
5555
|
-
return true
|
|
5556
|
-
}
|
|
5769
|
+
#selectNextRow() {
|
|
5770
|
+
const rows = this.tableRows;
|
|
5771
|
+
if (!rows) return
|
|
5557
5772
|
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5773
|
+
const nextRow = rows.at(this.currentRowIndex + 1);
|
|
5774
|
+
if (!nextRow) return
|
|
5775
|
+
|
|
5776
|
+
this.editor.update(() => {
|
|
5777
|
+
nextRow.getChildAtIndex(this.currentColumnIndex)?.selectEnd();
|
|
5778
|
+
});
|
|
5562
5779
|
}
|
|
5563
5780
|
|
|
5564
|
-
#
|
|
5565
|
-
const
|
|
5781
|
+
#selectPreviousCell() {
|
|
5782
|
+
const cell = this.currentCell;
|
|
5783
|
+
if (!cell) return
|
|
5566
5784
|
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
}
|
|
5785
|
+
this.editor.update(() => {
|
|
5786
|
+
cell.selectPrevious();
|
|
5787
|
+
});
|
|
5570
5788
|
}
|
|
5571
5789
|
|
|
5572
|
-
|
|
5573
|
-
|
|
5790
|
+
#insertRowAndSelectFirstCell() {
|
|
5791
|
+
this.executeTableCommand({ action: "insert", childType: "row", direction: "after" }, 0);
|
|
5574
5792
|
}
|
|
5575
5793
|
|
|
5576
|
-
#
|
|
5577
|
-
this
|
|
5578
|
-
|
|
5579
|
-
listItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
5580
|
-
listItem.focus();
|
|
5794
|
+
#deleteRowAndSelectLastCell() {
|
|
5795
|
+
this.executeTableCommand({ action: "delete", childType: "row" }, -1);
|
|
5796
|
+
}
|
|
5581
5797
|
|
|
5582
|
-
|
|
5583
|
-
this
|
|
5584
|
-
|
|
5585
|
-
});
|
|
5798
|
+
#deleteRowAndSelectNextNode() {
|
|
5799
|
+
const tableNode = this.currentTableNode;
|
|
5800
|
+
this.executeTableCommand({ action: "delete", childType: "row" });
|
|
5586
5801
|
|
|
5587
|
-
this
|
|
5588
|
-
|
|
5589
|
-
|
|
5802
|
+
this.editor.update(() => {
|
|
5803
|
+
const next = tableNode?.getNextSibling();
|
|
5804
|
+
if ($isParagraphNode(next)) {
|
|
5805
|
+
next.selectStart();
|
|
5806
|
+
} else {
|
|
5807
|
+
const newParagraph = $createParagraphNode();
|
|
5808
|
+
this.currentTableNode.insertAfter(newParagraph);
|
|
5809
|
+
newParagraph.selectStart();
|
|
5810
|
+
}
|
|
5811
|
+
});
|
|
5590
5812
|
}
|
|
5591
5813
|
|
|
5592
|
-
#
|
|
5593
|
-
this
|
|
5594
|
-
this.#editorContentElement.removeAttribute("aria-controls");
|
|
5595
|
-
this.#editorContentElement.removeAttribute("aria-activedescendant");
|
|
5596
|
-
this.#editorContentElement.removeAttribute("aria-haspopup");
|
|
5597
|
-
}
|
|
5814
|
+
#isCurrentCellEmpty() {
|
|
5815
|
+
if (!this.currentTableNode) return false
|
|
5598
5816
|
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5602
|
-
const contentRect = this.#editorContentElement.getBoundingClientRect();
|
|
5603
|
-
const verticalOffset = contentRect.top - editorRect.top;
|
|
5817
|
+
const cell = this.currentCell;
|
|
5818
|
+
if (!cell) return false
|
|
5604
5819
|
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
this.popoverElement.toggleAttribute("data-anchored", true);
|
|
5608
|
-
}
|
|
5820
|
+
return cell.getTextContent().trim() === ""
|
|
5821
|
+
}
|
|
5609
5822
|
|
|
5610
|
-
|
|
5611
|
-
this.
|
|
5823
|
+
#isCurrentRowLast() {
|
|
5824
|
+
if (!this.currentTableNode) return false
|
|
5612
5825
|
|
|
5613
|
-
const
|
|
5614
|
-
|
|
5826
|
+
const rows = this.tableRows;
|
|
5827
|
+
if (!rows) return false
|
|
5615
5828
|
|
|
5616
|
-
|
|
5617
|
-
this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
|
|
5618
|
-
this.popoverElement.style.bottom = "auto";
|
|
5619
|
-
this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
|
|
5620
|
-
}
|
|
5829
|
+
return rows.length === this.currentRowIndex + 1
|
|
5621
5830
|
}
|
|
5622
5831
|
|
|
5623
|
-
#
|
|
5624
|
-
this.
|
|
5625
|
-
|
|
5832
|
+
#isCurrentRowEmpty() {
|
|
5833
|
+
if (!this.currentTableNode) return false
|
|
5834
|
+
|
|
5835
|
+
const cells = this.currentRowCells;
|
|
5836
|
+
if (!cells) return false
|
|
5837
|
+
|
|
5838
|
+
return cells.every(cell => cell.getTextContent().trim() === "")
|
|
5626
5839
|
}
|
|
5627
5840
|
|
|
5628
|
-
|
|
5629
|
-
this
|
|
5630
|
-
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
|
|
5631
|
-
this.#editorElement.removeEventListener("lexxy:change", this.#filterOptions);
|
|
5632
|
-
this.#editorElement.removeEventListener("keydown", this.#handleKeydownOnPopover);
|
|
5841
|
+
#isFirstCellInRow() {
|
|
5842
|
+
if (!this.currentTableNode) return false
|
|
5633
5843
|
|
|
5634
|
-
this
|
|
5635
|
-
|
|
5844
|
+
const cells = this.currentRowCells;
|
|
5845
|
+
if (!cells) return false
|
|
5636
5846
|
|
|
5637
|
-
|
|
5638
|
-
this.#addTriggerListener();
|
|
5847
|
+
return cells.indexOf(this.currentCell) === 0
|
|
5639
5848
|
}
|
|
5640
5849
|
|
|
5641
|
-
#
|
|
5642
|
-
|
|
5643
|
-
this.
|
|
5850
|
+
#registerKeyHandlers() {
|
|
5851
|
+
// We can't prevent these externally using regular keydown because Lexical handles it first.
|
|
5852
|
+
this.unregisterBackspaceKeyHandler = this.editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => this.#handleBackspaceKey(event), COMMAND_PRIORITY_HIGH);
|
|
5853
|
+
this.unregisterEnterKeyHandler = this.editor.registerCommand(KEY_ENTER_COMMAND, (event) => this.#handleEnterKey(event), COMMAND_PRIORITY_HIGH);
|
|
5644
5854
|
}
|
|
5645
5855
|
|
|
5646
|
-
#
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5856
|
+
#unregisterKeyHandlers() {
|
|
5857
|
+
this.unregisterBackspaceKeyHandler?.();
|
|
5858
|
+
this.unregisterEnterKeyHandler?.();
|
|
5859
|
+
|
|
5860
|
+
this.unregisterBackspaceKeyHandler = null;
|
|
5861
|
+
this.unregisterEnterKeyHandler = null;
|
|
5862
|
+
}
|
|
5863
|
+
|
|
5864
|
+
#handleBackspaceKey(event) {
|
|
5865
|
+
if (!this.currentTableNode) return false
|
|
5866
|
+
|
|
5867
|
+
if (this.#isCurrentRowEmpty() && this.#isFirstCellInRow()) {
|
|
5868
|
+
event.preventDefault();
|
|
5869
|
+
this.#deleteRowAndSelectLastCell();
|
|
5870
|
+
return true
|
|
5650
5871
|
}
|
|
5651
5872
|
|
|
5652
|
-
if (this.#
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
} else {
|
|
5657
|
-
this.#hidePopover();
|
|
5873
|
+
if (this.#isCurrentCellEmpty() && !this.#isFirstCellInRow()) {
|
|
5874
|
+
event.preventDefault();
|
|
5875
|
+
this.#selectPreviousCell();
|
|
5876
|
+
return true
|
|
5658
5877
|
}
|
|
5878
|
+
|
|
5879
|
+
return false
|
|
5659
5880
|
}
|
|
5660
5881
|
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
const filteredListItems = await this.source.buildListItems(filter);
|
|
5664
|
-
this.popoverElement.innerHTML = "";
|
|
5882
|
+
#handleEnterKey(event) {
|
|
5883
|
+
if ((event.ctrlKey || event.metaKey) || event.shiftKey || !this.currentTableNode) return false
|
|
5665
5884
|
|
|
5666
|
-
if (
|
|
5667
|
-
|
|
5885
|
+
if (this.selection.isInsideList || this.selection.isInsideCodeBlock) return false
|
|
5886
|
+
|
|
5887
|
+
event.preventDefault();
|
|
5888
|
+
|
|
5889
|
+
if (this.#isCurrentRowLast() && this.#isCurrentRowEmpty()) {
|
|
5890
|
+
this.#deleteRowAndSelectNextNode();
|
|
5891
|
+
} else if (this.#isCurrentRowLast()) {
|
|
5892
|
+
this.#insertRowAndSelectFirstCell();
|
|
5668
5893
|
} else {
|
|
5669
|
-
this.#
|
|
5894
|
+
this.#selectNextRow();
|
|
5670
5895
|
}
|
|
5671
|
-
this.#selectFirstOption();
|
|
5672
|
-
}
|
|
5673
5896
|
|
|
5674
|
-
|
|
5675
|
-
this.popoverElement.classList.remove("lexxy-prompt-menu--empty");
|
|
5676
|
-
this.popoverElement.append(...filteredListItems);
|
|
5897
|
+
return true
|
|
5677
5898
|
}
|
|
5899
|
+
}
|
|
5678
5900
|
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5901
|
+
var TableIcons = {
|
|
5902
|
+
"insert-row-before":
|
|
5903
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5904
|
+
<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"/>
|
|
5905
|
+
<path d="M4.5 4.5H7.5V7.5H9V4.5H12L12 3L9 3V6.55671e-08L7.5 0V3L4.5 3V4.5Z"/>
|
|
5906
|
+
</svg>`,
|
|
5907
|
+
|
|
5908
|
+
"insert-row-after":
|
|
5909
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5910
|
+
<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"/>
|
|
5911
|
+
<path d="M7.5 15V18H9V15H12V13.5H9V10.5H7.5V13.5H4.5V15H7.5Z"/>
|
|
5912
|
+
</svg>`,
|
|
5913
|
+
|
|
5914
|
+
"delete-row":
|
|
5915
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5916
|
+
<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"/>
|
|
5917
|
+
<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"/>
|
|
5918
|
+
</svg>`,
|
|
5919
|
+
|
|
5920
|
+
"toggle-row":
|
|
5921
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5922
|
+
<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"/>
|
|
5923
|
+
</svg>`,
|
|
5924
|
+
|
|
5925
|
+
"insert-column-before":
|
|
5926
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5927
|
+
<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"/>
|
|
5928
|
+
<path d="M3 7.5H0V9H3V12H4.5V9H7.5V7.5H4.5V4.5H3V7.5Z"/>
|
|
5929
|
+
</svg>`,
|
|
5930
|
+
|
|
5931
|
+
"insert-column-after":
|
|
5932
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5933
|
+
<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"/>
|
|
5934
|
+
<path d="M15 7.5H18V9H15V12H13.5V9H10.5V7.5H13.5V4.5H15V7.5Z"/>
|
|
5935
|
+
</svg>`,
|
|
5936
|
+
|
|
5937
|
+
"delete-column":
|
|
5938
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5939
|
+
<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"/>
|
|
5940
|
+
<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"/>
|
|
5941
|
+
</svg>`,
|
|
5942
|
+
|
|
5943
|
+
"toggle-column":
|
|
5944
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
5945
|
+
<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" />
|
|
5946
|
+
</svg>`,
|
|
5947
|
+
|
|
5948
|
+
"delete-table":
|
|
5949
|
+
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
5950
|
+
<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"/>
|
|
5951
|
+
</svg>`
|
|
5952
|
+
};
|
|
5685
5953
|
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5954
|
+
class TableTools extends HTMLElement {
|
|
5955
|
+
connectedCallback() {
|
|
5956
|
+
this.tableController = new TableController(this.#editorElement);
|
|
5689
5957
|
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
this.#editorElement.focus();
|
|
5694
|
-
event.stopPropagation();
|
|
5695
|
-
}
|
|
5696
|
-
// Arrow keys are now handled via Lexical commands with HIGH priority
|
|
5958
|
+
this.#setUpButtons();
|
|
5959
|
+
this.#monitorForTableSelection();
|
|
5960
|
+
this.#registerKeyboardShortcuts();
|
|
5697
5961
|
}
|
|
5698
5962
|
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
if (nextIndex < this.#listItemElements.length) this.#selectOption(this.#listItemElements[nextIndex]);
|
|
5702
|
-
}
|
|
5963
|
+
disconnectedCallback() {
|
|
5964
|
+
this.#unregisterKeyboardShortcuts();
|
|
5703
5965
|
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5966
|
+
this.unregisterUpdateListener?.();
|
|
5967
|
+
this.unregisterUpdateListener = null;
|
|
5968
|
+
|
|
5969
|
+
this.removeEventListener("keydown", this.#handleToolsKeydown);
|
|
5970
|
+
|
|
5971
|
+
this.tableController?.destroy();
|
|
5972
|
+
this.tableController = null;
|
|
5707
5973
|
}
|
|
5708
5974
|
|
|
5709
|
-
get #
|
|
5710
|
-
return this.#
|
|
5975
|
+
get #editor() {
|
|
5976
|
+
return this.#editorElement.editor
|
|
5711
5977
|
}
|
|
5712
5978
|
|
|
5713
|
-
get #
|
|
5714
|
-
return this
|
|
5979
|
+
get #editorElement() {
|
|
5980
|
+
return this.closest("lexxy-editor")
|
|
5715
5981
|
}
|
|
5716
5982
|
|
|
5717
|
-
#
|
|
5718
|
-
|
|
5719
|
-
event.stopPropagation();
|
|
5720
|
-
this.#optionWasSelected();
|
|
5721
|
-
return true
|
|
5983
|
+
get #tableToolsButtons() {
|
|
5984
|
+
return Array.from(this.querySelectorAll("button, details > summary"))
|
|
5722
5985
|
}
|
|
5723
5986
|
|
|
5724
|
-
#
|
|
5725
|
-
this.#
|
|
5726
|
-
this.#
|
|
5727
|
-
|
|
5987
|
+
#setUpButtons() {
|
|
5988
|
+
this.appendChild(this.#createRowButtonsContainer());
|
|
5989
|
+
this.appendChild(this.#createColumnButtonsContainer());
|
|
5990
|
+
|
|
5991
|
+
this.appendChild(this.#createDeleteTableButton());
|
|
5992
|
+
this.addEventListener("keydown", this.#handleToolsKeydown);
|
|
5728
5993
|
}
|
|
5729
5994
|
|
|
5730
|
-
#
|
|
5731
|
-
const
|
|
5995
|
+
#createButtonsContainer(childType, setCountProperty, moreMenu) {
|
|
5996
|
+
const container = createElement("div", { className: `lexxy-table-control lexxy-table-control--${childType}` });
|
|
5732
5997
|
|
|
5733
|
-
|
|
5998
|
+
const plusButton = this.#createButton(`Add ${childType}`, { action: "insert", childType, direction: "after" }, "+");
|
|
5999
|
+
const minusButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType }, "−");
|
|
5734
6000
|
|
|
5735
|
-
const
|
|
5736
|
-
|
|
6001
|
+
const dropdown = createElement("details", { className: "lexxy-table-control__more-menu" });
|
|
6002
|
+
dropdown.setAttribute("name", "lexxy-dropdown");
|
|
6003
|
+
dropdown.tabIndex = -1;
|
|
5737
6004
|
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
this.#insertTemplatesAsAttachments(templates, stringToReplace, promptItem.getAttribute("sgid"));
|
|
5742
|
-
}
|
|
5743
|
-
}
|
|
6005
|
+
const count = createElement("summary", {}, `_ ${childType}s`);
|
|
6006
|
+
setCountProperty(count);
|
|
6007
|
+
dropdown.appendChild(count);
|
|
5744
6008
|
|
|
5745
|
-
|
|
5746
|
-
this.#editor.update(() => {
|
|
5747
|
-
const nodes = templates.flatMap(template => this.#buildEditableTextNodes(template));
|
|
5748
|
-
this.#editorContents.replaceTextBackUntil(stringToReplace, nodes);
|
|
5749
|
-
});
|
|
5750
|
-
}
|
|
6009
|
+
dropdown.appendChild(moreMenu);
|
|
5751
6010
|
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
6011
|
+
container.appendChild(minusButton);
|
|
6012
|
+
container.appendChild(dropdown);
|
|
6013
|
+
container.appendChild(plusButton);
|
|
5755
6014
|
|
|
5756
|
-
|
|
5757
|
-
this.#editor.update(() => {
|
|
5758
|
-
const attachmentNodes = this.#buildAttachmentNodes(templates, fallbackSgid);
|
|
5759
|
-
const spacedAttachmentNodes = attachmentNodes.flatMap(node => [ node, this.#getSpacerTextNode() ]).slice(0, -1);
|
|
5760
|
-
this.#editorContents.replaceTextBackUntil(stringToReplace, spacedAttachmentNodes);
|
|
5761
|
-
});
|
|
6015
|
+
return container
|
|
5762
6016
|
}
|
|
5763
6017
|
|
|
5764
|
-
#
|
|
5765
|
-
return
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
))
|
|
6018
|
+
#createRowButtonsContainer() {
|
|
6019
|
+
return this.#createButtonsContainer(
|
|
6020
|
+
"row",
|
|
6021
|
+
(count) => { this.rowCount = count; },
|
|
6022
|
+
this.#createMoreMenuSection("row")
|
|
6023
|
+
)
|
|
5771
6024
|
}
|
|
5772
6025
|
|
|
5773
|
-
#
|
|
5774
|
-
return
|
|
6026
|
+
#createColumnButtonsContainer() {
|
|
6027
|
+
return this.#createButtonsContainer(
|
|
6028
|
+
"column",
|
|
6029
|
+
(count) => { this.columnCount = count; },
|
|
6030
|
+
this.#createMoreMenuSection("column")
|
|
6031
|
+
)
|
|
5775
6032
|
}
|
|
5776
6033
|
|
|
5777
|
-
|
|
5778
|
-
const
|
|
5779
|
-
|
|
6034
|
+
#createMoreMenuSection(childType) {
|
|
6035
|
+
const section = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
6036
|
+
const addBeforeButton = this.#createButton(`Add ${childType} before`, { action: "insert", childType, direction: "before" });
|
|
6037
|
+
const addAfterButton = this.#createButton(`Add ${childType} after`, { action: "insert", childType, direction: "after" });
|
|
6038
|
+
const toggleStyleButton = this.#createButton(`Toggle ${childType} style`, { action: "toggle", childType });
|
|
6039
|
+
const deleteButton = this.#createButton(`Remove ${childType}`, { action: "delete", childType });
|
|
6040
|
+
|
|
6041
|
+
section.appendChild(addBeforeButton);
|
|
6042
|
+
section.appendChild(addAfterButton);
|
|
6043
|
+
section.appendChild(toggleStyleButton);
|
|
6044
|
+
section.appendChild(deleteButton);
|
|
6045
|
+
|
|
6046
|
+
return section
|
|
5780
6047
|
}
|
|
5781
6048
|
|
|
5782
|
-
#
|
|
5783
|
-
|
|
6049
|
+
#createDeleteTableButton() {
|
|
6050
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
6051
|
+
|
|
6052
|
+
const deleteTableButton = this.#createButton("Delete this table?", { action: "delete", childType: "table" });
|
|
6053
|
+
deleteTableButton.classList.add("lexxy-table-control__button--delete-table");
|
|
6054
|
+
|
|
6055
|
+
container.appendChild(deleteTableButton);
|
|
6056
|
+
|
|
6057
|
+
this.deleteContainer = container;
|
|
6058
|
+
|
|
6059
|
+
return container
|
|
5784
6060
|
}
|
|
5785
6061
|
|
|
5786
|
-
|
|
5787
|
-
|
|
6062
|
+
#createButton(label, command = {}, icon = this.#icon(command)) {
|
|
6063
|
+
const button = createElement("button", {
|
|
6064
|
+
className: "lexxy-table-control__button",
|
|
6065
|
+
"aria-label": label,
|
|
6066
|
+
type: "button"
|
|
6067
|
+
});
|
|
6068
|
+
button.tabIndex = -1;
|
|
6069
|
+
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
6070
|
+
|
|
6071
|
+
button.dataset.action = command.action;
|
|
6072
|
+
button.dataset.childType = command.childType;
|
|
6073
|
+
button.dataset.direction = command.direction;
|
|
6074
|
+
|
|
6075
|
+
button.addEventListener("click", () => this.#executeTableCommand(command));
|
|
6076
|
+
|
|
6077
|
+
button.addEventListener("mouseover", () => this.#handleCommandButtonHover());
|
|
6078
|
+
button.addEventListener("focus", () => this.#handleCommandButtonHover());
|
|
6079
|
+
button.addEventListener("mouseout", () => this.#handleCommandButtonHover());
|
|
6080
|
+
|
|
6081
|
+
return button
|
|
5788
6082
|
}
|
|
5789
6083
|
|
|
5790
|
-
|
|
5791
|
-
|
|
6084
|
+
#registerKeyboardShortcuts() {
|
|
6085
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleAccessibilityShortcutKey, COMMAND_PRIORITY_HIGH);
|
|
5792
6086
|
}
|
|
5793
6087
|
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
popoverContainer.style.position = "absolute";
|
|
5798
|
-
popoverContainer.setAttribute("nonce", getNonce());
|
|
5799
|
-
popoverContainer.append(...await this.source.buildListItems());
|
|
5800
|
-
popoverContainer.addEventListener("click", this.#handlePopoverClick);
|
|
5801
|
-
this.#editorElement.appendChild(popoverContainer);
|
|
5802
|
-
return popoverContainer
|
|
6088
|
+
#unregisterKeyboardShortcuts() {
|
|
6089
|
+
this.unregisterKeyboardShortcuts?.();
|
|
6090
|
+
this.unregisterKeyboardShortcuts = null;
|
|
5803
6091
|
}
|
|
5804
6092
|
|
|
5805
|
-
#
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
this.#optionWasSelected();
|
|
6093
|
+
#handleAccessibilityShortcutKey = (event) => {
|
|
6094
|
+
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
6095
|
+
const firstButton = this.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
6096
|
+
firstButton?.focus();
|
|
5810
6097
|
}
|
|
5811
6098
|
}
|
|
5812
6099
|
|
|
5813
|
-
#
|
|
5814
|
-
|
|
5815
|
-
|
|
6100
|
+
#handleToolsKeydown = (event) => {
|
|
6101
|
+
if (event.key === "Escape") {
|
|
6102
|
+
this.#handleEscapeKey();
|
|
6103
|
+
} else {
|
|
6104
|
+
handleRollingTabIndex(this.#tableToolsButtons, event);
|
|
6105
|
+
}
|
|
5816
6106
|
}
|
|
5817
|
-
}
|
|
5818
6107
|
|
|
5819
|
-
|
|
6108
|
+
#handleEscapeKey() {
|
|
6109
|
+
const cell = this.tableController.currentCell;
|
|
6110
|
+
if (!cell) return
|
|
5820
6111
|
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
6112
|
+
this.#editor.update(() => {
|
|
6113
|
+
cell.select();
|
|
6114
|
+
this.#editor.focus();
|
|
6115
|
+
});
|
|
5825
6116
|
|
|
5826
|
-
this.#
|
|
5827
|
-
this.#monitorForCodeBlockSelection();
|
|
6117
|
+
this.#update();
|
|
5828
6118
|
}
|
|
5829
6119
|
|
|
5830
|
-
#
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
this.languagePickerElement.addEventListener("change", () => {
|
|
5834
|
-
this.#updateCodeBlockLanguage(this.languagePickerElement.value);
|
|
5835
|
-
});
|
|
6120
|
+
async #handleCommandButtonHover() {
|
|
6121
|
+
await nextFrame();
|
|
5836
6122
|
|
|
5837
|
-
this
|
|
5838
|
-
this.appendChild(this.languagePickerElement);
|
|
5839
|
-
}
|
|
6123
|
+
this.#clearCellStyles();
|
|
5840
6124
|
|
|
5841
|
-
|
|
5842
|
-
|
|
6125
|
+
const activeElement = this.querySelector("button:hover, button:focus");
|
|
6126
|
+
if (!activeElement) return
|
|
5843
6127
|
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
}
|
|
6128
|
+
const command = {
|
|
6129
|
+
action: activeElement.dataset.action,
|
|
6130
|
+
childType: activeElement.dataset.childType,
|
|
6131
|
+
direction: activeElement.dataset.direction
|
|
6132
|
+
};
|
|
5850
6133
|
|
|
5851
|
-
|
|
5852
|
-
}
|
|
6134
|
+
let cellsToHighlight = null;
|
|
5853
6135
|
|
|
5854
|
-
|
|
5855
|
-
|
|
6136
|
+
switch (command.childType) {
|
|
6137
|
+
case "row":
|
|
6138
|
+
cellsToHighlight = this.tableController.currentRowCells;
|
|
6139
|
+
break
|
|
6140
|
+
case "column":
|
|
6141
|
+
cellsToHighlight = this.tableController.currentColumnCells;
|
|
6142
|
+
break
|
|
6143
|
+
case "table":
|
|
6144
|
+
cellsToHighlight = this.tableController.tableRows;
|
|
6145
|
+
break
|
|
6146
|
+
}
|
|
5856
6147
|
|
|
5857
|
-
if (!
|
|
5858
|
-
if (!languages.php) languages.php = "PHP";
|
|
5859
|
-
if (!languages.go) languages.go = "Go";
|
|
5860
|
-
if (!languages.bash) languages.bash = "Bash";
|
|
5861
|
-
if (!languages.json) languages.json = "JSON";
|
|
5862
|
-
if (!languages.diff) languages.diff = "Diff";
|
|
6148
|
+
if (!cellsToHighlight) return
|
|
5863
6149
|
|
|
5864
|
-
|
|
5865
|
-
|
|
6150
|
+
cellsToHighlight.forEach(cell => {
|
|
6151
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
6152
|
+
if (!cellElement) return
|
|
5866
6153
|
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
return Object.fromEntries([ plainEntry, ...sortedEntries ])
|
|
6154
|
+
cellElement.classList.toggle(theme.tableCellHighlight, true);
|
|
6155
|
+
Object.assign(cellElement.dataset, command);
|
|
6156
|
+
});
|
|
5871
6157
|
}
|
|
5872
6158
|
|
|
5873
|
-
#
|
|
5874
|
-
this.editor.
|
|
5875
|
-
|
|
6159
|
+
#monitorForTableSelection() {
|
|
6160
|
+
this.unregisterUpdateListener = this.#editor.registerUpdateListener(() => {
|
|
6161
|
+
this.tableController.updateSelectedTable();
|
|
5876
6162
|
|
|
5877
|
-
|
|
5878
|
-
|
|
6163
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6164
|
+
if (tableNode) {
|
|
6165
|
+
this.#show();
|
|
6166
|
+
} else {
|
|
6167
|
+
this.#hide();
|
|
5879
6168
|
}
|
|
5880
6169
|
});
|
|
5881
6170
|
}
|
|
5882
6171
|
|
|
5883
|
-
#
|
|
5884
|
-
this.
|
|
5885
|
-
|
|
5886
|
-
|
|
6172
|
+
#executeTableCommand(command) {
|
|
6173
|
+
this.tableController.executeTableCommand(command);
|
|
6174
|
+
this.#update();
|
|
6175
|
+
}
|
|
5887
6176
|
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
this.#hideLanguagePicker();
|
|
5892
|
-
}
|
|
5893
|
-
});
|
|
5894
|
-
});
|
|
6177
|
+
#show() {
|
|
6178
|
+
this.style.display = "flex";
|
|
6179
|
+
this.#update();
|
|
5895
6180
|
}
|
|
5896
6181
|
|
|
5897
|
-
#
|
|
5898
|
-
|
|
6182
|
+
#hide() {
|
|
6183
|
+
this.style.display = "none";
|
|
6184
|
+
this.#clearCellStyles();
|
|
6185
|
+
}
|
|
5899
6186
|
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
6187
|
+
#update() {
|
|
6188
|
+
this.#updateButtonsPosition();
|
|
6189
|
+
this.#updateRowColumnCount();
|
|
6190
|
+
this.#closeMoreMenu();
|
|
6191
|
+
this.#handleCommandButtonHover();
|
|
6192
|
+
}
|
|
5903
6193
|
|
|
5904
|
-
|
|
5905
|
-
|
|
6194
|
+
#closeMoreMenu() {
|
|
6195
|
+
this.querySelector("details[open]")?.removeAttribute("open");
|
|
6196
|
+
}
|
|
5906
6197
|
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
return parentNode
|
|
5911
|
-
}
|
|
6198
|
+
#updateButtonsPosition() {
|
|
6199
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6200
|
+
if (!tableNode) return
|
|
5912
6201
|
|
|
5913
|
-
|
|
5914
|
-
|
|
6202
|
+
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
6203
|
+
if (!tableElement) return
|
|
5915
6204
|
|
|
5916
|
-
|
|
5917
|
-
const
|
|
6205
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
6206
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
5918
6207
|
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
this
|
|
6208
|
+
const relativeTop = tableRect.top - editorRect.top;
|
|
6209
|
+
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
6210
|
+
this.style.top = `${relativeTop}px`;
|
|
6211
|
+
this.style.left = `${relativeCenter}px`;
|
|
5922
6212
|
}
|
|
5923
6213
|
|
|
5924
|
-
#
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
6214
|
+
#updateRowColumnCount() {
|
|
6215
|
+
const tableNode = this.tableController.currentTableNode;
|
|
6216
|
+
if (!tableNode) return
|
|
6217
|
+
|
|
6218
|
+
const tableElement = $getElementForTableNode(this.#editor, tableNode);
|
|
6219
|
+
if (!tableElement) return
|
|
6220
|
+
|
|
6221
|
+
const rowCount = tableElement.rows;
|
|
6222
|
+
const columnCount = tableElement.columns;
|
|
6223
|
+
|
|
6224
|
+
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
6225
|
+
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
5929
6226
|
}
|
|
5930
6227
|
|
|
5931
|
-
#
|
|
5932
|
-
const
|
|
5933
|
-
if (!
|
|
6228
|
+
#setTableCellFocus() {
|
|
6229
|
+
const cell = this.tableController.currentCell;
|
|
6230
|
+
if (!cell) return
|
|
5934
6231
|
|
|
5935
|
-
const
|
|
5936
|
-
|
|
5937
|
-
const relativeTop = codeRect.top - editorRect.top;
|
|
6232
|
+
const cellElement = this.#editor.getElementByKey(cell.getKey());
|
|
6233
|
+
if (!cellElement) return
|
|
5938
6234
|
|
|
5939
|
-
|
|
6235
|
+
cellElement.classList.add(theme.tableCellFocus);
|
|
5940
6236
|
}
|
|
5941
6237
|
|
|
5942
|
-
#
|
|
5943
|
-
this.
|
|
6238
|
+
#clearCellStyles() {
|
|
6239
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellFocus}`)?.forEach(cell => {
|
|
6240
|
+
cell.classList.remove(theme.tableCellFocus);
|
|
6241
|
+
});
|
|
6242
|
+
|
|
6243
|
+
this.#editorElement.querySelectorAll(`.${theme.tableCellHighlight}`)?.forEach(cell => {
|
|
6244
|
+
cell.classList.remove(theme.tableCellHighlight);
|
|
6245
|
+
cell.removeAttribute("data-action");
|
|
6246
|
+
cell.removeAttribute("data-child-type");
|
|
6247
|
+
cell.removeAttribute("data-direction");
|
|
6248
|
+
});
|
|
6249
|
+
|
|
6250
|
+
this.#setTableCellFocus();
|
|
5944
6251
|
}
|
|
5945
6252
|
|
|
5946
|
-
#
|
|
5947
|
-
|
|
6253
|
+
#icon(command) {
|
|
6254
|
+
const { action, childType } = command;
|
|
6255
|
+
const direction = (action == "insert" ? command.direction : null);
|
|
6256
|
+
const iconId = [ action, childType, direction ].filter(Boolean).join("-");
|
|
6257
|
+
return TableIcons[iconId]
|
|
5948
6258
|
}
|
|
5949
6259
|
}
|
|
5950
6260
|
|
|
5951
|
-
|
|
6261
|
+
function defineElements() {
|
|
6262
|
+
const elements = {
|
|
6263
|
+
"lexxy-toolbar": LexicalToolbarElement,
|
|
6264
|
+
"lexxy-editor": LexicalEditorElement,
|
|
6265
|
+
"lexxy-link-dropdown": LinkDropdown,
|
|
6266
|
+
"lexxy-highlight-dropdown": HighlightDropdown,
|
|
6267
|
+
"lexxy-prompt": LexicalPromptElement,
|
|
6268
|
+
"lexxy-code-language-picker": CodeLanguagePicker,
|
|
6269
|
+
"lexxy-table-tools": TableTools,
|
|
6270
|
+
};
|
|
6271
|
+
|
|
6272
|
+
Object.entries(elements).forEach(([ name, element ]) => {
|
|
6273
|
+
customElements.define(name, element);
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
5952
6276
|
|
|
5953
6277
|
class LexxyExtension {
|
|
5954
6278
|
#editorElement
|
|
@@ -5981,4 +6305,7 @@ class LexxyExtension {
|
|
|
5981
6305
|
|
|
5982
6306
|
const configure = Lexxy.configure;
|
|
5983
6307
|
|
|
6308
|
+
// Pushing elements definition to after the current call stack to allow global configuration to take place first
|
|
6309
|
+
setTimeout(defineElements, 0);
|
|
6310
|
+
|
|
5984
6311
|
export { ActionTextAttachmentNode, ActionTextAttachmentUploadNode, CustomActionTextAttachmentNode, LexxyExtension as Extension, HorizontalDividerNode, configure };
|