mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2025-11-30 19:37:55 +08:00
chore: better focus timning
This commit is contained in:
@@ -52,12 +52,14 @@ function TitleEditable({
|
||||
onUpdateName,
|
||||
onEnter,
|
||||
onFocus,
|
||||
autoFocus = true,
|
||||
}: {
|
||||
viewId: string;
|
||||
name: string;
|
||||
onUpdateName: (name: string) => void;
|
||||
onEnter?: (text: string) => void;
|
||||
onFocus?: () => void;
|
||||
autoFocus?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -68,7 +70,7 @@ function TitleEditable({
|
||||
pendingUpdate: null,
|
||||
updateId: null,
|
||||
});
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const cursorPositionRef = useRef<number>(0);
|
||||
@@ -135,11 +137,11 @@ function TitleEditable({
|
||||
newName: name,
|
||||
pendingUpdate: updateStateRef.current.pendingUpdate,
|
||||
lastConfirmedName: updateStateRef.current.lastConfirmedName,
|
||||
isEditing,
|
||||
isFocused,
|
||||
});
|
||||
|
||||
// If editing, ignore all remote updates
|
||||
if (isEditing) {
|
||||
// If focused, ignore all remote updates
|
||||
if (isFocused) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,7 +199,7 @@ function TitleEditable({
|
||||
if (contentRef.current) {
|
||||
contentRef.current.textContent = name;
|
||||
}
|
||||
}, [name, isEditing, smartSendUpdate]);
|
||||
}, [name, isFocused, smartSendUpdate]);
|
||||
|
||||
const focusedTextbox = useCallback(() => {
|
||||
const contentBox = contentRef.current;
|
||||
@@ -209,7 +211,7 @@ function TitleEditable({
|
||||
textbox?.focus();
|
||||
}, [viewId]);
|
||||
|
||||
// Initialize settings
|
||||
// Initialize content and handle autoFocus
|
||||
useEffect(() => {
|
||||
const contentBox = contentRef.current;
|
||||
|
||||
@@ -219,15 +221,22 @@ function TitleEditable({
|
||||
contentBox.textContent = updateStateRef.current.localName;
|
||||
initialEditValueRef.current = updateStateRef.current.localName;
|
||||
|
||||
contentBox.focus();
|
||||
|
||||
// Move cursor to end
|
||||
if (contentBox.textContent !== '') {
|
||||
setTimeout(() => {
|
||||
setCursorPosition(contentBox, contentBox.textContent?.length || 0);
|
||||
}, 0);
|
||||
// Ensure focus if autoFocus is true
|
||||
if (autoFocus) {
|
||||
// Use requestAnimationFrame for next paint cycle to ensure DOM is fully ready
|
||||
requestAnimationFrame(() => {
|
||||
// Double-check the element still exists and is in the document
|
||||
if (contentBox && document.contains(contentBox)) {
|
||||
contentBox.focus();
|
||||
// Move cursor to end if there's content
|
||||
if (contentBox.textContent) {
|
||||
setCursorPosition(contentBox, contentBox.textContent.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []); // Only execute once when component mounts
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []); // Only execute once when component mounts - autoFocus is intentionally not in deps
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -243,14 +252,14 @@ function TitleEditable({
|
||||
data-placeholder={t('menuAppHeader.defaultNewPageName')}
|
||||
contentEditable={true}
|
||||
aria-readonly={false}
|
||||
autoFocus={true}
|
||||
autoFocus={autoFocus}
|
||||
onFocus={() => {
|
||||
// Record initial value when starting to edit
|
||||
if (contentRef.current) {
|
||||
initialEditValueRef.current = contentRef.current.textContent || '';
|
||||
}
|
||||
|
||||
setIsEditing(true);
|
||||
setIsFocused(true);
|
||||
onFocus?.();
|
||||
}}
|
||||
onBlur={() => {
|
||||
@@ -272,10 +281,10 @@ function TitleEditable({
|
||||
}
|
||||
}
|
||||
|
||||
// Delay setting editing state to avoid issues with rapid focus switching
|
||||
setTimeout(() => {
|
||||
setIsEditing(false);
|
||||
}, 100);
|
||||
// Use microtask to avoid race conditions
|
||||
void Promise.resolve().then(() => {
|
||||
setIsFocused(false);
|
||||
});
|
||||
}}
|
||||
onInput={() => {
|
||||
if (!contentRef.current) return;
|
||||
@@ -314,7 +323,7 @@ function TitleEditable({
|
||||
|
||||
if (offset >= currentText.length || offset <= 0) {
|
||||
// Cursor at end or position inaccurate, keep all text
|
||||
setIsEditing(false);
|
||||
setIsFocused(false);
|
||||
updateStateRef.current = {
|
||||
...updateStateRef.current,
|
||||
localName: currentText,
|
||||
@@ -327,7 +336,7 @@ function TitleEditable({
|
||||
const afterText = currentText.slice(offset);
|
||||
|
||||
contentRef.current.textContent = beforeText;
|
||||
setIsEditing(false);
|
||||
setIsFocused(false);
|
||||
updateStateRef.current = {
|
||||
...updateStateRef.current,
|
||||
localName: beforeText,
|
||||
@@ -343,7 +352,7 @@ function TitleEditable({
|
||||
// Escape key: complete editing and save current content
|
||||
const currentText = contentRef.current.textContent || '';
|
||||
|
||||
setIsEditing(false);
|
||||
setIsFocused(false);
|
||||
updateStateRef.current = {
|
||||
...updateStateRef.current,
|
||||
localName: currentText,
|
||||
|
||||
Reference in New Issue
Block a user