Skip to content

Commit

Permalink
Initial implementation of search document feature
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroDinis committed Dec 8, 2023
1 parent c7ed6f0 commit 1121dfb
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 9 deletions.
34 changes: 34 additions & 0 deletions src/assets/scss/document_search_bar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@import "./imports.scss";
.document-search {
position: fixed;
right: 26vw;
margin-top: 5px;
z-index: 95;

.search-container {
background-color: #2f3032;
color: white;
padding: 10px;
width: 300px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: center;

.search-input {
font-size: 14px;
background: inherit;
color: inherit;
border: 0;
padding: 5px 8px;
width: 140px;
margin-right: 10px;
}

.search-loading {
margin-left: auto;
font-size: 13px;
color: #8a8f8d;
}
}
}
3 changes: 2 additions & 1 deletion src/assets/scss/document_toolbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@
}
}

.download-file {
.download-file,
.search-icon {
color: $toolbar-elements;

.is-active {
Expand Down
3 changes: 2 additions & 1 deletion src/assets/scss/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@
width: 100%;

.icon {
&.download-file {
&.download-file,
&.search-icon {
svg {
height: 100% !important;
width: 100% !important;
Expand Down
34 changes: 34 additions & 0 deletions src/components/DocumentPage/DocumentPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@
}"
/>
<template v-if="pageInVisibleRange && !editMode">
<template v-if="searchResults.length > 0">
<v-rect
v-for="(bbox, index) in searchResults"
:key="'sr' + index"
:config="{
...selectionTextRect(bbox),
fill:
bbox === currentSearchResultForPage
? 'mediumturquoise'
: 'paleturquoise',
}"
></v-rect>
</template>
<v-group v-if="!publicView || !isDocumentReviewed" ref="entities">
<v-rect
v-for="(entity, index) in scaledEntities"
Expand Down Expand Up @@ -266,6 +279,18 @@ export default {
);
},
searchResults() {
return this.$store.getters["display/searchResultsForPage"](
this.pageNumber
);
},
currentSearchResultForPage() {
return this.$store.getters["display/currentSearchResultForPage"](
this.page.pageNumber
);
},
...mapState("selection", ["isSelecting", "selectedEntities"]),
...mapState("display", ["scale", "categorizeModalIsActive"]),
...mapState("document", [
Expand Down Expand Up @@ -495,6 +520,15 @@ export default {
}
},
selectionTextRect(bbox) {
return {
fill: "greenyellow",
globalCompositeOperation: "multiply",
draggable: true,
...this.bboxToRect(bbox),
};
},
/**
* Builds the konva config object for the entity.
*/
Expand Down
11 changes: 10 additions & 1 deletion src/components/DocumentPage/DocumentToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@
</b-dropdown>
</div>

<div
v-if="!publicView"
class="search-document icons"
@click="toggleSearch"
>
<b-icon icon="search" size="small" class="search-icon" />
</div>

<div v-if="!publicView" class="toolbar-divider" />

<div class="icons icons-right">
Expand Down Expand Up @@ -80,7 +88,7 @@
</template>

<script>
import { mapState, mapGetters } from "vuex";
import { mapActions, mapState, mapGetters } from "vuex";
import FitZoomIcon from "../../assets/images/FitZoomIcon";
import PlusIcon from "../../assets/images/PlusIcon";
import MinusIcon from "../../assets/images/MinusIcon";
Expand Down Expand Up @@ -144,6 +152,7 @@ export default {
}
},
methods: {
...mapActions("display", ["toggleSearch"]),
handleEdit() {
if (this.editModeDisabled) return;
this.$store.dispatch("selection/disableSelection");
Expand Down
5 changes: 5 additions & 0 deletions src/components/DocumentPage/ScrollingDocument.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
v-scroll.immediate="updateScrollBounds"
class="scrolling-document"
>
<SearchBar v-if="searchEnabled" />

<div
v-if="
selectedDocument && scale && !loading && !recalculatingAnnotations
Expand Down Expand Up @@ -36,12 +38,14 @@ import scroll from "../../directives/scroll";
import ScrollingPage from "./ScrollingPage";
import Toolbar from "./DocumentToolbar";
import ActionBar from "./ActionBar";
import SearchBar from "./SearchBar";
export default {
components: {
ScrollingPage,
Toolbar,
ActionBar,
SearchBar,
},
directives: {
scroll,
Expand Down Expand Up @@ -73,6 +77,7 @@ export default {
"documentActionBar",
"pageChangedFromThumbnail",
"currentPage",
"searchEnabled",
]),
...mapGetters("display", ["visiblePageRange"]),
Expand Down
30 changes: 26 additions & 4 deletions src/components/DocumentPage/ScrollingPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export default {
},
computed: {
...mapState("display", ["pageChangedFromThumbnail"]),
...mapState("display", ["pageChangedFromThumbnail", "currentPage"]),
...mapState("document", ["pages", "documentAnnotationSelected", "loading"]),
...mapState("edit", ["editMode"]),
...mapGetters("display", ["visiblePageRange", "bboxToRect"]),
...mapGetters("document", ["scrollDocumentToAnnotation"]),
Expand Down Expand Up @@ -90,9 +92,11 @@ export default {
return this.scrollTop + this.clientHeight;
},
...mapState("display", ["currentPage"]),
...mapState("document", ["pages", "documentAnnotationSelected", "loading"]),
...mapState("edit", ["editMode"]),
currentSearchResultForPage() {
return this.$store.getters["display/currentSearchResultForPage"](
this.page.pageNumber
);
},
},
watch: {
Expand Down Expand Up @@ -128,6 +132,24 @@ export default {
this.$emit("page-jump", this.elementTop, 0);
}
},
/**
* Scroll to the search result if the current one changes and it's on this page.
*/
currentSearchResultForPage(res) {
// skip page jump if the result is null (the current search result is not on this page)
if (!res) {
return;
}
const y = this.getYForBbox(res); // y of the search result
const totalY = y + this.elementTop; // y of search result + page top
// skip page jump if the search result is already visible on this page
if (totalY < this.scrollBottom && totalY > this.scrollTop) {
return;
}
this.$nextTick(function () {
this.scrollTo(y);
});
},
},
mounted() {
this.updateElementBounds();
Expand Down
124 changes: 124 additions & 0 deletions src/components/DocumentPage/SearchBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<template>
<transition
mode="out-in"
enter-active-class="animate__animated animate__fast animate__slideInDown"
leave-active-class="animate__animated animate__faster animate__slideOutUp"
>
<div class="document-search">
<div class="search-container">
<div
style="font-size: 20px; margin: 0 10px; cursor: pointer"
@click="$store.dispatch('display/toggleSearch')"
>
<img
src="/static/images/close.svg"
style="height: 10px; padding-bottom: 2px"
/>
</div>
<input
ref="searchInput"
v-model="search"
:placeholder="$t('search')"
class="search-input"
@keyup.13="focusSearchResult(1)"
/>
<div class="search-loading">
<template v-if="searchLoading">
<svg
class="spinner"
width="20px"
height="20px"
viewBox="0 0 66 66"
xmlns="http://www.w3.org/2000/svg"
>
<circle
class="path"
fill="none"
stroke-width="6"
stroke-linecap="round"
cx="33"
cy="33"
r="30"
></circle>
</svg>
</template>
<template v-else-if="searchResults.length">
{{ currentSearchResult + 1 }} / {{ searchResults.length }}
<img
src="/static/images/caret-down.svg"
style="
margin-left: 8px;
height: 8px;
vertical-align: middle;
cursor: pointer;
"
@click="focusSearchResult(1)"
/>
<img
src="/static/images/caret-up.svg"
style="
margin-left: 8px;
margin-right: 4px;
height: 8px;
vertical-align: middle;
cursor: pointer;
"
@click="focusSearchResult(-1)"
/>
</template>
<template v-else-if="search.length >= 3">
{{ $t("no_results") }}
</template>
</div>
</div>
</div>
</transition>
</template>

<script>
import { mapState } from "vuex";
export default {
name: "SearchBar",
data() {
return {
search: "",
};
},
computed: {
...mapState("display", [
"currentSearchResult",
"searchEnabled",
"searchResults",
"searchLoading",
]),
},
watch: {
search(search) {
if (search.length >= 3) {
this.$store.dispatch("display/startSearchLoading");
}
this.$store.dispatch("display/debounceSearch", search);
},
searchEnabled(enabled) {
if (enabled) {
this.$nextTick(() => {
this.$refs.searchInput.focus();
});
}
},
},
methods: {
focusSearchResult(n) {
this.$store.dispatch("display/setCurrentSearchResult", n);
},
},
};
</script>

<style
scoped
lang="scss"
src="../../assets/scss/document_search_bar.scss"
></style>
4 changes: 3 additions & 1 deletion src/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
faArrowLeft,
faArrowRight,
faDownload,
faSearch,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon as Icons } from "@fortawesome/vue-fontawesome";

Expand All @@ -39,7 +40,8 @@ library.add(
faRepeat,
faArrowLeft,
faArrowRight,
faDownload
faDownload,
faSearch
);

export default Icons;
4 changes: 3 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,7 @@
"translated_string_title": "Translated text",
"no_translated_string": "No translation exists yet.",
"add_translation": "Click to add one",
"edit_translation": "Click to edit"
"edit_translation": "Click to edit",
"search": "Search",
"no_results": "No results"
}
Loading

0 comments on commit 1121dfb

Please sign in to comment.