This commit migrates away from NcSelect which has a couple of accesibility and display problems currently, hence a new component `SearchableList` is now used. Signed-off-by: fenn-cs <fenn25.fn@gmail.com>tags/v28.0.0beta2
@@ -47,7 +47,6 @@ export default { | |||
display: flex; | |||
align-items: center; | |||
padding-right: 5px; | |||
filter: grayscale(100%) invert(100%); | |||
img { | |||
width: 20px; |
@@ -21,7 +21,7 @@ | |||
--> | |||
<template> | |||
<NcPopover> | |||
<NcPopover :shown="opened"> | |||
<template #trigger> | |||
<slot name="trigger" /> | |||
</template> | |||
@@ -35,17 +35,17 @@ | |||
</NcTextField> | |||
<ul v-if="filteredList.length > 0" class="searchable-list__list"> | |||
<li v-for="element in filteredList" | |||
:key="element" | |||
:title="element" | |||
:key="element.id" | |||
:title="element.displayName" | |||
role="button"> | |||
<NcButton alignment="start" | |||
type="tertiary" | |||
:wide="true" | |||
@click="$emit(element)"> | |||
@click="itemSelected(element)"> | |||
<template #icon> | |||
<NcAvatar :display-name="element" :hide-favorite="false" /> | |||
<NcAvatar :user="element.user" :show-user-status="false" :hide-favorite="false" /> | |||
</template> | |||
{{ element }} | |||
{{ element.displayName }} | |||
</NcButton> | |||
</li> | |||
</ul> | |||
@@ -98,6 +98,7 @@ export default { | |||
data() { | |||
return { | |||
opened: false, | |||
error: false, | |||
searchTerm: '', | |||
} | |||
@@ -106,7 +107,10 @@ export default { | |||
computed: { | |||
filteredList() { | |||
return this.searchList.filter((element) => { | |||
return element.toLowerCase().includes(this.searchTerm.toLowerCase()) | |||
if (!this.searchTerm.toLowerCase().length) { | |||
return true | |||
} | |||
return ['displayName'].some(prop => element[prop].toLowerCase().includes(this.searchTerm.toLowerCase())) | |||
}) | |||
}, | |||
}, | |||
@@ -115,12 +119,16 @@ export default { | |||
clearSearch() { | |||
this.searchTerm = '' | |||
}, | |||
itemSelected(element) { | |||
this.$emit('item-selected', element) | |||
this.clearSearch() | |||
this.opened = false | |||
}, | |||
}, | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.searchable-list { | |||
&__wrapper { | |||
padding: calc(var(--default-grid-baseline) * 3); |
@@ -52,10 +52,19 @@ | |||
{{ t('core', 'Custom date range') }} | |||
</NcActionButton> | |||
</NcActions> | |||
<NcSelect v-bind="peopleSeclectProps" | |||
v-model="peopleSeclectProps.value" | |||
@search="filterContacts" | |||
@option:selected="applyPersonFilter" /> | |||
<SearchableList :label-text="t('core', 'Search people')" | |||
:search-list="userContacts" | |||
:empty-content-text="t('core', 'Not found')" | |||
@item-selected="applyPersonFilter"> | |||
<template #trigger> | |||
<NcButton> | |||
<template #icon> | |||
<AccountGroup :size="20" /> | |||
</template> | |||
{{ t('core', 'People') }} | |||
</NcButton> | |||
</template> | |||
</SearchableList> | |||
</div> | |||
<div class="global-search-modal__filters-applied"> | |||
<FilterChip v-for="filter in filters" | |||
@@ -64,7 +73,10 @@ | |||
:pretext="''" | |||
@delete="removeFilter(filter)"> | |||
<template #icon> | |||
<AccountIcon v-if="filter.type === 'person'" /> | |||
<NcAvatar v-if="filter.type === 'person'" | |||
:user="filter.user" | |||
:show-user-status="false" | |||
:hide-favorite="false" /> | |||
<CalendarRangeIcon v-else-if="filter.type === 'date'" /> | |||
<img v-else :src="filter.icon" alt=""> | |||
</template> | |||
@@ -130,7 +142,7 @@ | |||
<script> | |||
import ArrowRight from 'vue-material-design-icons/ArrowRight.vue' | |||
import AccountIcon from 'vue-material-design-icons/AccountCircle.vue' | |||
import AccountGroup from 'vue-material-design-icons/AccountGroup.vue' | |||
import CalendarRangeIcon from 'vue-material-design-icons/CalendarRange.vue' | |||
import CustomDateRangeModal from '../components/GlobalSearch/CustomDateRangeModal.vue' | |||
import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue' | |||
@@ -138,13 +150,14 @@ import FilterChip from '../components/GlobalSearch/SearchFilterChip.vue' | |||
import ListBox from 'vue-material-design-icons/ListBox.vue' | |||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' | |||
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' | |||
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' | |||
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' | |||
import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' | |||
import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js' | |||
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' | |||
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js' | |||
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js' | |||
import MagnifyIcon from 'vue-material-design-icons/Magnify.vue' | |||
import SearchableList from '../components/GlobalSearch/SearchableList.vue' | |||
import debounce from 'debounce' | |||
import { getProviders, search as globalSearch, getContacts } from '../services/GlobalSearchService.js' | |||
@@ -152,8 +165,8 @@ import { getProviders, search as globalSearch, getContacts } from '../services/G | |||
export default { | |||
name: 'GlobalSearchModal', | |||
components: { | |||
AccountIcon, | |||
ArrowRight, | |||
AccountGroup, | |||
CalendarRangeIcon, | |||
CustomDateRangeModal, | |||
DotsHorizontalIcon, | |||
@@ -161,13 +174,14 @@ export default { | |||
ListBox, | |||
NcActions, | |||
NcActionButton, | |||
NcAvatar, | |||
NcButton, | |||
NcEmptyContent, | |||
NcModal, | |||
NcListItem, | |||
NcSelect, | |||
NcInputField, | |||
MagnifyIcon, | |||
SearchableList, | |||
}, | |||
props: { | |||
isVisible: { | |||
@@ -182,7 +196,7 @@ export default { | |||
dateActionMenuIsOpen: false, | |||
providerResultLimit: 5, | |||
dateFilter: { id: 'date', type: 'date', text: '', startFrom: null, endAt: null }, | |||
personFilter: { id: 'person', type: 'person', text: '' }, | |||
personFilter: { id: 'person', type: 'person', name: '' }, | |||
dateFilterIsApplied: false, | |||
personFilterIsApplied: false, | |||
filteredProviders: [], | |||
@@ -198,17 +212,9 @@ export default { | |||
}, | |||
computed: { | |||
peopleSeclectProps: { | |||
userContacts: { | |||
get() { | |||
return { | |||
// inputId: getRandomId(), | |||
userSelect: true, | |||
label: t('core', 'People filter'), | |||
placeholder: t('core', 'Search people'), | |||
placement: 'top', | |||
options: this.contacts, | |||
value: null, | |||
} | |||
return this.contacts | |||
}, | |||
}, | |||
@@ -258,7 +264,7 @@ export default { | |||
} | |||
} | |||
if (this.providerResultLimit > 5) { | |||
if (this.providerResultLimit > 5) { | |||
params.limit = this.providerResultLimit | |||
} | |||
@@ -345,7 +351,18 @@ export default { | |||
}, | |||
applyPersonFilter(person) { | |||
this.personFilterIsApplied = true | |||
this.personFilter.id = person.id | |||
const existingPersonFilter = this.filters.findIndex(filter => filter.id === person.id) | |||
if (existingPersonFilter === -1) { | |||
this.personFilter.id = person.id | |||
this.personFilter.user = person.user | |||
this.personFilter.name = person.displayName | |||
this.filters.push(this.personFilter) | |||
} else { | |||
this.filters[existingPersonFilter].id = person.id | |||
this.filters[existingPersonFilter].user = person.user | |||
this.filters[existingPersonFilter].name = person.displayName | |||
} | |||
this.debouncedFind(this.searchQuery) | |||
console.debug('Person filter applied', person) | |||
}, | |||
@@ -504,39 +521,19 @@ $margin: 10px; | |||
&__filters { | |||
display: flex; | |||
padding-top: 5px; | |||
align-items: center; | |||
justify-content: space-between; | |||
/* Overwrite NcSelect styles */ | |||
::v-deep div.v-select { | |||
min-width: 0; // reset NcSelect min width | |||
div.vs__dropdown-toggle { | |||
height: 44px; // Overwrite height of NcSelect component to match button | |||
} | |||
>*:not(:last-child) { | |||
// flex: 1; | |||
margin-right: 0.5m; | |||
} | |||
::v-deep>* { | |||
min-width: auto; | |||
/* Reset hard set min widths */ | |||
min-height: 0; | |||
/* Reset any min heights */ | |||
display: flex; | |||
align-items: center; | |||
flex: 1; | |||
>* { | |||
flex: 1; | |||
min-width: auto; | |||
/* Reset hard set min widths */ | |||
min-height: 0; | |||
>* { | |||
button { | |||
min-width: 160px; | |||
} | |||
} | |||
::v-deep>*:not(:last-child) { | |||
margin: 0 2px; | |||
} | |||
} | |||
&__filters-applied { | |||
@@ -641,9 +638,9 @@ div.v-popper__wrapper { | |||
img { | |||
width: 24px; | |||
margin: 0 4px; | |||
// filter: invert(100%) grayscale(1) contrast(100) brightness(1); | |||
filter: grayscale(100%); | |||
filter: var(--background-invert-if-bright); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,3 @@ | |||
/*! For license information please see 2250-2250.js.LICENSE.txt */ | |||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[2250],{82250:(e,n,c)=>{c.d(n,{FilePickerVue:()=>s});const s=(0,c(20144).defineAsyncComponent)((()=>Promise.all([c.e(7874),c.e(2550)]).then(c.bind(c,58499))))}}]); | |||
//# sourceMappingURL=2250-2250.js.map?v=d93e0b93bdb5aa89ea02 |
@@ -1 +1 @@ | |||
{"version":3,"file":"3998-3998.js?v=a49373c9d79e30e60f7b","mappings":";oIAsBA,MAAMA,GAAI,kCAAE,IAAM","sources":["webpack:///nextcloud/node_modules/@nextcloud/dialogs/dist/chunks/index-22ace80c.mjs"],"sourcesContent":["import { defineAsyncComponent as e } from \"vue\";\n/**\n * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @author Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\nconst i = e(() => import(\"./FilePicker-5074f4ba.mjs\"));\nexport {\n i as FilePickerVue\n};\n"],"names":["i"],"sourceRoot":""} | |||
{"version":3,"file":"2250-2250.js?v=d93e0b93bdb5aa89ea02","mappings":";oIAsBA,MAAMA,GAAI,kCAAE,IAAM","sources":["webpack:///nextcloud/node_modules/@nextcloud/dialogs/dist/chunks/index-2b379907.mjs"],"sourcesContent":["import { defineAsyncComponent as e } from \"vue\";\n/**\n * @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @author Ferdinand Thiessen <opensource@fthiessen.de>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\nconst i = e(() => import(\"./FilePicker-c55dc760.mjs\"));\nexport {\n i as FilePickerVue\n};\n"],"names":["i"],"sourceRoot":""} |
@@ -87,28 +87,6 @@ | |||
* | |||
*/ | |||
/** | |||
* @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de> | |||
* | |||
* @author Ferdinand Thiessen <opensource@fthiessen.de> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
/** | |||
* @copyright Copyright (c) 2023 Ferdinand Thiessen <opensource@fthiessen.de> | |||
* |
@@ -1,3 +0,0 @@ | |||
/*! For license information please see 3998-3998.js.LICENSE.txt */ | |||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[3998],{83998:(e,n,c)=>{c.d(n,{FilePickerVue:()=>s});const s=(0,c(20144).defineAsyncComponent)((()=>Promise.all([c.e(7874),c.e(3240),c.e(9064)]).then(c.bind(c,39064))))}}]); | |||
//# sourceMappingURL=3998-3998.js.map?v=a49373c9d79e30e60f7b |
@@ -74,6 +74,8 @@ | |||
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */ | |||
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */ | |||
/** | |||
* @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* |
@@ -1,3 +1,25 @@ | |||
/** | |||
* @copyright 2023, Fon E. Noel NFEBE <fenn25.fn@gmail.com> | |||
* | |||
* @author Fon E. Noel NFEBE <fenn25.fn@gmail.com> | |||
* | |||
* @license AGPL-3.0-or-later | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
/** | |||
* @copyright Copyright (c) 2020 Fon E. Noel NFEBE <fenn25.fn@gmail.com> | |||
* |
@@ -1,5 +1,3 @@ | |||
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */ | |||
/** | |||
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> | |||
* |