import type { Command } from '@blockeditor/block-std'; import { assertExists } from '@blockeditor/global/utils'; import type { BlockElement } from '@blockeditor/lit'; type Filter = (view: BlockElement) => boolean; function getNextSibling( std: BlockEditor.Std, blockElement: BlockElement, filter?: Filter ) { const view = std.view; const nextView = view.findNext(blockElement.path, node => { if (node.type !== 'block' || node.view.contains(blockElement)) { return; } if (filter && !filter(node.view as BlockElement)) { return; } return true; }); if (!nextView) return null; return view.viewFromPath('block', nextView.path); } function getNextBlock( std: BlockEditor.Std, path: string[], filter?: (block: BlockElement) => boolean ) { const view = std.view; const focusBlock = view.viewFromPath('block', path); if (!focusBlock) return null; let next: BlockElement | null = null; if (focusBlock.childBlockElements[0]) { next = focusBlock.childBlockElements[0]; } if (!next) { next = getNextSibling(std, focusBlock, filter); } if (next && !next.contains(focusBlock)) { return next; } return null; } export const getNextBlockCommand: Command< 'currentSelectionPath', 'nextBlock', { path?: string[]; filter?: Filter; } > = (ctx, next) => { const path = ctx.path ?? ctx.currentSelectionPath; assertExists( path, '`path` is required, you need to pass it in args or ctx before adding this command to the pipeline.' ); const nextBlock = getNextBlock(ctx.std, path, ctx.filter); if (nextBlock) { next({ nextBlock }); } }; declare global { namespace BlockEditor { interface CommandContext { nextBlock?: BlockElement; } interface Commands { getNextBlock: typeof getNextBlockCommand; } } }