@37signals/lexxy 0.1.15-beta → 0.1.16-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 +390 -14
- package/package.json +1 -1
package/dist/lexxy.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import DOMPurify from 'dompurify';
|
|
2
|
-
import { $getSelection, $isRangeSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, $isNodeSelection, $getRoot, $isLineBreakNode, $isTextNode, $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, $createTextNode,
|
|
3
|
-
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, ListNode, ListItemNode, registerList } from '@lexical/list';
|
|
2
|
+
import { $getSelection, $isRangeSelection, DecoratorNode, $getNodeByKey, HISTORY_MERGE_TAG, FORMAT_TEXT_COMMAND, UNDO_COMMAND, REDO_COMMAND, PASTE_COMMAND, COMMAND_PRIORITY_LOW, $isNodeSelection, $getRoot, $isLineBreakNode, $isTextNode, $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, $createTextNode, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, $isParagraphNode, $insertNodes, $createLineBreakNode, CLEAR_HISTORY_COMMAND, $addUpdateTag, SKIP_DOM_SELECTION_TAG, createEditor, COMMAND_PRIORITY_NORMAL, KEY_TAB_COMMAND, KEY_SPACE_COMMAND } from 'lexical';
|
|
3
|
+
import { $isListNode, $isListItemNode, INSERT_UNORDERED_LIST_COMMAND, INSERT_ORDERED_LIST_COMMAND, $createListNode, ListNode, ListItemNode, registerList } from '@lexical/list';
|
|
4
4
|
import { $isQuoteNode, $isHeadingNode, $createQuoteNode, $createHeadingNode, QuoteNode, HeadingNode, registerRichText } from '@lexical/rich-text';
|
|
5
5
|
import { $isCodeNode, CodeNode, CodeHighlightNode, registerCodeHighlighting, CODE_LANGUAGE_FRIENDLY_NAME_MAP, normalizeCodeLang } from '@lexical/code';
|
|
6
6
|
import { $isLinkNode, $toggleLink, $createLinkNode, LinkNode, AutoLinkNode } from '@lexical/link';
|
|
@@ -1053,7 +1053,7 @@ class CommandDispatcher {
|
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
1055
|
dispatchInsertQuoteBlock() {
|
|
1056
|
-
this.contents.
|
|
1056
|
+
this.contents.toggleNodeWrappingAllSelectedNodes((node) => $isQuoteNode(node), () => $createQuoteNode());
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
1059
|
dispatchInsertCodeBlock() {
|
|
@@ -1920,10 +1920,277 @@ class CustomActionTextAttachmentNode extends DecoratorNode {
|
|
|
1920
1920
|
}
|
|
1921
1921
|
}
|
|
1922
1922
|
|
|
1923
|
+
class FormatEscaper {
|
|
1924
|
+
constructor(editorElement) {
|
|
1925
|
+
this.editorElement = editorElement;
|
|
1926
|
+
this.editor = editorElement.editor;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
monitor() {
|
|
1930
|
+
this.editor.registerCommand(
|
|
1931
|
+
KEY_ENTER_COMMAND,
|
|
1932
|
+
(event) => this.#handleEnterKey(event),
|
|
1933
|
+
COMMAND_PRIORITY_HIGH
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
#handleEnterKey(event) {
|
|
1938
|
+
const selection = $getSelection();
|
|
1939
|
+
if (!$isRangeSelection(selection)) return false
|
|
1940
|
+
|
|
1941
|
+
const anchorNode = selection.anchor.getNode();
|
|
1942
|
+
|
|
1943
|
+
return this.#handleLists(event, anchorNode)
|
|
1944
|
+
|| this.#handleBlockquotes(event, anchorNode)
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
#handleLists(event, anchorNode) {
|
|
1948
|
+
if (this.#shouldEscapeFromEmptyListItem(anchorNode) || this.#shouldEscapeFromEmptyParagraphInListItem(anchorNode)) {
|
|
1949
|
+
event.preventDefault();
|
|
1950
|
+
this.#escapeFromList(anchorNode);
|
|
1951
|
+
return true
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
return false
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
#handleBlockquotes(event, anchorNode) {
|
|
1958
|
+
if (this.#shouldEscapeFromEmptyParagraphInBlockquote(anchorNode)) {
|
|
1959
|
+
event.preventDefault();
|
|
1960
|
+
this.#escapeFromBlockquote(anchorNode);
|
|
1961
|
+
return true
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
return false
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
#shouldEscapeFromEmptyListItem(node) {
|
|
1968
|
+
const listItem = this.#getListItemNode(node);
|
|
1969
|
+
if (!listItem) return false
|
|
1970
|
+
|
|
1971
|
+
return this.#isNodeEmpty(listItem)
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
#shouldEscapeFromEmptyParagraphInListItem(node) {
|
|
1975
|
+
const paragraph = this.#getParagraphNode(node);
|
|
1976
|
+
if (!paragraph) return false
|
|
1977
|
+
|
|
1978
|
+
if (!this.#isNodeEmpty(paragraph)) return false
|
|
1979
|
+
|
|
1980
|
+
const parent = paragraph.getParent();
|
|
1981
|
+
return parent && $isListItemNode(parent)
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
#isNodeEmpty(node) {
|
|
1985
|
+
if (node.getTextContent().trim() !== "") return false
|
|
1986
|
+
|
|
1987
|
+
const children = node.getChildren();
|
|
1988
|
+
if (children.length === 0) return true
|
|
1989
|
+
|
|
1990
|
+
return children.every(child => {
|
|
1991
|
+
if ($isLineBreakNode(child)) return true
|
|
1992
|
+
return this.#isNodeEmpty(child)
|
|
1993
|
+
})
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
#getListItemNode(node) {
|
|
1997
|
+
let currentNode = node;
|
|
1998
|
+
|
|
1999
|
+
while (currentNode) {
|
|
2000
|
+
if ($isListItemNode(currentNode)) {
|
|
2001
|
+
return currentNode
|
|
2002
|
+
}
|
|
2003
|
+
currentNode = currentNode.getParent();
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
return null
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
#escapeFromList(anchorNode) {
|
|
2010
|
+
const listItem = this.#getListItemNode(anchorNode);
|
|
2011
|
+
if (!listItem) return
|
|
2012
|
+
|
|
2013
|
+
const parentList = listItem.getParent();
|
|
2014
|
+
if (!parentList || !$isListNode(parentList)) return
|
|
2015
|
+
|
|
2016
|
+
const blockquote = parentList.getParent();
|
|
2017
|
+
const isInBlockquote = blockquote && $isQuoteNode(blockquote);
|
|
2018
|
+
|
|
2019
|
+
if (isInBlockquote) {
|
|
2020
|
+
const listItemsAfter = this.#getListItemSiblingsAfter(listItem);
|
|
2021
|
+
const nonEmptyListItems = listItemsAfter.filter(item => !this.#isNodeEmpty(item));
|
|
2022
|
+
|
|
2023
|
+
if (nonEmptyListItems.length > 0) {
|
|
2024
|
+
this.#splitBlockquoteWithList(blockquote, parentList, listItem, nonEmptyListItems);
|
|
2025
|
+
return
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
const paragraph = $createParagraphNode();
|
|
2030
|
+
parentList.insertAfter(paragraph);
|
|
2031
|
+
|
|
2032
|
+
listItem.remove();
|
|
2033
|
+
paragraph.selectStart();
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
#shouldEscapeFromEmptyParagraphInBlockquote(node) {
|
|
2037
|
+
const paragraph = this.#getParagraphNode(node);
|
|
2038
|
+
if (!paragraph) return false
|
|
2039
|
+
|
|
2040
|
+
if (!this.#isNodeEmpty(paragraph)) return false
|
|
2041
|
+
|
|
2042
|
+
const parent = paragraph.getParent();
|
|
2043
|
+
return parent && $isQuoteNode(parent)
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
#getParagraphNode(node) {
|
|
2047
|
+
let currentNode = node;
|
|
2048
|
+
|
|
2049
|
+
while (currentNode) {
|
|
2050
|
+
if ($isParagraphNode(currentNode)) {
|
|
2051
|
+
return currentNode
|
|
2052
|
+
}
|
|
2053
|
+
currentNode = currentNode.getParent();
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
return null
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
#escapeFromBlockquote(anchorNode) {
|
|
2060
|
+
const paragraph = this.#getParagraphNode(anchorNode);
|
|
2061
|
+
if (!paragraph) return
|
|
2062
|
+
|
|
2063
|
+
const blockquote = paragraph.getParent();
|
|
2064
|
+
if (!blockquote || !$isQuoteNode(blockquote)) return
|
|
2065
|
+
|
|
2066
|
+
const siblingsAfter = this.#getSiblingsAfter(paragraph);
|
|
2067
|
+
const nonEmptySiblings = siblingsAfter.filter(sibling => !this.#isNodeEmpty(sibling));
|
|
2068
|
+
|
|
2069
|
+
if (nonEmptySiblings.length > 0) {
|
|
2070
|
+
this.#splitBlockquote(blockquote, paragraph, nonEmptySiblings);
|
|
2071
|
+
} else {
|
|
2072
|
+
const newParagraph = $createParagraphNode();
|
|
2073
|
+
blockquote.insertAfter(newParagraph);
|
|
2074
|
+
paragraph.remove();
|
|
2075
|
+
newParagraph.selectStart();
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
#getSiblingsAfter(node) {
|
|
2080
|
+
const siblings = [];
|
|
2081
|
+
let sibling = node.getNextSibling();
|
|
2082
|
+
|
|
2083
|
+
while (sibling) {
|
|
2084
|
+
siblings.push(sibling);
|
|
2085
|
+
sibling = sibling.getNextSibling();
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
return siblings
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
#getListItemSiblingsAfter(listItem) {
|
|
2092
|
+
const siblings = [];
|
|
2093
|
+
let sibling = listItem.getNextSibling();
|
|
2094
|
+
|
|
2095
|
+
while (sibling) {
|
|
2096
|
+
if ($isListItemNode(sibling)) {
|
|
2097
|
+
siblings.push(sibling);
|
|
2098
|
+
}
|
|
2099
|
+
sibling = sibling.getNextSibling();
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
return siblings
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
#splitBlockquoteWithList(blockquote, parentList, emptyListItem, listItemsAfter) {
|
|
2106
|
+
const blockquoteSiblingsAfterList = this.#getSiblingsAfter(parentList);
|
|
2107
|
+
const nonEmptyBlockquoteSiblings = blockquoteSiblingsAfterList.filter(sibling => !this.#isNodeEmpty(sibling));
|
|
2108
|
+
|
|
2109
|
+
const middleParagraph = $createParagraphNode();
|
|
2110
|
+
blockquote.insertAfter(middleParagraph);
|
|
2111
|
+
|
|
2112
|
+
const newList = $createListNode(parentList.getListType());
|
|
2113
|
+
|
|
2114
|
+
const newBlockquote = $createQuoteNode();
|
|
2115
|
+
middleParagraph.insertAfter(newBlockquote);
|
|
2116
|
+
newBlockquote.append(newList);
|
|
2117
|
+
|
|
2118
|
+
listItemsAfter.forEach(item => {
|
|
2119
|
+
newList.append(item);
|
|
2120
|
+
});
|
|
2121
|
+
|
|
2122
|
+
nonEmptyBlockquoteSiblings.forEach(sibling => {
|
|
2123
|
+
newBlockquote.append(sibling);
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
emptyListItem.remove();
|
|
2127
|
+
|
|
2128
|
+
this.#removeTrailingEmptyListItems(parentList);
|
|
2129
|
+
this.#removeTrailingEmptyNodes(newBlockquote);
|
|
2130
|
+
|
|
2131
|
+
if (parentList.getChildrenSize() === 0) {
|
|
2132
|
+
parentList.remove();
|
|
2133
|
+
|
|
2134
|
+
if (blockquote.getChildrenSize() === 0) {
|
|
2135
|
+
blockquote.remove();
|
|
2136
|
+
}
|
|
2137
|
+
} else {
|
|
2138
|
+
this.#removeTrailingEmptyNodes(blockquote);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
middleParagraph.selectStart();
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
#removeTrailingEmptyListItems(list) {
|
|
2145
|
+
const items = list.getChildren();
|
|
2146
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
2147
|
+
const item = items[i];
|
|
2148
|
+
if ($isListItemNode(item) && this.#isNodeEmpty(item)) {
|
|
2149
|
+
item.remove();
|
|
2150
|
+
} else {
|
|
2151
|
+
break
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
#removeTrailingEmptyNodes(blockquote) {
|
|
2157
|
+
const children = blockquote.getChildren();
|
|
2158
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
2159
|
+
const child = children[i];
|
|
2160
|
+
if (this.#isNodeEmpty(child)) {
|
|
2161
|
+
child.remove();
|
|
2162
|
+
} else {
|
|
2163
|
+
break
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
#splitBlockquote(blockquote, emptyParagraph, siblingsAfter) {
|
|
2169
|
+
const newParagraph = $createParagraphNode();
|
|
2170
|
+
blockquote.insertAfter(newParagraph);
|
|
2171
|
+
|
|
2172
|
+
const newBlockquote = $createQuoteNode();
|
|
2173
|
+
newParagraph.insertAfter(newBlockquote);
|
|
2174
|
+
|
|
2175
|
+
siblingsAfter.forEach(sibling => {
|
|
2176
|
+
newBlockquote.append(sibling);
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2179
|
+
emptyParagraph.remove();
|
|
2180
|
+
|
|
2181
|
+
this.#removeTrailingEmptyNodes(blockquote);
|
|
2182
|
+
this.#removeTrailingEmptyNodes(newBlockquote);
|
|
2183
|
+
|
|
2184
|
+
newParagraph.selectStart();
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
|
|
1923
2188
|
class Contents {
|
|
1924
2189
|
constructor(editorElement) {
|
|
1925
2190
|
this.editorElement = editorElement;
|
|
1926
2191
|
this.editor = editorElement.editor;
|
|
2192
|
+
|
|
2193
|
+
new FormatEscaper(editorElement).monitor();
|
|
1927
2194
|
}
|
|
1928
2195
|
|
|
1929
2196
|
insertHtml(html) {
|
|
@@ -1984,20 +2251,23 @@ class Contents {
|
|
|
1984
2251
|
if (isFormatAppliedFn(topLevelElement)) {
|
|
1985
2252
|
this.removeFormattingFromSelectedLines();
|
|
1986
2253
|
} else {
|
|
1987
|
-
this
|
|
2254
|
+
this.#insertNodeWrappingAllSelectedLines(newNodeFn);
|
|
1988
2255
|
}
|
|
1989
2256
|
});
|
|
1990
2257
|
}
|
|
1991
2258
|
|
|
1992
|
-
|
|
2259
|
+
toggleNodeWrappingAllSelectedNodes(isFormatAppliedFn, newNodeFn) {
|
|
1993
2260
|
this.editor.update(() => {
|
|
1994
2261
|
const selection = $getSelection();
|
|
1995
2262
|
if (!$isRangeSelection(selection)) return
|
|
1996
2263
|
|
|
1997
|
-
|
|
1998
|
-
|
|
2264
|
+
const topLevelElement = selection.anchor.getNode().getTopLevelElementOrThrow();
|
|
2265
|
+
|
|
2266
|
+
// Check if format is already applied
|
|
2267
|
+
if (isFormatAppliedFn(topLevelElement)) {
|
|
2268
|
+
this.#unwrap(topLevelElement);
|
|
1999
2269
|
} else {
|
|
2000
|
-
this.#
|
|
2270
|
+
this.#insertNodeWrappingAllSelectedNodes(newNodeFn);
|
|
2001
2271
|
}
|
|
2002
2272
|
});
|
|
2003
2273
|
}
|
|
@@ -2238,6 +2508,80 @@ class Contents {
|
|
|
2238
2508
|
return this.editorElement.selection
|
|
2239
2509
|
}
|
|
2240
2510
|
|
|
2511
|
+
#unwrap(node) {
|
|
2512
|
+
const children = node.getChildren();
|
|
2513
|
+
|
|
2514
|
+
children.forEach((child) => {
|
|
2515
|
+
node.insertBefore(child);
|
|
2516
|
+
});
|
|
2517
|
+
|
|
2518
|
+
node.remove();
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
#insertNodeWrappingAllSelectedNodes(newNodeFn) {
|
|
2522
|
+
this.editor.update(() => {
|
|
2523
|
+
const selection = $getSelection();
|
|
2524
|
+
if (!$isRangeSelection(selection)) return
|
|
2525
|
+
|
|
2526
|
+
const selectedNodes = selection.extract();
|
|
2527
|
+
if (selectedNodes.length === 0) return
|
|
2528
|
+
|
|
2529
|
+
const topLevelElements = new Set();
|
|
2530
|
+
selectedNodes.forEach((node) => {
|
|
2531
|
+
const topLevel = node.getTopLevelElementOrThrow();
|
|
2532
|
+
topLevelElements.add(topLevel);
|
|
2533
|
+
});
|
|
2534
|
+
|
|
2535
|
+
const elements = this.#removeTrailingEmptyParagraphs(Array.from(topLevelElements));
|
|
2536
|
+
if (elements.length === 0) return
|
|
2537
|
+
|
|
2538
|
+
const wrappingNode = newNodeFn();
|
|
2539
|
+
elements[0].insertBefore(wrappingNode);
|
|
2540
|
+
elements.forEach((element) => {
|
|
2541
|
+
wrappingNode.append(element);
|
|
2542
|
+
});
|
|
2543
|
+
|
|
2544
|
+
$setSelection(null);
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
#removeTrailingEmptyParagraphs(elements) {
|
|
2549
|
+
let lastNonEmptyIndex = elements.length - 1;
|
|
2550
|
+
|
|
2551
|
+
// Find the last non-empty paragraph
|
|
2552
|
+
while (lastNonEmptyIndex >= 0) {
|
|
2553
|
+
const element = elements[lastNonEmptyIndex];
|
|
2554
|
+
if (!$isParagraphNode(element) || !this.#isElementEmpty(element)) {
|
|
2555
|
+
break
|
|
2556
|
+
}
|
|
2557
|
+
lastNonEmptyIndex--;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
return elements.slice(0, lastNonEmptyIndex + 1)
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
#isElementEmpty(element) {
|
|
2564
|
+
// Check text content first
|
|
2565
|
+
if (element.getTextContent().trim() !== "") return false
|
|
2566
|
+
|
|
2567
|
+
// Check if it only contains line breaks
|
|
2568
|
+
const children = element.getChildren();
|
|
2569
|
+
return children.length === 0 || children.every(child => $isLineBreakNode(child))
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2572
|
+
#insertNodeWrappingAllSelectedLines(newNodeFn) {
|
|
2573
|
+
this.editor.update(() => {
|
|
2574
|
+
const selection = $getSelection();
|
|
2575
|
+
if (!$isRangeSelection(selection)) return
|
|
2576
|
+
|
|
2577
|
+
if (selection.isCollapsed()) {
|
|
2578
|
+
this.#wrapCurrentLine(selection, newNodeFn);
|
|
2579
|
+
} else {
|
|
2580
|
+
this.#wrapMultipleSelectedLines(selection, newNodeFn);
|
|
2581
|
+
}
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2241
2585
|
#wrapCurrentLine(selection, newNodeFn) {
|
|
2242
2586
|
const anchorNode = selection.anchor.getNode();
|
|
2243
2587
|
const topLevelElement = anchorNode.getTopLevelElementOrThrow();
|
|
@@ -2566,7 +2910,7 @@ class Clipboard {
|
|
|
2566
2910
|
|
|
2567
2911
|
if (!clipboardData) return false
|
|
2568
2912
|
|
|
2569
|
-
if (this.#isOnlyPlainTextPasted(clipboardData)) {
|
|
2913
|
+
if (this.#isOnlyPlainTextPasted(clipboardData) && !this.#isPastingIntoCodeBlock()) {
|
|
2570
2914
|
this.#pastePlainText(clipboardData);
|
|
2571
2915
|
event.preventDefault();
|
|
2572
2916
|
return true
|
|
@@ -2580,6 +2924,27 @@ class Clipboard {
|
|
|
2580
2924
|
return types.length === 1 && types[0] === "text/plain"
|
|
2581
2925
|
}
|
|
2582
2926
|
|
|
2927
|
+
#isPastingIntoCodeBlock() {
|
|
2928
|
+
let result = false;
|
|
2929
|
+
|
|
2930
|
+
this.editor.getEditorState().read(() => {
|
|
2931
|
+
const selection = $getSelection();
|
|
2932
|
+
if (!$isRangeSelection(selection)) return
|
|
2933
|
+
|
|
2934
|
+
let currentNode = selection.anchor.getNode();
|
|
2935
|
+
|
|
2936
|
+
while (currentNode) {
|
|
2937
|
+
if ($isCodeNode(currentNode)) {
|
|
2938
|
+
result = true;
|
|
2939
|
+
return
|
|
2940
|
+
}
|
|
2941
|
+
currentNode = currentNode.getParent();
|
|
2942
|
+
}
|
|
2943
|
+
});
|
|
2944
|
+
|
|
2945
|
+
return result
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2583
2948
|
#pastePlainText(clipboardData) {
|
|
2584
2949
|
const item = clipboardData.items[0];
|
|
2585
2950
|
item.getAsString((text) => {
|
|
@@ -3316,8 +3681,8 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
3316
3681
|
|
|
3317
3682
|
async #showPopover() {
|
|
3318
3683
|
this.popoverElement ??= await this.#buildPopover();
|
|
3684
|
+
this.#resetPopoverPosition();
|
|
3319
3685
|
await this.#filterOptions();
|
|
3320
|
-
this.#positionPopover();
|
|
3321
3686
|
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", true);
|
|
3322
3687
|
this.#selectFirstOption();
|
|
3323
3688
|
|
|
@@ -3372,19 +3737,29 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
3372
3737
|
const contentRect = this.#editorContentElement.getBoundingClientRect();
|
|
3373
3738
|
const verticalOffset = contentRect.top - editorRect.top;
|
|
3374
3739
|
|
|
3375
|
-
this.popoverElement.
|
|
3740
|
+
if (!this.popoverElement.hasAttribute("data-anchored")) {
|
|
3741
|
+
this.popoverElement.style.left = `${x}px`;
|
|
3742
|
+
this.popoverElement.toggleAttribute("data-anchored", true);
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3376
3745
|
this.popoverElement.style.top = `${y + verticalOffset}px`;
|
|
3377
3746
|
this.popoverElement.style.bottom = "auto";
|
|
3378
3747
|
|
|
3379
3748
|
const popoverRect = this.popoverElement.getBoundingClientRect();
|
|
3380
3749
|
const isClippedAtBottom = popoverRect.bottom > window.innerHeight;
|
|
3381
3750
|
|
|
3382
|
-
if (isClippedAtBottom) {
|
|
3383
|
-
this.popoverElement.style.
|
|
3384
|
-
this.popoverElement.style.
|
|
3751
|
+
if (isClippedAtBottom || this.popoverElement.hasAttribute("data-clipped-at-bottom")) {
|
|
3752
|
+
this.popoverElement.style.top = `${y + verticalOffset - popoverRect.height - fontSize}px`;
|
|
3753
|
+
this.popoverElement.style.bottom = "auto";
|
|
3754
|
+
this.popoverElement.toggleAttribute("data-clipped-at-bottom", true);
|
|
3385
3755
|
}
|
|
3386
3756
|
}
|
|
3387
3757
|
|
|
3758
|
+
#resetPopoverPosition() {
|
|
3759
|
+
this.popoverElement.removeAttribute("data-clipped-at-bottom");
|
|
3760
|
+
this.popoverElement.removeAttribute("data-anchored");
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3388
3763
|
async #hidePopover() {
|
|
3389
3764
|
this.#clearSelection();
|
|
3390
3765
|
this.popoverElement.classList.toggle("lexxy-prompt-menu--visible", false);
|
|
@@ -3410,6 +3785,7 @@ class LexicalPromptElement extends HTMLElement {
|
|
|
3410
3785
|
|
|
3411
3786
|
if (this.#editorContents.containsTextBackUntil(this.trigger)) {
|
|
3412
3787
|
await this.#showFilteredOptions();
|
|
3788
|
+
this.#positionPopover();
|
|
3413
3789
|
} else {
|
|
3414
3790
|
this.#hidePopover();
|
|
3415
3791
|
}
|