mirror of
https://gitnet.fr/deblan/side_menu.git
synced 2025-12-18 05:10:50 +01:00
add mount of menu
fix store usage add page loader
This commit is contained in:
parent
d8a622ce0f
commit
b287b671be
15 changed files with 175 additions and 81 deletions
|
|
@ -28,7 +28,7 @@
|
|||
"@nextcloud/browserslist-config": "^3.0.1",
|
||||
"@nextcloud/event-bus": "^3.3.1",
|
||||
"@nextcloud/initial-state": "^2.2.0",
|
||||
"@nextcloud/l10n": "^3.1.0",
|
||||
"@nextcloud/l10n": "^3.2.0",
|
||||
"babel-loader": "^9.1.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^9.19.0",
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
const createElement = require('./lib/createElement')
|
||||
|
||||
const PageLoader = () => {
|
||||
const pageLoader = createElement('div', { id: 'side-menu-loader' })
|
||||
const pageLoaderBar = createElement('div', { id: 'side-menu-loader-bar' })
|
||||
|
||||
pageLoader.appendChild(pageLoaderBar)
|
||||
document.querySelector('body').appendChild(pageLoader)
|
||||
|
||||
let pageLoaderValue = 0
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
setInterval(() => {
|
||||
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
|
||||
pageLoaderValue = Math.min(pageLoaderValue + 0.2, 100)
|
||||
}, 25)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = PageLoader
|
||||
|
|
@ -42,7 +42,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
@close="hideAddForm"
|
||||
>
|
||||
<div class="modal__content">
|
||||
<div v-for="(lang, key) in langs" :key="key">
|
||||
<div
|
||||
v-for="(lang, key) in langs"
|
||||
:key="key"
|
||||
>
|
||||
<span class="lang">{{ lang }}</span>
|
||||
<input
|
||||
v-model="newValue[lang]"
|
||||
|
|
@ -66,7 +69,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
@close="hideEditForm"
|
||||
>
|
||||
<div class="modal__content">
|
||||
<div v-for="(lang, key) in langs" :key="key">
|
||||
<div
|
||||
v-for="(lang, key) in langs"
|
||||
:key="key"
|
||||
>
|
||||
<span class="lang">{{ lang }}</span>
|
||||
<input
|
||||
v-model="editValue[lang]"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmit('input')
|
||||
const emit = defineEmits('input')
|
||||
const { value } = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
|
|
|
|||
24
src/components/PageLoader.vue
Normal file
24
src/components/PageLoader.vue
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<template>
|
||||
<div id="side-menu-loader">
|
||||
<div id="side-menu-loader-bar" :style="createStyle(width)"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const width = ref(0)
|
||||
const createStyle = (size) => {
|
||||
return {
|
||||
width: `${size}%`,
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('beforeunload', () => {
|
||||
setInterval(() => {
|
||||
width.value = Math.min(width.value + 0.2, 100)
|
||||
}, 25)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
const waitContainer = async (selector) => {
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
const container = document.querySelector(selector)
|
||||
return new Promise((resolve) => {
|
||||
const container = document.querySelector(selector)
|
||||
|
||||
if (container) {
|
||||
resolve(selector, container)
|
||||
}
|
||||
if (container) {
|
||||
return resolve(selector, container)
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
waitContainer(selector)
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,11 @@ const containsAppsMatchingSearch = (values, search) => {
|
|||
}
|
||||
|
||||
const isAppMatchingSearch = (item, search) => {
|
||||
if (search.value.trim() === '') {
|
||||
if (search.trim() === '') {
|
||||
return true
|
||||
}
|
||||
|
||||
return item.name.toLowerCase().includes(search.value.trim().toLowerCase())
|
||||
return item.name.toLowerCase().includes(search.trim().toLowerCase())
|
||||
}
|
||||
|
||||
export {
|
||||
containsAppsMatchingSearch,
|
||||
isAppMatchingSearch,
|
||||
}
|
||||
export { containsAppsMatchingSearch, isAppMatchingSearch }
|
||||
|
|
|
|||
44
src/menu.js
44
src/menu.js
|
|
@ -19,39 +19,29 @@ import './scss/menu.scss'
|
|||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import { waitContainer } from './lib/dom.js'
|
||||
import { createElement } from './lib/dom.js'
|
||||
|
||||
// import AppMenu from './menus/AppMenu.vue'
|
||||
import SideMenu from './menus/SideMenu.vue'
|
||||
import SideMenuBig from './menus/SideMenuBig.vue'
|
||||
import SideMenuWithCategories from './menus/SideMenuWithCategories.vue'
|
||||
import MenuContainer from './menus/MenuContainer.vue'
|
||||
|
||||
// import PageLoader from './components/PageLoader.vue'
|
||||
|
||||
// window.PageLoader = PageLoader
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
waitContainer('#side-menu').then((selector, container) => {
|
||||
const component = (() => {
|
||||
if (container.getAttribute('data-bigmenu')) {
|
||||
return SideMenuBig
|
||||
} else if (container.getAttribute('data-sidewithcategories')) {
|
||||
return SideMenuWithCategories
|
||||
} else {
|
||||
return SideMenu
|
||||
}
|
||||
})()
|
||||
|
||||
const app = createApp(component)
|
||||
|
||||
app.use(pinia)
|
||||
app.mount(selector)
|
||||
const body = document.querySelector('body')
|
||||
const container = createElement('div', {
|
||||
id: 'side-menu-container',
|
||||
})
|
||||
|
||||
waitContainer('#header .app-menu').then((selector) => {
|
||||
const app = createApp(AppMenu)
|
||||
body.appendChild(container)
|
||||
|
||||
app.use(pinia)
|
||||
app.mount(selector)
|
||||
})
|
||||
const app = createApp(MenuContainer)
|
||||
app.use(pinia)
|
||||
app.mixin({ methods: { t, n }})
|
||||
app.mount(container)
|
||||
|
||||
// waitContainer('#header .app-menu').then((selector) => {
|
||||
// const app = createApp(AppMenu)
|
||||
// app.use(pinia)
|
||||
// app.mixin({ methods: { t, n }})
|
||||
// app.mount(selector)
|
||||
// })
|
||||
|
|
|
|||
87
src/menus/MenuContainer.vue
Normal file
87
src/menus/MenuContainer.vue
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<template v-if="display">
|
||||
<PageLoader v-if="hasPageLoader" />
|
||||
<TopWideMenu v-if="display === 'big-menu'" />
|
||||
<SideMenuWithCategories v-else-if="display === 'side-with-categories'" />
|
||||
<SimpleSideMenu
|
||||
v-else-if="display === 'simple-side-menu'"
|
||||
:alawayDisplayed="display === 'always-displayed'"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { createElement } from '../lib/dom.js'
|
||||
import { translate as t } from '@nextcloud/l10n'
|
||||
|
||||
import SimpleSideMenu from './SimpleSideMenu'
|
||||
import TopWideMenu from './TopWideMenu'
|
||||
import SideMenuWithCategories from './SideMenuWithCategories'
|
||||
import PageLoader from '../components/PageLoader'
|
||||
|
||||
const config = ref(null)
|
||||
const configStore = useConfigStore()
|
||||
const display = ref(null)
|
||||
const alawayDisplayed = ref(false)
|
||||
const hasPageLoader = ref(false)
|
||||
const createOpener = () => {
|
||||
const nextcloud = document.querySelector('#nextcloud')
|
||||
const logo = document.querySelector('.header-left .logo, .header-start .logo')
|
||||
|
||||
if (!nextcloud || !logo) {
|
||||
return
|
||||
}
|
||||
|
||||
if (logo.parentNode !== nextcloud) {
|
||||
nextcloud.appendChild(logo)
|
||||
}
|
||||
|
||||
const opener = createElement('button', {
|
||||
'class': 'side-menu-opener',
|
||||
'arial-label': t('side_menu', 'Toggle the menu'),
|
||||
'html': `<span>${t('side_menu', 'Toggle the menu')}</span>`
|
||||
})
|
||||
|
||||
if (config.value['opener-position'] === 'before') {
|
||||
nextcloud.parentNode.insertBefore(opener, nextcloud)
|
||||
} else {
|
||||
nextcloud.parentNode.insertBefore(opener, nextcloud.nextSibling)
|
||||
}
|
||||
}
|
||||
|
||||
const createLoader = () => {
|
||||
const loader = createElement('div', { id: 'side-menu-loader' })
|
||||
const bar = createElement('div', { id: 'side-menu-loader-bar' })
|
||||
|
||||
loader.appendChild(bar)
|
||||
document.querySelector('body').appendChild(loader)
|
||||
|
||||
let pageLoaderValue = 0
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
setInterval(() => {
|
||||
pageLoaderBar.style.width = pageLoaderValue.toString() + '%'
|
||||
pageLoaderValue = Math.min(pageLoaderValue + 0.2, 100)
|
||||
}, 25)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
config.value = await configStore.getConfig()
|
||||
|
||||
if (config.value['big-menu']) {
|
||||
display.value = 'big-menu'
|
||||
} else if (config.value['side-with-categories']) {
|
||||
display.value = 'side-with-categories'
|
||||
} else {
|
||||
display.value = 'simple-side-menu'
|
||||
alawayDisplayed.value = config.value['always-displayed']
|
||||
}
|
||||
|
||||
hasPageLoader.value = config.value['loader-enabled']
|
||||
|
||||
createOpener()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -17,8 +17,8 @@ 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"
|
||||
class="side-menu-header"
|
||||
>
|
||||
<SettingsButton
|
||||
v-if="settings"
|
||||
|
|
@ -75,6 +75,8 @@ import SideMenuApp from '../components/SideMenuApp'
|
|||
import AppSearch from '../components/AppSearch'
|
||||
import Logo from '../components/Logo'
|
||||
|
||||
const navStore = useNavStore()
|
||||
const configStore = useConfigStore()
|
||||
const targetBlankApps = ref(null)
|
||||
const forceLightIcon = ref(null)
|
||||
const avatar = ref(null)
|
||||
|
|
@ -86,12 +88,7 @@ const alwaysDisplayed = ref(null)
|
|||
const search = ref('')
|
||||
const apps = ref([])
|
||||
|
||||
function getFiltredAndSortedApps(
|
||||
items,
|
||||
order,
|
||||
topMenuApps,
|
||||
topSideMenuApps
|
||||
) {
|
||||
function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) {
|
||||
const data = []
|
||||
|
||||
items.forEach((item) => {
|
||||
|
|
@ -115,8 +112,8 @@ function getFiltredAndSortedApps(
|
|||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const config = useConfigStore.getConfig()
|
||||
onMounted(async () => {
|
||||
const config = await configStore.getConfig()
|
||||
|
||||
targetBlankApps.value = config['target-blank-apps']
|
||||
forceLightIcon.value = config['force-light-icon']
|
||||
|
|
@ -128,10 +125,10 @@ onMounted(() => {
|
|||
alwaysDisplayed.value = config['always-displayed']
|
||||
|
||||
apps.value = getFiltredAndSortedApps(
|
||||
useNavStore.getApps(),
|
||||
await navStore.getApps(),
|
||||
config['apps-order'],
|
||||
config['top-menu-apps'],
|
||||
config['top-side-menu-apps'],
|
||||
config['top-side-menu-apps']
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { axios } from '@nextcloud/axios'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export const useConfigStore = defineStore('config', () => {
|
||||
|
|
@ -11,8 +11,12 @@ export const useConfigStore = defineStore('config', () => {
|
|||
return config.value
|
||||
}
|
||||
|
||||
config.value = await fetch(generateUrl('/apps/side_menu/js/config')).then((response) => response.data)
|
||||
config.value = await axios.get(generateUrl('/apps/side_menu/js/config')).then((response) => response.data)
|
||||
|
||||
return config.value
|
||||
}
|
||||
|
||||
return {
|
||||
getConfig,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { axios } from '@nextcloud/axios'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export const useNavStore = defineStore('nav', () => {
|
||||
const categories = ref(null)
|
||||
const apps = ref(null)
|
||||
|
||||
function getApps() {
|
||||
async function getApps() {
|
||||
if (apps.value !== null) {
|
||||
return apps.value
|
||||
}
|
||||
|
||||
apps.value = []
|
||||
const cats = await getCategories()
|
||||
|
||||
getCategories().forEach((category) => {
|
||||
cats.forEach((category) => {
|
||||
Object.values(category.apps).forEach((app) => {
|
||||
apps.value.push(app)
|
||||
})
|
||||
|
|
@ -28,8 +29,13 @@ export const useNavStore = defineStore('nav', () => {
|
|||
return categories.value
|
||||
}
|
||||
|
||||
categories.value = await fetch(generateUrl('/apps/side_menu/nav/items')).then((response) => response.data.items)
|
||||
categories.value = await axios.get(generateUrl('/apps/side_menu/nav/items')).then((response) => response.data.items)
|
||||
|
||||
return categories.value
|
||||
}
|
||||
|
||||
return {
|
||||
getApps,
|
||||
getCategories,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ const SMcreateElement = (tagName, attributes) => {
|
|||
sideMenuContainer.appendChild(sideMenu)
|
||||
|
||||
<?php if ($_['loader-enabled'] === true): ?>
|
||||
PageLoader()
|
||||
// PageLoader()
|
||||
<?php endif; ?>
|
||||
|
||||
if (nextcloud) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const isDev = buildMode === 'development'
|
|||
module.exports = {
|
||||
target: 'web',
|
||||
mode: buildMode,
|
||||
devtool: "inline-source-map",
|
||||
entry: {
|
||||
menu: path.resolve(path.join('src', 'menu.js')),
|
||||
// admin: path.resolve(path.join('src', 'admin.js')),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue