123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- <!--
- - @copyright 2023 Marco Ambrosini <marcoambrosini@proton.me>
- -
- - @author Marco Ambrosini <marcoambrosini@proton.me>
- -
- - @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/>.
- -
- -->
-
- <template>
- <NcPopover :shown="opened"
- @show="opened = true"
- @hide="opened = false">
- <template #trigger>
- <slot ref="popoverTrigger" name="trigger" />
- </template>
- <div class="searchable-list__wrapper">
- <NcTextField :value.sync="searchTerm"
- :label="labelText"
- trailing-button-icon="close"
- :show-trailing-button="searchTerm !== ''"
- @update:value="searchTermChanged"
- @trailing-button-click="clearSearch">
- <Magnify :size="20" />
- </NcTextField>
- <ul v-if="filteredList.length > 0" class="searchable-list__list">
- <li v-for="element in filteredList"
- :key="element.id"
- :title="element.displayName"
- role="button">
- <NcButton alignment="start"
- type="tertiary"
- :wide="true"
- @click="itemSelected(element)">
- <template #icon>
- <NcAvatar v-if="element.isUser" :user="element.user" :show-user-status="false" />
- <NcAvatar v-else
- :is-no-user="true"
- :display-name="element.displayName"
- :show-user-status="false" />
- </template>
- {{ element.displayName }}
- </NcButton>
- </li>
- </ul>
- <div v-else class="searchable-list__empty-content">
- <NcEmptyContent :name="emptyContentText">
- <template #icon>
- <AlertCircleOutline />
- </template>
- </NcEmptyContent>
- </div>
- </div>
- </NcPopover>
- </template>
-
- <script>
- import { NcPopover, NcTextField, NcAvatar, NcEmptyContent, NcButton } from '@nextcloud/vue'
-
- import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'
- import Magnify from 'vue-material-design-icons/Magnify.vue'
-
- export default {
- name: 'SearchableList',
-
- components: {
- NcPopover,
- NcTextField,
- Magnify,
- AlertCircleOutline,
- NcAvatar,
- NcEmptyContent,
- NcButton,
- },
-
- props: {
- labelText: {
- type: String,
- default: 'this is a label',
- },
-
- searchList: {
- type: Array,
- required: true,
- },
-
- emptyContentText: {
- type: String,
- required: true,
- },
- },
-
- data() {
- return {
- opened: false,
- error: false,
- searchTerm: '',
- }
- },
-
- computed: {
- filteredList() {
- return this.searchList.filter((element) => {
- if (!this.searchTerm.toLowerCase().length) {
- return true
- }
- return ['displayName'].some(prop => element[prop].toLowerCase().includes(this.searchTerm.toLowerCase()))
- })
- },
- },
-
- methods: {
- clearSearch() {
- this.searchTerm = ''
- },
- itemSelected(element) {
- this.$emit('item-selected', element)
- this.clearSearch()
- this.opened = false
- },
- searchTermChanged(term) {
- this.$emit('search-term-change', term)
- },
- },
- }
- </script>
-
- <style lang="scss" scoped>
- .searchable-list {
- &__wrapper {
- padding: calc(var(--default-grid-baseline) * 3);
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 250px;
- }
-
- &__list {
- width: 100%;
- max-height: 284px;
- overflow-y: auto;
- margin-top: var(--default-grid-baseline);
- padding: var(--default-grid-baseline);
-
- :deep(.button-vue) {
- border-radius: var(--border-radius-large) !important;
- span {
- font-weight: initial;
- }
- }
- }
-
- &__empty-content {
- margin-top: calc(var(--default-grid-baseline) * 3);
- }
- }
- </style>
|