1
0
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:
Laurent Cozic 2020-09-29 12:31:19 +01:00
parent 7b8ee467a0
commit 09f41dd50e
4 changed files with 89 additions and 18 deletions

View File

@ -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)}`;

View File

@ -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,
}; };
}; };

View File

@ -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>
); );
} }

View File

@ -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>