lexical-vue
Version:
An extensible Vue 3 web text-editor based on Lexical.
1,418 lines (1,392 loc) • 170 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
$createDecoratorBlockNode: () => $createDecoratorBlockNode,
$createHorizontalRuleNode: () => $createHorizontalRuleNode,
$isDecoratorBlockNode: () => $isDecoratorBlockNode,
$isHorizontalRuleNode: () => $isHorizontalRuleNode,
AutoEmbedOption: () => AutoEmbedOption,
DecoratorBlockNode: () => DecoratorBlockNode,
HR: () => HR,
HorizontalRuleNode: () => HorizontalRuleNode,
INSERT_EMBED_COMMAND: () => INSERT_EMBED_COMMAND,
INSERT_HORIZONTAL_RULE_COMMAND: () => INSERT_HORIZONTAL_RULE_COMMAND,
LexicalAutoEmbedPlugin: () => LexicalAutoEmbedPlugin_default,
LexicalAutoFocusPlugin: () => LexicalAutoFocusPlugin_default,
LexicalAutoLinkPlugin: () => LexicalAutoLinkPlugin_default,
LexicalAutoScrollPlugin: () => LexicalAutoScrollPlugin_default,
LexicalBlockWithAlignableContents: () => LexicalBlockWithAlignableContents_default,
LexicalCharacterLimitPlugin: () => LexicalCharacterLimitPlugin_default,
LexicalCheckListPlugin: () => LexicalCheckListPlugin_default,
LexicalClearEditorPlugin: () => LexicalClearEditorPlugin_default,
LexicalClickableLinkPlugin: () => LexicalClickableLinkPlugin_default,
LexicalCollaborationPlugin: () => LexicalCollaborationPlugin_default,
LexicalComposer: () => LexicalComposer_default,
LexicalContentEditable: () => LexicalContentEditable_default,
LexicalContextMenuPlugin: () => LexicalContextMenuPlugin_default,
LexicalDecoratedTeleports: () => LexicalDecoratedTeleports_default,
LexicalHashtagPlugin: () => LexicalHashtagPlugin_default,
LexicalHistoryPlugin: () => LexicalHistoryPlugin_default,
LexicalLinkPlugin: () => LexicalLinkPlugin_default,
LexicalListPlugin: () => LexicalListPlugin_default,
LexicalMarkdownShortcutPlugin: () => LexicalMarkdownShortcutPlugin_default,
LexicalNodeMenuPlugin: () => LexicalNodeMenuPlugin_default,
LexicalOnChangePlugin: () => LexicalOnChangePlugin_default,
LexicalPlainTextPlugin: () => LexicalPlainTextPlugin_default,
LexicalRichTextPlugin: () => LexicalRichTextPlugin_default,
LexicalTabIndentationPlugin: () => LexicalTabIndentationPlugin_default,
LexicalTablePlugin: () => LexicalTablePlugin_default,
LexicalTreeViewPlugin: () => LexicalTreeViewPlugin_default,
LexicalTypeaheadMenuPlugin: () => LexicalTypeaheadMenuPlugin_default,
MenuOption: () => MenuOption2,
URL_MATCHER: () => URL_MATCHER,
createLinkMatcherWithRegExp: () => createLinkMatcherWithRegExp,
mergePrevious: () => mergePrevious,
useBasicTypeaheadTriggerMatch: () => useBasicTypeaheadTriggerMatch,
useCanShowPlaceholder: () => useCanShowPlaceholder,
useCharacterLimit: () => useCharacterLimit,
useDecorators: () => useDecorators,
useEffect: () => useEffect,
useHistory: () => useHistory,
useLexicalCommandsLog: () => useLexicalCommandsLog,
useLexicalComposer: () => useLexicalComposer,
useLexicalIsTextContentEmpty: () => useLexicalIsTextContentEmpty,
useLexicalNodeSelection: () => useLexicalNodeSelection,
useLexicalTextEntity: () => useLexicalTextEntity,
useList: () => useList,
useMounted: () => useMounted,
usePlainTextSetup: () => usePlainTextSetup,
useRichTextSetup: () => useRichTextSetup,
useTableOfContents: () => useTableOfContents,
useYjsCollaboration: () => useYjsCollaboration,
useYjsFocusTracking: () => useYjsFocusTracking,
useYjsHistory: () => useYjsHistory
});
module.exports = __toCommonJS(index_exports);
// src/composables/useCanShowPlaceholder.ts
var import_vue2 = require("vue");
var import_text = require("@lexical/text");
var import_utils = require("@lexical/utils");
// src/composables/useMounted.ts
var import_vue = require("vue");
function useMounted(cb) {
let unregister;
(0, import_vue.onMounted)(() => {
unregister = cb();
});
(0, import_vue.onUnmounted)(() => {
unregister?.();
});
}
// src/composables/useCanShowPlaceholder.ts
function canShowPlaceholderFromCurrentEditorState(editor) {
const currentCanShowPlaceholder = editor.getEditorState().read((0, import_text.$canShowPlaceholderCurry)(editor.isComposing()));
return currentCanShowPlaceholder;
}
function useCanShowPlaceholder(editor) {
const initialState = editor.getEditorState().read((0, import_text.$canShowPlaceholderCurry)(editor.isComposing()));
const canShowPlaceholder = (0, import_vue2.ref)(initialState);
function resetCanShowPlaceholder() {
const currentCanShowPlaceholder = canShowPlaceholderFromCurrentEditorState(editor);
canShowPlaceholder.value = currentCanShowPlaceholder;
}
useMounted(() => {
return (0, import_utils.mergeRegister)(
editor.registerUpdateListener(() => {
resetCanShowPlaceholder();
}),
editor.registerEditableListener(() => {
resetCanShowPlaceholder();
})
);
});
return (0, import_vue2.readonly)(canShowPlaceholder);
}
// src/composables/useCharacterLimit.ts
var import_overflow = require("@lexical/overflow");
var import_text2 = require("@lexical/text");
var import_utils2 = require("@lexical/utils");
var import_lexical = require("lexical");
var import_tiny_invariant = __toESM(require("tiny-invariant"), 1);
var import_vue3 = require("vue");
function useCharacterLimit(editor, maxCharacters, optional = Object.freeze({})) {
(0, import_vue3.watchEffect)((onInvalidate) => {
if (!editor.hasNodes([import_overflow.OverflowNode])) {
(0, import_tiny_invariant.default)(
false,
"useCharacterLimit: OverflowNode not registered on editor"
);
}
const {
strlen = (input) => input.length,
// UTF-16
remainingCharacters = (_characters) => {
}
} = (0, import_vue3.toValue)(optional);
let text = editor.getEditorState().read(import_text2.$rootTextContent);
let lastComputedTextLength = 0;
const fn = (0, import_utils2.mergeRegister)(
editor.registerTextContentListener((currentText) => {
text = currentText;
}),
editor.registerUpdateListener(({ dirtyLeaves }) => {
const isComposing = editor.isComposing();
const hasDirtyLeaves = dirtyLeaves.size > 0;
if (isComposing || !hasDirtyLeaves)
return;
const textLength = strlen(text);
const textLengthAboveThreshold = textLength > (0, import_vue3.toValue)(maxCharacters) || lastComputedTextLength !== null && lastComputedTextLength > (0, import_vue3.toValue)(maxCharacters);
const diff = (0, import_vue3.toValue)(maxCharacters) - textLength;
remainingCharacters(diff);
if (lastComputedTextLength === null || textLengthAboveThreshold) {
const offset = findOffset(text, (0, import_vue3.toValue)(maxCharacters), strlen);
editor.update(
() => {
$wrapOverflowedNodes(offset);
},
{
tag: "history-merge"
}
);
}
lastComputedTextLength = textLength;
})
);
onInvalidate(() => {
fn();
});
});
}
function findOffset(text, maxCharacters, strlen) {
const Segmenter = Intl.Segmenter;
let offsetUtf16 = 0;
let offset = 0;
if (typeof Segmenter === "function") {
const segmenter = new Segmenter();
const graphemes = segmenter.segment(text);
for (const { segment: grapheme } of graphemes) {
const nextOffset = offset + strlen(grapheme);
if (nextOffset > maxCharacters)
break;
offset = nextOffset;
offsetUtf16 += grapheme.length;
}
} else {
const codepoints = Array.from(text);
const codepointsLength = codepoints.length;
for (let i = 0; i < codepointsLength; i++) {
const codepoint = codepoints[i];
const nextOffset = offset + strlen(codepoint);
if (nextOffset > maxCharacters)
break;
offset = nextOffset;
offsetUtf16 += codepoint.length;
}
}
return offsetUtf16;
}
function $wrapOverflowedNodes(offset) {
const dfsNodes = (0, import_utils2.$dfs)();
const dfsNodesLength = dfsNodes.length;
let accumulatedLength = 0;
for (let i = 0; i < dfsNodesLength; i += 1) {
const { node } = dfsNodes[i];
if ((0, import_overflow.$isOverflowNode)(node)) {
const previousLength = accumulatedLength;
const nextLength = accumulatedLength + node.getTextContentSize();
if (nextLength <= offset) {
const parent = node.getParent();
const previousSibling = node.getPreviousSibling();
const nextSibling = node.getNextSibling();
$unwrapNode(node);
const selection = (0, import_lexical.$getSelection)();
if ((0, import_lexical.$isRangeSelection)(selection) && (!selection.anchor.getNode().isAttached() || !selection.focus.getNode().isAttached())) {
if ((0, import_lexical.$isTextNode)(previousSibling))
previousSibling.select();
else if ((0, import_lexical.$isTextNode)(nextSibling))
nextSibling.select();
else if (parent !== null)
parent.select();
}
} else if (previousLength < offset) {
const descendant = node.getFirstDescendant();
const descendantLength = descendant !== null ? descendant.getTextContentSize() : 0;
const previousPlusDescendantLength = previousLength + descendantLength;
const firstDescendantIsSimpleText = (0, import_lexical.$isTextNode)(descendant) && descendant.isSimpleText();
const firstDescendantDoesNotOverflow = previousPlusDescendantLength <= offset;
if (firstDescendantIsSimpleText || firstDescendantDoesNotOverflow)
$unwrapNode(node);
}
} else if ((0, import_lexical.$isLeafNode)(node)) {
const previousAccumulatedLength = accumulatedLength;
accumulatedLength += node.getTextContentSize();
if (accumulatedLength > offset && !(0, import_overflow.$isOverflowNode)(node.getParent())) {
const previousSelection = (0, import_lexical.$getSelection)();
let overflowNode;
if (previousAccumulatedLength < offset && (0, import_lexical.$isTextNode)(node) && node.isSimpleText()) {
const [, overflowedText] = node.splitText(
offset - previousAccumulatedLength
);
overflowNode = $wrapNode(overflowedText);
} else {
overflowNode = $wrapNode(node);
}
if (previousSelection !== null)
(0, import_lexical.$setSelection)(previousSelection);
mergePrevious(overflowNode);
}
}
}
}
function $wrapNode(node) {
const overflowNode = (0, import_overflow.$createOverflowNode)();
node.insertBefore(overflowNode);
overflowNode.append(node);
return overflowNode;
}
function $unwrapNode(node) {
const children = node.getChildren();
const childrenLength = children.length;
for (let i = 0; i < childrenLength; i++)
node.insertBefore(children[i]);
node.remove();
return childrenLength > 0 ? children[childrenLength - 1] : null;
}
function mergePrevious(overflowNode) {
const previousNode = overflowNode.getPreviousSibling();
if (!(0, import_overflow.$isOverflowNode)(previousNode))
return;
const firstChild = overflowNode.getFirstChild();
const previousNodeChildren = previousNode.getChildren();
const previousNodeChildrenLength = previousNodeChildren.length;
if (firstChild === null) {
overflowNode.append(...previousNodeChildren);
} else {
for (let i = 0; i < previousNodeChildrenLength; i++)
firstChild.insertBefore(previousNodeChildren[i]);
}
const selection = (0, import_lexical.$getSelection)();
if ((0, import_lexical.$isRangeSelection)(selection)) {
const anchor = selection.anchor;
const anchorNode = anchor.getNode();
const focus = selection.focus;
const focusNode = anchor.getNode();
if (anchorNode.is(previousNode)) {
anchor.set(overflowNode.getKey(), anchor.offset, "element");
} else if (anchorNode.is(overflowNode)) {
anchor.set(
overflowNode.getKey(),
previousNodeChildrenLength + anchor.offset,
"element"
);
}
if (focusNode.is(previousNode)) {
focus.set(overflowNode.getKey(), focus.offset, "element");
} else if (focusNode.is(overflowNode)) {
focus.set(
overflowNode.getKey(),
previousNodeChildrenLength + focus.offset,
"element"
);
}
}
previousNode.remove();
}
// src/composables/useDecorators.ts
var import_vue4 = require("vue");
function useDecorators(editor) {
const decorators = (0, import_vue4.shallowRef)(editor.getDecorators());
useMounted(() => {
return editor.registerDecoratorListener((nextDecorators) => {
decorators.value = nextDecorators;
});
});
return (0, import_vue4.computed)(() => {
const decoratedTeleports = [];
const decoratorKeys = Object.keys((0, import_vue4.unref)(decorators));
for (let i = 0; i < decoratorKeys.length; i++) {
const nodeKey = decoratorKeys[i];
const vueDecorator = decorators.value[nodeKey];
const element = editor.getElementByKey(nodeKey);
if (element !== null) {
decoratedTeleports.push(
(0, import_vue4.h)(import_vue4.Teleport, {
to: element
}, vueDecorator)
);
}
}
return decoratedTeleports;
});
}
// src/composables/useEffect.ts
var import_vue5 = require("vue");
function useEffect(cb, options) {
(0, import_vue5.watchEffect)((onInvalidate) => {
const unregister = cb();
onInvalidate(() => unregister?.());
}, {
...options
});
}
// src/composables/useHistory.ts
var import_vue6 = require("vue");
var import_history = require("@lexical/history");
function useHistory(editor, externalHistoryState, delay) {
const historyState = (0, import_vue6.computed)(
() => (0, import_vue6.toValue)(externalHistoryState) || (0, import_history.createEmptyHistoryState)()
);
(0, import_vue6.watchEffect)((onInvalidate) => {
const unregisterListener = (0, import_history.registerHistory)((0, import_vue6.toValue)(editor), historyState.value, (0, import_vue6.toValue)(delay) || 1e3);
onInvalidate(unregisterListener);
});
}
// src/composables/useLexicalCommandsLog.ts
var import_lexical2 = require("lexical");
var import_vue7 = require("vue");
function useLexicalCommandsLog(editor) {
const loggedCommands = (0, import_vue7.ref)([]);
useMounted(() => {
const unregisterCommandListeners = /* @__PURE__ */ new Set();
for (const [command] of editor._commands) {
unregisterCommandListeners.add(
editor.registerCommand(
command,
(payload) => {
loggedCommands.value = [
...loggedCommands.value,
{
payload,
type: command.type ? command.type : "UNKNOWN"
}
];
if (loggedCommands.value.length > 10)
loggedCommands.value.shift();
return false;
},
import_lexical2.COMMAND_PRIORITY_HIGH
)
);
}
return () => {
unregisterCommandListeners.forEach((unregister) => unregister());
};
});
return (0, import_vue7.readonly)(loggedCommands);
}
// src/composables/useLexicalComposer.ts
var import_vue8 = require("vue");
var import_tiny_invariant2 = __toESM(require("tiny-invariant"), 1);
// src/composables/inject.ts
var LexicalEditorProviderKey = "LexicalEditorProviderKey";
// src/composables/useLexicalComposer.ts
function useLexicalComposer() {
const editor = (0, import_vue8.inject)(LexicalEditorProviderKey);
if (!editor) {
(0, import_tiny_invariant2.default)(
false,
"useLexicalComposer: cannot find a LexicalComposer"
);
}
return editor;
}
// src/composables/useLexicalIsTextContentEmpty.ts
var import_vue9 = require("vue");
var import_text3 = require("@lexical/text");
function useLexicalIsTextContentEmpty(editor, trim) {
const isEmpty = (0, import_vue9.ref)(
editor.getEditorState().read((0, import_text3.$isRootTextContentEmptyCurry)(editor.isComposing(), trim))
);
useMounted(() => {
return editor.registerUpdateListener(({ editorState }) => {
const isComposing = editor.isComposing();
isEmpty.value = editorState.read(
(0, import_text3.$isRootTextContentEmptyCurry)(isComposing, trim)
);
});
});
return (0, import_vue9.readonly)(isEmpty);
}
// src/composables/useLexicalNodeSelection.ts
var import_lexical3 = require("lexical");
var import_vue10 = require("vue");
function isNodeSelected(editor, key) {
return editor.getEditorState().read(() => {
const node = (0, import_lexical3.$getNodeByKey)(key);
if (node === null)
return false;
return node.isSelected();
});
}
function useLexicalNodeSelection(key) {
const editor = useLexicalComposer();
const isSelected = (0, import_vue10.ref)(isNodeSelected(editor, (0, import_vue10.toValue)(key)));
(0, import_vue10.watchEffect)((onInvalidate) => {
const unregisterListener = editor.registerUpdateListener(() => {
isSelected.value = isNodeSelected(editor, (0, import_vue10.toValue)(key));
});
onInvalidate(() => {
unregisterListener();
});
});
const setSelected = (selected) => {
editor.update(() => {
let selection = (0, import_lexical3.$getSelection)();
if (!(0, import_lexical3.$isNodeSelection)(selection)) {
selection = (0, import_lexical3.$createNodeSelection)();
(0, import_lexical3.$setSelection)(selection);
}
if ((0, import_lexical3.$isNodeSelection)(selection)) {
if (selected)
selection.add((0, import_vue10.toValue)(key));
else
selection.delete((0, import_vue10.toValue)(key));
}
});
};
const clearSelection = () => {
editor.update(() => {
const selection = (0, import_lexical3.$getSelection)();
if ((0, import_lexical3.$isNodeSelection)(selection))
selection.clear();
});
};
return {
isSelected: (0, import_vue10.readonly)(isSelected),
setSelected,
clearSelection
};
}
// src/composables/useLexicalTextEntity.ts
var import_text4 = require("@lexical/text");
var import_utils3 = require("@lexical/utils");
function useLexicalTextEntity(getMatch, targetNode, createNode) {
const editor = useLexicalComposer();
useMounted(() => {
return (0, import_utils3.mergeRegister)(
...(0, import_text4.registerLexicalTextEntity)(editor, getMatch, targetNode, createNode)
);
});
}
// src/composables/useList.ts
var import_list = require("@lexical/list");
var import_utils4 = require("@lexical/utils");
var import_lexical4 = require("lexical");
function useList(editor) {
useMounted(() => {
return (0, import_utils4.mergeRegister)(
editor.registerCommand(
import_list.INSERT_ORDERED_LIST_COMMAND,
() => {
(0, import_list.insertList)(editor, "number");
return true;
},
import_lexical4.COMMAND_PRIORITY_LOW
),
editor.registerCommand(
import_list.INSERT_UNORDERED_LIST_COMMAND,
() => {
(0, import_list.insertList)(editor, "bullet");
return true;
},
import_lexical4.COMMAND_PRIORITY_LOW
),
editor.registerCommand(
import_list.REMOVE_LIST_COMMAND,
() => {
(0, import_list.removeList)(editor);
return true;
},
import_lexical4.COMMAND_PRIORITY_LOW
),
editor.registerCommand(
import_lexical4.INSERT_PARAGRAPH_COMMAND,
() => {
const hasHandledInsertParagraph = (0, import_list.$handleListInsertParagraph)();
if (hasHandledInsertParagraph)
return true;
return false;
},
import_lexical4.COMMAND_PRIORITY_LOW
)
);
});
}
// src/composables/usePlainTextSetup.ts
var import_dragon = require("@lexical/dragon");
var import_plain_text = require("@lexical/plain-text");
var import_utils5 = require("@lexical/utils");
function usePlainTextSetup(editor) {
useMounted(() => {
return (0, import_utils5.mergeRegister)(
(0, import_plain_text.registerPlainText)(editor),
(0, import_dragon.registerDragonSupport)(editor)
);
});
}
// src/composables/useRichTextSetup.ts
var import_dragon2 = require("@lexical/dragon");
var import_rich_text = require("@lexical/rich-text");
var import_utils6 = require("@lexical/utils");
function useRichTextSetup(editor) {
useMounted(() => {
return (0, import_utils6.mergeRegister)(
(0, import_rich_text.registerRichText)(editor),
(0, import_dragon2.registerDragonSupport)(editor)
);
});
}
// src/composables/useTableOfContents.ts
var import_rich_text2 = require("@lexical/rich-text");
var import_utils7 = require("@lexical/utils");
var import_lexical5 = require("lexical");
var import_vue11 = require("vue");
function toEntry(heading) {
return [heading.getKey(), heading.getTextContent(), heading.getTag()];
}
function $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents) {
if (newHeading === null)
return currentTableOfContents;
const newEntry = toEntry(newHeading);
let newTableOfContents = [];
if (prevHeading === null) {
if (currentTableOfContents.length > 0 && currentTableOfContents[0][0] === newHeading.__key) {
return currentTableOfContents;
}
newTableOfContents = [newEntry, ...currentTableOfContents];
} else {
for (let i = 0; i < currentTableOfContents.length; i++) {
const key = currentTableOfContents[i][0];
newTableOfContents.push(currentTableOfContents[i]);
if (key === prevHeading.getKey() && key !== newHeading.getKey()) {
if (i + 1 < currentTableOfContents.length && currentTableOfContents[i + 1][0] === newHeading.__key) {
return currentTableOfContents;
}
newTableOfContents.push(newEntry);
}
}
}
return newTableOfContents;
}
function $deleteHeadingFromTableOfContents(key, currentTableOfContents) {
const newTableOfContents = [];
for (const heading of currentTableOfContents) {
if (heading[0] !== key)
newTableOfContents.push(heading);
}
return newTableOfContents;
}
function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
const newTableOfContents = [];
for (const oldHeading of currentTableOfContents) {
if (oldHeading[0] === heading.getKey())
newTableOfContents.push(toEntry(heading));
else
newTableOfContents.push(oldHeading);
}
return newTableOfContents;
}
function $updateHeadingPosition(prevHeading, heading, currentTableOfContents) {
const newTableOfContents = [];
const newEntry = toEntry(heading);
if (!prevHeading)
newTableOfContents.push(newEntry);
for (const oldHeading of currentTableOfContents) {
if (oldHeading[0] === heading.getKey())
continue;
newTableOfContents.push(oldHeading);
if (prevHeading && oldHeading[0] === prevHeading.getKey())
newTableOfContents.push(newEntry);
}
return newTableOfContents;
}
function $getPreviousHeading(node) {
let prevHeading = (0, import_utils7.$getNextRightPreorderNode)(node);
while (prevHeading !== null && !(0, import_rich_text2.$isHeadingNode)(prevHeading))
prevHeading = (0, import_utils7.$getNextRightPreorderNode)(prevHeading);
return prevHeading;
}
function useTableOfContents(editor) {
const tableOfContents = (0, import_vue11.ref)([]);
editor.getEditorState().read(() => {
const root = (0, import_lexical5.$getRoot)();
const rootChildren = root.getChildren();
for (const child of rootChildren) {
if ((0, import_rich_text2.$isHeadingNode)(child)) {
tableOfContents.value.push([
child.getKey(),
child.getTextContent(),
child.getTag()
]);
}
}
});
const removeRootUpdateListener = editor.registerUpdateListener(
({ editorState, dirtyElements }) => {
editorState.read(() => {
const updateChildHeadings = (node) => {
for (const child of node.getChildren()) {
if ((0, import_rich_text2.$isHeadingNode)(child)) {
const prevHeading = $getPreviousHeading(child);
tableOfContents.value = $updateHeadingPosition(
prevHeading,
child,
tableOfContents.value
);
} else if ((0, import_lexical5.$isElementNode)(child)) {
updateChildHeadings(child);
}
}
};
(0, import_lexical5.$getRoot)().getChildren().forEach((node) => {
if ((0, import_lexical5.$isElementNode)(node) && dirtyElements.get(node.__key))
updateChildHeadings(node);
});
});
}
);
const removeHeaderMutationListener = editor.registerMutationListener(
import_rich_text2.HeadingNode,
(mutatedNodes) => {
editor.getEditorState().read(() => {
for (const [nodeKey, mutation] of mutatedNodes) {
if (mutation === "created") {
const newHeading = (0, import_lexical5.$getNodeByKey)(nodeKey);
if (newHeading !== null) {
const prevHeading = $getPreviousHeading(newHeading);
tableOfContents.value = $insertHeadingIntoTableOfContents(
prevHeading,
newHeading,
tableOfContents.value
);
}
} else if (mutation === "destroyed") {
tableOfContents.value = $deleteHeadingFromTableOfContents(
nodeKey,
tableOfContents.value
);
} else if (mutation === "updated") {
const newHeading = (0, import_lexical5.$getNodeByKey)(nodeKey);
if (newHeading !== null) {
const prevHeading = $getPreviousHeading(newHeading);
tableOfContents.value = $updateHeadingPosition(
prevHeading,
newHeading,
tableOfContents.value
);
}
}
}
});
}
);
const removeTextNodeMutationListener = editor.registerMutationListener(
import_lexical5.TextNode,
(mutatedNodes) => {
editor.getEditorState().read(() => {
for (const [nodeKey, mutation] of mutatedNodes) {
if (mutation === "updated") {
const currNode = (0, import_lexical5.$getNodeByKey)(nodeKey);
if (currNode !== null) {
const parentNode = currNode.getParentOrThrow();
if ((0, import_rich_text2.$isHeadingNode)(parentNode)) {
tableOfContents.value = $updateHeadingInTableOfContents(
parentNode,
tableOfContents.value
);
}
}
}
}
});
}
);
useMounted(() => (0, import_utils7.mergeRegister)(
removeRootUpdateListener,
removeHeaderMutationListener,
removeTextNodeMutationListener
));
return tableOfContents;
}
// src/composables/useYjsCollaboration.ts
var import_utils8 = require("@lexical/utils");
var import_yjs = require("@lexical/yjs");
var import_lexical6 = require("lexical");
var import_yjs2 = require("yjs");
var import_vue12 = require("vue");
function useYjsCollaboration(editor, id, provider, docMap, name, color, shouldBootstrap, initialEditorState, excludedProperties, awarenessData) {
const isReloadingDoc = (0, import_vue12.ref)(false);
const doc = (0, import_vue12.ref)(docMap.get(id));
const binding = (0, import_vue12.computed)(() => (0, import_yjs.createBinding)(editor, provider, id, (0, import_vue12.toRaw)(doc.value), docMap, excludedProperties));
const connect = () => {
provider.connect();
};
const disconnect = () => {
try {
provider.disconnect();
} catch {
}
};
useEffect(() => {
const { root } = binding.value;
const { awareness } = provider;
const onStatus = ({ status }) => {
editor.dispatchCommand(import_yjs.CONNECTED_COMMAND, status === "connected");
};
const onSync = (isSynced) => {
if (shouldBootstrap && isSynced && root.isEmpty() && root._xmlText._length === 0 && isReloadingDoc.value === false) {
initializeEditor(editor, initialEditorState);
}
isReloadingDoc.value = false;
};
const onAwarenessUpdate = () => {
(0, import_yjs.syncCursorPositions)(binding.value, provider);
};
const onYjsTreeChanges = (events, transaction) => {
const origin = transaction.origin;
if ((0, import_vue12.toRaw)(origin) !== binding.value) {
const isFromUndoManger = origin instanceof import_yjs2.UndoManager;
(0, import_yjs.syncYjsChangesToLexical)(binding.value, provider, events, isFromUndoManger);
}
};
(0, import_yjs.initLocalState)(
provider,
name,
color,
document.activeElement === editor.getRootElement(),
awarenessData || {}
);
const onProviderDocReload = (ydoc) => {
clearEditorSkipCollab(editor, binding.value);
doc.value = ydoc;
docMap.set(id, ydoc);
isReloadingDoc.value = true;
};
provider.on("reload", onProviderDocReload);
provider.on("status", onStatus);
provider.on("sync", onSync);
awareness.on("update", onAwarenessUpdate);
root.getSharedType().observeDeep(onYjsTreeChanges);
const removeListener = editor.registerUpdateListener(
({ prevEditorState, editorState, dirtyLeaves, dirtyElements, normalizedNodes, tags }) => {
if (tags.has("skip-collab") === false) {
(0, import_yjs.syncLexicalUpdateToYjs)(
binding.value,
provider,
prevEditorState,
editorState,
dirtyElements,
dirtyLeaves,
normalizedNodes,
tags
);
}
}
);
connect();
return () => {
if (isReloadingDoc.value === false)
disconnect();
provider.off("sync", onSync);
provider.off("status", onStatus);
provider.off("reload", onProviderDocReload);
awareness.off("update", onAwarenessUpdate);
root.getSharedType().unobserveDeep(onYjsTreeChanges);
docMap.delete(id);
removeListener();
};
});
useEffect(() => {
return editor.registerCommand(
import_yjs.TOGGLE_CONNECT_COMMAND,
(payload) => {
if (connect !== void 0 && disconnect !== void 0) {
const shouldConnect = payload;
if (shouldConnect) {
console.log("Collaboration connected!");
connect();
} else {
console.log("Collaboration disconnected!");
disconnect();
}
}
return true;
},
import_lexical6.COMMAND_PRIORITY_EDITOR
);
});
return binding;
}
function useYjsFocusTracking(editor, provider, name, color, awarenessData) {
useEffect(() => {
return (0, import_utils8.mergeRegister)(
editor.registerCommand(
import_lexical6.FOCUS_COMMAND,
() => {
(0, import_yjs.setLocalStateFocus)(provider, name, color, true, awarenessData || {});
return false;
},
import_lexical6.COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
import_lexical6.BLUR_COMMAND,
() => {
(0, import_yjs.setLocalStateFocus)(provider, name, color, false, awarenessData || {});
return false;
},
import_lexical6.COMMAND_PRIORITY_EDITOR
)
);
});
}
function useYjsHistory(editor, binding) {
const undoManager = (0, import_vue12.computed)(() => (0, import_yjs.createUndoManager)(binding, binding.root.getSharedType()));
useEffect(() => {
const undo = () => {
undoManager.value.undo();
};
const redo = () => {
undoManager.value.redo();
};
return (0, import_utils8.mergeRegister)(
editor.registerCommand(
import_lexical6.UNDO_COMMAND,
() => {
undo();
return true;
},
import_lexical6.COMMAND_PRIORITY_EDITOR
),
editor.registerCommand(
import_lexical6.REDO_COMMAND,
() => {
redo();
return true;
},
import_lexical6.COMMAND_PRIORITY_EDITOR
)
);
});
const clearHistory = () => {
undoManager.value.clear();
};
return clearHistory;
}
function initializeEditor(editor, initialEditorState) {
editor.update(
() => {
const root = (0, import_lexical6.$getRoot)();
if (root.isEmpty()) {
if (initialEditorState) {
switch (typeof initialEditorState) {
case "string": {
const parsedEditorState = editor.parseEditorState(initialEditorState);
editor.setEditorState(parsedEditorState, { tag: "history-merge" });
break;
}
case "object": {
editor.setEditorState(initialEditorState, { tag: "history-merge" });
break;
}
case "function": {
editor.update(
() => {
const root1 = (0, import_lexical6.$getRoot)();
if (root1.isEmpty())
initialEditorState(editor);
},
{ tag: "history-merge" }
);
break;
}
}
} else {
const paragraph = (0, import_lexical6.$createParagraphNode)();
root.append(paragraph);
const { activeElement } = document;
if ((0, import_lexical6.$getSelection)() !== null || activeElement !== null && activeElement === editor.getRootElement()) {
paragraph.select();
}
}
}
},
{
tag: "history-merge"
}
);
}
function clearEditorSkipCollab(editor, binding) {
editor.update(
() => {
const root = (0, import_lexical6.$getRoot)();
root.clear();
root.select();
},
{
tag: "skip-collab"
}
);
if (binding.cursors == null)
return;
const cursors = binding.cursors;
if (cursors == null)
return;
const cursorsContainer = binding.cursorsContainer;
if (cursorsContainer == null)
return;
const cursorsArr = Array.from(cursors.values());
for (let i = 0; i < cursorsArr.length; i++) {
const cursor = cursorsArr[i];
const selection = cursor.selection;
if (selection && selection.selections !== null) {
const selections = selection.selections;
for (let j = 0; j < selections.length; j++) cursorsContainer.removeChild(selections[i]);
}
}
}
// src/components/LexicalDecoratedTeleports.ts
var import_vue13 = require("vue");
var LexicalDecoratedTeleports_default = (0, import_vue13.defineComponent)({
name: "LexicalDecoratedTeleports",
setup() {
const editor = useLexicalComposer();
const decorators = useDecorators(editor);
return () => decorators.value;
}
});
// src/components/LexicalContentEditable.vue
var import_vue17 = require("vue");
var import_vue18 = require("vue");
var import_vue19 = require("vue");
// src/components/LexicalContentEditableElement.vue
var import_vue14 = require("vue");
var import_vue15 = require("vue");
var import_vue16 = require("vue");
var _hoisted_1 = ["aria-activedescendant", "aria-autocomplete", "aria-controls", "aria-describedby", "aria-expanded", "aria-label", "aria-labelledby", "aria-multiline", "aria-owns", "aria-readonly", "aria-required", "autocapitalize", "contenteditable", "data-testid", "role", "spellcheck", "tabindex"];
var _sfc_main = /* @__PURE__ */ (0, import_vue14.defineComponent)({
__name: "LexicalContentEditableElement",
props: {
editor: {},
ariaActiveDescendant: {},
ariaAutoComplete: {},
ariaControls: {},
ariaDescribedBy: {},
ariaErrorMessage: {},
ariaExpanded: { type: [Boolean, String] },
ariaInvalid: { type: [Boolean, String] },
ariaLabel: {},
ariaLabelledBy: {},
ariaMultiline: { type: [Boolean, String] },
ariaOwns: {},
ariaRequired: { type: [Boolean, String] },
dataTestid: {},
innerHTML: {},
class: {},
style: { type: [Boolean, null, String, Object, Array] },
accesskey: {},
contenteditable: { type: [Boolean, String] },
contextmenu: {},
dir: {},
draggable: { type: [Boolean, String] },
hidden: { type: [Boolean, String] },
id: {},
inert: { type: [Boolean, String] },
lang: {},
spellcheck: { type: [Boolean, String], default: true },
tabindex: {},
title: {},
translate: {},
radiogroup: {},
role: { default: "textbox" },
about: {},
datatype: {},
inlist: {},
property: {},
resource: {},
typeof: {},
vocab: {},
autocapitalize: {},
autocorrect: {},
autosave: {},
color: {},
itemprop: {},
itemscope: { type: [Boolean, String] },
itemtype: {},
itemid: {},
itemref: {},
results: {},
security: {},
unselectable: {},
inputmode: {},
is: {},
"aria-activedescendant": {},
"aria-atomic": { type: [Boolean, String] },
"aria-autocomplete": {},
"aria-busy": { type: [Boolean, String] },
"aria-checked": { type: [Boolean, String] },
"aria-colcount": {},
"aria-colindex": {},
"aria-colspan": {},
"aria-controls": {},
"aria-current": { type: [Boolean, String] },
"aria-describedby": {},
"aria-details": {},
"aria-disabled": { type: [Boolean, String] },
"aria-dropeffect": {},
"aria-errormessage": {},
"aria-expanded": { type: [Boolean, String] },
"aria-flowto": {},
"aria-grabbed": { type: [Boolean, String] },
"aria-haspopup": { type: [Boolean, String] },
"aria-hidden": { type: [Boolean, String] },
"aria-invalid": { type: [Boolean, String] },
"aria-keyshortcuts": {},
"aria-label": {},
"aria-labelledby": {},
"aria-level": {},
"aria-live": {},
"aria-modal": { type: [Boolean, String] },
"aria-multiline": { type: [Boolean, String] },
"aria-multiselectable": { type: [Boolean, String] },
"aria-orientation": {},
"aria-owns": {},
"aria-placeholder": {},
"aria-posinset": {},
"aria-pressed": { type: [Boolean, String] },
"aria-readonly": { type: [Boolean, String] },
"aria-relevant": {},
"aria-required": { type: [Boolean, String] },
"aria-roledescription": {},
"aria-rowcount": {},
"aria-rowindex": {},
"aria-rowspan": {},
"aria-selected": { type: [Boolean, String] },
"aria-setsize": {},
"aria-sort": {},
"aria-valuemax": {},
"aria-valuemin": {},
"aria-valuenow": {},
"aria-valuetext": {}
},
setup(__props) {
const props = __props;
const root = (0, import_vue16.ref)(null);
const isEditable = (0, import_vue16.ref)(props.editor.isEditable());
const otherAttrs = (0, import_vue16.computed)(() => {
const ariaAttrs = {};
if (props.ariaInvalid != null)
ariaAttrs["aria-invalid"] = props.ariaInvalid;
if (props.ariaErrorMessage != null)
ariaAttrs["aria-errormessage"] = props.ariaErrorMessage;
return {
...props,
...ariaAttrs
};
});
useMounted(() => {
function handleRef(rootElement) {
if (rootElement && rootElement.ownerDocument && rootElement.ownerDocument.defaultView) {
props.editor.setRootElement(rootElement);
} else {
props.editor.setRootElement(null);
}
}
handleRef(root.value);
isEditable.value = props.editor.isEditable();
return props.editor.registerEditableListener((currentIsEditable) => {
isEditable.value = currentIsEditable;
});
});
return (_ctx, _cache) => {
return (0, import_vue15.openBlock)(), (0, import_vue15.createElementBlock)("div", (0, import_vue15.mergeProps)({
ref_key: "root",
ref: root
}, otherAttrs.value, {
"aria-activedescendant": isEditable.value ? _ctx.ariaActiveDescendant : void 0,
"aria-autocomplete": isEditable.value ? _ctx.ariaAutoComplete : "none",
"aria-controls": isEditable.value ? _ctx.ariaControls : void 0,
"aria-describedby": _ctx.ariaDescribedBy,
"aria-expanded": isEditable.value && _ctx.role === "combobox" ? !!_ctx.ariaExpanded : void 0,
"aria-label": _ctx.ariaLabel,
"aria-labelledby": _ctx.ariaLabelledBy,
"aria-multiline": _ctx.ariaMultiline,
"aria-owns": isEditable.value ? _ctx.ariaOwns : void 0,
"aria-readonly": isEditable.value ? void 0 : true,
"aria-required": _ctx.ariaRequired,
autocapitalize: _ctx.autocapitalize,
contenteditable: isEditable.value,
"data-testid": _ctx.dataTestid,
role: isEditable.value ? _ctx.role : void 0,
spellcheck: _ctx.spellcheck,
style: _ctx.style,
tabindex: _ctx.tabindex
}), null, 16, _hoisted_1);
};
}
});
var LexicalContentEditableElement_default = _sfc_main;
// src/components/LexicalContentEditable.vue
var _hoisted_12 = {
key: 0,
"aria-hidden": "true"
};
var _sfc_main2 = /* @__PURE__ */ (0, import_vue17.defineComponent)({
__name: "LexicalContentEditable",
props: {
ariaActiveDescendant: {},
ariaAutoComplete: {},
ariaControls: {},
ariaDescribedBy: {},
ariaErrorMessage: {},
ariaExpanded: {},
ariaInvalid: {},
ariaLabel: {},
ariaLabelledBy: {},
ariaMultiline: {},
ariaOwns: {},
ariaRequired: {},
dataTestid: {},
innerHTML: {},
class: {},
style: { type: [Boolean, null, String, Object, Array] },
accesskey: {},
contenteditable: { type: [Boolean, String] },
contextmenu: {},
dir: {},
draggable: { type: [Boolean, String] },
hidden: { type: [Boolean, String] },
id: {},
inert: { type: [Boolean, String] },
lang: {},
spellcheck: { type: [Boolean, String], default: true },
tabindex: {},
title: {},
translate: {},
radiogroup: {},
role: { default: "textbox" },
about: {},
datatype: {},
inlist: {},
property: {},
resource: {},
typeof: {},
vocab: {},
autocapitalize: {},
autocorrect: {},
autosave: {},
color: {},
itemprop: {},
itemscope: { type: [Boolean, String] },
itemtype: {},
itemid: {},
itemref: {},
results: {},
security: {},
unselectable: {},
inputmode: {},
is: {},
"aria-activedescendant": {},
"aria-atomic": { type: [Boolean, String] },
"aria-autocomplete": {},
"aria-busy": { type: [Boolean, String] },
"aria-checked": { type: [Boolean, String] },
"aria-colcount": {},
"aria-colindex": {},
"aria-colspan": {},
"aria-controls": {},
"aria-current": { type: [Boolean, String] },
"aria-describedby": {},
"aria-details": {},
"aria-disabled": { type: [Boolean, String] },
"aria-dropeffect": {},
"aria-errormessage": {},
"aria-expanded": { type: [Boolean, String] },
"aria-flowto": {},
"aria-grabbed": { type: [Boolean, String] },
"aria-haspopup": { type: [Boolean, String] },
"aria-hidden": { type: [Boolean, String] },
"aria-invalid": { type: [Boolean, String] },
"aria-keyshortcuts": {},
"aria-label": {},
"aria-labelledby": {},
"aria-level": {},
"aria-live": {},
"aria-modal": { type: [Boolean, String] },
"aria-multiline": { type: [Boolean, String] },
"aria-multiselectable": { type: [Boolean, String] },
"aria-orientation": {},
"aria-owns": {},
"aria-placeholder": {},
"aria-posinset": {},
"aria-pressed": { type: [Boolean, String] },
"aria-readonly": { type: [Boolean, String] },
"aria-relevant": {},
"aria-required": { type: [Boolean, String] },
"aria-roledescription": {},
"aria-rowcount": {},
"aria-rowindex": {},
"aria-rowspan": {},
"aria-selected": { type: [Boolean, String] },
"aria-setsize": {},
"aria-sort": {},
"aria-valuemax": {},
"aria-valuemin": {},
"aria-valuenow": {},
"aria-valuetext": {}
},
setup(__props) {
const editor = useLexicalComposer();
const isEditable = (0, import_vue19.ref)(false);
const showPlaceholder = useCanShowPlaceholder(editor);
useMounted(() => {
isEditable.value = editor.isEditable();
return editor.registerEditableListener((currentIsEditable) => {
isEditable.value = currentIsEditable;
});
});
return (_ctx, _cache) => {
return (0, import_vue18.openBlock)(), (0, import_vue18.createElementBlock)(
import_vue18.Fragment,
null,
[
(0, import_vue18.createVNode)(LexicalContentEditableElement_default, (0, import_vue18.mergeProps)({ editor: (0, import_vue18.unref)(editor) }, _ctx.$props), null, 16, ["editor"]),
(0, import_vue18.unref)(showPlaceholder) ? ((0, import_vue18.openBlock)(), (0, import_vue18.createElementBlock)("div", _hoisted_12, [
(0, import_vue18.renderSlot)(_ctx.$slots, "placeholder")
])) : (0, import_vue18.createCommentVNode)("v-if", true)
],
64
/* STABLE_FRAGMENT */
);
};
}
});
var LexicalContentEditable_default = _sfc_main2;
// src/components/LexicalPlainTextPlugin.vue
var import_vue20 = require("vue");
var import_vue21 = require("vue");
var _sfc_main3 = /* @__PURE__ */ (0, import_vue20.defineComponent)({
__name: "LexicalPlainTextPlugin",
setup(__props) {
const editor = useLexicalComposer();
const showPlaceholder = useCanShowPlaceholder(editor);
usePlainTextSetup(editor);
return (_ctx, _cache) => {
return (0, import_vue21.openBlock)(), (0, import_vue21.createElementBlock)(
import_vue21.Fragment,
null,
[
(0, import_vue21.unref)(showPlaceholder) ? (0, import_vue21.renderSlot)(_ctx.$slots, "placeholder", { key: 0 }) : (0, import_vue21.createCommentVNode)("v-if", true),
(0, import_vue21.renderSlot)(_ctx.$slots, "contentEditable"),
(0, import_vue21.createVNode)((0, import_vue21.unref)(LexicalDecoratedTeleports_default))
],
64
/* STABLE_FRAGMENT */
);
};
}
});
var LexicalPlainTextPlugin_default = _sfc_main3;
// src/components/LexicalComposer.vue
var import_vue22 = require("vue");
var import_vue23 = require("vue");
var import_vue24 = require("vue");
var import_lexical7 = require("lexical");
var _sfc_main4 = /* @__PURE__ */ (0, import_vue22.defineComponent)({
__name: "LexicalComposer",
props: {
initialConfig: {}
},
emits: ["error"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const HISTORY_MERGE_OPTIONS = { tag: "history-merge" };
const {
theme,
namespace,
nodes,
onError,
editorState: initialEditorState,
html
} = props.initialConfig;
const editor = (0, import_lexical7.createEditor)({
editable: props.initialConfig.editable,
html,
namespace,
nodes,
theme,
onError(error) {
emit("error", error, editor);
onError?.(error, editor);
}
});
initializeEditor2(editor, initialEditorState);
function initializeEditor2(editor2, initialEditorState2) {
if (initialEditorState2 === null)
return;
if (initialEditorState2 === void 0) {
editor2.update(() => {
const root = (0, import_lexical7.$getRoot)();
if (root.isEmpty()) {
const paragraph = (0, import_lexical7.$createParagraphNode)();
root.append(paragraph);
const activeElement = document.activeElement;
if ((0, import_lexical7.$getSelection)() !== null || activeElement !== null && activeElement === editor2.getRootElement()) {
paragraph.select();
}
}
}, HISTORY_MERGE_OPTIONS);
} else if (initialEditorState2 !== null) {
switch (typeof initialEditorState2) {
case "string": {
const parsedEditorState = editor2.parseEditorState(initialEditorState2);
editor2.setEditorState(parsedEditorState, HISTORY_MERGE_OPTIONS);
break;
}
case "object": {
editor2.setEditorState(initialEditorState2, HISTORY_MERGE_OPTIONS);
break;
}
case "function": {
editor2.update(() => {
const root = (0, import_lexical7.$getRoot)();
if (root.isEmpty())
initialEditorState2(editor2);
}, HISTORY_MERGE_OPTIONS);
break;
}
}
}
}
(0, import_vue24.provide)(LexicalEditorProviderKey, editor);
(0, import_vue24.onMounted)(() => {
const isEditable = props.initialConfig.editable;
editor.setEditable(isEditab