Skip to content

Commit

Permalink
Allow browsing search results with page up/down and fix scrolling iss…
Browse files Browse the repository at this point in the history
…ues (oliverschwendener#346)

* allow browsing search results using page up/down, fixed scrolling

* scroll to active (first) search result when search results have been updated

* parse maxSearchResultsPerPage as number
  • Loading branch information
tkohlmeier authored Sep 7, 2021
1 parent c44951a commit 0d6940a
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 26 deletions.
98 changes: 72 additions & 26 deletions src/renderer/search-results-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,62 @@ export const searchResultsComponent = Vue.extend({

if (viewModel.length > 0) {
viewModel[0].active = true;
this.scrollIntoView(viewModel[0].id);
}

this.searchResults = viewModel;
},
handleSearchResultPageBrowsing(direction: BrowseDirection): void {
const searchResults: SearchResultItemViewModel[] = this.searchResults;
if (searchResults.length === 0 )
return;

const activeSearchResult = searchResults.find(r => r.active);
if (activeSearchResult === undefined)
return;

const activeSearchResultIndex = searchResults.indexOf(activeSearchResult);
const firstVisibleSearchResultIndex = this.getIndexOfFirstVisibleSearchResult();
const lastVisibleSearchResultIndex = this.getIndexOfLastVisibleSearchResult();

let nextActiveIndex = 0;
const appearanceOptions: AppearanceOptions = this.appearance;
const maxSearchResultsPerPage = Number(appearanceOptions.maxSearchResultsPerPage);
if (direction === BrowseDirection.Next) {
nextActiveIndex = activeSearchResultIndex >= firstVisibleSearchResultIndex && activeSearchResultIndex < lastVisibleSearchResultIndex
? lastVisibleSearchResultIndex
: activeSearchResultIndex + maxSearchResultsPerPage;
} else {
nextActiveIndex = activeSearchResultIndex > firstVisibleSearchResultIndex && activeSearchResultIndex <= lastVisibleSearchResultIndex
? firstVisibleSearchResultIndex
: activeSearchResultIndex - maxSearchResultsPerPage;
}

if (nextActiveIndex < 0)
nextActiveIndex = 0;
else if (nextActiveIndex >= searchResults.length)
nextActiveIndex = searchResults.length - 1;

activeSearchResult.active = false;
searchResults[nextActiveIndex].active = true;
this.scrollIntoView(searchResults[nextActiveIndex].id);
},
getIndexOfFirstVisibleSearchResult(): number {
const searchResultsContainer = document.getElementById(this.containerId);
if (searchResultsContainer == null)
return 0;

const appearanceOptions: AppearanceOptions = this.appearance;
return Math.ceil(searchResultsContainer.scrollTop / appearanceOptions.searchResultHeight);
},
getIndexOfLastVisibleSearchResult(): number {
const searchResultsContainer = document.getElementById(this.containerId);
if (searchResultsContainer == null)
return 0;

const appearanceOptions: AppearanceOptions = this.appearance;
return Math.floor((searchResultsContainer.scrollTop + searchResultsContainer.clientHeight) / appearanceOptions.searchResultHeight) - 1;
},
handleSearchResultBrowsing(direction: BrowseDirection): void {
const searchResults: SearchResultItemViewModel[] = this.searchResults;
if (searchResults.length === 0) {
Expand All @@ -78,35 +130,23 @@ export const searchResultsComponent = Vue.extend({
}
}

this.searchResults[nextActiveIndex].active = true;
this.scrollIntoView(this.searchResults[nextActiveIndex].id);
searchResults[nextActiveIndex].active = true;
this.scrollIntoView(searchResults[nextActiveIndex].id);
},
scrollIntoView(index: string) {
scrollIntoView(elementId: string) {
const appearanceOptions: AppearanceOptions = this.appearance;
const scrollBehavior = appearanceOptions.smoothScrolling ? "smooth" : "auto";
const userInput = document.getElementById("user-input");
if (userInput !== undefined && userInput) {
const htmlElement = document.getElementById(index);
if (htmlElement !== undefined && htmlElement !== null) {
const outputContainer = document.getElementById(this.containerId);
if (outputContainer !== undefined && outputContainer !== null) {
const elementIsOutOfViewBottom =
htmlElement.offsetTop - userInput.clientHeight >
outputContainer.scrollTop + outputContainer.clientHeight - htmlElement.clientHeight;
const elementIsOutOfViewTop =
htmlElement.offsetTop - userInput.clientHeight < outputContainer.scrollTop;
if (elementIsOutOfViewBottom) {
const scrollTo = htmlElement.offsetTop - userInput.clientHeight + 30;
outputContainer.scrollTo({ top: scrollTo, behavior: scrollBehavior });
} else if (elementIsOutOfViewTop) {
let scrollTo = htmlElement.offsetTop - outputContainer.clientHeight + 50;
if (scrollTo < 0) {
scrollTo = 0;
}
outputContainer.scrollTo({ top: scrollTo, behavior: scrollBehavior });
}
}
}
const htmlElement = document.getElementById(elementId);
const searchResultsContainer = document.getElementById(this.containerId);
if (!htmlElement || !searchResultsContainer)
return;

const elementIsOutOfViewBottom =
(htmlElement.offsetTop + htmlElement.clientHeight) > (searchResultsContainer.scrollTop + searchResultsContainer.clientHeight);
const elementIsOutOfViewTop = htmlElement.offsetTop < searchResultsContainer.scrollTop;
if (elementIsOutOfViewBottom || elementIsOutOfViewTop) {
const scrollTo = Math.floor(htmlElement.offsetTop / searchResultsContainer.clientHeight) * searchResultsContainer.clientHeight;
searchResultsContainer.scrollTo({ top: scrollTo, behavior: scrollBehavior });
}
},
},
Expand Down Expand Up @@ -136,6 +176,12 @@ export const searchResultsComponent = Vue.extend({
vueEventDispatcher.$on(VueEventChannels.selectPreviousItem, () => {
this.handleSearchResultBrowsing(BrowseDirection.Previous);
});
vueEventDispatcher.$on(VueEventChannels.pageDownPress, () => {
this.handleSearchResultPageBrowsing(BrowseDirection.Next);
});
vueEventDispatcher.$on(VueEventChannels.pageUpPress, () => {
this.handleSearchResultPageBrowsing(BrowseDirection.Previous);
});
vueEventDispatcher.$on(
VueEventChannels.enterPress,
(userInput: string, privileged: boolean, userConfirmed?: boolean) => {
Expand Down
10 changes: 10 additions & 0 deletions src/renderer/user-input-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ export const userInputComponent = Vue.extend({
}
}

if (event.key === "PageUp") {
event.preventDefault();
vueEventDispatcher.$emit(VueEventChannels.pageUpPress);
}

if (event.key === "PageDown") {
event.preventDefault();
vueEventDispatcher.$emit(VueEventChannels.pageDownPress);
}

if (event.key.toLowerCase() === "f" && ctrlOrMeta) {
event.preventDefault();
vueEventDispatcher.$emit(VueEventChannels.favoritesRequested);
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/vue-event-channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,6 @@ export enum VueEventChannels {
refreshIndexesStarted = "refresh-indexes-started",
refreshIndexesFinished = "refresh-indexes-finished",
executionFinished = "execution-finished",
pageUpPress = "page-up-press",
pageDownPress = "page-down-press",
}

0 comments on commit 0d6940a

Please sign in to comment.