mirror of
https://github.com/laurent22/joplin.git
synced 2025-04-01 21:24:45 +02:00
Desktop: Make global search field wider when it has focus
This commit is contained in:
parent
7b8ee467a0
commit
09f41dd50e
@ -101,6 +101,7 @@ const appDefaultState = Object.assign({}, defaultState, {
|
|||||||
lastEditorScrollPercents: {},
|
lastEditorScrollPercents: {},
|
||||||
devToolsVisible: false,
|
devToolsVisible: false,
|
||||||
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
||||||
|
focusedField: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
class Application extends BaseApplication {
|
class Application extends BaseApplication {
|
||||||
@ -292,6 +293,21 @@ class Application extends BaseApplication {
|
|||||||
delete newState.visibleDialogs[state.name];
|
delete newState.visibleDialogs[state.name];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'FOCUS_SET':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.focusedField = action.field;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'FOCUS_CLEAR':
|
||||||
|
|
||||||
|
// A field can only clear its own state
|
||||||
|
if (action.field === state.focusedField) {
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.focusedField = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
||||||
|
@ -566,7 +566,7 @@ class MainScreenComponent extends React.Component<any, any> {
|
|||||||
const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
||||||
return <NoteEditor key={key} bodyEditor={bodyEditor} />;
|
return <NoteEditor key={key} bodyEditor={bodyEditor} />;
|
||||||
} else if (key === 'noteListControls') {
|
} else if (key === 'noteListControls') {
|
||||||
return <NoteListControls key={key} />;
|
return <NoteListControls key={key} showNewNoteButtons={this.props.focusedField !== 'globalSearch'} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`Invalid layout component: ${key}`);
|
throw new Error(`Invalid layout component: ${key}`);
|
||||||
@ -650,6 +650,7 @@ const mapStateToProps = (state:any) => {
|
|||||||
customCss: state.customCss,
|
customCss: state.customCss,
|
||||||
editorNoteStatuses: state.editorNoteStatuses,
|
editorNoteStatuses: state.editorNoteStatuses,
|
||||||
hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
||||||
|
focusedField: state.focusedField,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,9 +6,12 @@ import CommandService from 'lib/services/CommandService';
|
|||||||
import { runtime as focusSearchRuntime } from './commands/focusSearch';
|
import { runtime as focusSearchRuntime } from './commands/focusSearch';
|
||||||
const styled = require('styled-components').default;
|
const styled = require('styled-components').default;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
showNewNoteButtons: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
const StyledRoot = styled.div`
|
const StyledRoot = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/*height: 100%;*/
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||||
@ -19,7 +22,12 @@ const StyledButton = styled(Button)`
|
|||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function NoteListControls() {
|
const ButtonContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function NoteListControls(props:Props) {
|
||||||
const searchBarRef = useRef(null);
|
const searchBarRef = useRef(null);
|
||||||
|
|
||||||
useEffect(function() {
|
useEffect(function() {
|
||||||
@ -38,21 +46,31 @@ export default function NoteListControls() {
|
|||||||
CommandService.instance().execute('newNote');
|
CommandService.instance().execute('newNote');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderNewNoteButtons() {
|
||||||
|
if (!props.showNewNoteButtons) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ButtonContainer>
|
||||||
|
<StyledButton
|
||||||
|
tooltip={CommandService.instance().title('newTodo')}
|
||||||
|
iconName="far fa-check-square"
|
||||||
|
level={ButtonLevel.Primary}
|
||||||
|
onClick={onNewTodoButtonClick}
|
||||||
|
/>
|
||||||
|
<StyledButton
|
||||||
|
tooltip={CommandService.instance().title('newNote')}
|
||||||
|
iconName="icon-note"
|
||||||
|
level={ButtonLevel.Primary}
|
||||||
|
onClick={onNewNoteButtonClick}
|
||||||
|
/>
|
||||||
|
</ButtonContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRoot>
|
<StyledRoot>
|
||||||
<SearchBar inputRef={searchBarRef}/>
|
<SearchBar inputRef={searchBarRef}/>
|
||||||
<StyledButton
|
{renderNewNoteButtons()}
|
||||||
tooltip={CommandService.instance().title('newTodo')}
|
|
||||||
iconName="far fa-check-square"
|
|
||||||
level={ButtonLevel.Primary}
|
|
||||||
onClick={onNewTodoButtonClick}
|
|
||||||
/>
|
|
||||||
<StyledButton
|
|
||||||
tooltip={CommandService.instance().title('newNote')}
|
|
||||||
iconName="icon-note"
|
|
||||||
level={ButtonLevel.Primary}
|
|
||||||
onClick={onNewNoteButtonClick}
|
|
||||||
/>
|
|
||||||
</StyledRoot>
|
</StyledRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,42 @@ const { _ } = require('lib/locale.js');
|
|||||||
interface Props {
|
interface Props {
|
||||||
inputRef?: any,
|
inputRef?: any,
|
||||||
notesParentType: string,
|
notesParentType: string,
|
||||||
|
dispatch?: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchBar(props:Props) {
|
function SearchBar(props:Props) {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const iconName = !query ? CommandService.instance().iconName('search') : 'fa fa-times';
|
const iconName = !query ? CommandService.instance().iconName('search') : 'fa fa-times';
|
||||||
|
|
||||||
const onChange = (event:any) => {
|
function onChange(event:any) {
|
||||||
setQuery(event.currentTarget.value);
|
setQuery(event.currentTarget.value);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function onFocus() {
|
||||||
|
props.dispatch({
|
||||||
|
type: 'FOCUS_SET',
|
||||||
|
field: 'globalSearch',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlur() {
|
||||||
|
// Do it after a delay so that the "Clear" button
|
||||||
|
// can be clicked on (otherwise the field loses focus
|
||||||
|
// and is resized before the click event has been processed)
|
||||||
|
setTimeout(() => {
|
||||||
|
props.dispatch({
|
||||||
|
type: 'FOCUS_CLEAR',
|
||||||
|
field: 'globalSearch',
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyDown(event:any) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
setQuery('');
|
||||||
|
if (document.activeElement) (document.activeElement as any).blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onSearchButtonClick = useCallback(() => {
|
const onSearchButtonClick = useCallback(() => {
|
||||||
setQuery('');
|
setQuery('');
|
||||||
@ -34,7 +61,16 @@ function SearchBar(props:Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Root>
|
<Root>
|
||||||
<SearchInput ref={props.inputRef} value={query} type="text" placeholder={_('Search...')} onChange={onChange}/>
|
<SearchInput
|
||||||
|
ref={props.inputRef}
|
||||||
|
value={query}
|
||||||
|
type="text"
|
||||||
|
placeholder={_('Search...')}
|
||||||
|
onChange={onChange}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>
|
||||||
<SearchButton onClick={onSearchButtonClick}>
|
<SearchButton onClick={onSearchButtonClick}>
|
||||||
<SearchButtonIcon className={iconName}/>
|
<SearchButtonIcon className={iconName}/>
|
||||||
</SearchButton>
|
</SearchButton>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user