Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -2395,4 +2395,11 @@
* Defaults to ``true``
*/
'reference_opengraph' => true,

/**
* Enable use of old unified search
*
* Defaults to ``false``
*/
'unified_search.enabled' => false,
];
98 changes: 98 additions & 0 deletions core/src/components/GlobalSearch/CustomDateRangeModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<template>
<NcModal v-if="isModalOpen"
id="global-search"
:name="t('core', 'Date range filter')"
:show.sync="isModalOpen"
:size="'small'"
:clear-view-delay="0"
:title="t('Date range filter')"
@close="closeModal">
<!-- Custom date range -->
<div class="global-search-custom-date-modal">
<h1>{{ t('core', 'Date range filter') }}</h1>
<div class="global-search-custom-date-modal__pickers">
<NcDateTimePicker :id="'globalsearch-custom-date-range-start'"
v-model="dateFilter.startFrom"
:max="new Date()"
:label="t('core', 'Pick start date')"
type="date" />
<NcDateTimePicker :id="'globalsearch-custom-date-range-end'"
v-model="dateFilter.endAt"
:max="new Date()"
:label="t('core', 'Pick end date')"
type="date" />
</div>
<NcButton @click="applyCustomRange">
{{ t('core', 'Apply range') }}
<template #icon>
<CalendarRangeIcon :size="20" />
</template>
</NcButton>
</div>
</NcModal>
</template>

<script>
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcDateTimePicker from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import CalendarRangeIcon from 'vue-material-design-icons/CalendarRange.vue'

export default {
name: 'CustomDateRangeModal',
components: {
NcButton,
NcModal,
CalendarRangeIcon,
NcDateTimePicker,
},
props: {
isOpen: {
type: Boolean,
required: true,
},
},
data() {
return {
dateFilter: { startFrom: null, endAt: null },
}
},
computed: {
isModalOpen: {
get() {
return this.isOpen
},
set(value) {
this.$emit('update:is-open', value)
},
},
},
methods: {
closeModal() {
this.isModalOpen = false
},
applyCustomRange() {
this.$emit('set:custom-date-range', this.dateFilter)
this.closeModal()
},
},
}
</script>

<style lang="scss" scoped>
.global-search-custom-date-modal {
padding: 10px 20px 10px 20px;

h1 {
font-size: 16px;
font-weight: bolder;
line-height: 2em;
}

&__pickers {
display: flex;
flex-direction: column;
}

}
</style>
71 changes: 71 additions & 0 deletions core/src/components/GlobalSearch/SearchFilterChip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<div class="chip">
<span class="icon">
<slot name="icon" />
<span v-if="pretext.length"> {{ pretext }} : </span>
</span>
<span class="text">{{ text }}</span>
<span class="close-icon" @click="deleteChip">
<CloseIcon :size="16" />
</span>
</div>
</template>

<script>
import CloseIcon from 'vue-material-design-icons/CloseThick.vue'

export default {
name: 'SearchFilterChip',
components: {
CloseIcon,
},
props: {
text: String,
pretext: String,
},
methods: {
deleteChip() {
this.$emit('delete', this.filter)
},
},
}
</script>

<style lang="scss" scoped>
.chip {
display: flex;
align-items: center;
padding: 2px 4px;
border: 1px solid var(--color-primary-element-light);
border-radius: 20px;
background-color: var(--color-primary-element-light);
margin: 2px;
font-size: 10px;
font-weight: bolder;

.icon {
display: flex;
align-items: center;
padding-right: 5px;

img {
width: 20px;
padding: 2px;
border-radius: 20px;
}
}

.text {
margin: 0 2px;
}

.close-icon {
cursor: pointer;

:hover {
border-radius: 4px;
padding: 1px;
}
}
}
</style>
157 changes: 157 additions & 0 deletions core/src/components/GlobalSearch/SearchableList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<!--
- @copyright 2023 Marco Ambrosini <[email protected]>
-
- @author Marco Ambrosini <[email protected]>
-
- @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">
<template #trigger>
<slot name="trigger" />
</template>
<div class="searchable-list__wrapper">
<NcTextField :value.sync="searchTerm"
:label="labelText"
trailing-button-icon="close"
:show-trailing-button="searchTerm !== ''"
@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 :user="element.user" :show-user-status="false" :hide-favorite="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
},
},
}
</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;
}
}

&__empty-content {
margin-top: calc(var(--default-grid-baseline) * 3);
}
}
</style>
55 changes: 55 additions & 0 deletions core/src/global-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @copyright Copyright (c) 2020 Fon E. Noel NFEBE <[email protected]>
*
* @author Fon E. Noel NFEBE <[email protected]>
*
* @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/>.
*
*/

import { getLoggerBuilder } from '@nextcloud/logger'
import { getRequestToken } from '@nextcloud/auth'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import Vue from 'vue'

import GlobalSearch from './views/GlobalSearch.vue'

// eslint-disable-next-line camelcase
__webpack_nonce__ = btoa(getRequestToken())

const logger = getLoggerBuilder()
.setApp('global-search')
.detectUser()
.build()

Vue.mixin({
data() {
return {
logger,
}
},
methods: {
t,
n,
},
})

export default new Vue({
el: '#global-search',
// eslint-disable-next-line vue/match-component-file-name
name: 'GlobalSearchRoot',
render: h => h(GlobalSearch),
})
Loading