feat(outliner): indent and outdent entire subtree with all children on Tab and Shift+Tab
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m2s

This commit is contained in:
sandrodach committed 2026-10-27 16:06:31 +01:00
1 parent 1bdd7d245d
commit aea05e868a
2 files changed
+57 -11

No files matched your search

@@ -13,6 +13,7 @@ export interface OutlinerTreeProps {
remoteCursors?: Record<string, RemoteCursor>;
sendCursorMoved?: (blockId: string | null, selectionStart?: number, selectionEnd?: number) => void;
onUpdateBlock: (updated: DocumentBlock, immediate?: boolean) => void;
onUpdateBlocks?: (updated: DocumentBlock[]) => void;
onCreateBlock: (newBlock: DocumentBlock, afterBlockId?: string) => void;
onDeleteBlock: (blockId: string) => void;
onMoveBlock: (blockId: string, newIndex: number, level?: number) => void;
@@ -34,6 +35,7 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
remoteCursors,
sendCursorMoved,
onUpdateBlock,
onUpdateBlocks,
onCreateBlock,
onDeleteBlock,
onMoveBlock,
@@ -172,7 +174,7 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
}
return result;
}, [blocks]);
}, [blocks, levelOffset]);
const handleCreateBlockBelow = useCallback(
(afterBlockId: string, _currentContent: string) => {
@@ -241,9 +243,9 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
const current = blocks[idx];
const currentLevel = Math.max(0, current.level || 0);
const prevLevel = idx > 0 ? Math.max(0, blocks[idx - 1].level || 0) : 0;
const maxLevel = idx === 0 ? 0 : prevLevel + 1;
const minLevel = levelOffset || 0;
const maxLevel = idx === 0 ? minLevel : prevLevel + 1;
const nextLevel =
delta === 1
? Math.min(maxLevel, currentLevel + 1)
@@ -251,15 +253,38 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
if (nextLevel === currentLevel) return;
onUpdateBlock(
{
...current,
level: nextLevel,
},
true
);
const actualDelta = nextLevel - currentLevel;
// Find all contiguous children / descendants:
// contiguous blocks with level > currentLevel
let count = 1;
for (let i = idx + 1; i < blocks.length; i++) {
if (Math.max(0, blocks[i].level || 0) > currentLevel) {
count++;
} else {
break;
}
}
const updatedBlocks: DocumentBlock[] = [];
for (let i = idx; i < idx + count; i++) {
const b = blocks[i];
const newLvl = Math.max(minLevel, (b.level || 0) + actualDelta);
updatedBlocks.push({
...b,
level: newLvl,
});
}
if (onUpdateBlocks) {
onUpdateBlocks(updatedBlocks);
} else {
for (const b of updatedBlocks) {
onUpdateBlock(b, true);
}
}
},
[blocks, canEdit, onUpdateBlock]
[blocks, canEdit, levelOffset, onUpdateBlock, onUpdateBlocks]
);
const handleNavigateFocus = useCallback(