mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-13 10:09:27 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b90dabeb2c | |||
| 2f1edfeeab | |||
| 414d8bd383 | |||
| 1a813e52ce | |||
| 16b238124a | |||
| 4def45907b | |||
| 202e6e88d2 | |||
| 3b2473039f | |||
| 59d1ca3409 | |||
| 1d26303333 |
@@ -136,6 +136,18 @@ const app = Vue.createApp({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
isMobile() {
|
||||||
|
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
||||||
|
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
||||||
|
return smallScreen;// && touchCapable;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
isMobile: this.isMobile
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
searchfunction: function(searchsettings) {
|
searchfunction: function(searchsettings) {
|
||||||
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
|
return this.$api.call(ApiSearchbar.searchCis(searchsettings));
|
||||||
|
|||||||
@@ -238,13 +238,13 @@ const app = Vue.createApp({
|
|||||||
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
const smallScreen = window.matchMedia("(max-width: 767px)").matches;
|
||||||
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0;
|
||||||
return smallScreen;// && touchCapable;
|
return smallScreen;// && touchCapable;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return { // provide injectable & watchable language property
|
return { // provide injectable & watchable language property
|
||||||
language: Vue.computed(() => this.$p.user_language),
|
language: Vue.computed(() => this.$p.user_language),
|
||||||
renderers: Vue.computed(() => this.renderers),
|
renderers: Vue.computed(() => this.renderers),
|
||||||
isMobile: this.isMobile
|
isMobile: this.isMobile,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default {
|
|||||||
menuOpen:true,
|
menuOpen:true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
inject: ["isMobile"],
|
||||||
provide(){
|
provide(){
|
||||||
return{
|
return{
|
||||||
setActiveEntry: this.setActiveEntry,
|
setActiveEntry: this.setActiveEntry,
|
||||||
@@ -58,7 +59,7 @@ export default {
|
|||||||
},
|
},
|
||||||
site_url(){
|
site_url(){
|
||||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchMenu() {
|
fetchMenu() {
|
||||||
@@ -112,10 +113,26 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
template: /*html*/`
|
template: /*html*/`
|
||||||
<button id="nav-main-btn" class="navbar-toggler rounded-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
<div
|
||||||
<span class="navbar-toggler-icon"></span>
|
id="header-options-collapsible"
|
||||||
</button>
|
class="collapse multi-collapse collapse-horizontal show"
|
||||||
<fhc-searchbar ref="searchbar" id="nav-search" class="fhc-searchbar w-100 py-1 py-lg-2" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
>
|
||||||
|
<div class="d-flex flex-row align-items-center gap-2 h-100" style="width: 79px">
|
||||||
|
<button id="nav-main-btn" class="navbar-toggler rounded-0 px-2 border-0" type="button" data-bs-toggle="offcanvas" data-bs-target="#nav-main" aria-controls="nav-main" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<span v-if="isMobile" class="d-flex flex-row align-items-center">
|
||||||
|
<theme-switch></theme-switch>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fhc-searchbar
|
||||||
|
:searchoptions="searchbaroptions"
|
||||||
|
:searchfunction="searchfunction"
|
||||||
|
ref="searchbar"
|
||||||
|
id="nav-search"
|
||||||
|
class="fhc-searchbar w-100 py-1 py-lg-2"
|
||||||
|
></fhc-searchbar>
|
||||||
<div id="nav-logo" class="d-none d-lg-block">
|
<div id="nav-logo" class="d-none d-lg-block">
|
||||||
<div class="d-flex h-100 justify-content-between">
|
<div class="d-flex h-100 justify-content-between">
|
||||||
<a :href="rootUrl">
|
<a :href="rootUrl">
|
||||||
@@ -124,22 +141,32 @@ export default {
|
|||||||
<theme-switch></theme-switch>
|
<theme-switch></theme-switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="nav-user">
|
|
||||||
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
|
<div
|
||||||
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
id="header-usermenu-collapsible"
|
||||||
</button>
|
class="collapse multi-collapse collapse-horizontal show"
|
||||||
<ul ref="navUserDropdown"
|
>
|
||||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
<div
|
||||||
@[\`hide.bs.collapse\`]="handleHideNavUser"
|
:style="!isMobile ? '' : 'width: 51px'"
|
||||||
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
id="nav-user"
|
||||||
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
>
|
||||||
<li >
|
<button id="nav-user-btn" class="btn btn-link rounded-0" type="button" data-bs-toggle="collapse" data-bs-target="#nav-user-menu" aria-expanded="false" aria-controls="nav-user-menu">
|
||||||
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
|
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
||||||
</li>
|
</button>
|
||||||
<li><hr class="dropdown-divider m-0 "></li>
|
<ul ref="navUserDropdown"
|
||||||
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||||
</ul>
|
@[\`hide.bs.collapse\`]="handleHideNavUser"
|
||||||
|
id="nav-user-menu" class="top-100 end-0 collapse list-unstyled" aria-labelledby="nav-user-btn">
|
||||||
|
<li><a class="fhc-dark-bg btn rounded-0 d-block" :href="site_url + '/Cis/Profil'" id="menu-profil">Profil</a></li>
|
||||||
|
<li >
|
||||||
|
<cis-sprachen @languageChanged="fetchMenu"></cis-sprachen>
|
||||||
|
</li>
|
||||||
|
<li><hr class="dropdown-divider m-0 "></li>
|
||||||
|
<li ><a class="fhc-dark-bg btn rounded-0 d-block" :href="logoutUrl">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
|
<nav id="nav-main" class="offcanvas offcanvas-start" tabindex="-1" aria-labelledby="nav-main-btn" data-bs-backdrop="false">
|
||||||
<div id="nav-main-sticky">
|
<div id="nav-main-sticky">
|
||||||
<div id="nav-main-toggle" class="position-static d-none d-lg-block ">
|
<div id="nav-main-toggle" class="position-static d-none d-lg-block ">
|
||||||
|
|||||||
@@ -21,69 +21,71 @@ export default {
|
|||||||
dms,
|
dms,
|
||||||
cms,
|
cms,
|
||||||
mergedStudent,
|
mergedStudent,
|
||||||
mergedPerson
|
mergedPerson,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
searchoptions: {
|
searchoptions: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
searchfunction: {
|
searchfunction: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
showBtnSubmit: Boolean
|
showBtnSubmit: Boolean,
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
query: Vue.computed(() => this.lastQuery)
|
query: Vue.computed(() => this.lastQuery),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data: function() {
|
inject: ["isMobile"],
|
||||||
return {
|
data: function () {
|
||||||
searchtimer: null,
|
return {
|
||||||
hidetimer: null,
|
searchtimer: null,
|
||||||
searchsettings: {
|
hidetimer: null,
|
||||||
searchstr: this.getSearchStr(),
|
searchsettings: {
|
||||||
types: this.getInitiallySelectedTypes(),
|
searchstr: this.getSearchStr(),
|
||||||
},
|
types: this.getInitiallySelectedTypes(),
|
||||||
searchresult: [],
|
},
|
||||||
searchmode: '',
|
searchresult: [],
|
||||||
showresult: false,
|
searchmode: "",
|
||||||
searching: false,
|
showresult: false,
|
||||||
error: null,
|
searching: false,
|
||||||
abortController: null,
|
error: null,
|
||||||
|
abortController: null,
|
||||||
settingsDropdown: null,
|
settingsDropdown: null,
|
||||||
lastQuery: ''
|
lastQuery: "",
|
||||||
};
|
isSearchShownInMobileView: false,
|
||||||
},
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
searchTypesPlaceholder() {
|
searchTypesPlaceholder() {
|
||||||
if (!this.searchsettings.types.length) {
|
if (!this.searchsettings.types.length) {
|
||||||
return Object.values(this.typeLabels).join(' / ');
|
return Object.values(this.typeLabels).join(" / ");
|
||||||
}
|
}
|
||||||
return this.searchsettings.types.map(type => this.typeLabels[type]).join(' / ');
|
return this.searchsettings.types
|
||||||
|
.map((type) => this.typeLabels[type])
|
||||||
|
.join(" / ");
|
||||||
},
|
},
|
||||||
types() {
|
types() {
|
||||||
if (!this.searchoptions.types)
|
if (!this.searchoptions.types) return [];
|
||||||
return [];
|
|
||||||
if (Array.isArray(this.searchoptions.types))
|
if (Array.isArray(this.searchoptions.types))
|
||||||
return this.searchoptions.types;
|
return this.searchoptions.types;
|
||||||
return Object.keys(this.searchoptions.types);
|
return Object.keys(this.searchoptions.types);
|
||||||
},
|
},
|
||||||
typeLabels() {
|
typeLabels() {
|
||||||
if (!this.searchoptions.types)
|
if (!this.searchoptions.types) return {};
|
||||||
return {};
|
|
||||||
if (Array.isArray(this.searchoptions.types)) {
|
if (Array.isArray(this.searchoptions.types)) {
|
||||||
return this.searchoptions.types.reduce((res, type) => {
|
return this.searchoptions.types.reduce((res, type) => {
|
||||||
res[type] = type;
|
res[type] = type;
|
||||||
return res
|
return res;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
return this.searchoptions.types;
|
return this.searchoptions.types;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
template: /*html*/`
|
template: /*html*/ `
|
||||||
<form
|
<form
|
||||||
ref="searchform"
|
ref="searchform"
|
||||||
class="d-flex me-3"
|
class="d-flex me-3"
|
||||||
@@ -92,80 +94,91 @@ export default {
|
|||||||
@focusin="searchfocusin"
|
@focusin="searchfocusin"
|
||||||
@focusout="searchfocusout"
|
@focusout="searchfocusout"
|
||||||
>
|
>
|
||||||
<div
|
<span
|
||||||
ref="searchbox"
|
v-if="isMobile"
|
||||||
class="h-100 input-group me-2 searchbar_searchbox"
|
type="button"
|
||||||
:class="showresult ? 'open' : 'closed'"
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target=".multi-collapse"
|
||||||
|
aria-controls="header-searchbar-collapsible header-options-collapsible header-usermenu-collapsible"
|
||||||
|
aria-expanded="false"
|
||||||
|
class="d-flex flex-row align-items-center ps-3 pe-1"
|
||||||
>
|
>
|
||||||
<span class="input-group-text">
|
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left"></i>
|
||||||
<i class="fa-solid fa-magnifying-glass"></i>
|
<i v-else class="fa-solid fa-magnifying-glass"></i>
|
||||||
</span>
|
</span>
|
||||||
<input
|
|
||||||
ref="input"
|
|
||||||
@keyup="search"
|
|
||||||
@focus="showsearchresult"
|
|
||||||
v-model="searchsettings.searchstr"
|
|
||||||
class="form-control searchbar_input"
|
|
||||||
type="search"
|
|
||||||
:placeholder="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
|
|
||||||
:aria-label="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
v-if="searchsettings.searchstr"
|
|
||||||
type="button"
|
|
||||||
class="searchbar_input_clear btn btn-outline-secondary"
|
|
||||||
@click="clearInput"
|
|
||||||
@focusin.stop
|
|
||||||
>
|
|
||||||
<i class="fas fa-close"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="showBtnSubmit"
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-primary"
|
|
||||||
:title="$p.t('search/submit')"
|
|
||||||
:aria-label="$p.t('search/submit')"
|
|
||||||
>
|
|
||||||
<i class="fas fa-search"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
data-bs-toggle="collapse"
|
|
||||||
data-bs-target="#searchSettings"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-controls="searchSettings"
|
|
||||||
ref="settingsbutton"
|
|
||||||
class="searchbar_setting_btn btn btn-secondary"
|
|
||||||
type="button"
|
|
||||||
:title="$p.t('search/button_filter_label')"
|
|
||||||
:aria-label="$p.t('search/button_filter_label')"
|
|
||||||
>
|
|
||||||
<i class="fas fa-cog"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-show="showresult"
|
<div
|
||||||
class="searchbar_results" tabindex="-1">
|
:class="{'flex-grow-1': !isMobile, 'collapse multi-collapse collapse-horizontal': isMobile}"
|
||||||
<div class="searchbar_results_scroller" ref="result">
|
id="header-searchbar-collapsible"
|
||||||
<div class="searchbar_results_wrapper" ref="results">
|
@[\`show.bs.collapse\`]="isSearchShownInMobileView = true"
|
||||||
<div v-if="searching">
|
@[\`hidden.bs.collapse\`]="isSearchShownInMobileView = false"
|
||||||
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
>
|
||||||
</div>
|
<div
|
||||||
<div v-else-if="this.error !== null">{{ error }}</div>
|
:class="{open: showresult, closed: showresult, 'px-3': isMobile}"
|
||||||
<div v-else-if="searchresult.length < 1">{{ $p.t('search/error_no_results') }}</div>
|
ref="searchbox"
|
||||||
<template v-else v-for="res in searchresult">
|
class="h-100 input-group me-2 searchbar_searchbox"
|
||||||
<component
|
:style="isMobile ? 'width: ' + getMaxWidthOfSearchbarInMobileView() : ''"
|
||||||
v-if="isValidRenderer(res.renderer)"
|
>
|
||||||
:is="res.renderer"
|
<span class="input-group-text">
|
||||||
:mode="searchmode"
|
<i class="fa-solid fa-magnifying-glass color-white"></i>
|
||||||
:res="res"
|
</span>
|
||||||
:actions="getActions(res)"
|
<input
|
||||||
@actionexecuted="hideresult"
|
ref="input"
|
||||||
></component>
|
@keyup="search"
|
||||||
<div v-else class="searchbar-result text-danger fw-bold">{{ $p.t('search/error_unknown_type', res) }}</div>
|
@focus="showsearchresult"
|
||||||
</template>
|
v-model="searchsettings.searchstr"
|
||||||
</div>
|
class="form-control searchbar_input"
|
||||||
</div>
|
type="search"
|
||||||
</div>
|
:placeholder="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
|
||||||
|
:aria-label="$p.t('search/input_search_label', { types: searchTypesPlaceholder })"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-if="showBtnSubmit"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
:title="$p.t('search/submit')"
|
||||||
|
:aria-label="$p.t('search/submit')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-search"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#searchSettings"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="searchSettings"
|
||||||
|
ref="settingsbutton"
|
||||||
|
class="searchbar_setting_btn btn btn-secondary"
|
||||||
|
type="button"
|
||||||
|
:title="$p.t('search/button_filter_label')"
|
||||||
|
:aria-label="$p.t('search/button_filter_label')"
|
||||||
|
>
|
||||||
|
<i class="fas fa-filter"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-show="showresult"
|
||||||
|
class="searchbar_results" tabindex="-1">
|
||||||
|
<div class="searchbar_results_scroller" ref="result">
|
||||||
|
<div class="searchbar_results_wrapper" ref="results">
|
||||||
|
<div v-if="searching">
|
||||||
|
<i class="fas fa-spinner fa-spin fa-2x"></i>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="this.error !== null">{{ error }}</div>
|
||||||
|
<div v-else-if="searchresult.length < 1">{{ $p.t('search/error_no_results') }}</div>
|
||||||
|
<template v-else v-for="res in searchresult">
|
||||||
|
<component
|
||||||
|
v-if="isValidRenderer(res.renderer)"
|
||||||
|
:is="res.renderer"
|
||||||
|
:mode="searchmode"
|
||||||
|
:res="res"
|
||||||
|
:actions="getActions(res)"
|
||||||
|
@actionexecuted="hideresult"
|
||||||
|
></component>
|
||||||
|
<div v-else class="searchbar-result text-danger fw-bold">{{ $p.t('search/error_unknown_type', res) }}</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="searchSettings"
|
id="searchSettings"
|
||||||
@@ -207,34 +220,42 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
`,
|
`,
|
||||||
watch:{
|
watch: {
|
||||||
'searchsettings.searchstr': function (newSearchValue) {
|
"searchsettings.searchstr": function (newSearchValue) {
|
||||||
if(this.searchoptions.origin){
|
if (this.searchoptions.origin) {
|
||||||
sessionStorage.setItem(`${this.searchoptions.origin}_searchstr`,newSearchValue);
|
sessionStorage.setItem(
|
||||||
|
`${this.searchoptions.origin}_searchstr`,
|
||||||
|
newSearchValue,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'searchsettings.types'(newValue) {
|
"searchsettings.types"(newValue) {
|
||||||
if (Array.isArray(newValue) && newValue.length === 0) {
|
if (Array.isArray(newValue) && newValue.length === 0) {
|
||||||
this.searchsettings.types = [...this.types];
|
this.searchsettings.types = [...this.types];
|
||||||
}
|
}
|
||||||
// stores the search types in the localstorage, only if the newValue is also an array
|
// stores the search types in the localstorage, only if the newValue is also an array
|
||||||
if (Array.isArray(newValue) && this.searchoptions.origin) {
|
if (Array.isArray(newValue) && this.searchoptions.origin) {
|
||||||
localStorage.setItem(`${this.searchoptions.origin}_searchtypes`, JSON.stringify(newValue));
|
localStorage.setItem(
|
||||||
|
`${this.searchoptions.origin}_searchtypes`,
|
||||||
|
JSON.stringify(newValue),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.search();
|
this.search();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted() {
|
||||||
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
|
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
|
||||||
toggle: false
|
toggle: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.searchoptions.origin){
|
if (!this.searchoptions.origin) {
|
||||||
console.warn("No origin defined in the searchoptions for the searchbar, please define the origin property in the searchbaroptions to allow reliable storage of searchstr and searchtypes accross applications.");
|
console.warn(
|
||||||
|
"No origin defined in the searchoptions for the searchbar, please define the origin property in the searchbaroptions to allow reliable storage of searchstr and searchtypes accross applications.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
if(this.showresult) {
|
if (this.showresult) {
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
this.calcSearchResultHeight();
|
this.calcSearchResultHeight();
|
||||||
});
|
});
|
||||||
@@ -249,32 +270,34 @@ export default {
|
|||||||
getInitiallySelectedTypes() {
|
getInitiallySelectedTypes() {
|
||||||
let result = false;
|
let result = false;
|
||||||
if (this.searchoptions.origin) {
|
if (this.searchoptions.origin) {
|
||||||
let localStorageValue = localStorage.getItem(`${this.searchoptions.origin}_searchtypes`);
|
let localStorageValue = localStorage.getItem(
|
||||||
|
`${this.searchoptions.origin}_searchtypes`,
|
||||||
|
);
|
||||||
if (localStorageValue) {
|
if (localStorageValue) {
|
||||||
result = JSON.parse(localStorageValue);
|
result = JSON.parse(localStorageValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result)
|
if (result) return result;
|
||||||
return result;
|
if (!this.searchoptions.types) return [];
|
||||||
if (!this.searchoptions.types)
|
|
||||||
return [];
|
|
||||||
if (Array.isArray(this.searchoptions.types))
|
if (Array.isArray(this.searchoptions.types))
|
||||||
return [...this.searchoptions.types];
|
return [...this.searchoptions.types];
|
||||||
return Object.keys(this.searchoptions.types);
|
return Object.keys(this.searchoptions.types);
|
||||||
},
|
},
|
||||||
getSearchStr: function(){
|
getSearchStr: function () {
|
||||||
if (!this.searchoptions.origin)
|
if (!this.searchoptions.origin) return "";
|
||||||
return '';
|
return (
|
||||||
return sessionStorage.getItem(`${this.searchoptions.origin}_searchstr`) ?? '';
|
sessionStorage.getItem(
|
||||||
|
`${this.searchoptions.origin}_searchstr`,
|
||||||
|
) ?? ""
|
||||||
|
);
|
||||||
},
|
},
|
||||||
checkSettingsVisibility: function(event) {
|
checkSettingsVisibility: function (event) {
|
||||||
// hides the settings collapsible if the user clicks somewhere else
|
// hides the settings collapsible if the user clicks somewhere else
|
||||||
if (!this.$refs.settings.contains(event.target))
|
if (!this.$refs.settings.contains(event.target)) {
|
||||||
{
|
|
||||||
this.settingsDropdown.hide();
|
this.settingsDropdown.hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleShowSettings: function() {
|
handleShowSettings: function () {
|
||||||
// adds the event listener checkSettingsVisibility only when the collapsible is shown
|
// adds the event listener checkSettingsVisibility only when the collapsible is shown
|
||||||
document.addEventListener("click", this.checkSettingsVisibility);
|
document.addEventListener("click", this.checkSettingsVisibility);
|
||||||
},
|
},
|
||||||
@@ -282,183 +305,208 @@ export default {
|
|||||||
// removes the event listener checkSettingsVisibility when the collapsible is hidden
|
// removes the event listener checkSettingsVisibility when the collapsible is hidden
|
||||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||||
},
|
},
|
||||||
calcSearchResultHeight: function() {
|
calcSearchResultHeight: function () {
|
||||||
const rect = this.$refs.results.getBoundingClientRect();
|
const rect = this.$refs.results.getBoundingClientRect();
|
||||||
if( rect.height > 0 && rect.height < (window.innerHeight * 0.8) ) {
|
if (rect.height > 0 && rect.height < window.innerHeight * 0.8) {
|
||||||
this.$refs.result.style.height = Math.ceil(rect.height + 16) + 'px';
|
this.$refs.result.style.height =
|
||||||
|
Math.ceil(rect.height + 16) + "px";
|
||||||
} else {
|
} else {
|
||||||
this.$refs.result.style.height = Math.floor(window.innerHeight * 0.8) + 'px';
|
this.$refs.result.style.height =
|
||||||
|
Math.floor(window.innerHeight * 0.8) + "px";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calcSearchResultExtent: function() {
|
calcSearchResultExtent: function () {
|
||||||
if(!this.showresult) {
|
if (!this.showresult) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(this.searchoptions?.calcheightonly === undefined
|
if (
|
||||||
|| this.searchoptions.calcheightonly === false) {
|
this.searchoptions?.calcheightonly === undefined ||
|
||||||
|
this.searchoptions.calcheightonly === false
|
||||||
|
) {
|
||||||
var rect = this.$refs.searchbox.getBoundingClientRect();
|
var rect = this.$refs.searchbox.getBoundingClientRect();
|
||||||
this.$refs.result.style.top = Math.floor(rect.bottom + 3) + 'px';
|
this.$refs.result.style.top =
|
||||||
this.$refs.result.style.right = Math.floor(rect.right) + 'px';
|
Math.floor(rect.bottom + 3) + "px";
|
||||||
this.$refs.result.style.width = Math.floor(rect.width) + 'px';
|
this.$refs.result.style.right = Math.floor(rect.right) + "px";
|
||||||
|
this.$refs.result.style.width = Math.floor(rect.width) + "px";
|
||||||
}
|
}
|
||||||
this.calcSearchResultHeight();
|
this.calcSearchResultHeight();
|
||||||
},
|
},
|
||||||
search: function() {
|
search: function () {
|
||||||
if(this.searchoptions?.nolivesearch === true) return;
|
if (this.searchoptions?.nolivesearch === true) return;
|
||||||
|
|
||||||
this.abort();
|
this.abort();
|
||||||
if( this.searchsettings.searchstr.length >= 2 ) {
|
if (this.searchsettings.searchstr.length >= 2) {
|
||||||
this.calcSearchResultExtent();
|
|
||||||
this.searchtimer = setTimeout(
|
|
||||||
this.callsearchapi,
|
|
||||||
500
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.showresult = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
abort() {
|
|
||||||
if (this.searchtimer !== null) {
|
|
||||||
clearTimeout(this.searchtimer);
|
|
||||||
}
|
|
||||||
if (this.abortController) {
|
|
||||||
this.abortController.abort();
|
|
||||||
this.abortController = null;
|
|
||||||
}
|
|
||||||
this.searchresult = [];
|
|
||||||
},
|
|
||||||
callsearchapi: function() {
|
|
||||||
this.error = null;
|
|
||||||
this.searchresult.splice(0, this.searchresult.length);
|
|
||||||
this.searching = true;
|
|
||||||
this.showsearchresult();
|
|
||||||
if(this.searchsettings.types.length === 0) {
|
|
||||||
this.error = this.$p.t('search/error_missing_type');
|
|
||||||
this.searching = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.abortController)
|
|
||||||
this.abortController.abort();
|
|
||||||
this.abortController = new AbortController();
|
|
||||||
|
|
||||||
this.searchfunction(this.searchsettings, { timeout: 50000, signal: this.abortController.signal })
|
|
||||||
.then(response=>{
|
|
||||||
if (!response.data) {
|
|
||||||
this.error = this.$p.t('search/error_general');
|
|
||||||
} else {
|
|
||||||
let res = response.data.map(el => el.data ? {...el, ...JSON.parse(el.data)} : el);
|
|
||||||
this.lastQuery = response.meta.searchstring;
|
|
||||||
if (this.searchoptions.mergeResults) {
|
|
||||||
let counter = 0;
|
|
||||||
let mergeTypes = [];
|
|
||||||
let mergedType = 'merged-';
|
|
||||||
let mergeKey = '';
|
|
||||||
|
|
||||||
switch (this.searchoptions.mergeResults) {
|
|
||||||
case 'student':
|
|
||||||
mergeTypes = ['student', 'prestudent'];
|
|
||||||
mergedType += this.searchoptions.mergeResults;
|
|
||||||
mergeKey = 'uid';
|
|
||||||
break;
|
|
||||||
case 'person':
|
|
||||||
mergeTypes = ['person', 'employee', 'student', 'prestudent'];
|
|
||||||
mergedType += this.searchoptions.mergeResults;
|
|
||||||
mergeKey = 'person_id';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mergeTypes.length) {
|
|
||||||
res = Object.values(res.reduce((a, c) => {
|
|
||||||
if (!mergeTypes.includes(c.renderer)) {
|
|
||||||
a['nomerge' + counter++] = c;
|
|
||||||
} else if (c[mergeKey] === null) {
|
|
||||||
a['nomerge' + counter++] = c;
|
|
||||||
} else if (a[c[mergeKey]] === undefined) {
|
|
||||||
a[c[mergeKey]] = {
|
|
||||||
rank: c.rank,
|
|
||||||
renderer: mergedType,
|
|
||||||
type: mergedType,
|
|
||||||
list: [c]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
a[c[mergeKey]].list.push(c);
|
|
||||||
if (c.rank > a[c[mergeKey]].rank)
|
|
||||||
a[c[mergeKey]].rank = c.rank;
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}, {})).sort((a, b) => b.rank - a.rank);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.searchresult = res;
|
|
||||||
this.searchmode = response.meta.mode;
|
|
||||||
}
|
|
||||||
this.searching = false;
|
|
||||||
this.retry = 0;
|
|
||||||
})
|
|
||||||
.catch(error=> {
|
|
||||||
if (error.code == "ERR_CANCELED") {
|
|
||||||
return this.retry = 0;
|
|
||||||
}
|
|
||||||
if (error.code == "ECONNABORTED" && this.retry) {
|
|
||||||
this.retry--;
|
|
||||||
return this.callsearchapi();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error = this.$p.t('search/error_general', error);
|
|
||||||
this.searching = false;
|
|
||||||
this.retry = 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
refreshsearch: function() {
|
|
||||||
this.search();
|
|
||||||
this.togglesettings();
|
|
||||||
},
|
|
||||||
hideresult: function() {
|
|
||||||
this.showresult = false;
|
|
||||||
window.removeEventListener('resize', this.calcSearchResultExtent);
|
|
||||||
},
|
|
||||||
showsearchresult: function() {
|
|
||||||
if(this.searchoptions?.nolivesearch === true) return;
|
|
||||||
|
|
||||||
if( this.searchsettings.searchstr.length >= 2 ) {
|
|
||||||
this.showresult = true;
|
|
||||||
window.addEventListener('resize', this.calcSearchResultExtent);
|
|
||||||
this.calcSearchResultExtent();
|
this.calcSearchResultExtent();
|
||||||
}
|
this.searchtimer = setTimeout(this.callsearchapi, 500);
|
||||||
},
|
} else {
|
||||||
searchfocusin: function(e) {
|
this.showresult = false;
|
||||||
e.preventDefault();
|
}
|
||||||
e.stopPropagation();
|
},
|
||||||
if( this.hidetimer !== null ) {
|
abort() {
|
||||||
clearTimeout(this.hidetimer);
|
if (this.searchtimer !== null) {
|
||||||
}
|
clearTimeout(this.searchtimer);
|
||||||
if (this.searchsettings.searchstr.length >= 2
|
}
|
||||||
&& this.searchresult.length === 0) {
|
if (this.abortController) {
|
||||||
|
this.abortController.abort();
|
||||||
|
this.abortController = null;
|
||||||
|
}
|
||||||
|
this.searchresult = [];
|
||||||
|
},
|
||||||
|
callsearchapi: function () {
|
||||||
|
this.error = null;
|
||||||
|
this.searchresult.splice(0, this.searchresult.length);
|
||||||
|
this.searching = true;
|
||||||
|
this.showsearchresult();
|
||||||
|
if (this.searchsettings.types.length === 0) {
|
||||||
|
this.error = this.$p.t("search/error_missing_type");
|
||||||
|
this.searching = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.abortController) this.abortController.abort();
|
||||||
|
this.abortController = new AbortController();
|
||||||
|
|
||||||
|
this.searchfunction(this.searchsettings, {
|
||||||
|
timeout: 50000,
|
||||||
|
signal: this.abortController.signal,
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.data) {
|
||||||
|
this.error = this.$p.t("search/error_general");
|
||||||
|
} else {
|
||||||
|
let res = response.data.map((el) =>
|
||||||
|
el.data ? { ...el, ...JSON.parse(el.data) } : el,
|
||||||
|
);
|
||||||
|
this.lastQuery = response.meta.searchstring;
|
||||||
|
if (this.searchoptions.mergeResults) {
|
||||||
|
let counter = 0;
|
||||||
|
let mergeTypes = [];
|
||||||
|
let mergedType = "merged-";
|
||||||
|
let mergeKey = "";
|
||||||
|
|
||||||
|
switch (this.searchoptions.mergeResults) {
|
||||||
|
case "student":
|
||||||
|
mergeTypes = ["student", "prestudent"];
|
||||||
|
mergedType +=
|
||||||
|
this.searchoptions.mergeResults;
|
||||||
|
mergeKey = "uid";
|
||||||
|
break;
|
||||||
|
case "person":
|
||||||
|
mergeTypes = [
|
||||||
|
"person",
|
||||||
|
"employee",
|
||||||
|
"student",
|
||||||
|
"prestudent",
|
||||||
|
];
|
||||||
|
mergedType +=
|
||||||
|
this.searchoptions.mergeResults;
|
||||||
|
mergeKey = "person_id";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mergeTypes.length) {
|
||||||
|
res = Object.values(
|
||||||
|
res.reduce((a, c) => {
|
||||||
|
if (!mergeTypes.includes(c.renderer)) {
|
||||||
|
a["nomerge" + counter++] = c;
|
||||||
|
} else if (c[mergeKey] === null) {
|
||||||
|
a["nomerge" + counter++] = c;
|
||||||
|
} else if (
|
||||||
|
a[c[mergeKey]] === undefined
|
||||||
|
) {
|
||||||
|
a[c[mergeKey]] = {
|
||||||
|
rank: c.rank,
|
||||||
|
renderer: mergedType,
|
||||||
|
type: mergedType,
|
||||||
|
list: [c],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
a[c[mergeKey]].list.push(c);
|
||||||
|
if (c.rank > a[c[mergeKey]].rank)
|
||||||
|
a[c[mergeKey]].rank = c.rank;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}, {}),
|
||||||
|
).sort((a, b) => b.rank - a.rank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.searchresult = res;
|
||||||
|
this.searchmode = response.meta.mode;
|
||||||
|
}
|
||||||
|
this.searching = false;
|
||||||
|
this.retry = 0;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.code == "ERR_CANCELED") {
|
||||||
|
return (this.retry = 0);
|
||||||
|
}
|
||||||
|
if (error.code == "ECONNABORTED" && this.retry) {
|
||||||
|
this.retry--;
|
||||||
|
return this.callsearchapi();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error = this.$p.t("search/error_general", error);
|
||||||
|
this.searching = false;
|
||||||
|
this.retry = 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refreshsearch: function () {
|
||||||
|
this.search();
|
||||||
|
this.togglesettings();
|
||||||
|
},
|
||||||
|
hideresult: function () {
|
||||||
|
this.showresult = false;
|
||||||
|
window.removeEventListener("resize", this.calcSearchResultExtent);
|
||||||
|
},
|
||||||
|
showsearchresult: function () {
|
||||||
|
if (this.searchoptions?.nolivesearch === true) return;
|
||||||
|
|
||||||
|
if (this.searchsettings.searchstr.length >= 2) {
|
||||||
|
this.showresult = true;
|
||||||
|
window.addEventListener("resize", this.calcSearchResultExtent);
|
||||||
|
this.calcSearchResultExtent();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
searchfocusin: function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if (this.hidetimer !== null) {
|
||||||
|
clearTimeout(this.hidetimer);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.searchsettings.searchstr.length >= 2 &&
|
||||||
|
this.searchresult.length === 0
|
||||||
|
) {
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
searchfocusout: function(e) {
|
searchfocusout: function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.hidetimer = setTimeout(
|
this.hidetimer = setTimeout(this.hideresult, 100);
|
||||||
this.hideresult,
|
},
|
||||||
100
|
dash2camelCase(string) {
|
||||||
);
|
return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
||||||
},
|
},
|
||||||
dash2camelCase(string) {
|
isValidRenderer(renderer) {
|
||||||
return string.replace(/-([a-z])/g, g => g[1].toUpperCase());
|
const camelCaseRenderer = this.dash2camelCase(renderer);
|
||||||
},
|
return Object.keys(this.$.components).includes(camelCaseRenderer);
|
||||||
isValidRenderer(renderer) {
|
},
|
||||||
const camelCaseRenderer = this.dash2camelCase(renderer);
|
|
||||||
return Object.keys(this.$.components).includes(camelCaseRenderer);
|
|
||||||
},
|
|
||||||
getActions(res) {
|
getActions(res) {
|
||||||
let actions = this.searchoptions.actions[this.dash2camelCase(res.renderer)];
|
let actions =
|
||||||
|
this.searchoptions.actions[this.dash2camelCase(res.renderer)];
|
||||||
if (actions) {
|
if (actions) {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
return this.searchoptions.actions[res.type];
|
return this.searchoptions.actions[res.type];
|
||||||
}
|
},
|
||||||
}
|
getMaxWidthOfSearchbarInMobileView() {
|
||||||
|
// body width - hardcoded chevron width; necessary for accurate collapse transition transition
|
||||||
|
return (
|
||||||
|
document.querySelector("body").getBoundingClientRect().width -
|
||||||
|
27 +
|
||||||
|
"px"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user