phx-react
Version:
PHX REACT
162 lines • 6.89 kB
JavaScript
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $getSelection, $isElementNode, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_CRITICAL, INSERT_PARAGRAPH_COMMAND, } from 'lexical';
import { $isListItemNode } from '@lexical/list';
import { $getSelectionStyleValueForProperty, $patchStyleText, getCSSFromStyleObject, getStyleObjectFromCSS, } from '@lexical/selection';
import { useEffect } from 'react';
/**
* Extracts font size from a CSS style string.
* @param style CSS style text to parse.
* @returns Font size value, or an empty string when absent.
*/
function getFontSizeFromStyle(style) {
var _a;
return ((_a = getStyleObjectFromCSS(style)) === null || _a === void 0 ? void 0 : _a['font-size']) || '';
}
/**
* Gets the font size that should be preserved for a collapsed selection.
* @returns Font size value, or an empty string when no font size is available.
*/
function getCollapsedSelectionFontSize() {
const selection = $getSelection();
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
return '';
}
const selectionFontSize = $getSelectionStyleValueForProperty(selection, 'font-size', '');
if (selectionFontSize) {
return selectionFontSize;
}
const anchor = selection.anchor;
const anchorNode = anchor.getNode();
if ($isTextNode(anchorNode)) {
return getFontSizeFromStyle(anchorNode.getStyle());
}
if ($isElementNode(anchorNode)) {
const previousNode = anchor.offset > 0 ? anchorNode.getChildAtIndex(anchor.offset - 1) : null;
const nextNode = anchorNode.getChildAtIndex(anchor.offset);
if ($isTextNode(previousNode)) {
return getFontSizeFromStyle(previousNode.getStyle());
}
if ($isTextNode(nextNode)) {
return getFontSizeFromStyle(nextNode.getStyle());
}
}
return '';
}
/**
* Checks whether the current selection is inside an empty list item.
* @returns True when the collapsed selection is in an empty list item.
*/
function isSelectionInEmptyListItem() {
const selection = $getSelection();
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
return false;
}
const anchorNode = selection.anchor.getNode();
if ($isListItemNode(anchorNode) && anchorNode.getChildrenSize() === 0) {
return true;
}
if (!$isTextNode(anchorNode)) {
return false;
}
const parent = anchorNode.getParent();
return ($isListItemNode(parent) &&
parent.getChildren().every((node) => $isTextNode(node) && node.getTextContent().trim() === ''));
}
/**
* Syncs the inserted block and collapsed selection to a font size after Lexical handles list exit.
* @param editor Active Lexical editor.
* @param fontSize Font size that should drive the next caret height.
*/
function syncListExitFontSize(editor, fontSize) {
const applyFontSize = () => {
editor.update(() => {
const selection = $getSelection();
if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
return;
}
const anchorNode = selection.anchor.getNode();
const targetNode = $isElementNode(anchorNode)
? anchorNode
: $isTextNode(anchorNode)
? anchorNode.getTopLevelElement()
: null;
if (!$isElementNode(targetNode)) {
return;
}
const targetNodeKey = targetNode.getKey();
const blockStyles = getStyleObjectFromCSS(targetNode.getStyle());
const fontSizeStyle = getCSSFromStyleObject({ 'font-size': fontSize });
targetNode.setStyle(getCSSFromStyleObject({
...blockStyles,
'font-size': fontSize,
}));
targetNode.setTextStyle(fontSizeStyle);
$patchStyleText(selection, {
'font-size': fontSize,
});
const applyFontSizeToElement = () => {
const targetElement = editor.getElementByKey(targetNodeKey);
targetElement === null || targetElement === void 0 ? void 0 : targetElement.style.setProperty('font-size', fontSize);
};
if (typeof queueMicrotask === 'function') {
queueMicrotask(applyFontSizeToElement);
}
requestAnimationFrame(applyFontSizeToElement);
});
};
if (typeof queueMicrotask === 'function') {
queueMicrotask(applyFontSize);
}
else {
setTimeout(applyFontSize);
}
}
/**
* Preserves font size when inserting a paragraph with Enter.
* @returns Null because the plugin renders no UI.
*/
export default function PreserveFontSizeOnEnterPlugin() {
const [editor] = useLexicalComposerContext();
useEffect(() => {
return editor.registerCommand(INSERT_PARAGRAPH_COMMAND, () => {
const selection = $getSelection();
const fontSize = getCollapsedSelectionFontSize();
if (!$isRangeSelection(selection) || !selection.isCollapsed() || !fontSize) {
return false;
}
if (isSelectionInEmptyListItem()) {
syncListExitFontSize(editor, fontSize);
return false;
}
const insertedBlock = selection.insertParagraph();
if ($isElementNode(insertedBlock)) {
const insertedBlockKey = insertedBlock.getKey();
const blockStyles = getStyleObjectFromCSS(insertedBlock.getStyle());
const fontSizeStyle = getCSSFromStyleObject({ 'font-size': fontSize });
insertedBlock.setStyle(getCSSFromStyleObject({
...blockStyles,
'font-size': fontSize,
}));
insertedBlock.setTextStyle(fontSizeStyle);
insertedBlock.selectStart();
const applyFontSizeToInsertedElement = () => {
const insertedElement = editor.getElementByKey(insertedBlockKey);
insertedElement === null || insertedElement === void 0 ? void 0 : insertedElement.style.setProperty('font-size', fontSize);
};
if (typeof queueMicrotask === 'function') {
queueMicrotask(applyFontSizeToInsertedElement);
}
requestAnimationFrame(applyFontSizeToInsertedElement);
}
const nextSelection = $getSelection();
if ($isRangeSelection(nextSelection) && nextSelection.isCollapsed()) {
$patchStyleText(nextSelection, {
'font-size': fontSize,
});
}
return true;
}, COMMAND_PRIORITY_CRITICAL);
}, [editor]);
return null;
}
//# sourceMappingURL=index.js.map