feat: control editor focus (#43882)
* refactor: MultifileEditor to functional component. * fix: make editor acquire focus once on mount Now the editors can leave the dom (e.g. if a tab is clicked), but an editor will only call for focus if the MultifileEditor itself remounts
This commit is contained in:
committed by
GitHub
parent
2bddbbff42
commit
6c20301204
@ -1,5 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { ReflexContainer, ReflexElement, ReflexSplitter } from 'react-reflex';
|
import { ReflexContainer, ReflexElement, ReflexSplitter } from 'react-reflex';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
@ -73,14 +73,7 @@ const mapDispatchToProps = {
|
|||||||
updateFile
|
updateFile
|
||||||
};
|
};
|
||||||
|
|
||||||
class MultifileEditor extends Component {
|
const MultifileEditor = props => {
|
||||||
focusOnHotkeys() {
|
|
||||||
if (this.props.containerRef.current) {
|
|
||||||
this.props.containerRef.current.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
const {
|
||||||
challengeFiles,
|
challengeFiles,
|
||||||
containerRef,
|
containerRef,
|
||||||
@ -92,7 +85,7 @@ class MultifileEditor extends Component {
|
|||||||
title,
|
title,
|
||||||
visibleEditors: { indexcss, indexhtml, indexjs, indexjsx },
|
visibleEditors: { indexcss, indexhtml, indexjs, indexjsx },
|
||||||
usesMultifileEditor
|
usesMultifileEditor
|
||||||
} = this.props;
|
} = props;
|
||||||
const editorTheme = theme === 'night' ? 'vs-dark-custom' : 'vs-custom';
|
const editorTheme = theme === 'night' ? 'vs-dark-custom' : 'vs-custom';
|
||||||
// TODO: the tabs mess up the rendering (scroll doesn't work properly and
|
// TODO: the tabs mess up the rendering (scroll doesn't work properly and
|
||||||
// the in-editor description)
|
// the in-editor description)
|
||||||
@ -126,6 +119,11 @@ class MultifileEditor extends Component {
|
|||||||
// TODO: the tabs mess up the rendering (scroll doesn't work properly and
|
// TODO: the tabs mess up the rendering (scroll doesn't work properly and
|
||||||
// the in-editor description)
|
// the in-editor description)
|
||||||
const targetEditor = getTargetEditor(challengeFiles);
|
const targetEditor = getTargetEditor(challengeFiles);
|
||||||
|
|
||||||
|
// Only one editor should be focused and that should happen once, after it has
|
||||||
|
// been mounted. This ref allows the editors to co-ordinate, without having to
|
||||||
|
// resort to redux.
|
||||||
|
const canFocusOnMountRef = useRef(true);
|
||||||
return (
|
return (
|
||||||
<ReflexContainer
|
<ReflexContainer
|
||||||
orientation='horizontal'
|
orientation='horizontal'
|
||||||
@ -138,6 +136,7 @@ class MultifileEditor extends Component {
|
|||||||
{indexjsx && (
|
{indexjsx && (
|
||||||
<ReflexElement {...reflexProps} {...resizeProps}>
|
<ReflexElement {...reflexProps} {...resizeProps}>
|
||||||
<Editor
|
<Editor
|
||||||
|
canFocusOnMountRef={canFocusOnMountRef}
|
||||||
challengeFiles={challengeFiles}
|
challengeFiles={challengeFiles}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
description={targetEditor === 'indexjsx' ? description : null}
|
description={targetEditor === 'indexjsx' ? description : null}
|
||||||
@ -158,11 +157,10 @@ class MultifileEditor extends Component {
|
|||||||
{indexhtml && (
|
{indexhtml && (
|
||||||
<ReflexElement {...reflexProps} {...resizeProps}>
|
<ReflexElement {...reflexProps} {...resizeProps}>
|
||||||
<Editor
|
<Editor
|
||||||
|
canFocusOnMountRef={canFocusOnMountRef}
|
||||||
challengeFiles={challengeFiles}
|
challengeFiles={challengeFiles}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
description={
|
description={targetEditor === 'indexhtml' ? description : null}
|
||||||
targetEditor === 'indexhtml' ? description : null
|
|
||||||
}
|
|
||||||
editorRef={editorRef}
|
editorRef={editorRef}
|
||||||
fileKey='indexhtml'
|
fileKey='indexhtml'
|
||||||
initialTests={initialTests}
|
initialTests={initialTests}
|
||||||
@ -180,6 +178,7 @@ class MultifileEditor extends Component {
|
|||||||
{indexcss && (
|
{indexcss && (
|
||||||
<ReflexElement {...reflexProps} {...resizeProps}>
|
<ReflexElement {...reflexProps} {...resizeProps}>
|
||||||
<Editor
|
<Editor
|
||||||
|
canFocusOnMountRef={canFocusOnMountRef}
|
||||||
challengeFiles={challengeFiles}
|
challengeFiles={challengeFiles}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
description={targetEditor === 'indexcss' ? description : null}
|
description={targetEditor === 'indexcss' ? description : null}
|
||||||
@ -201,6 +200,7 @@ class MultifileEditor extends Component {
|
|||||||
{indexjs && (
|
{indexjs && (
|
||||||
<ReflexElement {...reflexProps} {...resizeProps}>
|
<ReflexElement {...reflexProps} {...resizeProps}>
|
||||||
<Editor
|
<Editor
|
||||||
|
canFocusOnMountRef={canFocusOnMountRef}
|
||||||
challengeFiles={challengeFiles}
|
challengeFiles={challengeFiles}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
description={targetEditor === 'indexjs' ? description : null}
|
description={targetEditor === 'indexjs' ? description : null}
|
||||||
@ -219,8 +219,7 @@ class MultifileEditor extends Component {
|
|||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
</ReflexContainer>
|
</ReflexContainer>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
MultifileEditor.displayName = 'MultifileEditor';
|
MultifileEditor.displayName = 'MultifileEditor';
|
||||||
MultifileEditor.propTypes = propTypes;
|
MultifileEditor.propTypes = propTypes;
|
||||||
|
@ -57,6 +57,7 @@ interface EditorProps {
|
|||||||
executeChallenge: (options?: { showCompletionModal: boolean }) => void;
|
executeChallenge: (options?: { showCompletionModal: boolean }) => void;
|
||||||
ext: ExtTypes;
|
ext: ExtTypes;
|
||||||
fileKey: FileKeyTypes;
|
fileKey: FileKeyTypes;
|
||||||
|
canFocusOnMountRef: MutableRefObject<boolean>;
|
||||||
initialEditorContent: string;
|
initialEditorContent: string;
|
||||||
initialExt: string;
|
initialExt: string;
|
||||||
initTests: (tests: Test[]) => void;
|
initTests: (tests: Test[]) => void;
|
||||||
@ -631,12 +632,15 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
|
|
||||||
function focusIfTargetEditor() {
|
function focusIfTargetEditor() {
|
||||||
const { editor } = dataRef.current;
|
const { editor } = dataRef.current;
|
||||||
if (!editor) return;
|
const { canFocusOnMountRef } = props;
|
||||||
|
if (!editor || !canFocusOnMountRef.current) return;
|
||||||
if (!props.usesMultifileEditor) {
|
if (!props.usesMultifileEditor) {
|
||||||
// Only one editor? Focus it.
|
// Only one editor? Focus it.
|
||||||
editor.focus();
|
editor.focus();
|
||||||
|
canFocusOnMountRef.current = false;
|
||||||
} else if (hasEditableRegion()) {
|
} else if (hasEditableRegion()) {
|
||||||
editor.focus();
|
editor.focus();
|
||||||
|
canFocusOnMountRef.current = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user