diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.tsx index f25615685f..870bcdc101 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Link.tsx @@ -34,7 +34,7 @@ const LinkContent = React.forwardRef( const [linkText, setLinkText] = React.useState(elementText); const [linkUrl, setLinkUrl] = React.useState(link.url); const linkInputRef = React.useRef(null); - const showRemoveButton = editor.lastInsertedLinkPath + const isLastInsertedLink = editor.lastInsertedLinkPath ? !Path.equals(path, editor.lastInsertedLinkPath) : true; const [isSaveDisabled, setIsSaveDisabled] = React.useState(false); @@ -147,7 +147,7 @@ const LinkContent = React.forwardRef( removeLink(editor)} - $visible={showRemoveButton} + $visible={isLastInsertedLink} > {formatMessage({ id: 'components.Blocks.popover.remove', diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.tsx index 21238d1db0..5f9b23dd2f 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/List.tsx @@ -272,9 +272,11 @@ const handleEnterKeyOnList = (editor: Editor) => { * Common handler for converting a node to a list */ const handleConvertToList = (editor: Editor, format: Block<'list'>['format']) => { - baseHandleConvert>(editor, { type: 'list-item' }); + const convertedPath = baseHandleConvert>(editor, { type: 'list-item' }); - Transforms.wrapNodes(editor, { type: 'list', format, children: [] }); + if (!convertedPath) return; + + Transforms.wrapNodes(editor, { type: 'list', format, children: [] }, { at: convertedPath }); }; /** diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/tests/List.test.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/tests/List.test.tsx index afe58a8b01..bd2e5d04af 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/tests/List.test.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/tests/List.test.tsx @@ -1419,38 +1419,29 @@ describe('List', () => { it('converts a heading with a link to a list', () => { const baseEditor = createEditor(); - baseEditor.children = [ { type: 'heading', + level: 1, children: [ - { - text: '', - type: 'text', - }, { type: 'link', url: 'https://strapi.io', children: [ { - text: 'Heading link', type: 'text', + text: 'Heading link', }, ], }, - { - text: '', - type: 'text', - }, ], - level: 1, }, ]; // Set the cursor on the heading Transforms.select(baseEditor, { - anchor: { path: [0, 1, 0], offset: 0 }, - focus: { path: [0, 1, 0], offset: 0 }, + anchor: { path: [0, 0, 0], offset: 0 }, + focus: { path: [0, 0, 0], offset: 0 }, }); listBlocks['list-ordered'].handleConvert!(baseEditor); diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.tsx index d9bffb1f72..97fb411c41 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.tsx +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksToolbar.tsx @@ -362,6 +362,39 @@ const ListButton = ({ block, format }: ListButtonProps) => { return false; }; + /** + * @TODO: Currently, applying list while multiple blocks are selected is not supported. + * We should implement this feature in the future. + */ + const isListDisabled = () => { + // Always disabled when the whole editor is disabled + if (disabled) { + return true; + } + + // Always enabled when there's no selection + if (!editor.selection) { + return false; + } + + // Get the block node closest to the anchor and focus + const anchorNodeEntry = Editor.above(editor, { + at: editor.selection.anchor, + match: (node) => !Editor.isEditor(node) && node.type !== 'text', + }); + const focusNodeEntry = Editor.above(editor, { + at: editor.selection.focus, + match: (node) => !Editor.isEditor(node) && node.type !== 'text', + }); + + if (!anchorNodeEntry || !focusNodeEntry) { + return false; + } + + // Disabled if the anchor and focus are not in the same block + return anchorNodeEntry[0] !== focusNodeEntry[0]; + }; + const toggleList = (format: Block<'list'>['format']) => { let currentListEntry; if (editor.selection) { @@ -403,7 +436,7 @@ const ListButton = ({ block, format }: ListButtonProps) => { name={format} label={block.label} isActive={isListActive()} - disabled={disabled} + disabled={isListDisabled()} handleClick={() => toggleList(format)} /> ); diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/conversions.ts b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/conversions.ts index 07102aba2a..5ce94dc1b2 100644 --- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/conversions.ts +++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/conversions.ts @@ -1,4 +1,4 @@ -import { type Element, Editor, Transforms } from 'slate'; +import { type Element, type Path, Editor, Transforms } from 'slate'; /** * Extracts some logic that is common to most blocks' handleConvert functions. @@ -7,7 +7,7 @@ import { type Element, Editor, Transforms } from 'slate'; const baseHandleConvert = ( editor: Editor, attributesToSet: Partial & { type: T['type'] } -): void => { +): void | Path => { // If there is no selection, convert last inserted node const [_, lastNodePath] = Editor.last(editor, []); @@ -18,27 +18,29 @@ const baseHandleConvert = ( at: editor.selection ?? lastNodePath, }); - // Make sure we get block nodes (elements), not an inline node + // Make sure we get a block node, not an inline node const [, updatedLastNodePath] = Editor.last(editor, []); - const nodes = Editor.nodes(editor, { + const entry = Editor.above(editor, { match: (node) => !Editor.isEditor(node) && node.type !== 'text' && node.type !== 'link', at: editor.selection ?? updatedLastNodePath, }); - if (!nodes) { + if (!entry || Editor.isEditor(entry[0])) { return; } - for (const [element, elementPath] of nodes) { - Transforms.setNodes( - editor, - { - ...getAttributesToClear(element as Element), // because of the match we can safely assume it's a Node of type Element - ...attributesToSet, - } as Partial, - { at: elementPath } - ); - } + const [element, elementPath] = entry; + + Transforms.setNodes( + editor, + { + ...getAttributesToClear(element), + ...attributesToSet, + } as Partial, + { at: elementPath } + ); + + return elementPath; }; /**