diff --git a/app/assets/javascripts/ui_models/app_state.ts b/app/assets/javascripts/ui_models/app_state.ts index b9db38c5a..7b48a0ffb 100644 --- a/app/assets/javascripts/ui_models/app_state.ts +++ b/app/assets/javascripts/ui_models/app_state.ts @@ -168,6 +168,7 @@ export class AppState { readonly noAccountWarning: NoAccountWarningState; readonly sync = new SyncState(); isSessionsModalVisible = false; + mouseUp = Promise.resolve(); private appEventObserverRemovers: (() => void)[] = []; @@ -195,6 +196,7 @@ export class AppState { this.notifyEvent(event); }; this.registerVisibilityObservers(); + document.addEventListener('mousedown', this.onMouseDown); if (this.bridge.appVersion.includes('-beta')) { this.showBetaWarning = storage.get(StorageKey.ShowBetaWarning) ?? true; @@ -231,9 +233,16 @@ export class AppState { this.rootScopeCleanup2 = undefined; } document.removeEventListener('visibilitychange', this.onVisibilityChange); + document.removeEventListener('mousedown', this.onMouseDown); this.onVisibilityChange = undefined; } + onMouseDown = (): void => { + this.mouseUp = new Promise((resolve) => { + document.addEventListener('mouseup', () => resolve(), { once: true }); + }); + }; + openSessionsModal() { this.isSessionsModalVisible = true; } diff --git a/app/assets/javascripts/views/notes/notes-view.pug b/app/assets/javascripts/views/notes/notes-view.pug index 1e7657102..6212a755e 100644 --- a/app/assets/javascripts/views/notes/notes-view.pug +++ b/app/assets/javascripts/views/notes/notes-view.pug @@ -12,7 +12,9 @@ i.icon.ion-plus.add-button .filter-section(role='search') input#search-bar.filter-bar( - ng-blur='self.onFilterEnter()', + ng-ref='self.searchBar' + ng-focus='self.onSearchInputFocus()' + ng-blur='self.onSearchInputBlur()', ng-change='self.filterTextChanged()', ng-keyup='$event.keyCode == 13 && self.onFilterEnter();', ng-model='self.state.noteFilter.text', @@ -24,13 +26,19 @@ ng-click='self.clearFilterText();', ng-show='self.state.noteFilter.text' ) ✕ - label.sk-horizontal-group.tight.mt-3 - input( - type="checkbox" - ng-checked="self.state.noteFilter.includeProtectedNoteText" - ng-on-click="self.onIncludeProtectedNoteTextChange($event)" - ) - p.capitalize Include protected contents + label.sk-panel-row.justify-left.mt-1( + ng-if='self.state.searchIsFocused || self.state.searchOptionsAreFocused' + style="padding-bottom: 0" + ) + .sk-horizontal-group.tight + input( + ng-focus="self.onSearchOptionsFocus()" + ng-blur="self.onSearchOptionsBlur()" + type="checkbox" + ng-checked="self.state.noteFilter.includeProtectedNoteText" + ng-on-click="self.onIncludeProtectedNoteTextChange($event)" + ) + p.sk-p.capitalize Include protected contents no-account-warning( application='self.application' app-state='self.appState' diff --git a/app/assets/javascripts/views/notes/notes_view.ts b/app/assets/javascripts/views/notes/notes_view.ts index 44f60a6a3..2fcd72a4c 100644 --- a/app/assets/javascripts/views/notes/notes_view.ts +++ b/app/assets/javascripts/views/notes/notes_view.ts @@ -35,6 +35,8 @@ type NotesState = { text: string; includeProtectedNoteText: boolean; } + searchIsFocused: boolean; + searchOptionsAreFocused: boolean; mutable: { showMenu: boolean } completedFullSync: boolean [PrefKey.TagsPanelWidth]?: number @@ -73,6 +75,7 @@ class NotesViewCtrl extends PureViewCtrl { private searchKeyObserver: any private noteFlags: Partial> = {} private removeObservers: Array<() => void> = []; + private searchBar?: JQLite; /* @ngInject */ constructor($timeout: ng.ITimeoutService,) { @@ -135,6 +138,8 @@ class NotesViewCtrl extends PureViewCtrl { panelTitle: '', completedFullSync: false, hideTags: true, + searchIsFocused: false, + searchOptionsAreFocused: false }; } @@ -156,6 +161,7 @@ class NotesViewCtrl extends PureViewCtrl { } async onIncludeProtectedNoteTextChange(event: Event) { + this.searchBar?.[0].focus(); if (this.state.noteFilter.includeProtectedNoteText) { this.state.noteFilter.includeProtectedNoteText = false; } else { @@ -707,6 +713,38 @@ class NotesViewCtrl extends PureViewCtrl { await this.reloadNotes(); } + async onSearchInputBlur() { + /** + * Wait a non-zero amount of time so the newly-focused element can have + * enough time to set its state + */ + await this.$timeout(10); + await this.appState.mouseUp; + this.setState({ + searchIsFocused: this.searchBar?.[0] === document.activeElement, + }); + this.onFilterEnter(); + } + + onSearchInputFocus() { + this.setState({ + searchIsFocused: true + }); + } + + onSearchOptionsFocus() { + this.setState({ + searchOptionsAreFocused: true + }); + } + + async onSearchOptionsBlur() { + await this.$timeout(100); + this.setState({ + searchOptionsAreFocused: false + }); + } + onFilterEnter() { /** * For Desktop, performing a search right away causes