@37signals/lexxy 0.1.24-beta → 0.1.26-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lexxy.esm.js
CHANGED
|
@@ -10,15 +10,17 @@ 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, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, 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, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag,
|
|
14
|
-
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $
|
|
13
|
+
import { $isTextNode, TextNode, $isRangeSelection, SKIP_DOM_SELECTION_TAG, $getSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, $createTextNode, 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, $createNodeSelection, $setSelection, $createParagraphNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag, createEditor, BLUR_COMMAND, FOCUS_COMMAND, KEY_DOWN_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
14
|
+
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, $getListDepth, $createListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
15
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
16
16
|
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
17
17
|
import { $isLinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
|
|
18
|
+
import { $getTableCellNodeFromLexicalNode, INSERT_TABLE_COMMAND, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, TableNode, TableCellNode, TableRowNode, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $getElementForTableNode, $isTableCellNode, TableCellHeaderStates } from '@lexical/table';
|
|
18
19
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
19
20
|
import { registerMarkdownShortcuts, TRANSFORMERS } from '@lexical/markdown';
|
|
20
21
|
import { createEmptyHistoryState, registerHistory } from '@lexical/history';
|
|
21
22
|
import { DirectUpload } from '@rails/activestorage';
|
|
23
|
+
import { $getNearestNodeOfType } from '@lexical/utils';
|
|
22
24
|
import { marked } from 'marked';
|
|
23
25
|
|
|
24
26
|
// Configure Prism for manual highlighting mode
|
|
@@ -27,7 +29,7 @@ window.Prism = window.Prism || {};
|
|
|
27
29
|
window.Prism.manual = true;
|
|
28
30
|
|
|
29
31
|
const ALLOWED_HTML_TAGS = [ "a", "action-text-attachment", "b", "blockquote", "br", "code", "em",
|
|
30
|
-
"figcaption", "figure", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "mark", "ol", "p", "pre", "q", "s", "strong", "ul" ];
|
|
32
|
+
"figcaption", "figure", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "mark", "ol", "p", "pre", "q", "s", "strong", "ul", "table", "tbody", "tr", "th", "td" ];
|
|
31
33
|
|
|
32
34
|
const ALLOWED_HTML_ATTRIBUTES = [ "alt", "caption", "class", "content", "content-type", "contenteditable",
|
|
33
35
|
"data-direct-upload-id", "data-sgid", "filename", "filesize", "height", "href", "presentation",
|
|
@@ -144,6 +146,93 @@ function hasHighlightStyles(cssOrStyles) {
|
|
|
144
146
|
return !!(styles.color || styles["background-color"])
|
|
145
147
|
}
|
|
146
148
|
|
|
149
|
+
function handleRollingTabIndex(elements, event) {
|
|
150
|
+
const previousActiveElement = document.activeElement;
|
|
151
|
+
|
|
152
|
+
if (elements.includes(previousActiveElement)) {
|
|
153
|
+
const finder = new NextElementFinder(elements, event.key);
|
|
154
|
+
|
|
155
|
+
if (finder.selectNext(previousActiveElement)) {
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
class NextElementFinder {
|
|
162
|
+
constructor(elements, key) {
|
|
163
|
+
this.elements = elements;
|
|
164
|
+
this.key = key;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
selectNext(fromElement) {
|
|
168
|
+
const nextElement = this.#findNextElement(fromElement);
|
|
169
|
+
|
|
170
|
+
if (nextElement) {
|
|
171
|
+
const inactiveElements = this.elements.filter(element => element !== nextElement);
|
|
172
|
+
this.#unsetTabIndex(inactiveElements);
|
|
173
|
+
this.#focusWithActiveTabIndex(nextElement);
|
|
174
|
+
return true
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#findNextElement(fromElement) {
|
|
181
|
+
switch (this.key) {
|
|
182
|
+
case "ArrowRight":
|
|
183
|
+
case "ArrowDown":
|
|
184
|
+
return this.#findNextSibling(fromElement)
|
|
185
|
+
|
|
186
|
+
case "ArrowLeft":
|
|
187
|
+
case "ArrowUp":
|
|
188
|
+
return this.#findPreviousSibling(fromElement)
|
|
189
|
+
|
|
190
|
+
case "Home":
|
|
191
|
+
return this.#findFirst()
|
|
192
|
+
|
|
193
|
+
case "End":
|
|
194
|
+
return this.#findLast()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#findFirst(elements = this.elements) {
|
|
199
|
+
return elements.find(isActiveAndVisible)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#findLast(elements = this.elements) {
|
|
203
|
+
return elements.findLast(isActiveAndVisible)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#findNextSibling(element) {
|
|
207
|
+
const afterElements = this.elements.slice(this.#indexOf(element) + 1);
|
|
208
|
+
return this.#findFirst(afterElements)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#findPreviousSibling(element) {
|
|
212
|
+
const beforeElements = this.elements.slice(0, this.#indexOf(element));
|
|
213
|
+
return this.#findLast(beforeElements)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
#indexOf(element) {
|
|
217
|
+
return this.elements.indexOf(element)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#focusWithActiveTabIndex(element) {
|
|
221
|
+
if (isActiveAndVisible(element)) {
|
|
222
|
+
element.tabIndex = 0;
|
|
223
|
+
element.focus();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#unsetTabIndex(elements) {
|
|
228
|
+
elements.forEach(element => element.tabIndex = -1);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isActiveAndVisible(element) {
|
|
233
|
+
return element && !element.disabled && element.checkVisibility()
|
|
234
|
+
}
|
|
235
|
+
|
|
147
236
|
class LexicalToolbarElement extends HTMLElement {
|
|
148
237
|
static observedAttributes = [ "connected" ]
|
|
149
238
|
|
|
@@ -155,17 +244,14 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
155
244
|
|
|
156
245
|
connectedCallback() {
|
|
157
246
|
requestAnimationFrame(() => this.#refreshToolbarOverflow());
|
|
158
|
-
|
|
159
|
-
this
|
|
160
|
-
this._resizeObserver.observe(this);
|
|
247
|
+
this.setAttribute("role", "toolbar");
|
|
248
|
+
this.#installResizeObserver();
|
|
161
249
|
}
|
|
162
250
|
|
|
163
251
|
disconnectedCallback() {
|
|
164
|
-
|
|
165
|
-
this._resizeObserver.disconnect();
|
|
166
|
-
this._resizeObserver = null;
|
|
167
|
-
}
|
|
252
|
+
this.#uninstallResizeObserver();
|
|
168
253
|
this.#unbindHotkeys();
|
|
254
|
+
this.#unbindFocusListeners();
|
|
169
255
|
}
|
|
170
256
|
|
|
171
257
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -179,11 +265,12 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
179
265
|
this.editor = editorElement.editor;
|
|
180
266
|
this.#bindButtons();
|
|
181
267
|
this.#bindHotkeys();
|
|
182
|
-
this.#
|
|
268
|
+
this.#resetTabIndexValues();
|
|
183
269
|
this.#setItemPositionValues();
|
|
184
270
|
this.#monitorSelectionChanges();
|
|
185
271
|
this.#monitorHistoryChanges();
|
|
186
272
|
this.#refreshToolbarOverflow();
|
|
273
|
+
this.#bindFocusListeners();
|
|
187
274
|
|
|
188
275
|
this.toggleAttribute("connected", true);
|
|
189
276
|
}
|
|
@@ -193,24 +280,39 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
193
280
|
this.connectedCallback();
|
|
194
281
|
}
|
|
195
282
|
|
|
283
|
+
#installResizeObserver() {
|
|
284
|
+
this.resizeObserver = new ResizeObserver(() => this.#refreshToolbarOverflow());
|
|
285
|
+
this.resizeObserver.observe(this);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#uninstallResizeObserver() {
|
|
289
|
+
if (this.resizeObserver) {
|
|
290
|
+
this.resizeObserver.disconnect();
|
|
291
|
+
this.resizeObserver = null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
196
295
|
#bindButtons() {
|
|
197
296
|
this.addEventListener("click", this.#handleButtonClicked.bind(this));
|
|
198
297
|
}
|
|
199
298
|
|
|
200
|
-
#handleButtonClicked(
|
|
201
|
-
this.#handleTargetClicked(
|
|
299
|
+
#handleButtonClicked(event) {
|
|
300
|
+
this.#handleTargetClicked(event, "[data-command]", this.#dispatchButtonCommand.bind(this));
|
|
202
301
|
}
|
|
203
302
|
|
|
204
|
-
#handleTargetClicked(
|
|
205
|
-
const button = target.closest(selector);
|
|
303
|
+
#handleTargetClicked(event, selector, callback) {
|
|
304
|
+
const button = event.target.closest(selector);
|
|
206
305
|
if (button) {
|
|
207
|
-
callback(button);
|
|
306
|
+
callback(event, button);
|
|
208
307
|
}
|
|
209
308
|
}
|
|
210
309
|
|
|
211
|
-
#dispatchButtonCommand(
|
|
212
|
-
const
|
|
213
|
-
|
|
310
|
+
#dispatchButtonCommand(event, { dataset: { command, payload } }) {
|
|
311
|
+
const isKeyboard = event instanceof PointerEvent && event.pointerId === -1;
|
|
312
|
+
|
|
313
|
+
this.editor.update(() => {
|
|
314
|
+
this.editor.dispatchCommand(command, payload);
|
|
315
|
+
}, { tag: isKeyboard ? SKIP_DOM_SELECTION_TAG : undefined } );
|
|
214
316
|
}
|
|
215
317
|
|
|
216
318
|
#bindHotkeys() {
|
|
@@ -245,9 +347,38 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
245
347
|
return [ ...modifiers, pressedKey ].join("+")
|
|
246
348
|
}
|
|
247
349
|
|
|
248
|
-
#
|
|
249
|
-
this
|
|
250
|
-
|
|
350
|
+
#bindFocusListeners() {
|
|
351
|
+
this.editorElement.addEventListener("lexxy:focus", this.#handleFocus);
|
|
352
|
+
this.editorElement.addEventListener("lexxy:blur", this.#handleFocusOut);
|
|
353
|
+
this.addEventListener("focusout", this.#handleFocusOut);
|
|
354
|
+
this.addEventListener("keydown", this.#handleKeydown);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
#unbindFocusListeners() {
|
|
358
|
+
this.editorElement.removeEventListener("lexxy:focus", this.#handleFocus);
|
|
359
|
+
this.editorElement.removeEventListener("lexxy:blur", this.#handleFocusOut);
|
|
360
|
+
this.removeEventListener("focusout", this.#handleFocusOut);
|
|
361
|
+
this.removeEventListener("keydown", this.#handleKeydown);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#handleFocus = () => {
|
|
365
|
+
this.#resetTabIndexValues();
|
|
366
|
+
this.#focusableItems[0].tabIndex = 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#handleFocusOut = () => {
|
|
370
|
+
if (!this.contains(document.activeElement)) {
|
|
371
|
+
this.#resetTabIndexValues();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
#handleKeydown = (event) => {
|
|
376
|
+
handleRollingTabIndex(this.#focusableItems, event);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
#resetTabIndexValues() {
|
|
380
|
+
this.#focusableItems.forEach((button) => {
|
|
381
|
+
button.tabIndex = -1;
|
|
251
382
|
});
|
|
252
383
|
}
|
|
253
384
|
|
|
@@ -294,6 +425,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
294
425
|
const isInCode = $isCodeNode(topLevelElement) || selection.hasFormat("code");
|
|
295
426
|
const isInList = this.#isInList(anchorNode);
|
|
296
427
|
const listType = getListType(anchorNode);
|
|
428
|
+
const isInTable = $getTableCellNodeFromLexicalNode(anchorNode) !== null;
|
|
297
429
|
|
|
298
430
|
this.#setButtonPressed("bold", isBold);
|
|
299
431
|
this.#setButtonPressed("italic", isItalic);
|
|
@@ -305,6 +437,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
305
437
|
this.#setButtonPressed("code", isInCode);
|
|
306
438
|
this.#setButtonPressed("unordered-list", isInList && listType === "bullet");
|
|
307
439
|
this.#setButtonPressed("ordered-list", isInList && listType === "number");
|
|
440
|
+
this.#setButtonPressed("table", isInTable);
|
|
308
441
|
|
|
309
442
|
this.#updateUndoRedoButtonStates();
|
|
310
443
|
}
|
|
@@ -355,6 +488,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
355
488
|
|
|
356
489
|
const isOverflowing = this.#overflowMenu.children.length > 0;
|
|
357
490
|
this.toggleAttribute("overflowing", isOverflowing);
|
|
491
|
+
this.#overflowMenu.toggleAttribute("disabled", !isOverflowing);
|
|
358
492
|
}
|
|
359
493
|
|
|
360
494
|
#compactMenu() {
|
|
@@ -406,6 +540,10 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
406
540
|
return Array.from(this.querySelectorAll(":scope > button"))
|
|
407
541
|
}
|
|
408
542
|
|
|
543
|
+
get #focusableItems() {
|
|
544
|
+
return Array.from(this.querySelectorAll(":scope button, :scope > details > summary"))
|
|
545
|
+
}
|
|
546
|
+
|
|
409
547
|
get #toolbarItems() {
|
|
410
548
|
return Array.from(this.querySelectorAll(":scope > *:not(.lexxy-editor__toolbar-overflow)"))
|
|
411
549
|
}
|
|
@@ -476,6 +614,10 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
476
614
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M16 8a2 2 0 110 4 2 2 0 010-4z""/><path d="M22 2a1 1 0 011 1v18a1 1 0 01-1 1H2a1 1 0 01-1-1V3a1 1 0 011-1h20zM3 18.714L9 11l5.25 6.75L17 15l4 4V4H3v14.714z"/></svg>
|
|
477
615
|
</button>
|
|
478
616
|
|
|
617
|
+
<button class="lexxy-editor__toolbar-button" type="button" name="table" data-command="insertTable" title="Insert a table">
|
|
618
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20.2041 2.01074C21.2128 2.113 22 2.96435 22 4V20L21.9893 20.2041C21.8938 21.1457 21.1457 21.8938 20.2041 21.9893L20 22H4C2.96435 22 2.113 21.2128 2.01074 20.2041L2 20V4C2 2.89543 2.89543 2 4 2H20L20.2041 2.01074ZM4 13V20H11V13H4ZM13 13V20H20V13H13ZM4 11H11V4H4V11ZM13 11H20V4H13V11Z"/></svg>
|
|
619
|
+
</button>
|
|
620
|
+
|
|
479
621
|
<button class="lexxy-editor__toolbar-button" type="button" name="divider" data-command="insertHorizontalDivider" title="Insert a divider">
|
|
480
622
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 12C0 11.4477 0.447715 11 1 11H23C23.5523 11 24 11.4477 24 12C24 12.5523 23.5523 13 23 13H1C0.447716 13 0 12.5523 0 12Z"/><path d="M4 5C4 3.89543 4.89543 3 6 3H18C19.1046 3 20 3.89543 20 5C20 6.10457 19.1046 7 18 7H6C4.89543 7 4 6.10457 4 5Z"/><path d="M4 19C4 17.8954 4.89543 17 6 17H18C19.1046 17 20 17.8954 20 19C20 20.1046 19.1046 21 18 21H6C4.89543 21 4 20.1046 4 19Z"/></svg>
|
|
481
623
|
</button>
|
|
@@ -508,6 +650,10 @@ var theme = {
|
|
|
508
650
|
underline: "lexxy-content__underline",
|
|
509
651
|
highlight: "lexxy-content__highlight"
|
|
510
652
|
},
|
|
653
|
+
tableCellHeader: "lexxy-content__table-cell--header",
|
|
654
|
+
tableCellSelected: "lexxy-content__table-cell--selected",
|
|
655
|
+
tableSelection: "lexxy-content__table--selection",
|
|
656
|
+
tableScrollableWrapper: "lexxy-content__table-wrapper",
|
|
511
657
|
list: {
|
|
512
658
|
nested: {
|
|
513
659
|
listitem: "lexxy-nested-listitem",
|
|
@@ -567,7 +713,7 @@ var theme = {
|
|
|
567
713
|
}
|
|
568
714
|
};
|
|
569
715
|
|
|
570
|
-
function createElement(name, properties) {
|
|
716
|
+
function createElement(name, properties, content = "") {
|
|
571
717
|
const element = document.createElement(name);
|
|
572
718
|
for (const [ key, value ] of Object.entries(properties || {})) {
|
|
573
719
|
if (key in element) {
|
|
@@ -576,6 +722,9 @@ function createElement(name, properties) {
|
|
|
576
722
|
element.setAttribute(key, value);
|
|
577
723
|
}
|
|
578
724
|
}
|
|
725
|
+
if (content) {
|
|
726
|
+
element.innerHTML = content;
|
|
727
|
+
}
|
|
579
728
|
return element
|
|
580
729
|
}
|
|
581
730
|
|
|
@@ -729,6 +878,10 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
729
878
|
return true
|
|
730
879
|
}
|
|
731
880
|
|
|
881
|
+
getTextContent() {
|
|
882
|
+
return `[${ this.caption || this.fileName }]\n\n`
|
|
883
|
+
}
|
|
884
|
+
|
|
732
885
|
isInline() {
|
|
733
886
|
return false
|
|
734
887
|
}
|
|
@@ -1095,6 +1248,10 @@ class HorizontalDividerNode extends DecoratorNode {
|
|
|
1095
1248
|
return true
|
|
1096
1249
|
}
|
|
1097
1250
|
|
|
1251
|
+
getTextContent() {
|
|
1252
|
+
return "┄\n\n"
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1098
1255
|
isInline() {
|
|
1099
1256
|
return false
|
|
1100
1257
|
}
|
|
@@ -1131,6 +1288,16 @@ const COMMANDS = [
|
|
|
1131
1288
|
"insertCodeBlock",
|
|
1132
1289
|
"insertHorizontalDivider",
|
|
1133
1290
|
"uploadAttachments",
|
|
1291
|
+
|
|
1292
|
+
"insertTable",
|
|
1293
|
+
"insertTableRowAbove",
|
|
1294
|
+
"insertTableRowBelow",
|
|
1295
|
+
"insertTableColumnAfter",
|
|
1296
|
+
"insertTableColumnBefore",
|
|
1297
|
+
"deleteTableRow",
|
|
1298
|
+
"deleteTableColumn",
|
|
1299
|
+
"deleteTable",
|
|
1300
|
+
|
|
1134
1301
|
"undo",
|
|
1135
1302
|
"redo"
|
|
1136
1303
|
];
|
|
@@ -1293,6 +1460,45 @@ class CommandDispatcher {
|
|
|
1293
1460
|
setTimeout(() => input.remove(), 1000);
|
|
1294
1461
|
}
|
|
1295
1462
|
|
|
1463
|
+
dispatchInsertTable() {
|
|
1464
|
+
this.editor.dispatchCommand(INSERT_TABLE_COMMAND, { "rows": 3, "columns": 3, "includeHeaders": true });
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
dispatchInsertTableRowBelow() {
|
|
1468
|
+
$insertTableRowAtSelection(true);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
dispatchInsertTableRowAbove() {
|
|
1472
|
+
$insertTableRowAtSelection(false);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
dispatchInsertTableColumnAfter() {
|
|
1476
|
+
$insertTableColumnAtSelection(true);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
dispatchInsertTableColumnBefore() {
|
|
1480
|
+
$insertTableColumnAtSelection(false);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
dispatchDeleteTableRow() {
|
|
1484
|
+
$deleteTableRowAtSelection();
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
dispatchDeleteTableColumn() {
|
|
1488
|
+
$deleteTableColumnAtSelection();
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
dispatchDeleteTable() {
|
|
1492
|
+
this.editor.update(() => {
|
|
1493
|
+
const selection = $getSelection();
|
|
1494
|
+
if (!$isRangeSelection(selection)) return
|
|
1495
|
+
|
|
1496
|
+
const anchorNode = selection.anchor.getNode();
|
|
1497
|
+
const tableNode = $findTableNode(anchorNode);
|
|
1498
|
+
tableNode.remove();
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1296
1502
|
dispatchUndo() {
|
|
1297
1503
|
this.editor.dispatchCommand(UNDO_COMMAND, undefined);
|
|
1298
1504
|
}
|
|
@@ -1315,7 +1521,7 @@ class CommandDispatcher {
|
|
|
1315
1521
|
}
|
|
1316
1522
|
|
|
1317
1523
|
#registerKeyboardCommands() {
|
|
1318
|
-
this.editor.registerCommand(KEY_TAB_COMMAND, this.#
|
|
1524
|
+
this.editor.registerCommand(KEY_TAB_COMMAND, this.#handleTabKey.bind(this), COMMAND_PRIORITY_NORMAL);
|
|
1319
1525
|
}
|
|
1320
1526
|
|
|
1321
1527
|
#registerDragAndDropHandlers() {
|
|
@@ -1365,18 +1571,28 @@ class CommandDispatcher {
|
|
|
1365
1571
|
this.editor.focus();
|
|
1366
1572
|
}
|
|
1367
1573
|
|
|
1368
|
-
#
|
|
1574
|
+
#handleTabKey(event) {
|
|
1369
1575
|
if (this.selection.isInsideList) {
|
|
1370
|
-
event
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
} else {
|
|
1374
|
-
return this.editor.dispatchCommand(INDENT_CONTENT_COMMAND, undefined)
|
|
1375
|
-
}
|
|
1576
|
+
return this.#handleTabForList(event)
|
|
1577
|
+
} else if (this.selection.isInsideCodeBlock) {
|
|
1578
|
+
return this.#handleTabForCode()
|
|
1376
1579
|
}
|
|
1377
1580
|
return false
|
|
1378
1581
|
}
|
|
1379
1582
|
|
|
1583
|
+
#handleTabForList(event) {
|
|
1584
|
+
if (event.shiftKey && !this.selection.isIndentedList) return false
|
|
1585
|
+
|
|
1586
|
+
event.preventDefault();
|
|
1587
|
+
const command = event.shiftKey? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND;
|
|
1588
|
+
return this.editor.dispatchCommand(command)
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
#handleTabForCode() {
|
|
1592
|
+
const selection = $getSelection();
|
|
1593
|
+
return $isRangeSelection(selection) && selection.isCollapsed()
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1380
1596
|
// Not using TOGGLE_LINK_COMMAND because it's not handled unless you use React/LinkPlugin
|
|
1381
1597
|
#toggleLink(url) {
|
|
1382
1598
|
this.editor.update(() => {
|
|
@@ -1553,6 +1769,29 @@ class Selection {
|
|
|
1553
1769
|
return getNearestListItemNode(anchorNode) !== null
|
|
1554
1770
|
}
|
|
1555
1771
|
|
|
1772
|
+
get isIndentedList() {
|
|
1773
|
+
const selection = $getSelection();
|
|
1774
|
+
if (!$isRangeSelection(selection)) return false
|
|
1775
|
+
|
|
1776
|
+
const nodes = selection.getNodes();
|
|
1777
|
+
for (const node of nodes) {
|
|
1778
|
+
const closestListNode = $getNearestNodeOfType(node, ListNode);
|
|
1779
|
+
if (closestListNode && $getListDepth(closestListNode) > 1) {
|
|
1780
|
+
return true
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
return false
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
get isInsideCodeBlock() {
|
|
1788
|
+
const selection = $getSelection();
|
|
1789
|
+
if (!$isRangeSelection(selection)) return false
|
|
1790
|
+
|
|
1791
|
+
const anchorNode = selection.anchor.getNode();
|
|
1792
|
+
return $getNearestNodeOfType(anchorNode, CodeNode) !== null
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1556
1795
|
get nodeAfterCursor() {
|
|
1557
1796
|
const { anchorNode, offset } = this.#getCollapsedSelectionData();
|
|
1558
1797
|
if (!anchorNode) return null
|
|
@@ -2132,6 +2371,10 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2132
2371
|
return true
|
|
2133
2372
|
}
|
|
2134
2373
|
|
|
2374
|
+
getTextContent() {
|
|
2375
|
+
return this.createDOM().textContent.trim() || `[${this.contentType}]`
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2135
2378
|
isInline() {
|
|
2136
2379
|
return true
|
|
2137
2380
|
}
|
|
@@ -3432,7 +3675,7 @@ function applyLanguage(conversionOutput, element) {
|
|
|
3432
3675
|
|
|
3433
3676
|
class LexicalEditorElement extends HTMLElement {
|
|
3434
3677
|
static formAssociated = true
|
|
3435
|
-
static debug =
|
|
3678
|
+
static debug = false
|
|
3436
3679
|
static commands = [ "bold", "italic", "strikethrough" ]
|
|
3437
3680
|
|
|
3438
3681
|
static observedAttributes = [ "connected", "required" ]
|
|
@@ -3507,6 +3750,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3507
3750
|
return this.dataset.blobUrlTemplate
|
|
3508
3751
|
}
|
|
3509
3752
|
|
|
3753
|
+
get isEmpty() {
|
|
3754
|
+
return [ "<p><br></p>", "<p></p>", "" ].includes(this.value.trim())
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
get isBlank() {
|
|
3758
|
+
return this.isEmpty || this.toString().match(/^\s*$/g) !== null
|
|
3759
|
+
}
|
|
3760
|
+
|
|
3761
|
+
get hasOpenPrompt() {
|
|
3762
|
+
return this.querySelector(".lexxy-prompt-menu.lexxy-prompt-menu--visible") !== null
|
|
3763
|
+
}
|
|
3764
|
+
|
|
3510
3765
|
get isSingleLineMode() {
|
|
3511
3766
|
return this.hasAttribute("single-line")
|
|
3512
3767
|
}
|
|
@@ -3550,6 +3805,16 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3550
3805
|
});
|
|
3551
3806
|
}
|
|
3552
3807
|
|
|
3808
|
+
toString() {
|
|
3809
|
+
if (!this.cachedStringValue) {
|
|
3810
|
+
this.editor?.getEditorState().read(() => {
|
|
3811
|
+
this.cachedStringValue = $getRoot().getTextContent();
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
return this.cachedStringValue
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3553
3818
|
#parseHtmlIntoLexicalNodes(html) {
|
|
3554
3819
|
if (!html) html = "<p></p>";
|
|
3555
3820
|
const nodes = $generateNodesFromDOM(this.editor, parseHtml(`<div>${html}</div>`));
|
|
@@ -3572,6 +3837,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3572
3837
|
this.#listenForInvalidatedNodes();
|
|
3573
3838
|
this.#handleEnter();
|
|
3574
3839
|
this.#handleFocus();
|
|
3840
|
+
this.#handleTables();
|
|
3575
3841
|
this.#attachDebugHooks();
|
|
3576
3842
|
this.#attachToolbar();
|
|
3577
3843
|
this.#loadInitialValue();
|
|
@@ -3608,6 +3874,9 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3608
3874
|
LinkNode,
|
|
3609
3875
|
AutoLinkNode,
|
|
3610
3876
|
HorizontalDividerNode,
|
|
3877
|
+
TableNode,
|
|
3878
|
+
TableCellNode,
|
|
3879
|
+
TableRowNode,
|
|
3611
3880
|
|
|
3612
3881
|
CustomActionTextAttachmentNode,
|
|
3613
3882
|
];
|
|
@@ -3655,7 +3924,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3655
3924
|
|
|
3656
3925
|
this.internals.setFormValue(html);
|
|
3657
3926
|
this._internalFormValue = html;
|
|
3658
|
-
this.#validationTextArea.value = this
|
|
3927
|
+
this.#validationTextArea.value = this.isEmpty ? "" : html;
|
|
3659
3928
|
|
|
3660
3929
|
if (changed) {
|
|
3661
3930
|
dispatch(this, "lexxy:change");
|
|
@@ -3681,13 +3950,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3681
3950
|
|
|
3682
3951
|
#synchronizeWithChanges() {
|
|
3683
3952
|
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
3684
|
-
this
|
|
3953
|
+
this.#clearCachedValues();
|
|
3685
3954
|
this.#internalFormValue = this.value;
|
|
3686
3955
|
this.#toggleEmptyStatus();
|
|
3687
3956
|
this.#setValidity();
|
|
3688
3957
|
}));
|
|
3689
3958
|
}
|
|
3690
3959
|
|
|
3960
|
+
#clearCachedValues() {
|
|
3961
|
+
this.cachedValue = null;
|
|
3962
|
+
this.cachedStringValue = null;
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3691
3965
|
#addUnregisterHandler(handler) {
|
|
3692
3966
|
this.unregisterHandlers = this.unregisterHandlers || [];
|
|
3693
3967
|
this.unregisterHandlers.push(handler);
|
|
@@ -3705,13 +3979,21 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3705
3979
|
this.historyState = createEmptyHistoryState();
|
|
3706
3980
|
registerHistory(this.editor, this.historyState, 20);
|
|
3707
3981
|
registerList(this.editor);
|
|
3982
|
+
this.#registerTableComponents();
|
|
3708
3983
|
this.#registerCodeHiglightingComponents();
|
|
3709
3984
|
registerMarkdownShortcuts(this.editor, TRANSFORMERS);
|
|
3710
3985
|
}
|
|
3711
3986
|
|
|
3987
|
+
#registerTableComponents() {
|
|
3988
|
+
registerTablePlugin(this.editor);
|
|
3989
|
+
this.tableHandler = createElement("lexxy-table-handler");
|
|
3990
|
+
this.append(this.tableHandler);
|
|
3991
|
+
}
|
|
3992
|
+
|
|
3712
3993
|
#registerCodeHiglightingComponents() {
|
|
3713
3994
|
registerCodeHighlighting(this.editor);
|
|
3714
|
-
this.
|
|
3995
|
+
this.codeLanguagePicker = createElement("lexxy-code-language-picker");
|
|
3996
|
+
this.append(this.codeLanguagePicker);
|
|
3715
3997
|
}
|
|
3716
3998
|
|
|
3717
3999
|
#listenForInvalidatedNodes() {
|
|
@@ -3760,12 +4042,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3760
4042
|
this.editor.registerCommand(FOCUS_COMMAND, () => { dispatch(this, "lexxy:focus"); }, COMMAND_PRIORITY_NORMAL);
|
|
3761
4043
|
}
|
|
3762
4044
|
|
|
4045
|
+
#handleTables() {
|
|
4046
|
+
this.removeTableSelectionObserver = registerTableSelectionObserver(this.editor, true);
|
|
4047
|
+
setScrollableTablesActive(this.editor, true);
|
|
4048
|
+
}
|
|
4049
|
+
|
|
3763
4050
|
#attachDebugHooks() {
|
|
3764
4051
|
if (!LexicalEditorElement.debug) return
|
|
3765
4052
|
|
|
3766
4053
|
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
3767
4054
|
editorState.read(() => {
|
|
3768
|
-
console.debug("HTML: ", this.value);
|
|
4055
|
+
console.debug("HTML: ", this.value, "String:", this.toString());
|
|
4056
|
+
console.debug("empty", this.isEmpty, "blank", this.isBlank);
|
|
3769
4057
|
});
|
|
3770
4058
|
}));
|
|
3771
4059
|
}
|
|
@@ -3794,11 +4082,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3794
4082
|
}
|
|
3795
4083
|
|
|
3796
4084
|
#toggleEmptyStatus() {
|
|
3797
|
-
this.classList.toggle("lexxy-editor--empty", this
|
|
3798
|
-
}
|
|
3799
|
-
|
|
3800
|
-
get #isEmpty() {
|
|
3801
|
-
return [ "<p><br></p>", "<p></p>", "" ].includes(this.value.trim())
|
|
4085
|
+
this.classList.toggle("lexxy-editor--empty", this.isEmpty);
|
|
3802
4086
|
}
|
|
3803
4087
|
|
|
3804
4088
|
#setValidity() {
|
|
@@ -3825,6 +4109,16 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3825
4109
|
this.toolbar = null;
|
|
3826
4110
|
}
|
|
3827
4111
|
|
|
4112
|
+
if (this.codeLanguagePicker) {
|
|
4113
|
+
this.codeLanguagePicker.remove();
|
|
4114
|
+
this.codeLanguagePicker = null;
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
if (this.tableHandler) {
|
|
4118
|
+
this.tableHandler.remove();
|
|
4119
|
+
this.tableHandler = null;
|
|
4120
|
+
}
|
|
4121
|
+
|
|
3828
4122
|
this.selection = null;
|
|
3829
4123
|
|
|
3830
4124
|
document.removeEventListener("turbo:before-cache", this.#handleTurboBeforeCache);
|
|
@@ -3845,8 +4139,6 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
3845
4139
|
|
|
3846
4140
|
this.container.addEventListener("toggle", this.#handleToggle.bind(this));
|
|
3847
4141
|
this.container.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
3848
|
-
|
|
3849
|
-
this.#setTabIndexValues();
|
|
3850
4142
|
}
|
|
3851
4143
|
|
|
3852
4144
|
disconnectedCallback() {
|
|
@@ -3874,14 +4166,14 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
3874
4166
|
}
|
|
3875
4167
|
}
|
|
3876
4168
|
|
|
3877
|
-
#handleOpen(
|
|
3878
|
-
this.trigger = trigger;
|
|
4169
|
+
#handleOpen() {
|
|
3879
4170
|
this.#interactiveElements[0].focus();
|
|
3880
4171
|
this.#setupClickOutsideHandler();
|
|
4172
|
+
|
|
4173
|
+
this.#resetTabIndexValues();
|
|
3881
4174
|
}
|
|
3882
4175
|
|
|
3883
4176
|
#handleClose() {
|
|
3884
|
-
this.trigger = null;
|
|
3885
4177
|
this.#removeClickOutsideHandler();
|
|
3886
4178
|
this.editor.focus();
|
|
3887
4179
|
}
|
|
@@ -3911,16 +4203,20 @@ class ToolbarDropdown extends HTMLElement {
|
|
|
3911
4203
|
}
|
|
3912
4204
|
}
|
|
3913
4205
|
|
|
3914
|
-
async #
|
|
4206
|
+
async #resetTabIndexValues() {
|
|
3915
4207
|
await nextFrame();
|
|
3916
|
-
this.#
|
|
3917
|
-
element.setAttribute("tabindex", 0);
|
|
4208
|
+
this.#buttons.forEach((element, index) => {
|
|
4209
|
+
element.setAttribute("tabindex", index === 0 ? 0 : "-1");
|
|
3918
4210
|
});
|
|
3919
4211
|
}
|
|
3920
4212
|
|
|
3921
4213
|
get #interactiveElements() {
|
|
3922
4214
|
return Array.from(this.querySelectorAll("button, input"))
|
|
3923
4215
|
}
|
|
4216
|
+
|
|
4217
|
+
get #buttons() {
|
|
4218
|
+
return Array.from(this.querySelectorAll("button"))
|
|
4219
|
+
}
|
|
3924
4220
|
}
|
|
3925
4221
|
|
|
3926
4222
|
class LinkDropdown extends ToolbarDropdown {
|
|
@@ -4077,6 +4373,507 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4077
4373
|
|
|
4078
4374
|
customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
|
|
4079
4375
|
|
|
4376
|
+
class TableHandler extends HTMLElement {
|
|
4377
|
+
connectedCallback() {
|
|
4378
|
+
this.#setUpButtons();
|
|
4379
|
+
this.#monitorForTableSelection();
|
|
4380
|
+
this.#registerKeyboardShortcuts();
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4383
|
+
disconnectedCallback() {
|
|
4384
|
+
this.#unregisterKeyboardShortcuts();
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
get #editor() {
|
|
4388
|
+
return this.#editorElement.editor
|
|
4389
|
+
}
|
|
4390
|
+
|
|
4391
|
+
get #editorElement() {
|
|
4392
|
+
return this.closest("lexxy-editor")
|
|
4393
|
+
}
|
|
4394
|
+
|
|
4395
|
+
get #currentCell() {
|
|
4396
|
+
const selection = $getSelection();
|
|
4397
|
+
if (!$isRangeSelection(selection)) return null
|
|
4398
|
+
|
|
4399
|
+
const anchorNode = selection.anchor.getNode();
|
|
4400
|
+
return $getTableCellNodeFromLexicalNode(anchorNode)
|
|
4401
|
+
}
|
|
4402
|
+
|
|
4403
|
+
get #currentRow() {
|
|
4404
|
+
const currentCell = this.#currentCell;
|
|
4405
|
+
if (!currentCell) return 0
|
|
4406
|
+
return $getTableRowIndexFromTableCellNode(currentCell)
|
|
4407
|
+
}
|
|
4408
|
+
|
|
4409
|
+
get #currentColumn() {
|
|
4410
|
+
const currentCell = this.#currentCell;
|
|
4411
|
+
if (!currentCell) return 0
|
|
4412
|
+
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
get #tableHandlerButtons() {
|
|
4416
|
+
return Array.from(this.buttonsContainer.querySelectorAll("button, details > summary"))
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
#registerKeyboardShortcuts() {
|
|
4420
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleKeyDown, COMMAND_PRIORITY_HIGH);
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
#unregisterKeyboardShortcuts() {
|
|
4424
|
+
this.unregisterKeyboardShortcuts();
|
|
4425
|
+
}
|
|
4426
|
+
|
|
4427
|
+
#handleKeyDown = (event) => {
|
|
4428
|
+
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
4429
|
+
const firstButton = this.buttonsContainer?.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
4430
|
+
this.#setFocusStateOnSelectedCell();
|
|
4431
|
+
firstButton?.focus();
|
|
4432
|
+
} else if (event.key === "Escape") {
|
|
4433
|
+
this.#editor.getEditorState().read(() => {
|
|
4434
|
+
const cell = this.#currentCell;
|
|
4435
|
+
if (!cell) return
|
|
4436
|
+
|
|
4437
|
+
this.#editor.update(() => {
|
|
4438
|
+
cell.select();
|
|
4439
|
+
});
|
|
4440
|
+
});
|
|
4441
|
+
this.#closeMoreMenu();
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
|
|
4445
|
+
#handleTableHandlerKeydown = (event) => {
|
|
4446
|
+
if (event.key === "Escape") {
|
|
4447
|
+
this.#editor.focus();
|
|
4448
|
+
} else {
|
|
4449
|
+
handleRollingTabIndex(this.#tableHandlerButtons, event);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4453
|
+
#setUpButtons() {
|
|
4454
|
+
this.buttonsContainer = createElement("div", {
|
|
4455
|
+
className: "lexxy-table-handle-buttons"
|
|
4456
|
+
});
|
|
4457
|
+
|
|
4458
|
+
this.buttonsContainer.appendChild(this.#createRowButtonsContainer());
|
|
4459
|
+
this.buttonsContainer.appendChild(this.#createColumnButtonsContainer());
|
|
4460
|
+
|
|
4461
|
+
this.moreMenu = this.#createMoreMenu();
|
|
4462
|
+
this.buttonsContainer.appendChild(this.moreMenu);
|
|
4463
|
+
this.buttonsContainer.addEventListener("keydown", this.#handleTableHandlerKeydown);
|
|
4464
|
+
|
|
4465
|
+
this.#editorElement.appendChild(this.buttonsContainer);
|
|
4466
|
+
}
|
|
4467
|
+
|
|
4468
|
+
#showTableHandlerButtons() {
|
|
4469
|
+
this.buttonsContainer.style.display = "flex";
|
|
4470
|
+
this.#closeMoreMenu();
|
|
4471
|
+
|
|
4472
|
+
this.#updateRowColumnCount();
|
|
4473
|
+
this.#setTableFocusState(true);
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
|
+
#hideTableHandlerButtons() {
|
|
4477
|
+
this.buttonsContainer.style.display = "none";
|
|
4478
|
+
this.#closeMoreMenu();
|
|
4479
|
+
|
|
4480
|
+
this.#setTableFocusState(false);
|
|
4481
|
+
this.currentTableNode = null;
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
#updateButtonsPosition(tableNode) {
|
|
4485
|
+
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
4486
|
+
if (!tableElement) return
|
|
4487
|
+
|
|
4488
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
4489
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
4490
|
+
|
|
4491
|
+
const relativeTop = tableRect.top - editorRect.top;
|
|
4492
|
+
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
4493
|
+
this.buttonsContainer.style.top = `${relativeTop}px`;
|
|
4494
|
+
this.buttonsContainer.style.left = `${relativeCenter}px`;
|
|
4495
|
+
}
|
|
4496
|
+
|
|
4497
|
+
#updateRowColumnCount() {
|
|
4498
|
+
if (!this.currentTableNode) return
|
|
4499
|
+
|
|
4500
|
+
const tableElement = $getElementForTableNode(this.#editor, this.currentTableNode);
|
|
4501
|
+
if (!tableElement) return
|
|
4502
|
+
|
|
4503
|
+
const rowCount = tableElement.rows;
|
|
4504
|
+
const columnCount = tableElement.columns;
|
|
4505
|
+
|
|
4506
|
+
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
4507
|
+
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
#createButton(icon, label, onClick) {
|
|
4511
|
+
const button = createElement("button", {
|
|
4512
|
+
className: "lexxy-table-control__button",
|
|
4513
|
+
"aria-label": label,
|
|
4514
|
+
type: "button"
|
|
4515
|
+
});
|
|
4516
|
+
button.tabIndex = -1;
|
|
4517
|
+
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
4518
|
+
button.addEventListener("click", onClick.bind(this));
|
|
4519
|
+
|
|
4520
|
+
return button
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
#createRowButtonsContainer() {
|
|
4524
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
4525
|
+
|
|
4526
|
+
const plusButton = this.#createButton("+", "Add row", () => this.#insertTableRow("end"));
|
|
4527
|
+
const minusButton = this.#createButton("−", "Remove row", () => this.#deleteTableRow("end"));
|
|
4528
|
+
|
|
4529
|
+
this.rowCount = createElement("span");
|
|
4530
|
+
this.rowCount.textContent = "_ rows";
|
|
4531
|
+
|
|
4532
|
+
container.appendChild(minusButton);
|
|
4533
|
+
container.appendChild(this.rowCount);
|
|
4534
|
+
container.appendChild(plusButton);
|
|
4535
|
+
|
|
4536
|
+
return container
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
#createColumnButtonsContainer() {
|
|
4540
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
4541
|
+
|
|
4542
|
+
const plusButton = this.#createButton("+", "Add column", () => this.#insertTableColumn("end"));
|
|
4543
|
+
const minusButton = this.#createButton("−", "Remove column", () => this.#deleteTableColumn("end"));
|
|
4544
|
+
|
|
4545
|
+
this.columnCount = createElement("span");
|
|
4546
|
+
this.columnCount.textContent = "_ columns";
|
|
4547
|
+
|
|
4548
|
+
container.appendChild(minusButton);
|
|
4549
|
+
container.appendChild(this.columnCount);
|
|
4550
|
+
container.appendChild(plusButton);
|
|
4551
|
+
|
|
4552
|
+
return container
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
#createMoreMenu() {
|
|
4556
|
+
const container = createElement("details", {
|
|
4557
|
+
className: "lexxy-table-control lexxy-table-control__more-menu"
|
|
4558
|
+
});
|
|
4559
|
+
|
|
4560
|
+
container.tabIndex = -1;
|
|
4561
|
+
|
|
4562
|
+
const summary = createElement("summary", {}, "•••");
|
|
4563
|
+
container.appendChild(summary);
|
|
4564
|
+
|
|
4565
|
+
const details = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
4566
|
+
container.appendChild(details);
|
|
4567
|
+
|
|
4568
|
+
details.appendChild(this.#createRowSection());
|
|
4569
|
+
details.appendChild(this.#createColumnSection());
|
|
4570
|
+
details.appendChild(this.#createDeleteTableSection());
|
|
4571
|
+
|
|
4572
|
+
container.addEventListener("toggle", this.#handleMoreMenuToggle.bind(this));
|
|
4573
|
+
|
|
4574
|
+
return container
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
#createColumnSection() {
|
|
4578
|
+
const columnSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4579
|
+
|
|
4580
|
+
const columnButtons = [
|
|
4581
|
+
{ icon: this.#icon("add-column-before"), label: "Add column before", onClick: () => this.#insertTableColumn("left") },
|
|
4582
|
+
{ icon: this.#icon("add-column-after"), label: "Add column after", onClick: () => this.#insertTableColumn("right") },
|
|
4583
|
+
{ icon: this.#icon("remove-column"), label: "Remove column", onClick: this.#deleteTableColumn },
|
|
4584
|
+
{ icon: this.#icon("toggle-column-style"), label: "Toggle column style", onClick: this.#toggleColumnHeaderStyle },
|
|
4585
|
+
];
|
|
4586
|
+
|
|
4587
|
+
columnButtons.forEach(button => {
|
|
4588
|
+
const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
|
|
4589
|
+
columnSection.appendChild(buttonElement);
|
|
4590
|
+
});
|
|
4591
|
+
|
|
4592
|
+
return columnSection
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
#createRowSection() {
|
|
4596
|
+
const rowSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4597
|
+
|
|
4598
|
+
const rowButtons = [
|
|
4599
|
+
{ icon: this.#icon("add-row-above"), label: "Add row above", onClick: () => this.#insertTableRow("above") },
|
|
4600
|
+
{ icon: this.#icon("add-row-below"), label: "Add row below", onClick: () => this.#insertTableRow("below") },
|
|
4601
|
+
{ icon: this.#icon("remove-row"), label: "Remove row", onClick: this.#deleteTableRow },
|
|
4602
|
+
{ icon: this.#icon("toggle-row-style"), label: "Toggle row style", onClick: this.#toggleRowHeaderStyle }
|
|
4603
|
+
];
|
|
4604
|
+
|
|
4605
|
+
rowButtons.forEach(button => {
|
|
4606
|
+
const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
|
|
4607
|
+
rowSection.appendChild(buttonElement);
|
|
4608
|
+
});
|
|
4609
|
+
|
|
4610
|
+
return rowSection
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4613
|
+
#createDeleteTableSection() {
|
|
4614
|
+
const deleteSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4615
|
+
|
|
4616
|
+
const deleteButton = { icon: this.#icon("delete-table"), label: "Delete table", onClick: this.#deleteTable };
|
|
4617
|
+
|
|
4618
|
+
const buttonElement = this.#createButton(deleteButton.icon, deleteButton.label, deleteButton.onClick);
|
|
4619
|
+
deleteSection.appendChild(buttonElement);
|
|
4620
|
+
|
|
4621
|
+
return deleteSection
|
|
4622
|
+
}
|
|
4623
|
+
|
|
4624
|
+
#handleMoreMenuToggle() {
|
|
4625
|
+
if (this.moreMenu.open) {
|
|
4626
|
+
this.#setFocusStateOnSelectedCell();
|
|
4627
|
+
} else {
|
|
4628
|
+
this.#removeFocusStateFromSelectedCell();
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
#closeMoreMenu() {
|
|
4633
|
+
this.#removeFocusStateFromSelectedCell();
|
|
4634
|
+
this.moreMenu.removeAttribute("open");
|
|
4635
|
+
}
|
|
4636
|
+
|
|
4637
|
+
#monitorForTableSelection() {
|
|
4638
|
+
this.#editor.registerUpdateListener(() => {
|
|
4639
|
+
this.#editor.getEditorState().read(() => {
|
|
4640
|
+
const selection = $getSelection();
|
|
4641
|
+
if (!$isRangeSelection(selection)) return
|
|
4642
|
+
|
|
4643
|
+
const anchorNode = selection.anchor.getNode();
|
|
4644
|
+
const tableNode = $findTableNode(anchorNode);
|
|
4645
|
+
|
|
4646
|
+
if (tableNode) {
|
|
4647
|
+
this.#tableCellWasSelected(tableNode);
|
|
4648
|
+
} else {
|
|
4649
|
+
this.#hideTableHandlerButtons();
|
|
4650
|
+
}
|
|
4651
|
+
});
|
|
4652
|
+
});
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
#setTableFocusState(focused) {
|
|
4656
|
+
this.#editorElement.querySelector("div.node--selected:has(table)")?.classList.remove("node--selected");
|
|
4657
|
+
|
|
4658
|
+
if (focused && this.currentTableNode) {
|
|
4659
|
+
const tableParent = this.#editor.getElementByKey(this.currentTableNode.getKey());
|
|
4660
|
+
if (!tableParent) return
|
|
4661
|
+
tableParent.classList.add("node--selected");
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
|
|
4665
|
+
#tableCellWasSelected(tableNode) {
|
|
4666
|
+
this.currentTableNode = tableNode;
|
|
4667
|
+
this.#updateButtonsPosition(tableNode);
|
|
4668
|
+
this.#showTableHandlerButtons();
|
|
4669
|
+
}
|
|
4670
|
+
|
|
4671
|
+
#setFocusStateOnSelectedCell() {
|
|
4672
|
+
this.#editor.getEditorState().read(() => {
|
|
4673
|
+
const currentCell = this.#currentCell;
|
|
4674
|
+
if (!currentCell) return
|
|
4675
|
+
|
|
4676
|
+
const cellElement = this.#editor.getElementByKey(currentCell.getKey());
|
|
4677
|
+
if (!cellElement) return
|
|
4678
|
+
|
|
4679
|
+
cellElement.classList.add("table-cell--selected");
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
|
|
4683
|
+
#removeFocusStateFromSelectedCell() {
|
|
4684
|
+
this.#editorElement.querySelector(".table-cell--selected")?.classList.remove("table-cell--selected");
|
|
4685
|
+
}
|
|
4686
|
+
|
|
4687
|
+
#selectLastTableCell() {
|
|
4688
|
+
if (!this.currentTableNode) return
|
|
4689
|
+
|
|
4690
|
+
const last = this.currentTableNode.getLastChild().getLastChild();
|
|
4691
|
+
if (!$isTableCellNode(last)) return
|
|
4692
|
+
|
|
4693
|
+
last.selectEnd();
|
|
4694
|
+
}
|
|
4695
|
+
|
|
4696
|
+
#deleteTable() {
|
|
4697
|
+
this.#editor.dispatchCommand("deleteTable");
|
|
4698
|
+
|
|
4699
|
+
this.#closeMoreMenu();
|
|
4700
|
+
this.#updateRowColumnCount();
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
#insertTableRow(direction) {
|
|
4704
|
+
this.#executeTableCommand("insert", "row", direction);
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
#insertTableColumn(direction) {
|
|
4708
|
+
this.#executeTableCommand("insert", "column", direction);
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
#deleteTableRow(direction) {
|
|
4712
|
+
this.#executeTableCommand("delete", "row", direction);
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
#deleteTableColumn(direction) {
|
|
4716
|
+
this.#executeTableCommand("delete", "column", direction);
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
#executeTableCommand(action = "insert", childType = "row", direction) {
|
|
4720
|
+
this.#editor.update(() => {
|
|
4721
|
+
const currentCell = this.#currentCell;
|
|
4722
|
+
if (!currentCell) return
|
|
4723
|
+
|
|
4724
|
+
if (direction === "end") {
|
|
4725
|
+
this.#selectLastTableCell();
|
|
4726
|
+
}
|
|
4727
|
+
|
|
4728
|
+
this.#dispatchTableCommand(action, childType, direction);
|
|
4729
|
+
|
|
4730
|
+
if (currentCell.isAttached()) {
|
|
4731
|
+
currentCell.selectEnd();
|
|
4732
|
+
}
|
|
4733
|
+
});
|
|
4734
|
+
|
|
4735
|
+
this.#closeMoreMenu();
|
|
4736
|
+
this.#updateRowColumnCount();
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4739
|
+
#dispatchTableCommand(action, childType, direction) {
|
|
4740
|
+
switch (action) {
|
|
4741
|
+
case "insert":
|
|
4742
|
+
switch (childType) {
|
|
4743
|
+
case "row":
|
|
4744
|
+
if (direction === "above") {
|
|
4745
|
+
this.#editor.dispatchCommand("insertTableRowAbove");
|
|
4746
|
+
} else {
|
|
4747
|
+
this.#editor.dispatchCommand("insertTableRowBelow");
|
|
4748
|
+
}
|
|
4749
|
+
break
|
|
4750
|
+
case "column":
|
|
4751
|
+
if (direction === "left") {
|
|
4752
|
+
this.#editor.dispatchCommand("insertTableColumnBefore");
|
|
4753
|
+
} else {
|
|
4754
|
+
this.#editor.dispatchCommand("insertTableColumnAfter");
|
|
4755
|
+
}
|
|
4756
|
+
break
|
|
4757
|
+
}
|
|
4758
|
+
break
|
|
4759
|
+
case "delete":
|
|
4760
|
+
switch (childType) {
|
|
4761
|
+
case "row":
|
|
4762
|
+
this.#editor.dispatchCommand("deleteTableRow");
|
|
4763
|
+
break
|
|
4764
|
+
case "column":
|
|
4765
|
+
this.#editor.dispatchCommand("deleteTableColumn");
|
|
4766
|
+
break
|
|
4767
|
+
}
|
|
4768
|
+
break
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
#toggleRowHeaderStyle() {
|
|
4773
|
+
this.#editor.update(() => {
|
|
4774
|
+
const rows = this.currentTableNode.getChildren();
|
|
4775
|
+
|
|
4776
|
+
const row = rows[this.#currentRow];
|
|
4777
|
+
if (!row) return
|
|
4778
|
+
|
|
4779
|
+
const cells = row.getChildren();
|
|
4780
|
+
const firstCell = $getTableCellNodeFromLexicalNode(cells[0]);
|
|
4781
|
+
if (!firstCell) return
|
|
4782
|
+
|
|
4783
|
+
const currentStyle = firstCell.getHeaderStyles();
|
|
4784
|
+
const newStyle = currentStyle ^ TableCellHeaderStates.ROW;
|
|
4785
|
+
|
|
4786
|
+
cells.forEach(cell => {
|
|
4787
|
+
this.#setHeaderStyle(cell, newStyle, TableCellHeaderStates.ROW);
|
|
4788
|
+
});
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
#toggleColumnHeaderStyle() {
|
|
4793
|
+
this.#editor.update(() => {
|
|
4794
|
+
const rows = this.currentTableNode.getChildren();
|
|
4795
|
+
|
|
4796
|
+
const row = rows[this.#currentRow];
|
|
4797
|
+
if (!row) return
|
|
4798
|
+
|
|
4799
|
+
const cells = row.getChildren();
|
|
4800
|
+
const selectedCell = $getTableCellNodeFromLexicalNode(cells[this.#currentColumn]);
|
|
4801
|
+
if (!selectedCell) return
|
|
4802
|
+
|
|
4803
|
+
const currentStyle = selectedCell.getHeaderStyles();
|
|
4804
|
+
const newStyle = currentStyle ^ TableCellHeaderStates.COLUMN;
|
|
4805
|
+
|
|
4806
|
+
rows.forEach(row => {
|
|
4807
|
+
const cell = row.getChildren()[this.#currentColumn];
|
|
4808
|
+
if (!cell) return
|
|
4809
|
+
this.#setHeaderStyle(cell, newStyle, TableCellHeaderStates.COLUMN);
|
|
4810
|
+
});
|
|
4811
|
+
});
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
#setHeaderStyle(cell, newStyle, headerState) {
|
|
4815
|
+
const tableCellNode = $getTableCellNodeFromLexicalNode(cell);
|
|
4816
|
+
|
|
4817
|
+
if (tableCellNode) {
|
|
4818
|
+
tableCellNode.setHeaderStyles(newStyle, headerState);
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
#icon(name) {
|
|
4823
|
+
const icons =
|
|
4824
|
+
{
|
|
4825
|
+
"add-row-above":
|
|
4826
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4827
|
+
<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"/>
|
|
4828
|
+
</svg>`,
|
|
4829
|
+
|
|
4830
|
+
"add-row-below":
|
|
4831
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4832
|
+
<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"/>
|
|
4833
|
+
</svg>`,
|
|
4834
|
+
|
|
4835
|
+
"remove-row":
|
|
4836
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4837
|
+
<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"/>
|
|
4838
|
+
</svg>`,
|
|
4839
|
+
|
|
4840
|
+
"toggle-row-style":
|
|
4841
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4842
|
+
<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"/>
|
|
4843
|
+
</svg>`,
|
|
4844
|
+
|
|
4845
|
+
"add-column-before":
|
|
4846
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4847
|
+
<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"/>
|
|
4848
|
+
</svg>`,
|
|
4849
|
+
|
|
4850
|
+
"add-column-after":
|
|
4851
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4852
|
+
<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"/>
|
|
4853
|
+
</svg>`,
|
|
4854
|
+
|
|
4855
|
+
"remove-column":
|
|
4856
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4857
|
+
<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"/>
|
|
4858
|
+
</svg>`,
|
|
4859
|
+
|
|
4860
|
+
"toggle-column-style":
|
|
4861
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4862
|
+
<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"/>
|
|
4863
|
+
</svg>`,
|
|
4864
|
+
|
|
4865
|
+
"delete-table":
|
|
4866
|
+
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
4867
|
+
<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"/>
|
|
4868
|
+
</svg>`
|
|
4869
|
+
};
|
|
4870
|
+
|
|
4871
|
+
return icons[name]
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
|
|
4875
|
+
customElements.define("lexxy-table-handler", TableHandler);
|
|
4876
|
+
|
|
4080
4877
|
class BaseSource {
|
|
4081
4878
|
// Template method to override
|
|
4082
4879
|
async buildListItems(filter = "") {
|