@37signals/lexxy 0.1.23-beta → 0.1.25-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ A modern rich text editor for Rails.
|
|
|
23
23
|
Add this line to your application's Gemfile:
|
|
24
24
|
|
|
25
25
|
```ruby
|
|
26
|
-
gem 'lexxy', '~> 0.1.
|
|
26
|
+
gem 'lexxy', '~> 0.1.23.beta' # Need to specify the version since it's a pre-release
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
And then execute:
|
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
6
|
-
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
7
|
-
import { $isLinkNode, $createAutoLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
|
|
1
|
+
import Prism from 'prismjs';
|
|
2
|
+
import 'prismjs/components/prism-clike';
|
|
3
|
+
import 'prismjs/components/prism-markup';
|
|
4
|
+
import 'prismjs/components/prism-markup-templating';
|
|
8
5
|
import 'prismjs/components/prism-ruby';
|
|
9
6
|
import 'prismjs/components/prism-php';
|
|
10
7
|
import 'prismjs/components/prism-go';
|
|
11
8
|
import 'prismjs/components/prism-bash';
|
|
12
9
|
import 'prismjs/components/prism-json';
|
|
13
10
|
import 'prismjs/components/prism-diff';
|
|
11
|
+
import DOMPurify from 'dompurify';
|
|
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, SKIP_DOM_SELECTION_TAG, 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, $createListNode, ListNode, ListItemNode, registerList } from '@lexical/list';
|
|
15
|
+
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
16
|
+
import { $isCodeNode, CodeNode, normalizeCodeLang, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
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';
|
|
14
19
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
15
20
|
import { registerMarkdownShortcuts, TRANSFORMERS } from '@lexical/markdown';
|
|
16
21
|
import { createEmptyHistoryState, registerHistory } from '@lexical/history';
|
|
17
22
|
import { DirectUpload } from '@rails/activestorage';
|
|
18
23
|
import { marked } from 'marked';
|
|
19
24
|
|
|
25
|
+
// Configure Prism for manual highlighting mode
|
|
26
|
+
// This must be set before importing prismjs
|
|
27
|
+
window.Prism = window.Prism || {};
|
|
28
|
+
window.Prism.manual = true;
|
|
29
|
+
|
|
20
30
|
const ALLOWED_HTML_TAGS = [ "a", "action-text-attachment", "b", "blockquote", "br", "code", "em",
|
|
21
|
-
"figcaption", "figure", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "mark", "ol", "p", "pre", "q", "s", "strong", "ul" ];
|
|
31
|
+
"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" ];
|
|
22
32
|
|
|
23
33
|
const ALLOWED_HTML_ATTRIBUTES = [ "alt", "caption", "class", "content", "content-type", "contenteditable",
|
|
24
34
|
"data-direct-upload-id", "data-sgid", "filename", "filesize", "height", "href", "presentation",
|
|
@@ -285,6 +295,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
285
295
|
const isInCode = $isCodeNode(topLevelElement) || selection.hasFormat("code");
|
|
286
296
|
const isInList = this.#isInList(anchorNode);
|
|
287
297
|
const listType = getListType(anchorNode);
|
|
298
|
+
const isInTable = $getTableCellNodeFromLexicalNode(anchorNode) !== null;
|
|
288
299
|
|
|
289
300
|
this.#setButtonPressed("bold", isBold);
|
|
290
301
|
this.#setButtonPressed("italic", isItalic);
|
|
@@ -296,6 +307,7 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
296
307
|
this.#setButtonPressed("code", isInCode);
|
|
297
308
|
this.#setButtonPressed("unordered-list", isInList && listType === "bullet");
|
|
298
309
|
this.#setButtonPressed("ordered-list", isInList && listType === "number");
|
|
310
|
+
this.#setButtonPressed("table", isInTable);
|
|
299
311
|
|
|
300
312
|
this.#updateUndoRedoButtonStates();
|
|
301
313
|
}
|
|
@@ -467,6 +479,10 @@ class LexicalToolbarElement extends HTMLElement {
|
|
|
467
479
|
<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>
|
|
468
480
|
</button>
|
|
469
481
|
|
|
482
|
+
<button class="lexxy-editor__toolbar-button" type="button" name="table" data-command="insertTable" title="Insert a table">
|
|
483
|
+
<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>
|
|
484
|
+
</button>
|
|
485
|
+
|
|
470
486
|
<button class="lexxy-editor__toolbar-button" type="button" name="divider" data-command="insertHorizontalDivider" title="Insert a divider">
|
|
471
487
|
<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>
|
|
472
488
|
</button>
|
|
@@ -499,6 +515,10 @@ var theme = {
|
|
|
499
515
|
underline: "lexxy-content__underline",
|
|
500
516
|
highlight: "lexxy-content__highlight"
|
|
501
517
|
},
|
|
518
|
+
tableCellHeader: "lexxy-content__table-cell--header",
|
|
519
|
+
tableCellSelected: "lexxy-content__table-cell--selected",
|
|
520
|
+
tableSelection: "lexxy-content__table--selection",
|
|
521
|
+
tableScrollableWrapper: "lexxy-content__table-wrapper",
|
|
502
522
|
list: {
|
|
503
523
|
nested: {
|
|
504
524
|
listitem: "lexxy-nested-listitem",
|
|
@@ -558,7 +578,7 @@ var theme = {
|
|
|
558
578
|
}
|
|
559
579
|
};
|
|
560
580
|
|
|
561
|
-
function createElement(name, properties) {
|
|
581
|
+
function createElement(name, properties, content = "") {
|
|
562
582
|
const element = document.createElement(name);
|
|
563
583
|
for (const [ key, value ] of Object.entries(properties || {})) {
|
|
564
584
|
if (key in element) {
|
|
@@ -567,6 +587,9 @@ function createElement(name, properties) {
|
|
|
567
587
|
element.setAttribute(key, value);
|
|
568
588
|
}
|
|
569
589
|
}
|
|
590
|
+
if (content) {
|
|
591
|
+
element.innerHTML = content;
|
|
592
|
+
}
|
|
570
593
|
return element
|
|
571
594
|
}
|
|
572
595
|
|
|
@@ -720,6 +743,10 @@ class ActionTextAttachmentNode extends DecoratorNode {
|
|
|
720
743
|
return true
|
|
721
744
|
}
|
|
722
745
|
|
|
746
|
+
getTextContent() {
|
|
747
|
+
return `[${ this.caption || this.fileName }]\n\n`
|
|
748
|
+
}
|
|
749
|
+
|
|
723
750
|
isInline() {
|
|
724
751
|
return false
|
|
725
752
|
}
|
|
@@ -878,6 +905,11 @@ class ActionTextAttachmentUploadNode extends ActionTextAttachmentNode {
|
|
|
878
905
|
return new ActionTextAttachmentUploadNode({ ...serializedNode })
|
|
879
906
|
}
|
|
880
907
|
|
|
908
|
+
// Should never run since this is a transient node. Defined to remove console warning.
|
|
909
|
+
static importDOM() {
|
|
910
|
+
return null
|
|
911
|
+
}
|
|
912
|
+
|
|
881
913
|
constructor({ file, uploadUrl, blobUrlTemplate, editor, progress }, key) {
|
|
882
914
|
super({ contentType: file.type }, key);
|
|
883
915
|
this.file = file;
|
|
@@ -1081,6 +1113,10 @@ class HorizontalDividerNode extends DecoratorNode {
|
|
|
1081
1113
|
return true
|
|
1082
1114
|
}
|
|
1083
1115
|
|
|
1116
|
+
getTextContent() {
|
|
1117
|
+
return "┄\n\n"
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1084
1120
|
isInline() {
|
|
1085
1121
|
return false
|
|
1086
1122
|
}
|
|
@@ -1117,6 +1153,16 @@ const COMMANDS = [
|
|
|
1117
1153
|
"insertCodeBlock",
|
|
1118
1154
|
"insertHorizontalDivider",
|
|
1119
1155
|
"uploadAttachments",
|
|
1156
|
+
|
|
1157
|
+
"insertTable",
|
|
1158
|
+
"insertTableRowAbove",
|
|
1159
|
+
"insertTableRowBelow",
|
|
1160
|
+
"insertTableColumnAfter",
|
|
1161
|
+
"insertTableColumnBefore",
|
|
1162
|
+
"deleteTableRow",
|
|
1163
|
+
"deleteTableColumn",
|
|
1164
|
+
"deleteTable",
|
|
1165
|
+
|
|
1120
1166
|
"undo",
|
|
1121
1167
|
"redo"
|
|
1122
1168
|
];
|
|
@@ -1279,6 +1325,45 @@ class CommandDispatcher {
|
|
|
1279
1325
|
setTimeout(() => input.remove(), 1000);
|
|
1280
1326
|
}
|
|
1281
1327
|
|
|
1328
|
+
dispatchInsertTable() {
|
|
1329
|
+
this.editor.dispatchCommand(INSERT_TABLE_COMMAND, { "rows": 3, "columns": 3, "includeHeaders": true });
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
dispatchInsertTableRowBelow() {
|
|
1333
|
+
$insertTableRowAtSelection(true);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
dispatchInsertTableRowAbove() {
|
|
1337
|
+
$insertTableRowAtSelection(false);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
dispatchInsertTableColumnAfter() {
|
|
1341
|
+
$insertTableColumnAtSelection(true);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
dispatchInsertTableColumnBefore() {
|
|
1345
|
+
$insertTableColumnAtSelection(false);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
dispatchDeleteTableRow() {
|
|
1349
|
+
$deleteTableRowAtSelection();
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
dispatchDeleteTableColumn() {
|
|
1353
|
+
$deleteTableColumnAtSelection();
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
dispatchDeleteTable() {
|
|
1357
|
+
this.editor.update(() => {
|
|
1358
|
+
const selection = $getSelection();
|
|
1359
|
+
if (!$isRangeSelection(selection)) return
|
|
1360
|
+
|
|
1361
|
+
const anchorNode = selection.anchor.getNode();
|
|
1362
|
+
const tableNode = $findTableNode(anchorNode);
|
|
1363
|
+
tableNode.remove();
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1282
1367
|
dispatchUndo() {
|
|
1283
1368
|
this.editor.dispatchCommand(UNDO_COMMAND, undefined);
|
|
1284
1369
|
}
|
|
@@ -2118,6 +2203,10 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
2118
2203
|
return true
|
|
2119
2204
|
}
|
|
2120
2205
|
|
|
2206
|
+
getTextContent() {
|
|
2207
|
+
return this.createDOM().textContent.trim() || `[${this.contentType}]`
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2121
2210
|
isInline() {
|
|
2122
2211
|
return true
|
|
2123
2212
|
}
|
|
@@ -3179,7 +3268,7 @@ class Clipboard {
|
|
|
3179
3268
|
|
|
3180
3269
|
if (!clipboardData) return false
|
|
3181
3270
|
|
|
3182
|
-
if (this.#
|
|
3271
|
+
if (this.#isPlainTextOrURLPasted(clipboardData) && !this.#isPastingIntoCodeBlock()) {
|
|
3183
3272
|
this.#pastePlainText(clipboardData);
|
|
3184
3273
|
event.preventDefault();
|
|
3185
3274
|
return true
|
|
@@ -3188,11 +3277,21 @@ class Clipboard {
|
|
|
3188
3277
|
this.#handlePastedFiles(clipboardData);
|
|
3189
3278
|
}
|
|
3190
3279
|
|
|
3280
|
+
#isPlainTextOrURLPasted(clipboardData) {
|
|
3281
|
+
return this.#isOnlyPlainTextPasted(clipboardData) || this.#isOnlyURLPasted(clipboardData)
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3191
3284
|
#isOnlyPlainTextPasted(clipboardData) {
|
|
3192
3285
|
const types = Array.from(clipboardData.types);
|
|
3193
3286
|
return types.length === 1 && types[0] === "text/plain"
|
|
3194
3287
|
}
|
|
3195
3288
|
|
|
3289
|
+
#isOnlyURLPasted(clipboardData) {
|
|
3290
|
+
// Safari URLs are copied as a text/plain + text/uri-list object
|
|
3291
|
+
const types = Array.from(clipboardData.types);
|
|
3292
|
+
return types.length === 2 && types.includes("text/uri-list") && types.includes("text/plain")
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3196
3295
|
#isPastingIntoCodeBlock() {
|
|
3197
3296
|
let result = false;
|
|
3198
3297
|
|
|
@@ -3408,7 +3507,7 @@ function applyLanguage(conversionOutput, element) {
|
|
|
3408
3507
|
|
|
3409
3508
|
class LexicalEditorElement extends HTMLElement {
|
|
3410
3509
|
static formAssociated = true
|
|
3411
|
-
static debug =
|
|
3510
|
+
static debug = false
|
|
3412
3511
|
static commands = [ "bold", "italic", "strikethrough" ]
|
|
3413
3512
|
|
|
3414
3513
|
static observedAttributes = [ "connected", "required" ]
|
|
@@ -3483,6 +3582,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3483
3582
|
return this.dataset.blobUrlTemplate
|
|
3484
3583
|
}
|
|
3485
3584
|
|
|
3585
|
+
get isEmpty() {
|
|
3586
|
+
return [ "<p><br></p>", "<p></p>", "" ].includes(this.value.trim())
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
get isBlank() {
|
|
3590
|
+
return this.isEmpty || this.toString().match(/^\s*$/g) !== null
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
get hasOpenPrompt() {
|
|
3594
|
+
return this.querySelector(".lexxy-prompt-menu.lexxy-prompt-menu--visible") !== null
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3486
3597
|
get isSingleLineMode() {
|
|
3487
3598
|
return this.hasAttribute("single-line")
|
|
3488
3599
|
}
|
|
@@ -3526,6 +3637,16 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3526
3637
|
});
|
|
3527
3638
|
}
|
|
3528
3639
|
|
|
3640
|
+
toString() {
|
|
3641
|
+
if (!this.cachedStringValue) {
|
|
3642
|
+
this.editor?.getEditorState().read(() => {
|
|
3643
|
+
this.cachedStringValue = $getRoot().getTextContent();
|
|
3644
|
+
});
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3647
|
+
return this.cachedStringValue
|
|
3648
|
+
}
|
|
3649
|
+
|
|
3529
3650
|
#parseHtmlIntoLexicalNodes(html) {
|
|
3530
3651
|
if (!html) html = "<p></p>";
|
|
3531
3652
|
const nodes = $generateNodesFromDOM(this.editor, parseHtml(`<div>${html}</div>`));
|
|
@@ -3548,6 +3669,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3548
3669
|
this.#listenForInvalidatedNodes();
|
|
3549
3670
|
this.#handleEnter();
|
|
3550
3671
|
this.#handleFocus();
|
|
3672
|
+
this.#handleTables();
|
|
3551
3673
|
this.#attachDebugHooks();
|
|
3552
3674
|
this.#attachToolbar();
|
|
3553
3675
|
this.#loadInitialValue();
|
|
@@ -3584,6 +3706,9 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3584
3706
|
LinkNode,
|
|
3585
3707
|
AutoLinkNode,
|
|
3586
3708
|
HorizontalDividerNode,
|
|
3709
|
+
TableNode,
|
|
3710
|
+
TableCellNode,
|
|
3711
|
+
TableRowNode,
|
|
3587
3712
|
|
|
3588
3713
|
CustomActionTextAttachmentNode,
|
|
3589
3714
|
];
|
|
@@ -3631,7 +3756,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3631
3756
|
|
|
3632
3757
|
this.internals.setFormValue(html);
|
|
3633
3758
|
this._internalFormValue = html;
|
|
3634
|
-
this.#validationTextArea.value = this
|
|
3759
|
+
this.#validationTextArea.value = this.isEmpty ? "" : html;
|
|
3635
3760
|
|
|
3636
3761
|
if (changed) {
|
|
3637
3762
|
dispatch(this, "lexxy:change");
|
|
@@ -3657,13 +3782,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3657
3782
|
|
|
3658
3783
|
#synchronizeWithChanges() {
|
|
3659
3784
|
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
3660
|
-
this
|
|
3785
|
+
this.#clearCachedValues();
|
|
3661
3786
|
this.#internalFormValue = this.value;
|
|
3662
3787
|
this.#toggleEmptyStatus();
|
|
3663
3788
|
this.#setValidity();
|
|
3664
3789
|
}));
|
|
3665
3790
|
}
|
|
3666
3791
|
|
|
3792
|
+
#clearCachedValues() {
|
|
3793
|
+
this.cachedValue = null;
|
|
3794
|
+
this.cachedStringValue = null;
|
|
3795
|
+
}
|
|
3796
|
+
|
|
3667
3797
|
#addUnregisterHandler(handler) {
|
|
3668
3798
|
this.unregisterHandlers = this.unregisterHandlers || [];
|
|
3669
3799
|
this.unregisterHandlers.push(handler);
|
|
@@ -3681,13 +3811,21 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3681
3811
|
this.historyState = createEmptyHistoryState();
|
|
3682
3812
|
registerHistory(this.editor, this.historyState, 20);
|
|
3683
3813
|
registerList(this.editor);
|
|
3814
|
+
this.#registerTableComponents();
|
|
3684
3815
|
this.#registerCodeHiglightingComponents();
|
|
3685
3816
|
registerMarkdownShortcuts(this.editor, TRANSFORMERS);
|
|
3686
3817
|
}
|
|
3687
3818
|
|
|
3819
|
+
#registerTableComponents() {
|
|
3820
|
+
registerTablePlugin(this.editor);
|
|
3821
|
+
this.tableHandler = createElement("lexxy-table-handler");
|
|
3822
|
+
this.append(this.tableHandler);
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3688
3825
|
#registerCodeHiglightingComponents() {
|
|
3689
3826
|
registerCodeHighlighting(this.editor);
|
|
3690
|
-
this.
|
|
3827
|
+
this.codeLanguagePicker = createElement("lexxy-code-language-picker");
|
|
3828
|
+
this.append(this.codeLanguagePicker);
|
|
3691
3829
|
}
|
|
3692
3830
|
|
|
3693
3831
|
#listenForInvalidatedNodes() {
|
|
@@ -3736,12 +3874,18 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3736
3874
|
this.editor.registerCommand(FOCUS_COMMAND, () => { dispatch(this, "lexxy:focus"); }, COMMAND_PRIORITY_NORMAL);
|
|
3737
3875
|
}
|
|
3738
3876
|
|
|
3877
|
+
#handleTables() {
|
|
3878
|
+
this.removeTableSelectionObserver = registerTableSelectionObserver(this.editor, true);
|
|
3879
|
+
setScrollableTablesActive(this.editor, true);
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3739
3882
|
#attachDebugHooks() {
|
|
3740
3883
|
if (!LexicalEditorElement.debug) return
|
|
3741
3884
|
|
|
3742
3885
|
this.#addUnregisterHandler(this.editor.registerUpdateListener(({ editorState }) => {
|
|
3743
3886
|
editorState.read(() => {
|
|
3744
|
-
console.debug("HTML: ", this.value);
|
|
3887
|
+
console.debug("HTML: ", this.value, "String:", this.toString());
|
|
3888
|
+
console.debug("empty", this.isEmpty, "blank", this.isBlank);
|
|
3745
3889
|
});
|
|
3746
3890
|
}));
|
|
3747
3891
|
}
|
|
@@ -3770,11 +3914,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3770
3914
|
}
|
|
3771
3915
|
|
|
3772
3916
|
#toggleEmptyStatus() {
|
|
3773
|
-
this.classList.toggle("lexxy-editor--empty", this
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
get #isEmpty() {
|
|
3777
|
-
return [ "<p><br></p>", "<p></p>", "" ].includes(this.value.trim())
|
|
3917
|
+
this.classList.toggle("lexxy-editor--empty", this.isEmpty);
|
|
3778
3918
|
}
|
|
3779
3919
|
|
|
3780
3920
|
#setValidity() {
|
|
@@ -3801,6 +3941,16 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
3801
3941
|
this.toolbar = null;
|
|
3802
3942
|
}
|
|
3803
3943
|
|
|
3944
|
+
if (this.codeLanguagePicker) {
|
|
3945
|
+
this.codeLanguagePicker.remove();
|
|
3946
|
+
this.codeLanguagePicker = null;
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
if (this.tableHandler) {
|
|
3950
|
+
this.tableHandler.remove();
|
|
3951
|
+
this.tableHandler = null;
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3804
3954
|
this.selection = null;
|
|
3805
3955
|
|
|
3806
3956
|
document.removeEventListener("turbo:before-cache", this.#handleTurboBeforeCache);
|
|
@@ -4053,6 +4203,494 @@ class HighlightDropdown extends ToolbarDropdown {
|
|
|
4053
4203
|
|
|
4054
4204
|
customElements.define("lexxy-highlight-dropdown", HighlightDropdown);
|
|
4055
4205
|
|
|
4206
|
+
class TableHandler extends HTMLElement {
|
|
4207
|
+
connectedCallback() {
|
|
4208
|
+
this.#setUpButtons();
|
|
4209
|
+
this.#monitorForTableSelection();
|
|
4210
|
+
this.#registerKeyboardShortcuts();
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
disconnectedCallback() {
|
|
4214
|
+
this.#unregisterKeyboardShortcuts();
|
|
4215
|
+
}
|
|
4216
|
+
|
|
4217
|
+
get #editor() {
|
|
4218
|
+
return this.#editorElement.editor
|
|
4219
|
+
}
|
|
4220
|
+
|
|
4221
|
+
get #editorElement() {
|
|
4222
|
+
return this.closest("lexxy-editor")
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
get #currentCell() {
|
|
4226
|
+
const selection = $getSelection();
|
|
4227
|
+
if (!$isRangeSelection(selection)) return null
|
|
4228
|
+
|
|
4229
|
+
const anchorNode = selection.anchor.getNode();
|
|
4230
|
+
return $getTableCellNodeFromLexicalNode(anchorNode)
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
get #currentRow() {
|
|
4234
|
+
const currentCell = this.#currentCell;
|
|
4235
|
+
if (!currentCell) return 0
|
|
4236
|
+
return $getTableRowIndexFromTableCellNode(currentCell)
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
get #currentColumn() {
|
|
4240
|
+
const currentCell = this.#currentCell;
|
|
4241
|
+
if (!currentCell) return 0
|
|
4242
|
+
return $getTableColumnIndexFromTableCellNode(currentCell)
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
#registerKeyboardShortcuts() {
|
|
4246
|
+
this.unregisterKeyboardShortcuts = this.#editor.registerCommand(KEY_DOWN_COMMAND, this.#handleKeyDown.bind(this), COMMAND_PRIORITY_HIGH);
|
|
4247
|
+
}
|
|
4248
|
+
|
|
4249
|
+
#unregisterKeyboardShortcuts() {
|
|
4250
|
+
this.unregisterKeyboardShortcuts();
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
#handleKeyDown(event) {
|
|
4254
|
+
if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === "F10") {
|
|
4255
|
+
const firstButton = this.buttonsContainer?.querySelector("button, [tabindex]:not([tabindex='-1'])");
|
|
4256
|
+
this.#setFocusStateOnSelectedCell();
|
|
4257
|
+
firstButton?.focus();
|
|
4258
|
+
} else if (event.key === "Escape") {
|
|
4259
|
+
this.#editor.getEditorState().read(() => {
|
|
4260
|
+
const cell = this.#currentCell;
|
|
4261
|
+
if (!cell) return
|
|
4262
|
+
|
|
4263
|
+
this.#editor.update(() => {
|
|
4264
|
+
cell.select();
|
|
4265
|
+
});
|
|
4266
|
+
});
|
|
4267
|
+
this.#closeMoreMenu();
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
|
|
4271
|
+
#setUpButtons() {
|
|
4272
|
+
this.buttonsContainer = createElement("div", {
|
|
4273
|
+
className: "lexxy-table-handle-buttons"
|
|
4274
|
+
});
|
|
4275
|
+
|
|
4276
|
+
this.buttonsContainer.appendChild(this.#createRowButtonsContainer());
|
|
4277
|
+
this.buttonsContainer.appendChild(this.#createColumnButtonsContainer());
|
|
4278
|
+
|
|
4279
|
+
this.moreMenu = this.#createMoreMenu();
|
|
4280
|
+
this.buttonsContainer.appendChild(this.moreMenu);
|
|
4281
|
+
|
|
4282
|
+
this.#editorElement.appendChild(this.buttonsContainer);
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
#showTableHandlerButtons() {
|
|
4286
|
+
this.buttonsContainer.style.display = "flex";
|
|
4287
|
+
this.#closeMoreMenu();
|
|
4288
|
+
|
|
4289
|
+
this.#updateRowColumnCount();
|
|
4290
|
+
this.#setTableFocusState(true);
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
#hideTableHandlerButtons() {
|
|
4294
|
+
this.buttonsContainer.style.display = "none";
|
|
4295
|
+
this.#closeMoreMenu();
|
|
4296
|
+
|
|
4297
|
+
this.#setTableFocusState(false);
|
|
4298
|
+
this.currentTableNode = null;
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
#updateButtonsPosition(tableNode) {
|
|
4302
|
+
const tableElement = this.#editor.getElementByKey(tableNode.getKey());
|
|
4303
|
+
if (!tableElement) return
|
|
4304
|
+
|
|
4305
|
+
const tableRect = tableElement.getBoundingClientRect();
|
|
4306
|
+
const editorRect = this.#editorElement.getBoundingClientRect();
|
|
4307
|
+
|
|
4308
|
+
const relativeTop = tableRect.top - editorRect.top;
|
|
4309
|
+
const relativeCenter = (tableRect.left + tableRect.right) / 2 - editorRect.left;
|
|
4310
|
+
this.buttonsContainer.style.top = `${relativeTop}px`;
|
|
4311
|
+
this.buttonsContainer.style.left = `${relativeCenter}px`;
|
|
4312
|
+
}
|
|
4313
|
+
|
|
4314
|
+
#updateRowColumnCount() {
|
|
4315
|
+
if (!this.currentTableNode) return
|
|
4316
|
+
|
|
4317
|
+
const tableElement = $getElementForTableNode(this.#editor, this.currentTableNode);
|
|
4318
|
+
if (!tableElement) return
|
|
4319
|
+
|
|
4320
|
+
const rowCount = tableElement.rows;
|
|
4321
|
+
const columnCount = tableElement.columns;
|
|
4322
|
+
|
|
4323
|
+
this.rowCount.textContent = `${rowCount} row${rowCount === 1 ? "" : "s"}`;
|
|
4324
|
+
this.columnCount.textContent = `${columnCount} column${columnCount === 1 ? "" : "s"}`;
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
#createButton(icon, label, onClick) {
|
|
4328
|
+
const button = createElement("button", {
|
|
4329
|
+
className: "lexxy-table-control__button",
|
|
4330
|
+
"aria-label": label,
|
|
4331
|
+
type: "button"
|
|
4332
|
+
});
|
|
4333
|
+
button.tabIndex = -1;
|
|
4334
|
+
button.innerHTML = `${icon} <span>${label}</span>`;
|
|
4335
|
+
button.addEventListener("click", onClick.bind(this));
|
|
4336
|
+
|
|
4337
|
+
return button
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
#createRowButtonsContainer() {
|
|
4341
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
4342
|
+
|
|
4343
|
+
const plusButton = this.#createButton("+", "Add row", () => this.#insertTableRow("end"));
|
|
4344
|
+
const minusButton = this.#createButton("−", "Remove row", () => this.#deleteTableRow("end"));
|
|
4345
|
+
|
|
4346
|
+
this.rowCount = createElement("span");
|
|
4347
|
+
this.rowCount.textContent = "_ rows";
|
|
4348
|
+
|
|
4349
|
+
container.appendChild(minusButton);
|
|
4350
|
+
container.appendChild(this.rowCount);
|
|
4351
|
+
container.appendChild(plusButton);
|
|
4352
|
+
|
|
4353
|
+
return container
|
|
4354
|
+
}
|
|
4355
|
+
|
|
4356
|
+
#createColumnButtonsContainer() {
|
|
4357
|
+
const container = createElement("div", { className: "lexxy-table-control" });
|
|
4358
|
+
|
|
4359
|
+
const plusButton = this.#createButton("+", "Add column", () => this.#insertTableColumn("end"));
|
|
4360
|
+
const minusButton = this.#createButton("−", "Remove column", () => this.#deleteTableColumn("end"));
|
|
4361
|
+
|
|
4362
|
+
this.columnCount = createElement("span");
|
|
4363
|
+
this.columnCount.textContent = "_ columns";
|
|
4364
|
+
|
|
4365
|
+
container.appendChild(minusButton);
|
|
4366
|
+
container.appendChild(this.columnCount);
|
|
4367
|
+
container.appendChild(plusButton);
|
|
4368
|
+
|
|
4369
|
+
return container
|
|
4370
|
+
}
|
|
4371
|
+
|
|
4372
|
+
#createMoreMenu() {
|
|
4373
|
+
const container = createElement("details", {
|
|
4374
|
+
className: "lexxy-table-control lexxy-table-control__more-menu"
|
|
4375
|
+
});
|
|
4376
|
+
|
|
4377
|
+
container.tabIndex = -1;
|
|
4378
|
+
|
|
4379
|
+
const summary = createElement("summary", {}, "•••");
|
|
4380
|
+
container.appendChild(summary);
|
|
4381
|
+
|
|
4382
|
+
const details = createElement("div", { className: "lexxy-table-control__more-menu-details" });
|
|
4383
|
+
container.appendChild(details);
|
|
4384
|
+
|
|
4385
|
+
details.appendChild(this.#createRowSection());
|
|
4386
|
+
details.appendChild(this.#createColumnSection());
|
|
4387
|
+
details.appendChild(this.#createDeleteTableSection());
|
|
4388
|
+
|
|
4389
|
+
container.addEventListener("toggle", this.#handleMoreMenuToggle.bind(this));
|
|
4390
|
+
|
|
4391
|
+
return container
|
|
4392
|
+
}
|
|
4393
|
+
|
|
4394
|
+
#createColumnSection() {
|
|
4395
|
+
const columnSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4396
|
+
|
|
4397
|
+
const columnButtons = [
|
|
4398
|
+
{ icon: this.#icon("add-column-before"), label: "Add column before", onClick: () => this.#insertTableColumn("left") },
|
|
4399
|
+
{ icon: this.#icon("add-column-after"), label: "Add column after", onClick: () => this.#insertTableColumn("right") },
|
|
4400
|
+
{ icon: this.#icon("remove-column"), label: "Remove column", onClick: this.#deleteTableColumn },
|
|
4401
|
+
{ icon: this.#icon("toggle-column-style"), label: "Toggle column style", onClick: this.#toggleColumnHeaderStyle },
|
|
4402
|
+
];
|
|
4403
|
+
|
|
4404
|
+
columnButtons.forEach(button => {
|
|
4405
|
+
const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
|
|
4406
|
+
columnSection.appendChild(buttonElement);
|
|
4407
|
+
});
|
|
4408
|
+
|
|
4409
|
+
return columnSection
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
#createRowSection() {
|
|
4413
|
+
const rowSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4414
|
+
|
|
4415
|
+
const rowButtons = [
|
|
4416
|
+
{ icon: this.#icon("add-row-above"), label: "Add row above", onClick: () => this.#insertTableRow("above") },
|
|
4417
|
+
{ icon: this.#icon("add-row-below"), label: "Add row below", onClick: () => this.#insertTableRow("below") },
|
|
4418
|
+
{ icon: this.#icon("remove-row"), label: "Remove row", onClick: this.#deleteTableRow },
|
|
4419
|
+
{ icon: this.#icon("toggle-row-style"), label: "Toggle row style", onClick: this.#toggleRowHeaderStyle }
|
|
4420
|
+
];
|
|
4421
|
+
|
|
4422
|
+
rowButtons.forEach(button => {
|
|
4423
|
+
const buttonElement = this.#createButton(button.icon, button.label, button.onClick);
|
|
4424
|
+
rowSection.appendChild(buttonElement);
|
|
4425
|
+
});
|
|
4426
|
+
|
|
4427
|
+
return rowSection
|
|
4428
|
+
}
|
|
4429
|
+
|
|
4430
|
+
#createDeleteTableSection() {
|
|
4431
|
+
const deleteSection = createElement("section", { className: "lexxy-table-control__more-menu-section" });
|
|
4432
|
+
|
|
4433
|
+
const deleteButton = { icon: this.#icon("delete-table"), label: "Delete table", onClick: this.#deleteTable };
|
|
4434
|
+
|
|
4435
|
+
const buttonElement = this.#createButton(deleteButton.icon, deleteButton.label, deleteButton.onClick);
|
|
4436
|
+
deleteSection.appendChild(buttonElement);
|
|
4437
|
+
|
|
4438
|
+
return deleteSection
|
|
4439
|
+
}
|
|
4440
|
+
|
|
4441
|
+
#handleMoreMenuToggle() {
|
|
4442
|
+
if (this.moreMenu.open) {
|
|
4443
|
+
this.#setFocusStateOnSelectedCell();
|
|
4444
|
+
} else {
|
|
4445
|
+
this.#removeFocusStateFromSelectedCell();
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
|
|
4449
|
+
#closeMoreMenu() {
|
|
4450
|
+
this.#removeFocusStateFromSelectedCell();
|
|
4451
|
+
this.moreMenu.removeAttribute("open");
|
|
4452
|
+
}
|
|
4453
|
+
|
|
4454
|
+
#monitorForTableSelection() {
|
|
4455
|
+
this.#editor.registerUpdateListener(() => {
|
|
4456
|
+
this.#editor.getEditorState().read(() => {
|
|
4457
|
+
const selection = $getSelection();
|
|
4458
|
+
if (!$isRangeSelection(selection)) return
|
|
4459
|
+
|
|
4460
|
+
const anchorNode = selection.anchor.getNode();
|
|
4461
|
+
const tableNode = $findTableNode(anchorNode);
|
|
4462
|
+
|
|
4463
|
+
if (tableNode) {
|
|
4464
|
+
this.#tableCellWasSelected(tableNode);
|
|
4465
|
+
} else {
|
|
4466
|
+
this.#hideTableHandlerButtons();
|
|
4467
|
+
}
|
|
4468
|
+
});
|
|
4469
|
+
});
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
#setTableFocusState(focused) {
|
|
4473
|
+
this.#editorElement.querySelector("div.node--selected:has(table)")?.classList.remove("node--selected");
|
|
4474
|
+
|
|
4475
|
+
if (focused && this.currentTableNode) {
|
|
4476
|
+
const tableParent = this.#editor.getElementByKey(this.currentTableNode.getKey());
|
|
4477
|
+
if (!tableParent) return
|
|
4478
|
+
tableParent.classList.add("node--selected");
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4482
|
+
#tableCellWasSelected(tableNode) {
|
|
4483
|
+
this.currentTableNode = tableNode;
|
|
4484
|
+
this.#updateButtonsPosition(tableNode);
|
|
4485
|
+
this.#showTableHandlerButtons();
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
#setFocusStateOnSelectedCell() {
|
|
4489
|
+
this.#editor.getEditorState().read(() => {
|
|
4490
|
+
const currentCell = this.#currentCell;
|
|
4491
|
+
if (!currentCell) return
|
|
4492
|
+
|
|
4493
|
+
const cellElement = this.#editor.getElementByKey(currentCell.getKey());
|
|
4494
|
+
if (!cellElement) return
|
|
4495
|
+
|
|
4496
|
+
cellElement.classList.add("table-cell--selected");
|
|
4497
|
+
});
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
#removeFocusStateFromSelectedCell() {
|
|
4501
|
+
this.#editorElement.querySelector(".table-cell--selected")?.classList.remove("table-cell--selected");
|
|
4502
|
+
}
|
|
4503
|
+
|
|
4504
|
+
#selectLastTableCell() {
|
|
4505
|
+
if (!this.currentTableNode) return
|
|
4506
|
+
|
|
4507
|
+
const last = this.currentTableNode.getLastChild().getLastChild();
|
|
4508
|
+
if (!$isTableCellNode(last)) return
|
|
4509
|
+
|
|
4510
|
+
last.selectEnd();
|
|
4511
|
+
}
|
|
4512
|
+
|
|
4513
|
+
#deleteTable() {
|
|
4514
|
+
this.#editor.dispatchCommand("deleteTable");
|
|
4515
|
+
|
|
4516
|
+
this.#closeMoreMenu();
|
|
4517
|
+
this.#updateRowColumnCount();
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
#insertTableRow(direction) {
|
|
4521
|
+
this.#executeTableCommand("insert", "row", direction);
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4524
|
+
#insertTableColumn(direction) {
|
|
4525
|
+
this.#executeTableCommand("insert", "column", direction);
|
|
4526
|
+
}
|
|
4527
|
+
|
|
4528
|
+
#deleteTableRow(direction) {
|
|
4529
|
+
this.#executeTableCommand("delete", "row", direction);
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4532
|
+
#deleteTableColumn(direction) {
|
|
4533
|
+
this.#executeTableCommand("delete", "column", direction);
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
#executeTableCommand(action = "insert", childType = "row", direction) {
|
|
4537
|
+
this.#editor.update(() => {
|
|
4538
|
+
const currentCell = this.#currentCell;
|
|
4539
|
+
if (!currentCell) return
|
|
4540
|
+
|
|
4541
|
+
if (direction === "end") {
|
|
4542
|
+
this.#selectLastTableCell();
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
this.#dispatchTableCommand(action, childType, direction);
|
|
4546
|
+
|
|
4547
|
+
if (currentCell.isAttached()) {
|
|
4548
|
+
currentCell.selectEnd();
|
|
4549
|
+
}
|
|
4550
|
+
});
|
|
4551
|
+
|
|
4552
|
+
this.#closeMoreMenu();
|
|
4553
|
+
this.#updateRowColumnCount();
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
#dispatchTableCommand(action, childType, direction) {
|
|
4557
|
+
switch (action) {
|
|
4558
|
+
case "insert":
|
|
4559
|
+
switch (childType) {
|
|
4560
|
+
case "row":
|
|
4561
|
+
if (direction === "above") {
|
|
4562
|
+
this.#editor.dispatchCommand("insertTableRowAbove");
|
|
4563
|
+
} else {
|
|
4564
|
+
this.#editor.dispatchCommand("insertTableRowBelow");
|
|
4565
|
+
}
|
|
4566
|
+
break
|
|
4567
|
+
case "column":
|
|
4568
|
+
if (direction === "left") {
|
|
4569
|
+
this.#editor.dispatchCommand("insertTableColumnBefore");
|
|
4570
|
+
} else {
|
|
4571
|
+
this.#editor.dispatchCommand("insertTableColumnAfter");
|
|
4572
|
+
}
|
|
4573
|
+
break
|
|
4574
|
+
}
|
|
4575
|
+
break
|
|
4576
|
+
case "delete":
|
|
4577
|
+
switch (childType) {
|
|
4578
|
+
case "row":
|
|
4579
|
+
this.#editor.dispatchCommand("deleteTableRow");
|
|
4580
|
+
break
|
|
4581
|
+
case "column":
|
|
4582
|
+
this.#editor.dispatchCommand("deleteTableColumn");
|
|
4583
|
+
break
|
|
4584
|
+
}
|
|
4585
|
+
break
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4589
|
+
#toggleRowHeaderStyle() {
|
|
4590
|
+
this.#editor.update(() => {
|
|
4591
|
+
const rows = this.currentTableNode.getChildren();
|
|
4592
|
+
|
|
4593
|
+
const row = rows[this.#currentRow];
|
|
4594
|
+
if (!row) return
|
|
4595
|
+
|
|
4596
|
+
const cells = row.getChildren();
|
|
4597
|
+
const firstCell = $getTableCellNodeFromLexicalNode(cells[0]);
|
|
4598
|
+
if (!firstCell) return
|
|
4599
|
+
|
|
4600
|
+
const currentStyle = firstCell.getHeaderStyles();
|
|
4601
|
+
const newStyle = currentStyle ^ TableCellHeaderStates.ROW;
|
|
4602
|
+
|
|
4603
|
+
cells.forEach(cell => {
|
|
4604
|
+
this.#setHeaderStyle(cell, newStyle, TableCellHeaderStates.ROW);
|
|
4605
|
+
});
|
|
4606
|
+
});
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4609
|
+
#toggleColumnHeaderStyle() {
|
|
4610
|
+
this.#editor.update(() => {
|
|
4611
|
+
const rows = this.currentTableNode.getChildren();
|
|
4612
|
+
|
|
4613
|
+
const row = rows[this.#currentRow];
|
|
4614
|
+
if (!row) return
|
|
4615
|
+
|
|
4616
|
+
const cells = row.getChildren();
|
|
4617
|
+
const selectedCell = $getTableCellNodeFromLexicalNode(cells[this.#currentColumn]);
|
|
4618
|
+
if (!selectedCell) return
|
|
4619
|
+
|
|
4620
|
+
const currentStyle = selectedCell.getHeaderStyles();
|
|
4621
|
+
const newStyle = currentStyle ^ TableCellHeaderStates.COLUMN;
|
|
4622
|
+
|
|
4623
|
+
rows.forEach(row => {
|
|
4624
|
+
const cell = row.getChildren()[this.#currentColumn];
|
|
4625
|
+
if (!cell) return
|
|
4626
|
+
this.#setHeaderStyle(cell, newStyle, TableCellHeaderStates.COLUMN);
|
|
4627
|
+
});
|
|
4628
|
+
});
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4631
|
+
#setHeaderStyle(cell, newStyle, headerState) {
|
|
4632
|
+
const tableCellNode = $getTableCellNodeFromLexicalNode(cell);
|
|
4633
|
+
|
|
4634
|
+
if (tableCellNode) {
|
|
4635
|
+
tableCellNode.setHeaderStyles(newStyle, headerState);
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
|
|
4639
|
+
#icon(name) {
|
|
4640
|
+
const icons =
|
|
4641
|
+
{
|
|
4642
|
+
"add-row-above":
|
|
4643
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4644
|
+
<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"/>
|
|
4645
|
+
</svg>`,
|
|
4646
|
+
|
|
4647
|
+
"add-row-below":
|
|
4648
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4649
|
+
<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"/>
|
|
4650
|
+
</svg>`,
|
|
4651
|
+
|
|
4652
|
+
"remove-row":
|
|
4653
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4654
|
+
<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"/>
|
|
4655
|
+
</svg>`,
|
|
4656
|
+
|
|
4657
|
+
"toggle-row-style":
|
|
4658
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4659
|
+
<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"/>
|
|
4660
|
+
</svg>`,
|
|
4661
|
+
|
|
4662
|
+
"add-column-before":
|
|
4663
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4664
|
+
<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"/>
|
|
4665
|
+
</svg>`,
|
|
4666
|
+
|
|
4667
|
+
"add-column-after":
|
|
4668
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4669
|
+
<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"/>
|
|
4670
|
+
</svg>`,
|
|
4671
|
+
|
|
4672
|
+
"remove-column":
|
|
4673
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4674
|
+
<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"/>
|
|
4675
|
+
</svg>`,
|
|
4676
|
+
|
|
4677
|
+
"toggle-column-style":
|
|
4678
|
+
`<svg viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
|
4679
|
+
<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"/>
|
|
4680
|
+
</svg>`,
|
|
4681
|
+
|
|
4682
|
+
"delete-table":
|
|
4683
|
+
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
4684
|
+
<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"/>
|
|
4685
|
+
</svg>`
|
|
4686
|
+
};
|
|
4687
|
+
|
|
4688
|
+
return icons[name]
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
customElements.define("lexxy-table-handler", TableHandler);
|
|
4693
|
+
|
|
4056
4694
|
class BaseSource {
|
|
4057
4695
|
// Template method to override
|
|
4058
4696
|
async buildListItems(filter = "") {
|
|
@@ -4738,24 +5376,17 @@ function highlightAll() {
|
|
|
4738
5376
|
|
|
4739
5377
|
function highlightElement(preElement) {
|
|
4740
5378
|
const language = preElement.getAttribute("data-language");
|
|
4741
|
-
|
|
4742
5379
|
let code = preElement.innerHTML.replace(/<br\s*\/?>/gi, "\n");
|
|
4743
5380
|
|
|
4744
|
-
const grammar = Prism.languages[language];
|
|
5381
|
+
const grammar = Prism.languages?.[language];
|
|
4745
5382
|
if (!grammar) return
|
|
4746
5383
|
|
|
4747
5384
|
// unescape HTML entities in the code block
|
|
4748
5385
|
code = new DOMParser().parseFromString(code, "text/html").body.textContent || "";
|
|
4749
5386
|
|
|
4750
5387
|
const highlightedHtml = Prism.highlight(code, grammar, language);
|
|
4751
|
-
|
|
4752
5388
|
const codeElement = createElement("code", { "data-language": language, innerHTML: highlightedHtml });
|
|
4753
5389
|
preElement.replaceWith(codeElement);
|
|
4754
5390
|
}
|
|
4755
5391
|
|
|
4756
|
-
// Manual highlighting mode to prevent invocation on every page. See https://prismjs.com/docs/prism
|
|
4757
|
-
// This must happen before importing any Prism components
|
|
4758
|
-
window.Prism = window.Prism || {};
|
|
4759
|
-
Prism.manual = true;
|
|
4760
|
-
|
|
4761
5392
|
export { highlightAll };
|
|
@@ -195,6 +195,42 @@
|
|
|
195
195
|
.token.punctuation {
|
|
196
196
|
color: var(--lexxy-color-code-token-punctuation);
|
|
197
197
|
}
|
|
198
|
+
|
|
199
|
+
/* Tables */
|
|
200
|
+
:where(.lexxy-content__table-wrapper) {
|
|
201
|
+
margin-block: 1ch;
|
|
202
|
+
overflow-x: auto;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
table {
|
|
206
|
+
border-collapse: collapse;
|
|
207
|
+
border-spacing: 0;
|
|
208
|
+
inline-size: calc(100% - 0.5ch);
|
|
209
|
+
margin: 0.25ch;
|
|
210
|
+
|
|
211
|
+
th,
|
|
212
|
+
td {
|
|
213
|
+
border: 1px solid var(--lexxy-color-ink-lighter);
|
|
214
|
+
padding: 1ch;
|
|
215
|
+
text-align: start;
|
|
216
|
+
word-break: normal;
|
|
217
|
+
|
|
218
|
+
*:last-child {
|
|
219
|
+
margin-block-end: 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
&.lexxy-content__table-cell--header {
|
|
223
|
+
background-color: var(--lexxy-color-ink-lightest);
|
|
224
|
+
font-weight: bold;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
*:is(code, pre) {
|
|
228
|
+
hyphens: auto;
|
|
229
|
+
text-wrap: wrap;
|
|
230
|
+
white-space: pre-wrap;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
198
234
|
}
|
|
199
235
|
|
|
200
236
|
:where([data-lexical-cursor]) {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
cursor: pointer;
|
|
37
37
|
font-size: inherit;
|
|
38
38
|
inline-size: auto;
|
|
39
|
+
padding: 0;
|
|
39
40
|
|
|
40
41
|
@media(any-hover: hover) {
|
|
41
42
|
&:hover:not([aria-disabled="true"]) {
|
|
@@ -52,6 +53,23 @@
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
table {
|
|
57
|
+
.table-cell--selected {
|
|
58
|
+
background-color: var(--lexxy-color-table-cell-selected-bg) !important;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.lexxy-content__table-cell--selected {
|
|
62
|
+
background-color: var(--lexxy-color-table-cell-selected-bg) !important;
|
|
63
|
+
border-color: var(--lexxy-color-table-cell-selected-border) !important;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&.lexxy-content__table--selection {
|
|
67
|
+
::selection {
|
|
68
|
+
background: transparent;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
55
73
|
action-text-attachment {
|
|
56
74
|
cursor: pointer;
|
|
57
75
|
}
|
|
@@ -189,7 +207,7 @@
|
|
|
189
207
|
user-select: none;
|
|
190
208
|
-webkit-user-select: none;
|
|
191
209
|
|
|
192
|
-
.lexxy-editor__toolbar-dropdown-content {
|
|
210
|
+
:where(.lexxy-editor__toolbar-dropdown-content) {
|
|
193
211
|
--dropdown-padding: 1ch;
|
|
194
212
|
--dropdown-gap: calc(var(--dropdown-padding) / 2);
|
|
195
213
|
|
|
@@ -358,6 +376,212 @@
|
|
|
358
376
|
}
|
|
359
377
|
}
|
|
360
378
|
|
|
379
|
+
/* Table dropdown
|
|
380
|
+
/* -------------------------------------------------------------------------- */
|
|
381
|
+
|
|
382
|
+
:where(lexxy-table-dropdown) {
|
|
383
|
+
display: flex;
|
|
384
|
+
flex-direction: column;
|
|
385
|
+
gap: 1ch;
|
|
386
|
+
|
|
387
|
+
.lexxy-editor__table-create {
|
|
388
|
+
display: flex;
|
|
389
|
+
flex-direction: column;
|
|
390
|
+
flex-wrap: wrap;
|
|
391
|
+
gap: 0;
|
|
392
|
+
|
|
393
|
+
.lexxy-editor__table-buttons {
|
|
394
|
+
background-color: var(--lexxy-color-ink-lighter);
|
|
395
|
+
display: flex;
|
|
396
|
+
flex-direction: column;
|
|
397
|
+
gap: 1px;
|
|
398
|
+
padding: 1px;
|
|
399
|
+
|
|
400
|
+
div {
|
|
401
|
+
display: flex;
|
|
402
|
+
flex-direction: row;
|
|
403
|
+
gap: 1px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
button {
|
|
407
|
+
aspect-ratio: 1.5 / 1;
|
|
408
|
+
border: 0;
|
|
409
|
+
border-radius: 0;
|
|
410
|
+
color: var(--lexxy-color-ink);
|
|
411
|
+
font-family: var(--lexxy-font-base);
|
|
412
|
+
font-size: var(--lexxy-text-small);
|
|
413
|
+
font-weight: normal;
|
|
414
|
+
inline-size: 4ch;
|
|
415
|
+
margin: 0;
|
|
416
|
+
|
|
417
|
+
&.active {
|
|
418
|
+
background-color: var(--lexxy-color-ink-lightest);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
label {
|
|
424
|
+
align-items: center;
|
|
425
|
+
display: flex;
|
|
426
|
+
gap: 0.5ch;
|
|
427
|
+
padding: 0.5ch 0;
|
|
428
|
+
margin-block-start: 1ch;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
&:has(input[type="checkbox"]:checked) {
|
|
432
|
+
.lexxy-editor__table-buttons {
|
|
433
|
+
div:first-child button,
|
|
434
|
+
button:first-child {
|
|
435
|
+
filter: brightness(0.95);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.lexxy-editor__table-edit {
|
|
442
|
+
display: flex;
|
|
443
|
+
flex-direction: column;
|
|
444
|
+
flex-wrap: wrap;
|
|
445
|
+
gap: 0;
|
|
446
|
+
|
|
447
|
+
button {
|
|
448
|
+
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* Table handle buttons
|
|
454
|
+
/* -------------------------------------------------------------------------- */
|
|
455
|
+
|
|
456
|
+
:where(.lexxy-table-handle-buttons) {
|
|
457
|
+
--button-size: 2.5lh;
|
|
458
|
+
color: var(--lexxy-color-ink-inverted);
|
|
459
|
+
display: none;
|
|
460
|
+
flex-direction: row;
|
|
461
|
+
font-size: var(--lexxy-text-small);
|
|
462
|
+
gap: 0.25ch;
|
|
463
|
+
line-height: 1;
|
|
464
|
+
position: absolute;
|
|
465
|
+
transform: translate(-50%, -120%);
|
|
466
|
+
z-index: 10;
|
|
467
|
+
|
|
468
|
+
.lexxy-table-control {
|
|
469
|
+
align-items: center;
|
|
470
|
+
background-color: var(--lexxy-color-ink);
|
|
471
|
+
border-radius: 0.75ch;
|
|
472
|
+
display: flex;
|
|
473
|
+
flex-direction: row;
|
|
474
|
+
gap: 1ch;
|
|
475
|
+
padding: 2px;
|
|
476
|
+
white-space: nowrap;
|
|
477
|
+
|
|
478
|
+
button {
|
|
479
|
+
aspect-ratio: 1 / 1;
|
|
480
|
+
align-items: center;
|
|
481
|
+
background-color: transparent;
|
|
482
|
+
border-radius: var(--lexxy-radius);
|
|
483
|
+
border: 0;
|
|
484
|
+
color: var(--lexxy-color-ink-inverted);
|
|
485
|
+
cursor: pointer;
|
|
486
|
+
display: flex;
|
|
487
|
+
font-weight: bold;
|
|
488
|
+
justify-content: center;
|
|
489
|
+
line-height: 1;
|
|
490
|
+
min-block-size: var(--button-size);
|
|
491
|
+
min-inline-size: var(--button-size);
|
|
492
|
+
padding: 0;
|
|
493
|
+
|
|
494
|
+
&:hover,
|
|
495
|
+
&:focus-visible {
|
|
496
|
+
background-color: var(--lexxy-color-ink-medium);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
svg {
|
|
500
|
+
block-size: 1em;
|
|
501
|
+
inline-size: 1em;
|
|
502
|
+
fill: currentColor;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
span {
|
|
506
|
+
display: none;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.lexxy-table-control__more-menu {
|
|
512
|
+
gap: 0;
|
|
513
|
+
padding: 2px;
|
|
514
|
+
position: relative;
|
|
515
|
+
|
|
516
|
+
summary {
|
|
517
|
+
aspect-ratio: 1 / 1;
|
|
518
|
+
align-items: center;
|
|
519
|
+
background: transparent;
|
|
520
|
+
border-radius: var(--lexxy-radius);
|
|
521
|
+
border: 0;
|
|
522
|
+
box-sizing: border-box;
|
|
523
|
+
display: flex;
|
|
524
|
+
font-size: inherit;
|
|
525
|
+
justify-content: center;
|
|
526
|
+
list-style: none;
|
|
527
|
+
min-block-size: var(--button-size);
|
|
528
|
+
min-inline-size: var(--button-size);
|
|
529
|
+
padding: 0;
|
|
530
|
+
user-select: none;
|
|
531
|
+
-webkit-user-select: none;
|
|
532
|
+
|
|
533
|
+
&::-webkit-details-marker {
|
|
534
|
+
display: none;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
&:hover {
|
|
538
|
+
background: var(--lexxy-color-ink-medium);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.lexxy-table-control__more-menu-details {
|
|
543
|
+
display: flex;
|
|
544
|
+
flex-direction: column;
|
|
545
|
+
gap: 0.25ch;
|
|
546
|
+
inset-block-start: 105%;
|
|
547
|
+
inset-inline-start: 0;
|
|
548
|
+
padding: 0;
|
|
549
|
+
position: absolute;
|
|
550
|
+
|
|
551
|
+
.lexxy-table-control__more-menu-section {
|
|
552
|
+
background: var(--lexxy-color-ink);
|
|
553
|
+
border-radius: 0.75ch;
|
|
554
|
+
display: flex;
|
|
555
|
+
flex-direction: column;
|
|
556
|
+
padding: 2px;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
button {
|
|
560
|
+
aspect-ratio: unset;
|
|
561
|
+
align-items: center;
|
|
562
|
+
display: flex;
|
|
563
|
+
flex-direction: row;
|
|
564
|
+
font-weight: normal;
|
|
565
|
+
gap: 1ch;
|
|
566
|
+
justify-content: flex-start;
|
|
567
|
+
padding: 0.5ch 2ch;
|
|
568
|
+
padding-inline-start: 1ch;
|
|
569
|
+
white-space: nowrap;
|
|
570
|
+
|
|
571
|
+
span {
|
|
572
|
+
display: inline-block;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
svg {
|
|
576
|
+
block-size: 1.3lh;
|
|
577
|
+
inline-size: 1.3lh;
|
|
578
|
+
fill: currentColor;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
361
585
|
|
|
362
586
|
/* Language picker
|
|
363
587
|
/* -------------------------------------------------------------------------- */
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
--lexxy-color-selected-dark: var(--lexxy-color-blue);
|
|
36
36
|
--lexxy-color-code-bg: var(--lexxy-color-ink-lightest);
|
|
37
37
|
|
|
38
|
+
/* Text color highlights */
|
|
38
39
|
--highlight-1: rgb(136, 118, 38);
|
|
39
40
|
--highlight-2: rgb(185, 94, 6);
|
|
40
41
|
--highlight-3: rgb(207, 0, 0);
|
|
@@ -55,6 +56,13 @@
|
|
|
55
56
|
--highlight-bg-8: rgba(221, 170, 123, 0.3);
|
|
56
57
|
--highlight-bg-9: rgba(200, 200, 200, 0.3);
|
|
57
58
|
|
|
59
|
+
/* Tables */
|
|
60
|
+
--lexxy-color-table-header-bg: var(--lexxy-color-ink-lightest);
|
|
61
|
+
--lexxy-color-table-cell-border: var(--lexxy-color-ink-lighter);
|
|
62
|
+
--lexxy-color-table-cell-selected: var(--lexxy-color-selected);
|
|
63
|
+
--lexxy-color-table-cell-selected-border: highlight;
|
|
64
|
+
--lexxy-color-table-cell-selected-bg: highlight;
|
|
65
|
+
|
|
58
66
|
/* Typography */
|
|
59
67
|
--lexxy-font-base: system-ui, sans-serif;
|
|
60
68
|
--lexxy-font-mono: ui-monospace, "Menlo", "Monaco", Consolas, monospace;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@37signals/lexxy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25-beta",
|
|
4
4
|
"description": "Lexxy - A modern rich text editor for Rails.",
|
|
5
5
|
"module": "dist/lexxy.esm.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@eslint/js": "^9.15.0",
|
|
16
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
17
|
+
"@rollup/plugin-inject": "^5.0.5",
|
|
16
18
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
17
19
|
"@rollup/plugin-terser": "^0.4.4",
|
|
18
20
|
"eslint": "^9.15.0",
|