refactoring: update severals components
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
<!--
|
||||
- @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
|
||||
-
|
||||
@@ -26,45 +25,70 @@
|
||||
:aria-label="t('core', 'Applications menu')"
|
||||
>
|
||||
<ul
|
||||
v-if="appList.length"
|
||||
class="app-menu-main"
|
||||
:class="{ 'app-menu-main__hidden-label': hiddenLabels === 1, 'app-menu-main__show-hovered': hiddenLabels === 2 }"
|
||||
v-if="appList.length"
|
||||
>
|
||||
<li v-for="app in mainAppList(state)"
|
||||
>
|
||||
<li
|
||||
v-for="app in mainAppList(state)"
|
||||
:key="app.id"
|
||||
:data-app-id="app.id"
|
||||
class="app-menu-entry"
|
||||
:class="{ 'app-menu-entry__active': app.active, 'app-menu-entry__hidden-label': hiddenLabels === 1, 'app-menu-main__show-hovered': hiddenLabels === 2 }"
|
||||
:style="makeStyle(app)"
|
||||
>
|
||||
<a :href="app.href"
|
||||
>
|
||||
<a
|
||||
:href="app.href"
|
||||
:class="{ 'has-unread': app.unread > 0 }"
|
||||
:aria-label="appLabel(app)"
|
||||
:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
|
||||
:aria-current="app.active ? 'page' : false">
|
||||
<img :src="app.icon" alt="">
|
||||
:aria-current="app.active ? 'page' : false"
|
||||
>
|
||||
<img
|
||||
:src="app.icon"
|
||||
alt=""
|
||||
/>
|
||||
<div class="app-menu-entry--label">
|
||||
{{ app.name }}
|
||||
<span v-if="app.unread > 0" class="hidden-visually unread-counter">{{ app.unread }}</span>
|
||||
<span
|
||||
v-if="app.unread > 0"
|
||||
class="hidden-visually unread-counter"
|
||||
>{{ app.unread }}</span
|
||||
>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<NcActions class="app-menu-more" :aria-label="t('core', 'More apps')">
|
||||
<NcActionLink v-for="app in popoverAppList(state)"
|
||||
<NcActions
|
||||
class="app-menu-more"
|
||||
:aria-label="t('core', 'More apps')"
|
||||
>
|
||||
<NcActionLink
|
||||
v-for="app in popoverAppList(state)"
|
||||
:key="app.id"
|
||||
:aria-label="appLabel(app)"
|
||||
:aria-current="app.active ? 'page' : false"
|
||||
:href="app.href"
|
||||
:style="makeStyle(app)"
|
||||
class="app-menu-popover-entry">
|
||||
class="app-menu-popover-entry"
|
||||
>
|
||||
<template #icon>
|
||||
<div class="app-icon" :class="{ 'has-unread': app.unread > 0 }">
|
||||
<img :src="app.icon" alt="">
|
||||
<div
|
||||
class="app-icon"
|
||||
:class="{ 'has-unread': app.unread > 0 }"
|
||||
>
|
||||
<img
|
||||
:src="app.icon"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
{{ app.name }}
|
||||
<span v-if="app.unread > 0" class="hidden-visually unread-counter">{{ app.unread }}</span>
|
||||
<span
|
||||
v-if="app.unread > 0"
|
||||
class="hidden-visually unread-counter"
|
||||
>{{ app.unread }}</span
|
||||
>
|
||||
</NcActionLink>
|
||||
</NcActions>
|
||||
</nav>
|
||||
@@ -91,8 +115,8 @@ export default defineComponent({
|
||||
|
||||
setup() {
|
||||
return {
|
||||
t,
|
||||
n,
|
||||
t,
|
||||
n,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -108,7 +132,8 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
axios.get(generateOcsUrl('core/navigation', 2) + '/apps?format=json')
|
||||
axios
|
||||
.get(generateOcsUrl('core/navigation', 2) + '/apps?format=json')
|
||||
.then((response) => response.data)
|
||||
.then((data) => {
|
||||
if (data.ocs.meta.statuscode !== 200) {
|
||||
@@ -181,16 +206,18 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
appLabel(app) {
|
||||
return app.name
|
||||
+ (app.active ? ' (' + t('core', 'Currently open') + ')' : '')
|
||||
+ (app.unread > 0 ? ' (' + n('core', '{count} notification', '{count} notifications', app.unread, { count: app.unread }) + ')' : '')
|
||||
return (
|
||||
app.name +
|
||||
(app.active ? ' (' + t('core', 'Currently open') + ')' : '') +
|
||||
(app.unread > 0 ? ' (' + n('core', '{count} notification', '{count} notifications', app.unread, { count: app.unread }) + ')' : '')
|
||||
)
|
||||
},
|
||||
|
||||
makeStyle(app) {
|
||||
if (app.order !== null) {
|
||||
return `order: ${app.order}`
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -219,7 +246,7 @@ $header-icon-size: 20px;
|
||||
opacity: 1;
|
||||
|
||||
&::before {
|
||||
content: " ";
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border-bottom-color: var(--color-main-background);
|
||||
@@ -312,7 +339,8 @@ $header-icon-size: 20px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&::before, .app-menu-entry::before {
|
||||
&::before,
|
||||
.app-menu-entry::before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@@ -328,18 +356,19 @@ $header-icon-size: 20px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&::before, .app-menu-entry::before {
|
||||
&::before,
|
||||
.app-menu-entry::before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .app-menu-more .button-vue--vue-tertiary {
|
||||
opacity: .7;
|
||||
opacity: 0.7;
|
||||
margin: 3px;
|
||||
filter: var(--background-image-invert-if-bright, var(--primary-invert-if-bright));
|
||||
|
||||
&:not([aria-expanded="true"]) {
|
||||
&:not([aria-expanded='true']) {
|
||||
color: var(--color-main-text);
|
||||
|
||||
&:hover {
|
||||
@@ -348,10 +377,10 @@ $header-icon-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
opacity: 1;
|
||||
outline: none !important;
|
||||
}
|
||||
&:focus-visible {
|
||||
opacity: 1;
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.app-menu-popover-entry {
|
||||
@@ -376,7 +405,7 @@ $header-icon-size: 20px;
|
||||
}
|
||||
|
||||
.has-unread::after {
|
||||
content: "";
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-primary-element-text);
|
||||
+95
-128
@@ -16,155 +16,122 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<template>
|
||||
<div id="side-menu">
|
||||
<div class="side-menu-header" v-if="settings || !openerHover || (!avatar && !alwaysDisplayed && logo) || avatar">
|
||||
<div
|
||||
class="side-menu-header"
|
||||
v-if="settings || !openerHover || (!avatar && !alwaysDisplayed && logo) || avatar"
|
||||
>
|
||||
<SettingsButton
|
||||
v-if="settings"
|
||||
v-bind:href="settings.href"
|
||||
v-bind:label="settings.name"
|
||||
v-bind:avatar="settings.avatar" />
|
||||
:href="settings.href"
|
||||
:label="settings.name"
|
||||
:avatar="settings.avatar"
|
||||
/>
|
||||
<AppSearch v-model:search="search" />
|
||||
<OpenerButton v-if="!alwaysDisplayed" />
|
||||
<Logo
|
||||
v-if="!avatar && !alwaysDisplayed && logo" v-bind:classes="{'side-menu-logo': true, 'avatardiv': false}"
|
||||
v-bind:image="logo"
|
||||
v-bind:link="logoLink"
|
||||
v-if="!avatar && !alwaysDisplayed && logo"
|
||||
:classes="{ 'side-menu-logo': true, avatardiv: false }"
|
||||
:image="logo"
|
||||
:link="logoLink"
|
||||
/>
|
||||
<Logo
|
||||
v-if="avatar" v-bind:classes="{'side-menu-logo': true, 'avatardiv': true}"
|
||||
v-bind:image="avatar"
|
||||
v-bind:link="logoLink"
|
||||
v-if="avatar"
|
||||
:classes="{ 'side-menu-logo': true, avatardiv: true }"
|
||||
:image="avatar"
|
||||
:link="logoLink"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ul class="side-menu-apps-list" :class="{'side-menu-apps-list--with-settings': !!settings}">
|
||||
<SideMenuApp
|
||||
<ul
|
||||
class="side-menu-apps-list"
|
||||
:class="{ 'side-menu-apps-list--with-settings': !!settings }"
|
||||
>
|
||||
<template
|
||||
v-for="(app, key) in apps"
|
||||
v-if="searchMatch(app.name)"
|
||||
v-bind:classes="{'side-menu-app': true, 'active': app.active}"
|
||||
v-bind:key="key"
|
||||
v-bind:icon="app.icon"
|
||||
v-bind:label="app.name"
|
||||
v-bind:href="app.href"
|
||||
v-bind:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
:key="key"
|
||||
>
|
||||
<SideMenuApp
|
||||
v-if="isAppMatchingSearch(app.name, search)"
|
||||
:classes="{ 'side-menu-app': true, active: app.active }"
|
||||
:icon="app.icon"
|
||||
:label="app.name"
|
||||
:href="app.href"
|
||||
:target="targetBlankApps.indexOf(app.id) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import OpenerButton from './OpenerButton'
|
||||
import SettingsButton from './SettingsButton'
|
||||
import SideMenuApp from './SideMenuApp'
|
||||
import AppSearch from './AppSearch'
|
||||
import Logo from './Logo'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { isAppMatchingSearch } from '../lib/search.js'
|
||||
|
||||
export default {
|
||||
name: 'SideMenu',
|
||||
components: {
|
||||
SettingsButton,
|
||||
OpenerButton,
|
||||
SideMenuApp,
|
||||
Logo,
|
||||
AppSearch,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
apps: [],
|
||||
logo: null,
|
||||
logoLink: null,
|
||||
avatar: null,
|
||||
forceLightIcon: false,
|
||||
targetBlankApps: [],
|
||||
hiddenApps: [],
|
||||
settings: null,
|
||||
openerHover: false,
|
||||
alwaysDisplayed: false,
|
||||
search: '',
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
import SettingsButton from '../components/SettingsButton'
|
||||
import SideMenuApp from '../components/SideMenuApp'
|
||||
import AppSearch from '../components/AppSearch'
|
||||
import Logo from '../components/Logo'
|
||||
|
||||
const targetBlankApps = ref(null)
|
||||
const forceLightIcon = ref(null)
|
||||
const avatar = ref(null)
|
||||
const logo = ref(null)
|
||||
const logoLink = ref(null)
|
||||
const settings = ref(null)
|
||||
const openerHover = ref(null)
|
||||
const alwaysDisplayed = ref(null)
|
||||
const search = ref('')
|
||||
const apps = ref([])
|
||||
|
||||
function getFiltredAndSortedApps(
|
||||
items,
|
||||
order,
|
||||
topMenuApps,
|
||||
topSideMenuApps
|
||||
) {
|
||||
const data = []
|
||||
|
||||
items.forEach((item) => {
|
||||
if (topMenuApps.includes(item.id) && !topSideMenuApps.includes(item.id)) {
|
||||
return
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
retrieveApps() {
|
||||
const ncApps = loadState('core', 'apps', [])
|
||||
let orders = {}
|
||||
let finalApps = []
|
||||
|
||||
window.menuAppsOrder.forEach((app, order) => {
|
||||
orders[app] = order + 1
|
||||
})
|
||||
item.order = items.length + 1
|
||||
|
||||
for (let app of ncApps) {
|
||||
if (window.topMenuApps.includes(app.id) && !window.topSideMenuApps.includes(app.id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (this.hiddenApps.includes(app.id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
app.order = orders[app.id] || null
|
||||
|
||||
finalApps.push(app)
|
||||
order.forEach((id, key) => {
|
||||
if (id === item.id) {
|
||||
item.order = key + 1
|
||||
}
|
||||
})
|
||||
|
||||
finalApps.sort((a, b) => {
|
||||
if (a.order === null || b.order === null) {
|
||||
return a.name < b.name ? -1 : 1
|
||||
}
|
||||
data.push(item)
|
||||
})
|
||||
|
||||
return a.order < b.order ? -1 : 1
|
||||
})
|
||||
|
||||
this.apps = finalApps
|
||||
|
||||
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
|
||||
detail: {apps: this.apps},
|
||||
}))
|
||||
},
|
||||
|
||||
retrieveConfig() {
|
||||
},
|
||||
|
||||
hasSearchMatch(apps) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
for (let key in apps) {
|
||||
if (this.searchMatch(apps[key].name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
searchMatch(name) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
return name.toLowerCase().includes(this.search.toLowerCase())
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
axios
|
||||
.get(OC.generateUrl('/apps/side_menu/js/config'))
|
||||
.then((response) => {
|
||||
const config = response.data
|
||||
|
||||
this.targetBlankApps = config['target-blank-apps']
|
||||
this.forceLightIcon = config['force-light-icon']
|
||||
this.avatar = config['avatar']
|
||||
this.logo = config['logo']
|
||||
this.logoLink = config['logo-link']
|
||||
this.settings = config['settings']
|
||||
this.openerHover = config['opener-hover']
|
||||
this.alwaysDisplayed = config['always-displayed']
|
||||
this.hiddenApps = config['big-menu-hidden-apps']
|
||||
this.retrieveApps()
|
||||
})
|
||||
}
|
||||
return data.sort((a, b) => {
|
||||
return a.order < b.order ? -1 : 1
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const config = useConfigStore.getConfig()
|
||||
|
||||
targetBlankApps.value = config['target-blank-apps']
|
||||
forceLightIcon.value = config['force-light-icon']
|
||||
avatar.value = config['avatar']
|
||||
logo.value = config['logo']
|
||||
logoLink.value = config['logo-link']
|
||||
settings.value = config['settings']
|
||||
openerHover.value = config['opener-hover']
|
||||
alwaysDisplayed.value = config['always-displayed']
|
||||
|
||||
apps.value = getFiltredAndSortedApps(
|
||||
useNavStore.getApps(),
|
||||
config['apps-order'],
|
||||
config['top-menu-apps'],
|
||||
config['top-side-menu-apps'],
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
+62
-113
@@ -15,14 +15,17 @@ 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>
|
||||
<div id="side-menu" class="side-menu-big">
|
||||
<div
|
||||
id="side-menu"
|
||||
class="side-menu-big"
|
||||
>
|
||||
<div class="side-menu-header">
|
||||
<CloserButton />
|
||||
<SettingsButton
|
||||
v-if="settings"
|
||||
v-bind:href="settings.href"
|
||||
v-bind:label="settings.name"
|
||||
v-bind:avatar="settings.avatar"
|
||||
:href="settings.href"
|
||||
:label="settings.name"
|
||||
:avatar="settings.avatar"
|
||||
/>
|
||||
<AppSearch v-model:search="search" />
|
||||
<OpenerButton />
|
||||
@@ -32,124 +35,70 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<div class="side-menu-categories">
|
||||
<Loader v-if="!items.length" />
|
||||
|
||||
<div class="side-menu-category" v-for="(category, key) in items" v-if="hasSearchMatch(category.apps)" v-bind:key="key">
|
||||
<h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2>
|
||||
<template
|
||||
v-for="(category, key) in items"
|
||||
:key="key"
|
||||
>
|
||||
<div
|
||||
v-if="containsAppsMatchingSearch(category.apps, search)"
|
||||
class="side-menu-category"
|
||||
>
|
||||
<h2
|
||||
v-if="category.name != ''"
|
||||
class="side-menu-category-title"
|
||||
>
|
||||
{{ category.name }}
|
||||
</h2>
|
||||
|
||||
<ul class="side-menu-apps-list">
|
||||
<SideMenuBigApp
|
||||
v-for="(app, appId) in category.apps"
|
||||
v-if="searchMatch(app.name)"
|
||||
v-bind:key="appId"
|
||||
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
|
||||
v-bind:icon="app.icon"
|
||||
v-bind:label="app.name"
|
||||
v-bind:href="app.href"
|
||||
v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="side-menu-apps-list">
|
||||
<template
|
||||
v-for="(app, appId) in category.apps"
|
||||
:key="appId"
|
||||
>
|
||||
<SideMenuBigApp
|
||||
v-if="isAppMatchingSearch(app.name, search)"
|
||||
:classes="{ 'side-menu-app': true, active: activeApp === appId }"
|
||||
:icon="app.icon"
|
||||
:label="app.name"
|
||||
:href="app.href"
|
||||
:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import OpenerButton from './OpenerButton'
|
||||
import CloserButton from './CloserButton'
|
||||
import SettingsButton from './SettingsButton'
|
||||
import Loader from './Loader'
|
||||
import AppSearch from './AppSearch'
|
||||
import SideMenuBigApp from './SideMenuBigApp'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { getActiveAppId } from '../lib/app.js'
|
||||
import { containsAppsMatchingSearch, isAppMatchingSearch } from '../lib/search.js'
|
||||
|
||||
export default {
|
||||
name: 'SideMenuBig',
|
||||
components: {
|
||||
SettingsButton,
|
||||
OpenerButton,
|
||||
CloserButton,
|
||||
Loader,
|
||||
SideMenuBigApp,
|
||||
AppSearch,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
activeApp: null,
|
||||
targetBlank: false,
|
||||
targetBlankApps: [],
|
||||
settings: null,
|
||||
search: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
retrieveApps() {
|
||||
axios
|
||||
.get(OC.generateUrl('/apps/side_menu/nav/items'))
|
||||
.then((response) => {
|
||||
this.items = response.data.items
|
||||
let apps = []
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
import CloserButton from '../components/CloserButton'
|
||||
import SettingsButton from '../components/SettingsButton'
|
||||
import Loader from '../components/Loader'
|
||||
import AppSearch from '../components/AppSearch'
|
||||
import SideMenuBigApp from '../components/SideMenuBigApp'
|
||||
|
||||
for (let category of this.items) {
|
||||
for (let a in category.apps) {
|
||||
apps.push(category.apps[a])
|
||||
}
|
||||
}
|
||||
const items = ref([])
|
||||
const activeApp = ref(null)
|
||||
const targetBlankApps = ref([])
|
||||
const settings = ref(null)
|
||||
const search = ref('')
|
||||
|
||||
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
|
||||
detail: {apps: apps},
|
||||
}))
|
||||
})
|
||||
},
|
||||
onMounted(() => {
|
||||
const config = useConfigStore.getConfig()
|
||||
|
||||
retrieveActiveApp() {
|
||||
const ncApps = loadState('core', 'apps', {})
|
||||
targetBlankApps.value = config['target-blank-apps']
|
||||
settings.value = config['settings']
|
||||
|
||||
for (let id in ncApps) {
|
||||
if (ncApps[id].active) {
|
||||
this.activeApp = id
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
retrieveConfig() {
|
||||
axios
|
||||
.get(OC.generateUrl('/apps/side_menu/js/config'))
|
||||
.then((response) => {
|
||||
const config = response.data
|
||||
|
||||
this.targetBlankApps = config['target-blank-apps']
|
||||
this.settings = config['settings']
|
||||
})
|
||||
},
|
||||
|
||||
hasSearchMatch(apps) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
for (let key in apps) {
|
||||
if (this.searchMatch(apps[key].name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
searchMatch(name) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
return name.toLowerCase().includes(this.search.toLowerCase())
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.retrieveConfig()
|
||||
this.retrieveApps()
|
||||
this.retrieveActiveApp()
|
||||
}
|
||||
}
|
||||
items.value = useNavStore.getCategories()
|
||||
activeApp.value = getActiveAppId()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -15,13 +15,16 @@ 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>
|
||||
<div id="side-menu" class="side-menu-with-categories">
|
||||
<div
|
||||
id="side-menu"
|
||||
class="side-menu-with-categories"
|
||||
>
|
||||
<div class="side-menu-header">
|
||||
<SettingsButton
|
||||
v-if="settings"
|
||||
v-bind:href="settings.href"
|
||||
v-bind:label="settings.name"
|
||||
v-bind:avatar="settings.avatar"
|
||||
:href="settings.href"
|
||||
:label="settings.name"
|
||||
:avatar="settings.avatar"
|
||||
/>
|
||||
<AppSearch v-model:search="search" />
|
||||
<OpenerButton />
|
||||
@@ -31,124 +34,69 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
<div class="side-menu-categories">
|
||||
<Loader v-if="!items.length" />
|
||||
|
||||
<div class="side-menu-category" v-for="(category, key) in items" v-if="hasSearchMatch(category.apps)" v-bind:key="key">
|
||||
<h2 class="side-menu-category-title" v-if="category.name != ''" v-text="category.name"></h2>
|
||||
<template
|
||||
v-for="(category, key) in items"
|
||||
:key="key"
|
||||
>
|
||||
<div
|
||||
v-if="containsAppsMatchingSearch(category.apps, search)"
|
||||
class="side-menu-category"
|
||||
>
|
||||
<h2
|
||||
v-if="category.name != ''"
|
||||
class="side-menu-category-title"
|
||||
>
|
||||
{{ category.name }}
|
||||
</h2>
|
||||
|
||||
<ul class="side-menu-apps-list">
|
||||
<SideMenuBigApp
|
||||
v-for="(app, appId) in category.apps"
|
||||
v-if="searchMatch(app.name)"
|
||||
v-bind:key="appId"
|
||||
v-bind:classes="{'side-menu-app': true, 'active': activeApp === appId}"
|
||||
v-bind:icon="app.icon"
|
||||
v-bind:label="app.name"
|
||||
v-bind:href="app.href"
|
||||
v-bind:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="side-menu-apps-list">
|
||||
<template
|
||||
v-for="(app, appId) in category.apps"
|
||||
:key="appId"
|
||||
>
|
||||
<SideMenuBigApp
|
||||
v-if="isAppMatchingSearch(app.name, search)"
|
||||
:classes="{ 'side-menu-app': true, active: activeApp === appId }"
|
||||
:icon="app.icon"
|
||||
:label="app.name"
|
||||
:href="app.href"
|
||||
:target="targetBlankApps.indexOf(appId) !== -1 ? '_blank' : undefined"
|
||||
/>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import axios from 'axios'
|
||||
import OpenerButton from './OpenerButton'
|
||||
import SettingsButton from './SettingsButton'
|
||||
import Loader from './Loader'
|
||||
import AppSearch from './AppSearch'
|
||||
import SideMenuBigApp from './SideMenuBigApp'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { getActiveAppId } from '../lib/app.js'
|
||||
import { containsAppsMatchingSearch, isAppMatchingSearch } from '../lib/search.js'
|
||||
|
||||
import { ref } from 'vue'
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
import SettingsButton from '../components/SettingsButton'
|
||||
import Loader from '../components/Loader'
|
||||
import AppSearch from '../components/AppSearch'
|
||||
import SideMenuBigApp from '../components/SideMenuBigApp'
|
||||
|
||||
export default {
|
||||
name: 'SideMenuWithCategories',
|
||||
components: {
|
||||
SettingsButton,
|
||||
OpenerButton,
|
||||
Loader,
|
||||
SideMenuBigApp,
|
||||
AppSearch,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
activeApp: null,
|
||||
targetBlank: false,
|
||||
targetBlankApps: [],
|
||||
settings: null,
|
||||
search: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
retrieveApps() {
|
||||
axios
|
||||
.get(OC.generateUrl('/apps/side_menu/nav/items'))
|
||||
.then((response) => {
|
||||
this.items = response.data.items
|
||||
let apps = []
|
||||
const items = ref([])
|
||||
const activeApp = ref(null)
|
||||
const targetBlankApps = ref([])
|
||||
const settings = ref(null)
|
||||
const search = ref('')
|
||||
|
||||
for (let category of this.items) {
|
||||
for (let a in category.apps) {
|
||||
apps.push(category.apps[a])
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
const config = useConfigStore.getConfig()
|
||||
|
||||
document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', {
|
||||
detail: {apps: apps},
|
||||
}))
|
||||
})
|
||||
},
|
||||
targetBlankApps.value = config['target-blank-apps']
|
||||
settings.value = config['settings']
|
||||
|
||||
retrieveActiveApp() {
|
||||
const ncApps = loadState('core', 'apps', {})
|
||||
|
||||
for (let id in ncApps) {
|
||||
if (ncApps[id].active) {
|
||||
this.activeApp = id
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
retrieveConfig() {
|
||||
axios
|
||||
.get(OC.generateUrl('/apps/side_menu/js/config'))
|
||||
.then((response) => {
|
||||
const config = response.data
|
||||
|
||||
this.targetBlankApps = config['target-blank-apps']
|
||||
this.settings = config['settings']
|
||||
})
|
||||
},
|
||||
|
||||
hasSearchMatch(apps) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
for (let key in apps) {
|
||||
if (this.searchMatch(apps[key].name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
|
||||
searchMatch(name) {
|
||||
if (this.search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
return name.toLowerCase().includes(this.search.toLowerCase())
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.retrieveConfig()
|
||||
this.retrieveApps()
|
||||
this.retrieveActiveApp()
|
||||
}
|
||||
}
|
||||
items.value = useNavStore.getCategories()
|
||||
activeApp.value = getActiveAppId()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user