@37signals/lexxy 0.9.20 → 0.9.22
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 +2 -19
- package/dist/lexxy.esm.js +70 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A modern rich text editor for Rails.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[Documentation](https://basecamp.github.io/lexxy/docs/) | <mark>[Try it out!](https://basecamp.github.io/lexxy/sandbox/)</mark>
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -15,24 +15,7 @@ A modern rich text editor for Rails.
|
|
|
15
15
|
- Preview attachments like PDFs and Videos in the editor.
|
|
16
16
|
- Works seamlessly with Action Text, generating the same canonical HTML format it expects for attachments.
|
|
17
17
|
|
|
18
|
-

|
|
19
|
-
|
|
20
|
-
## Documentation
|
|
21
|
-
|
|
22
|
-
Visit the **[documentation site](https://basecamp.github.io/lexxy)**.
|
|
23
|
-
|
|
24
|
-
## Roadmap
|
|
25
|
-
|
|
26
|
-
This is a beta. Here's what's coming next:
|
|
27
|
-
|
|
28
|
-
- [x] Configurable editors in Action Text: Choose your editor like you choose your database.
|
|
29
|
-
- [x] More editing features:
|
|
30
|
-
- [x] Tables
|
|
31
|
-
- [x] Text highlighting
|
|
32
|
-
- [x] Configuration hooks.
|
|
33
|
-
- [x] Standalone JS package: to use in non-Rails environments.
|
|
34
|
-
- [x] Image galleries: The only remaining feature for full Action Text compatibility
|
|
35
|
-
- [ ] Install task that generates the necessary JS and adds stylesheets.
|
|
18
|
+

|
|
36
19
|
|
|
37
20
|
## Contributing
|
|
38
21
|
|
package/dist/lexxy.esm.js
CHANGED
|
@@ -10,7 +10,7 @@ import { registerPlainText } from '@lexical/plain-text';
|
|
|
10
10
|
import { RichTextExtension, $isQuoteNode, QuoteNode, $isHeadingNode, $createHeadingNode, $createQuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
11
11
|
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
|
|
12
12
|
import { HistoryExtension } from '@lexical/history';
|
|
13
|
-
import { $isCodeNode, CodeHighlightNode, CodeNode, $createCodeNode, $isCodeHighlightNode, $createCodeHighlightNode,
|
|
13
|
+
import { $isCodeNode, CodeHighlightNode, CodeNode, $createCodeNode, $isCodeHighlightNode, $createCodeHighlightNode, registerCodeHighlighting, normalizeCodeLang, CODE_LANGUAGE_FRIENDLY_NAME_MAP } from '@lexical/code';
|
|
14
14
|
import { TRANSFORMERS, registerMarkdownShortcuts } from '@lexical/markdown';
|
|
15
15
|
import { INSERT_TABLE_COMMAND, $getTableCellNodeFromLexicalNode, TableCellNode, TableNode, TableRowNode, setScrollableTablesActive, registerTablePlugin, registerTableSelectionObserver, TableCellHeaderStates, $insertTableRowAtSelection, $insertTableColumnAtSelection, $deleteTableRowAtSelection, $deleteTableColumnAtSelection, $findTableNode, $getTableRowIndexFromTableCellNode, $getTableColumnIndexFromTableCellNode, $findCellNode, $getElementForTableNode } from '@lexical/table';
|
|
16
16
|
import { marked } from 'marked';
|
|
@@ -6486,7 +6486,7 @@ class Clipboard {
|
|
|
6486
6486
|
}
|
|
6487
6487
|
|
|
6488
6488
|
if (this.#isPlainTextOrURLPasted(clipboardData)) {
|
|
6489
|
-
this.#
|
|
6489
|
+
this.#pastePlainTextOrURL(clipboardData);
|
|
6490
6490
|
event.preventDefault();
|
|
6491
6491
|
return true
|
|
6492
6492
|
}
|
|
@@ -6524,14 +6524,34 @@ class Clipboard {
|
|
|
6524
6524
|
return types.length === 1 && types[0] === "text/plain"
|
|
6525
6525
|
}
|
|
6526
6526
|
|
|
6527
|
+
// Browsers expose a copied URL in several shapes:
|
|
6528
|
+
// Safari [ text/plain, text/uri-list ]
|
|
6529
|
+
// App ShareSheet [ text/uri-list ]
|
|
6530
|
+
// Chromium macOS [ text/plain, text/html, (?:text/link-preview) ]
|
|
6527
6531
|
#isOnlyURLPasted(clipboardData) {
|
|
6528
|
-
|
|
6529
|
-
|
|
6532
|
+
if (this.#isLexicalClipboardData(clipboardData)) return false
|
|
6533
|
+
|
|
6530
6534
|
const types = Array.from(clipboardData.types);
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
+
if (types.includes("text/uri-list")) {
|
|
6536
|
+
return types.every(type => type === "text/plain" || type === "text/uri-list")
|
|
6537
|
+
}
|
|
6538
|
+
|
|
6539
|
+
if (clipboardData.files.length) return false
|
|
6540
|
+
|
|
6541
|
+
const text = clipboardData.getData("text/plain").trim();
|
|
6542
|
+
if (!isAutolinkableURL(text)) return false
|
|
6543
|
+
|
|
6544
|
+
const html = clipboardData.getData("text/html");
|
|
6545
|
+
return !html || this.#htmlIsBareLinkToURL(html, text)
|
|
6546
|
+
}
|
|
6547
|
+
|
|
6548
|
+
#htmlIsBareLinkToURL(html, url) {
|
|
6549
|
+
const doc = parseHtml(html);
|
|
6550
|
+
if (doc.body.textContent.trim() !== url) return false
|
|
6551
|
+
|
|
6552
|
+
const links = doc.body.querySelectorAll("a");
|
|
6553
|
+
if (links.length === 0) return true
|
|
6554
|
+
return links.length === 1 && links[0].getAttribute("href") === url
|
|
6535
6555
|
}
|
|
6536
6556
|
|
|
6537
6557
|
#isPastingIntoCodeBlock() {
|
|
@@ -6565,14 +6585,12 @@ class Clipboard {
|
|
|
6565
6585
|
}, { tag: PASTE_TAG });
|
|
6566
6586
|
}
|
|
6567
6587
|
|
|
6568
|
-
#
|
|
6569
|
-
const item = clipboardData
|
|
6588
|
+
#pastePlainTextOrURL(clipboardData) {
|
|
6589
|
+
const item = this.#plainTextOrURLItem(clipboardData);
|
|
6570
6590
|
item.getAsString((text) => {
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
const nodeKey = this.contents.createLink(text);
|
|
6575
|
-
this.#dispatchLinkInsertEvent(nodeKey, { url: text });
|
|
6591
|
+
const url = text.trim();
|
|
6592
|
+
if (isAutolinkableURL(url)) {
|
|
6593
|
+
this.#pasteURL(url);
|
|
6576
6594
|
} else if (this.editorElement.supportsMarkdown) {
|
|
6577
6595
|
this.#pasteMarkdown(text);
|
|
6578
6596
|
} else {
|
|
@@ -6581,6 +6599,20 @@ class Clipboard {
|
|
|
6581
6599
|
});
|
|
6582
6600
|
}
|
|
6583
6601
|
|
|
6602
|
+
#plainTextOrURLItem(clipboardData) {
|
|
6603
|
+
const items = Array.from(clipboardData.items);
|
|
6604
|
+
return items.find((item) => item.type === "text/plain") || items.find((item) => item.type === "text/uri-list")
|
|
6605
|
+
}
|
|
6606
|
+
|
|
6607
|
+
#pasteURL(url) {
|
|
6608
|
+
if (this.contents.hasSelectedText()) {
|
|
6609
|
+
this.contents.createLinkWithSelectedText(url);
|
|
6610
|
+
} else {
|
|
6611
|
+
const nodeKey = this.contents.createLink(url);
|
|
6612
|
+
this.#dispatchLinkInsertEvent(nodeKey, { url });
|
|
6613
|
+
}
|
|
6614
|
+
}
|
|
6615
|
+
|
|
6584
6616
|
#insertSingleLinkAt(selection, url) {
|
|
6585
6617
|
if (!$isRangeSelection(selection)) return
|
|
6586
6618
|
|
|
@@ -7039,6 +7071,26 @@ function $getAllProvisionalParagraphs(rootNode = $getRoot()) {
|
|
|
7039
7071
|
return $descendantsMatching(rootNode.getChildren(), $isProvisionalParagraphNode)
|
|
7040
7072
|
}
|
|
7041
7073
|
|
|
7074
|
+
// Registers code highlighting through the extension system so its node
|
|
7075
|
+
// transforms exist before the initial editor state is applied. Registering
|
|
7076
|
+
// after editor creation misses code blocks loaded from the initial value:
|
|
7077
|
+
// their transform pass has already run, and Lexical's catch-up dirtying
|
|
7078
|
+
// only sees the committed (still empty) state.
|
|
7079
|
+
class CodeHighlightingExtension extends LexxyExtension {
|
|
7080
|
+
get enabled() {
|
|
7081
|
+
return this.editorElement.supportsRichText
|
|
7082
|
+
}
|
|
7083
|
+
|
|
7084
|
+
get lexicalExtension() {
|
|
7085
|
+
return defineExtension({
|
|
7086
|
+
name: "lexxy/code-highlighting",
|
|
7087
|
+
register(editor) {
|
|
7088
|
+
return registerCodeHighlighting(editor)
|
|
7089
|
+
}
|
|
7090
|
+
})
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7042
7094
|
const TRIX_LANGUAGE_ATTR = "language";
|
|
7043
7095
|
|
|
7044
7096
|
class TrixContentExtension extends LexxyExtension {
|
|
@@ -8559,6 +8611,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
8559
8611
|
get baseExtensions() {
|
|
8560
8612
|
return [
|
|
8561
8613
|
ProvisionalParagraphExtension,
|
|
8614
|
+
CodeHighlightingExtension,
|
|
8562
8615
|
HighlightExtension,
|
|
8563
8616
|
TrixContentExtension,
|
|
8564
8617
|
TablesExtension,
|
|
@@ -8966,7 +9019,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
8966
9019
|
registerList(this.editor)
|
|
8967
9020
|
);
|
|
8968
9021
|
this.#registerTableComponents();
|
|
8969
|
-
this.#
|
|
9022
|
+
this.#registerCodeLanguagePicker();
|
|
8970
9023
|
if (this.supportsMarkdown) {
|
|
8971
9024
|
const transformers = [ ...TRANSFORMERS, HORIZONTAL_DIVIDER ];
|
|
8972
9025
|
registered.push(
|
|
@@ -8988,8 +9041,7 @@ class LexicalEditorElement extends HTMLElement {
|
|
|
8988
9041
|
this.#disposables.push(tableTools);
|
|
8989
9042
|
}
|
|
8990
9043
|
|
|
8991
|
-
#
|
|
8992
|
-
registerCodeHighlighting(this.editor);
|
|
9044
|
+
#registerCodeLanguagePicker() {
|
|
8993
9045
|
let codeLanguagePicker = this.querySelector("lexxy-code-language-picker");
|
|
8994
9046
|
codeLanguagePicker ??= createElement("lexxy-code-language-picker");
|
|
8995
9047
|
this.append(codeLanguagePicker);
|