search works good now :)

This commit is contained in:
2022-02-04 20:13:32 +01:00
parent 580bc3ad41
commit 17363ee620
9 changed files with 173 additions and 37 deletions
@@ -20,6 +20,6 @@ public class SearchController : ZeroApiController
{
return new Paged<SearchResult>(new List<SearchResult>(), 0, query.Page, query.PageSize);
}
return await Service.Query(query.Search);
return await Service.Query(query.Search, query.Page, query.PageSize);
}
}
@@ -4,17 +4,22 @@
<script>
import * as overlays from '../../services/overlay';
import { useSearchStore } from './store';
export default {
data: () => ({
listener: null
open: false,
listener: null,
store: null
}),
mounted()
{
this.store = useSearchStore();
this.listener = e =>
{
if (e.key === "f" && (e.ctrlKey || e.metaKey))
if (this.store.shortcutEnabled() && e.key === "f" && (e.ctrlKey || e.metaKey) && !this.open)
{
e.preventDefault();
this.openSearch();
@@ -32,13 +37,17 @@
methods: {
async openSearch()
{
this.open = true;
const result = await overlays.open({
component: () => import('./overlay.vue'),
autoclose: false,
softdismiss: true,
width: 780
//class: 'app-search-overlay'
width: 780,
class: 'app-search-overlay'
});
this.open = false;
}
}
}
+112 -28
View File
@@ -1,25 +1,34 @@
<template>
<div class="app-search">
<form class="app-search-form" @submit.prevent="onSubmit">
<input ref="input" class="app-search-form-input" type="search" v-model="query" @input="onSearch" placeholder="Search..." />
<input ref="input" class="app-search-form-input" type="search" v-model="query" @input="onSearch" v-localize:placeholder="'@search.input_placeholder'" />
<ui-button class="app-search-submit" :submit="true" type="blank" icon="fth-search" />
</form>
<div v-if="list.items.length" class="app-search-items">
<ui-link :to="item.url" v-for="item in list.items" :key="item.id" class="app-search-item" @click.native="config.close(true)">
<ui-icon :symbol="item.icon" :size="18" class="app-search-item-icon" />
<span class="app-search-item-text">
<span class="app-search-item-name">{{item.name}} <span class="app-search-item-group" v-localize="item.group"></span></span>
<span class="app-search-item-group" v-if="item.description">{{item.description}}</span>
{{item.name}}<span class="-minor" v-if="item.description"> <span>&ndash;</span> {{item.description}}</span>
</span>
<span class="app-search-item-group" v-localize="item.group"></span>
</ui-link>
</div>
</div>
<div class="app-search-hint">
<p v-localize:html="'@search.native_hint'"></p>
<label class="ui-native-check is-right app-search-shortcut onbg">
<input type="checkbox" v-model="useShortcut" @change="onShortcutUpdated" />
<span class="ui-native-check-toggle"></span>
<p v-localize:html="'@search.native_toggle'"></p>
</label>
</div>
</template>
<script>
import api from './api';
import { debounce } from '../../utils';
import { useSearchStore } from './store';
export default {
@@ -38,7 +47,10 @@
totalPages: 1,
items: []
},
onSearch: null
onSearch: null,
closeListener: null,
useShortcut: true,
store: null
}),
created()
@@ -48,17 +60,48 @@
mounted()
{
this.store = useSearchStore();
this.useShortcut = this.store.shortcutEnabled();
this.$nextTick(() =>
{
this.$refs.input.focus();
//this.$refs.input.select();
});
this.closeListener = e =>
{
let isSearchCombi = this.store.shortcutEnabled() && e.key === "f" && (e.ctrlKey || e.metaKey);
if (e.key === "Escape" || isSearchCombi)
{
if (!isSearchCombi)
{
e.preventDefault();
}
this.config.close(true);
}
};
document.addEventListener('keydown', this.closeListener.bind(this));
},
beforeDestroy()
{
document.removeEventListener('keydown', this.closeListener);
},
methods: {
async search()
{
const result = await api.query(this.query);
if (!this.query)
{
this.list.items = [];
this.list.page = 1;
this.list.totalPages = 1;
return;
}
const result = await api.query(this.query, { pageSize: 20 });
if (result.data)
{
@@ -66,6 +109,11 @@
this.list.page = result.paging.page;
this.list.totalPages = result.paging.totalPages;
}
},
onShortcutUpdated()
{
this.store.setShortcutEnabled(this.useShortcut);
}
}
}
@@ -74,7 +122,12 @@
<style lang="scss">
.app-search-overlay .app-overlay
{
padding: var(--padding);
padding: 0;
}
.app-search
{
padding: var(--padding-s);
}
.app-search-dialog
@@ -112,7 +165,7 @@
{
display: grid;
width: 100%;
grid-template-columns: 26px 1fr auto;
grid-template-columns: 20px 1fr auto;
gap: 12px;
align-items: center;
position: relative;
@@ -134,40 +187,71 @@
.app-search-item-text
{
display: flex;
flex-direction: column;
position: relative;
top: 1px;
}
.app-search-item-name
{
font-size: var(--font-size);
/*display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;*/
}
position: relative;
top: 1px;
font-size: var(--font-size);
.app-search-item-description
{
color: var(--color-text-dim);
margin-top: 3px;
.-minor
{
font-size: var(--font-size-xs);
color: var(--color-text-dim);
margin-left: 0.5em;
span
{
color: var(--color-text-dim-one);
}
}
}
.app-search-item-group
{
display: block;
display: inline-flex;
align-self: center;
align-items: center;
font-size: var(--font-size-xs);
color: var(--color-text-dim);
margin-top: 3px;
//margin-left: 8px;
color: var(--color-text);
font-weight: 600;
background: var(--color-table-highlight);
padding: 4px 12px;
border-radius: 20px;
}
.app-search-item-icon
{
position: relative;
top: -1px;
left: 4px;
color: var(--color-text);
}
.app-search-hint
{
display: flex;
justify-content: space-between;
margin-top: 0;
border-bottom-left-radius: var(--radius);
border-bottom-right-radius: var(--radius);
background: var(--color-bg-shade-2);
border-top: 1px solid var(--color-line-dashed);
padding: var(--padding-s) var(--padding-s);
font-size: var(--font-size-s);
align-items: center;
color: var(--color-text-dim);
code
{
border: 1px dashed var(--color-line-dashed-onbg);
}
p + p
{
margin-top: 0;
}
}
.app-search-shortcut
{
font-size: var(--font-size-s);
}
</style>
@@ -0,0 +1,20 @@
import { defineStore } from 'pinia';
const SEARCH_BY_SHORTCUT_KEY = 'zero.search.shortcut';
export const useSearchStore = defineStore('zero.search', {
state: () => ({}),
actions: {
shortcutEnabled()
{
return localStorage.getItem(SEARCH_BY_SHORTCUT_KEY) !== 'false';
},
setShortcutEnabled(enabled: boolean)
{
localStorage.setItem(SEARCH_BY_SHORTCUT_KEY, enabled.toString());
}
}
});
+3 -1
View File
@@ -29,6 +29,7 @@ export interface OverlayOptions
component?: any;
theme?: OverlayTheme;
model?: any;
class?: string;
}
export interface OverlayInstance extends OverlayOptions
@@ -139,7 +140,8 @@ export function open(options: OverlayOptions)
softdismiss: options.display !== 'editor',
theme: 'default',
model: null,
component: null
component: null,
class: options.class
} as OverlayOptions, options) as OverlayOptions;
const instance = {
@@ -152,6 +152,11 @@ textarea
{
cursor: default;
}
&.is-right
{
flex-direction: row-reverse;
}
}
.ui-native-check-toggle
@@ -180,6 +185,8 @@ textarea
font-family: var(--font-icon);
color: transparent;
font-size: var(--font-size-s);
position: relative;
top: -1px;
}
input:focus ~ &
@@ -199,4 +206,10 @@ textarea
color: var(--color-checked-fg);
}
}
.ui-native-check.is-right &
{
margin-left: 10px;
margin-right: 0;
}
}
@@ -765,6 +765,10 @@
"search_group": "Suche",
"search": "Suchen"
},
"input_placeholder": "Gehe zu...",
"native_hint": "Drücke <code>STRG+F</code> erneut um die native Browsersuche zu verwenden",
"native_toggle": "Öffnen mit <code>STRG+F</code>",
"noresults": "Keine Ergebnisse",
"collection": {
"page": "Seite",
"mediafolder": "Medien"
@@ -811,6 +811,10 @@
"search_group": "Search",
"search": "Use search"
},
"input_placeholder": "Go to...",
"native_hint": "Press <code>CTRL+F</code> again to use native browser search",
"native_toggle": "Open on <code>CTRL+F</code>",
"noresults": "No results",
"collection": {
"page": "Page",
"mediaFolder": "Media folder"
+3 -3
View File
@@ -18,7 +18,7 @@ public class SearchService : ISearchService
}
public async Task<Paged<SearchResult>> Query(string searchTerm)
public async Task<Paged<SearchResult>> Query(string searchTerm, int page = 1, int pageSize = 10)
{
string[] searchParts = searchTerm.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(x =>
{
@@ -30,7 +30,7 @@ public class SearchService : ISearchService
.Search(x => x.Name, searchParts, 2, @operator: SearchOperator.And)
.Search(x => x.Fields, searchParts, 1, Raven.Client.Documents.SearchOptions.Or, @operator: SearchOperator.And)
.OrderByScoreDescending()
.Paging(1, 6)
.Paging(page, pageSize)
.As<ZeroEntity>()
.ToListAsync();
@@ -70,5 +70,5 @@ public class SearchService : ISearchService
public interface ISearchService
{
Task<Paged<SearchResult>> Query(string searchTerm);
Task<Paged<SearchResult>> Query(string searchTerm, int page = 1, int pageSize = 10);
}