Skip to content

Commit 167856a

Browse files
committed
fix: editor focus error.
1 parent f457c93 commit 167856a

3 files changed

Lines changed: 58 additions & 1 deletion

File tree

frontend/src/components/Editor/Editor.jsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,13 +382,14 @@ class WikilinkMenu {
382382

383383
const wikilinkPluginKey = new PluginKey('wikilink-autocomplete')
384384

385-
const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawioOpen, onMediaPickerOpen }, ref) {
385+
const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawioOpen, onMediaPickerOpen, onTabOut }, ref) {
386386
const { t } = useTranslation()
387387
const editorRef = useRef(null)
388388
const containerRef = useRef(null)
389389
const onChangeRef = useRef(onChange)
390390
const drawioHandlerRef = useRef(onDrawioOpen)
391391
const mediaHandlerRef = useRef(onMediaPickerOpen)
392+
const onTabOutRef = useRef(onTabOut)
392393
const editorViewRef = useRef(null)
393394
// The slash menu and wikilink menu both render plain DOM (not React),
394395
// so they cannot read t() from context. Snapshot translated strings into
@@ -412,13 +413,22 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi
412413
mediaHandlerRef.current = onMediaPickerOpen || null
413414
}, [onMediaPickerOpen])
414415

416+
useEffect(() => {
417+
onTabOutRef.current = onTabOut || null
418+
}, [onTabOut])
419+
415420
useImperativeHandle(ref, () => ({
416421
insertText(text) {
417422
const view = editorViewRef.current
418423
if (!view) return
419424
const { state, dispatch } = view
420425
const pos = state.selection.from
421426
dispatch(state.tr.insertText(text, pos))
427+
},
428+
focus() {
429+
const view = editorViewRef.current
430+
if (!view) return
431+
view.focus()
422432
}
423433
}), [])
424434

@@ -478,6 +488,28 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi
478488
})
479489
})
480490

491+
const tabOutPlugin = $prose(() => {
492+
return new Plugin({
493+
props: {
494+
handleKeyDown(view, event) {
495+
if (event.key !== 'Tab' || event.shiftKey) return false
496+
if (event.isComposing || event.keyCode === 229) return false
497+
if (!onTabOutRef.current) return false
498+
const { $from } = view.state.selection
499+
for (let depth = $from.depth; depth >= 0; depth--) {
500+
const name = $from.node(depth).type.name
501+
if (['list_item', 'bullet_list', 'ordered_list', 'code_block', 'table'].includes(name)) {
502+
return false
503+
}
504+
}
505+
event.preventDefault()
506+
onTabOutRef.current()
507+
return true
508+
},
509+
},
510+
})
511+
})
512+
481513
const init = async () => {
482514
const editor = await MilkdownEditor.make()
483515
.config((ctx) => {
@@ -508,6 +540,7 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi
508540
.use(slash)
509541
.use(wikilink)
510542
.use(wikilinkPlugin)
543+
.use(tabOutPlugin)
511544
.create()
512545

513546
// StrictMode: if cleanup ran while we were awaiting, destroy immediately

frontend/src/pages/NewPage.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export default function NewPage() {
5252
const [error, setError] = useState('')
5353
const [editorKey, setEditorKey] = useState(0)
5454
const editorRef = useRef(null)
55+
const createBtnRef = useRef(null)
56+
const handleEditorTabOut = useCallback(() => {
57+
createBtnRef.current?.focus()
58+
}, [])
5559
const dirty = !saved && !showTemplates && (title.trim() !== '' || content.trim() !== '')
5660

5761
// Draw.io state
@@ -269,6 +273,12 @@ export default function NewPage() {
269273
type="text"
270274
value={title}
271275
onChange={(e) => setTitle(e.target.value)}
276+
onKeyDown={(e) => {
277+
if (e.key === 'Tab' && !e.shiftKey) {
278+
e.preventDefault()
279+
editorRef.current?.focus()
280+
}
281+
}}
272282
className="text-2xl font-bold text-text bg-transparent border-none outline-none flex-1 mr-4"
273283
placeholder={t('newPage.titlePlaceholder')}
274284
autoFocus
@@ -281,6 +291,7 @@ export default function NewPage() {
281291
{t('newPage.cancel')}
282292
</button>
283293
<button
294+
ref={createBtnRef}
284295
onClick={handleSave}
285296
disabled={saving || !title.trim()}
286297
className="px-3 py-1.5 text-sm bg-primary text-primary-text rounded-lg hover:bg-primary-hover disabled:opacity-50"
@@ -324,6 +335,7 @@ export default function NewPage() {
324335
onChange={setContent}
325336
onDrawioOpen={handleDrawioOpen}
326337
onMediaPickerOpen={handleMediaPickerOpen}
338+
onTabOut={handleEditorTabOut}
327339
/>
328340
</div>
329341

frontend/src/pages/PageEdit.jsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export default function PageEdit() {
2929
const [original, setOriginal] = useState({ title: '', content: '' })
3030
const baseVersionRef = useRef(null)
3131
const editorRef = useRef(null)
32+
const saveBtnRef = useRef(null)
33+
const handleEditorTabOut = useCallback(() => {
34+
saveBtnRef.current?.focus()
35+
}, [])
3236

3337
const dirty = !!page && (title !== original.title || content !== original.content)
3438

@@ -261,6 +265,12 @@ export default function PageEdit() {
261265
type="text"
262266
value={title}
263267
onChange={(e) => setTitle(e.target.value)}
268+
onKeyDown={(e) => {
269+
if (e.key === 'Tab' && !e.shiftKey) {
270+
e.preventDefault()
271+
editorRef.current?.focus()
272+
}
273+
}}
264274
className="text-2xl font-bold text-text bg-transparent border-none outline-none w-full"
265275
placeholder={t('pageEdit.titlePlaceholder')}
266276
/>
@@ -316,6 +326,7 @@ export default function PageEdit() {
316326
onChange={setContent}
317327
onDrawioOpen={handleDrawioOpen}
318328
onMediaPickerOpen={handleMediaPickerOpen}
329+
onTabOut={handleEditorTabOut}
319330
/>
320331
</div>
321332

@@ -388,6 +399,7 @@ export default function PageEdit() {
388399
{t('pageEdit.cancel')}
389400
</button>
390401
<button
402+
ref={saveBtnRef}
391403
onClick={handleSave}
392404
disabled={saving}
393405
className="fab-btn fab-btn-primary"

0 commit comments

Comments
 (0)