fix(workspace): select and focus exact block line when jumping from todos or tags
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 18s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 18s
This commit is contained in:
1 parent
928cc72491
commit
149cfdc244
1 file changed
+52
-37
@@ -45,27 +45,33 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
|
||||
|
||||
const textareaMapRef = useRef<Map<string, HTMLTextAreaElement>>(new Map());
|
||||
const pendingFocusRef = useRef<{ blockId: string; cursor: 'start' | 'end' } | null>(null);
|
||||
const pendingFocusRef = useRef<{ blockId: string; cursor: 'start' | 'end' | 'select' } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialFocusedBlockId) {
|
||||
setHighlightedBlockId(initialFocusedBlockId);
|
||||
const timer = setTimeout(() => {
|
||||
focusBlockInput(initialFocusedBlockId, 'end');
|
||||
const el = textareaMapRef.current.get(initialFocusedBlockId);
|
||||
if (el) {
|
||||
const focusBlockInput = useCallback(
|
||||
(blockId: string, cursor: 'start' | 'end' | 'select' = 'end') => {
|
||||
setFocusedBlockId(blockId);
|
||||
const el = textareaMapRef.current.get(blockId);
|
||||
if (el) {
|
||||
requestAnimationFrame(() => {
|
||||
el.focus();
|
||||
try {
|
||||
if (cursor === 'select') {
|
||||
el.select();
|
||||
} else {
|
||||
const pos = cursor === 'end' ? el.value.length : 0;
|
||||
el.setSelectionRange(pos, pos);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
}, 80);
|
||||
const fadeTimer = setTimeout(() => {
|
||||
setHighlightedBlockId(null);
|
||||
}, 2500);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
clearTimeout(fadeTimer);
|
||||
};
|
||||
}
|
||||
}, [initialFocusedBlockId]);
|
||||
});
|
||||
} else {
|
||||
pendingFocusRef.current = { blockId, cursor };
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const registerTextareaRef = useCallback((blockId: string, el: HTMLTextAreaElement | null) => {
|
||||
if (el) {
|
||||
@@ -74,13 +80,19 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
const target = pendingFocusRef.current;
|
||||
pendingFocusRef.current = null;
|
||||
requestAnimationFrame(() => {
|
||||
setFocusedBlockId(blockId);
|
||||
el.focus();
|
||||
const pos = target.cursor === 'end' ? el.value.length : 0;
|
||||
try {
|
||||
el.setSelectionRange(pos, pos);
|
||||
if (target.cursor === 'select') {
|
||||
el.select();
|
||||
} else {
|
||||
const pos = target.cursor === 'end' ? el.value.length : 0;
|
||||
el.setSelectionRange(pos, pos);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -88,23 +100,26 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const focusBlockInput = useCallback((blockId: string, cursor: 'start' | 'end' = 'end') => {
|
||||
setFocusedBlockId(blockId);
|
||||
const el = textareaMapRef.current.get(blockId);
|
||||
if (el) {
|
||||
requestAnimationFrame(() => {
|
||||
el.focus();
|
||||
const pos = cursor === 'end' ? el.value.length : 0;
|
||||
try {
|
||||
el.setSelectionRange(pos, pos);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
} else {
|
||||
pendingFocusRef.current = { blockId, cursor };
|
||||
useEffect(() => {
|
||||
if (initialFocusedBlockId) {
|
||||
setHighlightedBlockId(initialFocusedBlockId);
|
||||
focusBlockInput(initialFocusedBlockId, 'select');
|
||||
const timer = setTimeout(() => {
|
||||
focusBlockInput(initialFocusedBlockId, 'select');
|
||||
}, 70);
|
||||
const timer2 = setTimeout(() => {
|
||||
focusBlockInput(initialFocusedBlockId, 'select');
|
||||
}, 200);
|
||||
const fadeTimer = setTimeout(() => {
|
||||
setHighlightedBlockId(null);
|
||||
}, 2800);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
clearTimeout(timer2);
|
||||
clearTimeout(fadeTimer);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
}, [initialFocusedBlockId, blocks.length, focusBlockInput]);
|
||||
|
||||
// Compute visibility based on collapsed ancestor blocks
|
||||
const visibleEntries = useMemo(() => {
|
||||
|
||||
Reference in new issue
Block a user