mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 20:29:29 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b90dabeb2c | |||
| 2f1edfeeab | |||
| 414d8bd383 | |||
| 1a813e52ce | |||
| 16b238124a | |||
| 4def45907b | |||
| 202e6e88d2 | |||
| 3b2473039f | |||
| 59d1ca3409 | |||
| 1d26303333 |
@@ -30,6 +30,7 @@ export default {
|
||||
menuOpen:true,
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
provide(){
|
||||
return{
|
||||
setActiveEntry: this.setActiveEntry,
|
||||
@@ -58,7 +59,7 @@ export default {
|
||||
},
|
||||
site_url(){
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchMenu() {
|
||||
@@ -112,10 +113,26 @@ export default {
|
||||
});
|
||||
},
|
||||
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">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<fhc-searchbar ref="searchbar" id="nav-search" class="fhc-searchbar w-100 py-1 py-lg-2" :searchoptions="searchbaroptions" :searchfunction="searchfunction"></fhc-searchbar>
|
||||
<div
|
||||
id="header-options-collapsible"
|
||||
class="collapse multi-collapse collapse-horizontal show"
|
||||
>
|
||||
<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 class="d-flex h-100 justify-content-between">
|
||||
<a :href="rootUrl">
|
||||
@@ -124,22 +141,32 @@ export default {
|
||||
<theme-switch></theme-switch>
|
||||
</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">
|
||||
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
||||
</button>
|
||||
<ul ref="navUserDropdown"
|
||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||
@[\`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
|
||||
id="header-usermenu-collapsible"
|
||||
class="collapse multi-collapse collapse-horizontal show"
|
||||
>
|
||||
<div
|
||||
:style="!isMobile ? '' : 'width: 51px'"
|
||||
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">
|
||||
<img :src="avatarUrl" :alt="$p.t('profilUpdate/profilBild')" class="bg-dark avatar rounded-circle border border-dark"/>
|
||||
</button>
|
||||
<ul ref="navUserDropdown"
|
||||
@[\`shown.bs.collapse\`]="handleShowNavUser"
|
||||
@[\`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>
|
||||
|
||||
<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-toggle" class="position-static d-none d-lg-block ">
|
||||
|
||||
@@ -3,12 +3,14 @@ import VueDatePicker from '../../vueDatepicker.js.php';
|
||||
import ApiOrt from '../../../api/factory/ort.js'
|
||||
export const Raumsuche = {
|
||||
name: "Raumsuche",
|
||||
props: {
|
||||
|
||||
},
|
||||
components: {
|
||||
VueDatePicker,
|
||||
CoreFilterCmpt,
|
||||
InputNumber: primevue.inputnumber,
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
data() {
|
||||
return {
|
||||
phrasenPromise: null,
|
||||
@@ -71,11 +73,6 @@ export const Raumsuche = {
|
||||
}
|
||||
]};
|
||||
},
|
||||
computed: {
|
||||
isDarkMode(){
|
||||
return this.$theme.theme_name.value == 'dark';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tableResolve(resolve) {
|
||||
this.tableBuiltResolve = resolve
|
||||
@@ -181,6 +178,11 @@ export const Raumsuche = {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDarkMode(){
|
||||
return this.$theme.theme_name.value == 'dark';
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.phrasenPromise = this.$p.loadCategory(['rauminfo', 'global'])
|
||||
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
|
||||
@@ -192,50 +194,46 @@ export const Raumsuche = {
|
||||
<h1 class="h3">{{$p.t('rauminfo/roomSearch')}}</h1>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-2">
|
||||
<div class="col-12 col-lg-2">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
v-model="datum"
|
||||
:dark="isDarkMode"
|
||||
v-model="datum"
|
||||
:clearable="false"
|
||||
date-picker
|
||||
:enable-time-picker="false"
|
||||
:format="dateFormat"
|
||||
:text-input="datepickerTextInputOptions"
|
||||
:min-date="new Date()"
|
||||
date-picker
|
||||
auto-apply
|
||||
>
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
|
||||
<div class="col-12 col-lg-1">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
:dark="isDarkMode"
|
||||
v-model="von"
|
||||
:dark="isDarkMode"
|
||||
:clearable="false"
|
||||
time-picker
|
||||
:format="timeFormat"
|
||||
:text-input="timepickerTextInputOptions"
|
||||
:is-24="true"
|
||||
time-picker
|
||||
auto-apply
|
||||
>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-1">
|
||||
<div class="col-12 col-lg-1">
|
||||
<VueDatePicker
|
||||
@contextmenu="(e) => {if (isMobile) {e.preventDefault();}}"
|
||||
v-model="bis"
|
||||
:dark="isDarkMode"
|
||||
v-model="bis"
|
||||
:clearable="false"
|
||||
time-picker
|
||||
:format="timeFormat"
|
||||
:text-input="timepickerTextInputOptions"
|
||||
:is-24="true"
|
||||
time-picker
|
||||
auto-apply>
|
||||
</VueDatePicker>
|
||||
</div>
|
||||
|
||||
<div :class="{'pb-1': isMobile}" class="col-12 col-lg-3">
|
||||
<div class="col-12 col-lg-3">
|
||||
<select ref="raumtyp" id="raumtypSelect" v-model="selectedType" class="form-select"
|
||||
:aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setRoute($event.target.value)">
|
||||
<option :key="defaultType" selected :value="defaultType">{{defaultType.beschreibung}}</option>
|
||||
@@ -244,7 +242,7 @@ export const Raumsuche = {
|
||||
</div>
|
||||
|
||||
|
||||
<div :class="{'pb-2': isMobile}" class="col-12 col-lg-3">
|
||||
<div class="col-12 col-lg-3">
|
||||
<InputNumber v-model="anzahl"
|
||||
:prefix="$p.t('rauminfo/minCapacity') + ': '"
|
||||
inputId="anzahlInput" :min="1" :max="1000"
|
||||
|
||||
@@ -21,69 +21,71 @@ export default {
|
||||
dms,
|
||||
cms,
|
||||
mergedStudent,
|
||||
mergedPerson
|
||||
mergedPerson,
|
||||
},
|
||||
props: {
|
||||
searchoptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
searchfunction: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
showBtnSubmit: Boolean
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
query: Vue.computed(() => this.lastQuery)
|
||||
};
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
searchsettings: {
|
||||
searchstr: this.getSearchStr(),
|
||||
types: this.getInitiallySelectedTypes(),
|
||||
},
|
||||
searchresult: [],
|
||||
searchmode: '',
|
||||
showresult: false,
|
||||
searching: false,
|
||||
error: null,
|
||||
abortController: null,
|
||||
props: {
|
||||
searchoptions: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
searchfunction: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
showBtnSubmit: Boolean,
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
query: Vue.computed(() => this.lastQuery),
|
||||
};
|
||||
},
|
||||
inject: ["isMobile"],
|
||||
data: function () {
|
||||
return {
|
||||
searchtimer: null,
|
||||
hidetimer: null,
|
||||
searchsettings: {
|
||||
searchstr: this.getSearchStr(),
|
||||
types: this.getInitiallySelectedTypes(),
|
||||
},
|
||||
searchresult: [],
|
||||
searchmode: "",
|
||||
showresult: false,
|
||||
searching: false,
|
||||
error: null,
|
||||
abortController: null,
|
||||
settingsDropdown: null,
|
||||
lastQuery: ''
|
||||
};
|
||||
},
|
||||
lastQuery: "",
|
||||
isSearchShownInMobileView: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
searchTypesPlaceholder() {
|
||||
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() {
|
||||
if (!this.searchoptions.types)
|
||||
return [];
|
||||
if (!this.searchoptions.types) return [];
|
||||
if (Array.isArray(this.searchoptions.types))
|
||||
return this.searchoptions.types;
|
||||
return Object.keys(this.searchoptions.types);
|
||||
},
|
||||
typeLabels() {
|
||||
if (!this.searchoptions.types)
|
||||
return {};
|
||||
if (!this.searchoptions.types) return {};
|
||||
if (Array.isArray(this.searchoptions.types)) {
|
||||
return this.searchoptions.types.reduce((res, type) => {
|
||||
res[type] = type;
|
||||
return res
|
||||
return res;
|
||||
}, {});
|
||||
}
|
||||
return this.searchoptions.types;
|
||||
}
|
||||
},
|
||||
},
|
||||
template: /*html*/`
|
||||
template: /*html*/ `
|
||||
<form
|
||||
ref="searchform"
|
||||
class="d-flex me-3"
|
||||
@@ -92,80 +94,91 @@ export default {
|
||||
@focusin="searchfocusin"
|
||||
@focusout="searchfocusout"
|
||||
>
|
||||
<div
|
||||
ref="searchbox"
|
||||
class="h-100 input-group me-2 searchbar_searchbox"
|
||||
:class="showresult ? 'open' : 'closed'"
|
||||
<span
|
||||
v-if="isMobile"
|
||||
type="button"
|
||||
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 class="fa-solid fa-magnifying-glass"></i>
|
||||
</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>
|
||||
<i v-if="isSearchShownInMobileView" class="fa-solid fa-chevron-left"></i>
|
||||
<i v-else class="fa-solid fa-magnifying-glass"></i>
|
||||
</span>
|
||||
|
||||
<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
|
||||
:class="{'flex-grow-1': !isMobile, 'collapse multi-collapse collapse-horizontal': isMobile}"
|
||||
id="header-searchbar-collapsible"
|
||||
@[\`show.bs.collapse\`]="isSearchShownInMobileView = true"
|
||||
@[\`hidden.bs.collapse\`]="isSearchShownInMobileView = false"
|
||||
>
|
||||
<div
|
||||
:class="{open: showresult, closed: showresult, 'px-3': isMobile}"
|
||||
ref="searchbox"
|
||||
class="h-100 input-group me-2 searchbar_searchbox"
|
||||
:style="isMobile ? 'width: ' + getMaxWidthOfSearchbarInMobileView() : ''"
|
||||
>
|
||||
<span class="input-group-text">
|
||||
<i class="fa-solid fa-magnifying-glass color-white"></i>
|
||||
</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="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
|
||||
id="searchSettings"
|
||||
@@ -207,34 +220,42 @@ export default {
|
||||
</div>
|
||||
</form>
|
||||
`,
|
||||
watch:{
|
||||
'searchsettings.searchstr': function (newSearchValue) {
|
||||
if(this.searchoptions.origin){
|
||||
sessionStorage.setItem(`${this.searchoptions.origin}_searchstr`,newSearchValue);
|
||||
watch: {
|
||||
"searchsettings.searchstr": function (newSearchValue) {
|
||||
if (this.searchoptions.origin) {
|
||||
sessionStorage.setItem(
|
||||
`${this.searchoptions.origin}_searchstr`,
|
||||
newSearchValue,
|
||||
);
|
||||
}
|
||||
},
|
||||
'searchsettings.types'(newValue) {
|
||||
"searchsettings.types"(newValue) {
|
||||
if (Array.isArray(newValue) && newValue.length === 0) {
|
||||
this.searchsettings.types = [...this.types];
|
||||
}
|
||||
// stores the search types in the localstorage, only if the newValue is also an array
|
||||
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();
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.settingsDropdown = new bootstrap.Collapse(this.$refs.settings, {
|
||||
toggle: false
|
||||
toggle: false,
|
||||
});
|
||||
|
||||
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.");
|
||||
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.",
|
||||
);
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
if(this.showresult) {
|
||||
if (this.showresult) {
|
||||
Vue.nextTick(() => {
|
||||
this.calcSearchResultHeight();
|
||||
});
|
||||
@@ -249,32 +270,34 @@ export default {
|
||||
getInitiallySelectedTypes() {
|
||||
let result = false;
|
||||
if (this.searchoptions.origin) {
|
||||
let localStorageValue = localStorage.getItem(`${this.searchoptions.origin}_searchtypes`);
|
||||
let localStorageValue = localStorage.getItem(
|
||||
`${this.searchoptions.origin}_searchtypes`,
|
||||
);
|
||||
if (localStorageValue) {
|
||||
result = JSON.parse(localStorageValue);
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
return result;
|
||||
if (!this.searchoptions.types)
|
||||
return [];
|
||||
if (result) return result;
|
||||
if (!this.searchoptions.types) return [];
|
||||
if (Array.isArray(this.searchoptions.types))
|
||||
return [...this.searchoptions.types];
|
||||
return Object.keys(this.searchoptions.types);
|
||||
},
|
||||
getSearchStr: function(){
|
||||
if (!this.searchoptions.origin)
|
||||
return '';
|
||||
return sessionStorage.getItem(`${this.searchoptions.origin}_searchstr`) ?? '';
|
||||
getSearchStr: function () {
|
||||
if (!this.searchoptions.origin) return "";
|
||||
return (
|
||||
sessionStorage.getItem(
|
||||
`${this.searchoptions.origin}_searchstr`,
|
||||
) ?? ""
|
||||
);
|
||||
},
|
||||
checkSettingsVisibility: function(event) {
|
||||
checkSettingsVisibility: function (event) {
|
||||
// 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();
|
||||
}
|
||||
},
|
||||
handleShowSettings: function() {
|
||||
handleShowSettings: function () {
|
||||
// adds the event listener checkSettingsVisibility only when the collapsible is shown
|
||||
document.addEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
@@ -282,183 +305,208 @@ export default {
|
||||
// removes the event listener checkSettingsVisibility when the collapsible is hidden
|
||||
document.removeEventListener("click", this.checkSettingsVisibility);
|
||||
},
|
||||
calcSearchResultHeight: function() {
|
||||
calcSearchResultHeight: function () {
|
||||
const rect = this.$refs.results.getBoundingClientRect();
|
||||
if( rect.height > 0 && rect.height < (window.innerHeight * 0.8) ) {
|
||||
this.$refs.result.style.height = Math.ceil(rect.height + 16) + 'px';
|
||||
if (rect.height > 0 && rect.height < window.innerHeight * 0.8) {
|
||||
this.$refs.result.style.height =
|
||||
Math.ceil(rect.height + 16) + "px";
|
||||
} 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() {
|
||||
if(!this.showresult) {
|
||||
calcSearchResultExtent: function () {
|
||||
if (!this.showresult) {
|
||||
return;
|
||||
}
|
||||
if(this.searchoptions?.calcheightonly === undefined
|
||||
|| this.searchoptions.calcheightonly === false) {
|
||||
if (
|
||||
this.searchoptions?.calcheightonly === undefined ||
|
||||
this.searchoptions.calcheightonly === false
|
||||
) {
|
||||
var rect = this.$refs.searchbox.getBoundingClientRect();
|
||||
this.$refs.result.style.top = Math.floor(rect.bottom + 3) + 'px';
|
||||
this.$refs.result.style.right = Math.floor(rect.right) + 'px';
|
||||
this.$refs.result.style.width = Math.floor(rect.width) + 'px';
|
||||
this.$refs.result.style.top =
|
||||
Math.floor(rect.bottom + 3) + "px";
|
||||
this.$refs.result.style.right = Math.floor(rect.right) + "px";
|
||||
this.$refs.result.style.width = Math.floor(rect.width) + "px";
|
||||
}
|
||||
this.calcSearchResultHeight();
|
||||
},
|
||||
search: function() {
|
||||
if(this.searchoptions?.nolivesearch === true) return;
|
||||
this.calcSearchResultHeight();
|
||||
},
|
||||
search: function () {
|
||||
if (this.searchoptions?.nolivesearch === true) return;
|
||||
|
||||
this.abort();
|
||||
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.abort();
|
||||
if (this.searchsettings.searchstr.length >= 2) {
|
||||
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.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();
|
||||
}
|
||||
},
|
||||
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();
|
||||
}
|
||||
},
|
||||
searchfocusout: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.hidetimer = setTimeout(
|
||||
this.hideresult,
|
||||
100
|
||||
);
|
||||
},
|
||||
dash2camelCase(string) {
|
||||
return string.replace(/-([a-z])/g, g => g[1].toUpperCase());
|
||||
},
|
||||
isValidRenderer(renderer) {
|
||||
const camelCaseRenderer = this.dash2camelCase(renderer);
|
||||
return Object.keys(this.$.components).includes(camelCaseRenderer);
|
||||
},
|
||||
},
|
||||
searchfocusout: function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.hidetimer = setTimeout(this.hideresult, 100);
|
||||
},
|
||||
dash2camelCase(string) {
|
||||
return string.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
||||
},
|
||||
isValidRenderer(renderer) {
|
||||
const camelCaseRenderer = this.dash2camelCase(renderer);
|
||||
return Object.keys(this.$.components).includes(camelCaseRenderer);
|
||||
},
|
||||
getActions(res) {
|
||||
let actions = this.searchoptions.actions[this.dash2camelCase(res.renderer)];
|
||||
let actions =
|
||||
this.searchoptions.actions[this.dash2camelCase(res.renderer)];
|
||||
if (actions) {
|
||||
return actions;
|
||||
}
|
||||
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