Skip to content

Commit f8efc52

Browse files
kamilpitulaivailop7etrepum
authored
[lexical-selection][lexical-playground] Bug Fix: unbulleting an image doesn't work facebook#5698 (facebook#7568)
Co-authored-by: Ivaylo Pavlov <ivailo90@gmail.com> Co-authored-by: Bob Ippolito <bob@redivi.com>
1 parent f20b1c6 commit f8efc52

2 files changed

Lines changed: 81 additions & 31 deletions

File tree

packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
$getRoot,
3838
$getSelection,
3939
$isElementNode,
40+
$isNodeSelection,
4041
$isRangeSelection,
4142
$isRootOrShadowRoot,
4243
CAN_REDO_COMMAND,
@@ -48,6 +49,7 @@ import {
4849
HISTORIC_TAG,
4950
INDENT_CONTENT_COMMAND,
5051
LexicalEditor,
52+
LexicalNode,
5153
NodeKey,
5254
OUTDENT_CONTENT_COMMAND,
5355
REDO_COMMAND,
@@ -468,6 +470,21 @@ function ElementFormatDropdown({
468470
);
469471
}
470472

473+
function $findTopLevelElement(node: LexicalNode) {
474+
let topLevelElement =
475+
node.getKey() === 'root'
476+
? node
477+
: $findMatchingParent(node, (e) => {
478+
const parent = e.getParent();
479+
return parent !== null && $isRootOrShadowRoot(parent);
480+
});
481+
482+
if (topLevelElement === null) {
483+
topLevelElement = node.getTopLevelElementOrThrow();
484+
}
485+
return topLevelElement;
486+
}
487+
471488
export default function ToolbarPlugin({
472489
editor,
473490
activeEditor,
@@ -486,6 +503,37 @@ export default function ToolbarPlugin({
486503
const [isEditable, setIsEditable] = useState(() => editor.isEditable());
487504
const {toolbarState, updateToolbarState} = useToolbarState();
488505

506+
const $handleHeadingNode = useCallback(
507+
(selectedElement: LexicalNode) => {
508+
const type = $isHeadingNode(selectedElement)
509+
? selectedElement.getTag()
510+
: selectedElement.getType();
511+
512+
if (type in blockTypeToBlockName) {
513+
updateToolbarState(
514+
'blockType',
515+
type as keyof typeof blockTypeToBlockName,
516+
);
517+
}
518+
},
519+
[updateToolbarState],
520+
);
521+
522+
const $handleCodeNode = useCallback(
523+
(element: LexicalNode) => {
524+
if ($isCodeNode(element)) {
525+
const language =
526+
element.getLanguage() as keyof typeof CODE_LANGUAGE_MAP;
527+
updateToolbarState(
528+
'codeLanguage',
529+
language ? CODE_LANGUAGE_MAP[language] || language : '',
530+
);
531+
return;
532+
}
533+
},
534+
[updateToolbarState],
535+
);
536+
489537
const $updateToolbar = useCallback(() => {
490538
const selection = $getSelection();
491539
if ($isRangeSelection(selection)) {
@@ -502,18 +550,7 @@ export default function ToolbarPlugin({
502550
}
503551

504552
const anchorNode = selection.anchor.getNode();
505-
let element =
506-
anchorNode.getKey() === 'root'
507-
? anchorNode
508-
: $findMatchingParent(anchorNode, (e) => {
509-
const parent = e.getParent();
510-
return parent !== null && $isRootOrShadowRoot(parent);
511-
});
512-
513-
if (element === null) {
514-
element = anchorNode.getTopLevelElementOrThrow();
515-
}
516-
553+
const element = $findTopLevelElement(anchorNode);
517554
const elementKey = element.getKey();
518555
const elementDOM = activeEditor.getElementByKey(elementKey);
519556

@@ -545,26 +582,11 @@ export default function ToolbarPlugin({
545582

546583
updateToolbarState('blockType', type);
547584
} else {
548-
const type = $isHeadingNode(element)
549-
? element.getTag()
550-
: element.getType();
551-
if (type in blockTypeToBlockName) {
552-
updateToolbarState(
553-
'blockType',
554-
type as keyof typeof blockTypeToBlockName,
555-
);
556-
}
557-
if ($isCodeNode(element)) {
558-
const language =
559-
element.getLanguage() as keyof typeof CODE_LANGUAGE_MAP;
560-
updateToolbarState(
561-
'codeLanguage',
562-
language ? CODE_LANGUAGE_MAP[language] || language : '',
563-
);
564-
return;
565-
}
585+
$handleHeadingNode(element);
586+
$handleCodeNode(element);
566587
}
567588
}
589+
568590
// Handle buttons
569591
updateToolbarState(
570592
'fontColor',
@@ -622,7 +644,30 @@ export default function ToolbarPlugin({
622644
updateToolbarState('isUppercase', selection.hasFormat('uppercase'));
623645
updateToolbarState('isCapitalize', selection.hasFormat('capitalize'));
624646
}
625-
}, [activeEditor, editor, updateToolbarState]);
647+
if ($isNodeSelection(selection)) {
648+
const nodes = selection.getNodes();
649+
for (const selectedNode of nodes) {
650+
const parentList = $getNearestNodeOfType<ListNode>(
651+
selectedNode,
652+
ListNode,
653+
);
654+
if (parentList) {
655+
const type = parentList.getListType();
656+
updateToolbarState('blockType', type);
657+
} else {
658+
const selectedElement = $findTopLevelElement(selectedNode);
659+
$handleHeadingNode(selectedElement);
660+
$handleCodeNode(selectedElement);
661+
}
662+
}
663+
}
664+
}, [
665+
activeEditor,
666+
editor,
667+
updateToolbarState,
668+
$handleHeadingNode,
669+
$handleCodeNode,
670+
]);
626671

627672
useEffect(() => {
628673
return editor.registerCommand(

packages/lexical-selection/src/range-selection.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ export function $setBlocksType<T extends ElementNode>(
9494
for (const node of selection.getNodes()) {
9595
if ($isElementNode(node) && INTERNAL_$isBlock(node)) {
9696
blockMap.set(node.getKey(), node);
97+
} else if (anchorAndFocus === null) {
98+
const ancestorBlock = $getAncestor(node, INTERNAL_$isBlock);
99+
if ($isElementNode(ancestorBlock)) {
100+
blockMap.set(ancestorBlock.getKey(), ancestorBlock);
101+
}
97102
}
98103
}
99104
for (const [key, prevNode] of blockMap) {

0 commit comments

Comments
 (0)