import type { Command } from '@blockeditor/block-std'; import { PathFinder } from '@blockeditor/block-std'; import { assertExists } from '@blockeditor/global/utils'; import type { BlockElement } from '@blockeditor/lit'; type Filter = (view: BlockElement) => boolean; function getPrevSibling(std: BlockEditor.Std, path: string[], filter?: Filter) { const view = std.view; const nextView = view.findPrev(path, node => { if (node.type !== 'block') { return; } if (filter && !filter(node.view as BlockElement)) { return; } return true; }); if (!nextView) return null; return view.viewFromPath('block', nextView.path); } function getLastGrandChild(std: BlockEditor.Std, blockElement: BlockElement) { const view = std.view; let output = blockElement; view.walkThrough((node, _index, parent) => { if ( node.children.filter(n => n.type === 'block').length === 0 && parent.children.filter(n => n.type === 'block').at(-1) === node ) { output = node.view as BlockElement; return true; } return; }, blockElement.path); return output; } function getPrevBlock( std: BlockEditor.Std, path: string[], filter?: (block: BlockElement) => boolean ) { const view = std.view; let prev: BlockElement | null = getPrevSibling(std, path, filter); if (!prev) { return null; } const block = view.viewFromPath('block', path); if (!block) return null; if (!prev.contains(block)) { prev = getLastGrandChild(std, prev); } if (prev && !PathFinder.equals(prev.path, path)) { return prev; } return null; } export const getPrevBlockCommand: Command< 'currentSelectionPath', 'prevBlock', { 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 prevBlock = getPrevBlock(ctx.std, path, ctx.filter); if (prevBlock) { next({ prevBlock }); } }; declare global { namespace BlockEditor { interface CommandContext { prevBlock?: BlockElement; } interface Commands { getPrevBlock: typeof getPrevBlockCommand; } } }