mirror of
https://gitnet.fr/deblan/side_menu.git
synced 2025-12-17 21:02:25 +01:00
refactor of menus
begin work on settings
This commit is contained in:
parent
ecbe2f7d72
commit
52b2d18a03
26 changed files with 1613 additions and 1667 deletions
|
|
@ -123,14 +123,14 @@ class Application extends App implements IBootstrap
|
|||
//Util::addStyle(self::APP_ID, 'sideMenu');
|
||||
|
||||
$assets = [
|
||||
// 'stylesheet' => [
|
||||
// 'route' => 'side_menu.Css.stylesheet',
|
||||
// 'type' => 'link',
|
||||
// 'route_attr' => 'href',
|
||||
// 'attr' => [
|
||||
// 'rel' => 'stylesheet',
|
||||
// ],
|
||||
// ],
|
||||
'stylesheet' => [
|
||||
'route' => 'side_menu.Css.stylesheet',
|
||||
'type' => 'link',
|
||||
'route_attr' => 'href',
|
||||
'attr' => [
|
||||
'rel' => 'stylesheet',
|
||||
],
|
||||
],
|
||||
// 'script' => [
|
||||
// 'route' => 'side_menu.Js.script',
|
||||
// 'type' => 'script',
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ class JsController extends Controller
|
|||
'opener-hover' => $this->config->getAppValueBool('opener-hover', '0'),
|
||||
'external-sites-in-top-menu' => $this->config->getAppValueBool('external-sites-in-top-menu', '0'),
|
||||
'force-light-icon' => $this->config->getAppValueBool('force-light-icon', '0'),
|
||||
'display-logo' => $this->config->getAppValueBool('display-logo', '1'),
|
||||
'use-avatar' => $this->config->getAppValueBool('use-avatar', '0'),
|
||||
'hide-when-no-apps' => $this->config->getAppValueBool('hide-when-no-apps', '0'),
|
||||
'loader-enabled' => $this->config->getAppValueBool('loader-enabled', '1'),
|
||||
'always-displayed' => $this->config->getAppValueBool('always-displayed', '0'),
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ class NavController extends Controller
|
|||
$appsCategories[$app['id']][] = $category;
|
||||
|
||||
$items[$category]['apps'][$app['id']] = [
|
||||
'id' => $app['id'],
|
||||
'name' => $app['name'],
|
||||
'href' => $app['href'],
|
||||
'icon' => $app['icon'],
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@
|
|||
"format": "./node_modules/.bin/prettier src --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": ">=7.12.0 <8.0.0",
|
||||
"@nextcloud/router": "^3.0.1",
|
||||
"@nextcloud/vue": "^9.0.0-alpha.8",
|
||||
"node-polyfill-webpack-plugin": "^4.1.0",
|
||||
"pinia": "^3.0.1",
|
||||
"postcss": "^7.0.0 || ^8.0.1",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"browserslist": [
|
||||
|
|
|
|||
269
src/admin.js
269
src/admin.js
|
|
@ -8,268 +8,25 @@
|
|||
*
|
||||
* 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
|
||||
* 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/>.
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import './scss/admin.scss'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import AdminCategoriesCustom from './components/AdminCategoriesCustom.vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import { waitContainer } from './lib/dom.js'
|
||||
|
||||
let elements = []
|
||||
import AdminSettings from './pages/AdminSettings.vue'
|
||||
|
||||
const selector = '#side-menu-message'
|
||||
|
||||
const userConfig = (name, value, callbacks) => {
|
||||
const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet')
|
||||
const formData = []
|
||||
|
||||
formData.push('name=' + encodeURIComponent(name))
|
||||
formData.push('value=' + encodeURIComponent(value))
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: formData.join('&'),
|
||||
})
|
||||
.then(callbacks.success)
|
||||
.catch(callbacks.error)
|
||||
}
|
||||
|
||||
const appConfig = (name, value, callbacks) => {
|
||||
OCP.AppConfig.setValue('side_menu', name, value, callbacks)
|
||||
}
|
||||
|
||||
const saveSettings = (key) => {
|
||||
const element = elements[key]
|
||||
|
||||
if (!element) {
|
||||
return
|
||||
}
|
||||
|
||||
let value
|
||||
let name
|
||||
|
||||
if (element.hasAttribute('data-checkbox')) {
|
||||
name = element.getAttribute('data-name')
|
||||
value = []
|
||||
|
||||
const inputs = document.querySelectorAll('input[name="' + name + '[]"]:checked')
|
||||
|
||||
for (let input of inputs) {
|
||||
value.push(input.value)
|
||||
}
|
||||
|
||||
value = JSON.stringify(value)
|
||||
} else {
|
||||
name = element.getAttribute('name')
|
||||
value = element.value
|
||||
}
|
||||
|
||||
const size = elements.length
|
||||
|
||||
if (name === 'cache') {
|
||||
++value
|
||||
}
|
||||
|
||||
const progress = document.querySelector('#side-menu-save-progress')
|
||||
|
||||
progress.style.width = '40px'
|
||||
progress.style.marginLeft = '5px'
|
||||
|
||||
const callbacks = {
|
||||
success: () => {
|
||||
const percent = parseInt(((key + 1) * 100) / size)
|
||||
|
||||
progress.setAttribute('value', percent)
|
||||
|
||||
if (key < size - 1) {
|
||||
saveSettings(key + 1)
|
||||
} else {
|
||||
location.reload()
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
OC.msg.finishedError(selector, t('side_menu', 'Error while saving "' + element + '"'))
|
||||
},
|
||||
}
|
||||
|
||||
if (element.hasAttribute('data-personal')) {
|
||||
userConfig(name, value, callbacks)
|
||||
} else {
|
||||
appConfig(name, value, callbacks)
|
||||
}
|
||||
}
|
||||
|
||||
const elementToggler = (element) => {
|
||||
let display = 'none'
|
||||
|
||||
if (window.getComputedStyle(element).display === 'none') {
|
||||
display = 'block'
|
||||
}
|
||||
|
||||
element.style.display = display
|
||||
}
|
||||
|
||||
const updateAppsCategoriesCustom = () => {
|
||||
let values = {}
|
||||
|
||||
for (let item of document.querySelectorAll('.apps-categories-custom')) {
|
||||
let app = item.getAttribute('data-app')
|
||||
let value = item.value
|
||||
|
||||
if (value) {
|
||||
values[app] = value
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('#apps-categories-custom').value = JSON.stringify(values)
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
$('*[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
if (document.querySelector('#side-menu-categories-custom')) {
|
||||
const app = createApp(AdminCategoriesCustom)
|
||||
app.mount('#side-menu-categories-custom')
|
||||
}
|
||||
|
||||
elements = document.querySelectorAll('.side-menu-setting')
|
||||
|
||||
document.querySelector('#side-menu-save').addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
OC.msg.startSaving(selector)
|
||||
|
||||
saveSettings(0)
|
||||
})
|
||||
|
||||
const resets = document.querySelectorAll('.btn-reset')
|
||||
|
||||
for (let btn of resets) {
|
||||
btn.addEventListener('click', (event) => {
|
||||
const target = event.target
|
||||
const values = JSON.parse(target.getAttribute('data-reset'))
|
||||
|
||||
target.classList.toggle('btn-reset--progress', true)
|
||||
|
||||
for (let i in values) {
|
||||
document.querySelector(`#${i}`).value = values[i]
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
target.classList.toggle('btn-reset--progress', false)
|
||||
}, 800)
|
||||
})
|
||||
}
|
||||
|
||||
const displays = document.querySelectorAll('.side-menu-display')
|
||||
|
||||
for (let display of displays) {
|
||||
display.addEventListener('click', (event) => {
|
||||
const target = event.target
|
||||
|
||||
for (let d of displays) {
|
||||
d.classList.toggle('is-active', d === display)
|
||||
}
|
||||
|
||||
document.querySelector('#side-menu-always-displayed').value = target.getAttribute('data-alwaysdiplayed')
|
||||
document.querySelector('#side-menu-big-menu').value = target.getAttribute('data-bigmenu')
|
||||
document.querySelector('#side-menu-side-with-categories').value = target.getAttribute('data-sidewithcategories')
|
||||
})
|
||||
}
|
||||
|
||||
for (let item of document.querySelectorAll('.apps-categories-custom')) {
|
||||
item.addEventListener('change', (event) => {
|
||||
updateAppsCategoriesCustom()
|
||||
})
|
||||
}
|
||||
|
||||
for (let item of document.querySelectorAll('.side-menu-setting-live')) {
|
||||
item.addEventListener('change', (event) => {
|
||||
const target = event.target
|
||||
const name = target.getAttribute('name')
|
||||
|
||||
let value = target.value
|
||||
let id = null
|
||||
|
||||
if (name === 'background-color-opacity') {
|
||||
id = '#side-menu-background-color, #side-menu-background-color-to'
|
||||
} else if (name === 'dark-mode-background-color-opacity') {
|
||||
id = '#side-menu-dark-mode-background-color, #side-menu-dark-mode-background-color-to'
|
||||
}
|
||||
|
||||
if (id) {
|
||||
document.querySelector(id).dispatchEvent(new CustomEvent('change'))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (name === 'opener') {
|
||||
const url = OC.generateUrl(`/apps/side_menu/img/${value}.svg`).replace('/index.php', '')
|
||||
|
||||
value = `url(${url})`
|
||||
}
|
||||
|
||||
if (name === 'icon-invert-filter' || name === 'icon-opacity') {
|
||||
value /= 100
|
||||
}
|
||||
|
||||
if (['dark-mode-background-color', 'dark-mode-background-color-to'].indexOf(name) > -1) {
|
||||
const opacity = parseInt((document.querySelector('#side-menu-dark-mode-background-color-opacity').value * 255) / 100)
|
||||
|
||||
value = [value, opacity.toString(16)].join('')
|
||||
} else if (['background-color', 'background-color-to'].indexOf(name) > -1) {
|
||||
const opacity = parseInt((document.querySelector('#side-menu-background-color-opacity').value * 255) / 100)
|
||||
|
||||
value = [value, opacity.toString(16)].join('')
|
||||
}
|
||||
|
||||
document.documentElement.style.setProperty('--side-menu-' + name, value)
|
||||
})
|
||||
}
|
||||
|
||||
for (let toggler of document.querySelectorAll('.side-menu-toggler')) {
|
||||
toggler.addEventListener('click', (event) => {
|
||||
const target = event.target
|
||||
const element = document.querySelector(target.getAttribute('data-target'))
|
||||
|
||||
elementToggler(element)
|
||||
})
|
||||
}
|
||||
|
||||
sortable('#categories-list .side-menu-setting-list', {
|
||||
placeholderClass: 'side-menu-setting-list-drop',
|
||||
})
|
||||
|
||||
try {
|
||||
sortable('#categories-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => {
|
||||
let value = []
|
||||
|
||||
for (let item of document.querySelectorAll('#categories-list .side-menu-setting-list-item')) {
|
||||
value.push(item.getAttribute('data-id'))
|
||||
}
|
||||
|
||||
document.querySelector('input[name="categories-order"]').value = JSON.stringify(value)
|
||||
})
|
||||
} catch (e) {}
|
||||
|
||||
sortable('#apps-order-list .side-menu-setting-list', {
|
||||
placeholderClass: 'side-menu-setting-list-drop',
|
||||
})
|
||||
|
||||
try {
|
||||
sortable('#apps-order-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => {
|
||||
let value = []
|
||||
|
||||
for (let item of document.querySelectorAll('#apps-order-list .side-menu-setting-list-item')) {
|
||||
value.push(item.getAttribute('data-id'))
|
||||
}
|
||||
|
||||
document.querySelector('input[name="apps-order"]').value = JSON.stringify(value)
|
||||
})
|
||||
} catch (e) {}
|
||||
waitContainer('#side-menu-admin-settings').then((selector) => {
|
||||
const pinia = createPinia()
|
||||
const app = createApp(AdminSettings)
|
||||
app.use(pinia)
|
||||
app.mixin({ methods: { t, n }})
|
||||
app.mount(selector)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -102,101 +102,92 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
|
||||
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
|
||||
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
|
||||
<script setup>
|
||||
import { NcModal, NcActions, NcActionButton } from '@nextcloud/vue'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'AdminCategoriesCustom',
|
||||
components: {
|
||||
NcModal,
|
||||
NcActions,
|
||||
NcActionButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
input: null,
|
||||
values: [],
|
||||
langs: [],
|
||||
addForm: false,
|
||||
editForm: false,
|
||||
newValue: {},
|
||||
editValue: {},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.input = document.querySelector('input[name="categories-custom"]')
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.values = JSON.parse(this.input.value)
|
||||
this.langs = JSON.parse(this.input.getAttribute('data-langs'))
|
||||
},
|
||||
update() {
|
||||
this.input.value = JSON.stringify(this.values)
|
||||
},
|
||||
showAddForm() {
|
||||
this.newValue = { id: 'cat' + Math.random().toString().replace('0.', '') }
|
||||
const { langs, values } = defineProps(['lang', 'values'])
|
||||
|
||||
this.addForm = true
|
||||
},
|
||||
showEditForm(value) {
|
||||
this.editValue = { id: value.id }
|
||||
const input = ref(null)
|
||||
const values = ref([])
|
||||
const langs = ref([])
|
||||
const addForm = ref(false)
|
||||
const editForm = ref(false)
|
||||
const newValue = ref({})
|
||||
const editValue = ref({})
|
||||
|
||||
for (let i of this.langs) {
|
||||
this.editValue[i] = typeof value[i] !== 'undefined' ? value[i] : ''
|
||||
}
|
||||
|
||||
this.editForm = true
|
||||
},
|
||||
saveAdd() {
|
||||
for (let i of this.langs) {
|
||||
if (!this.newValue[i] || /^\s*$/.test(this.newValue[i])) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.values.push(this.newValue)
|
||||
this.update()
|
||||
this.hideAddForm()
|
||||
this.newValue = {}
|
||||
},
|
||||
saveEdit() {
|
||||
for (let i of this.langs) {
|
||||
if (!this.editValue[i] || /^\s*$/.test(this.editValue[i])) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in this.values) {
|
||||
if (this.values[i].id === this.editValue.id) {
|
||||
this.values[i] = this.editValue
|
||||
}
|
||||
}
|
||||
|
||||
this.update()
|
||||
this.hideEditForm()
|
||||
},
|
||||
removeEdit() {
|
||||
for (let i in this.values) {
|
||||
if (this.values[i].id === this.editValue.id) {
|
||||
this.values.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
this.update()
|
||||
this.hideEditForm()
|
||||
},
|
||||
hideAddForm() {
|
||||
this.addForm = false
|
||||
},
|
||||
hideEditForm() {
|
||||
this.editForm = false
|
||||
},
|
||||
},
|
||||
}
|
||||
// mounted() {
|
||||
// this.input = document.querySelector('input[name="categories-custom"]')
|
||||
// this.init()
|
||||
// },
|
||||
// methods: {
|
||||
// init() {
|
||||
// this.values = JSON.parse(this.input.value)
|
||||
// this.langs = JSON.parse(this.input.getAttribute('data-langs'))
|
||||
// },
|
||||
// update() {
|
||||
// this.input.value = JSON.stringify(this.values)
|
||||
// },
|
||||
// showAddForm() {
|
||||
// this.newValue = { id: 'cat' + Math.random().toString().replace('0.', '') }
|
||||
//
|
||||
// this.addForm = true
|
||||
// },
|
||||
// showEditForm(value) {
|
||||
// this.editValue = { id: value.id }
|
||||
//
|
||||
// for (let i of this.langs) {
|
||||
// this.editValue[i] = typeof value[i] !== 'undefined' ? value[i] : ''
|
||||
// }
|
||||
//
|
||||
// this.editForm = true
|
||||
// },
|
||||
// saveAdd() {
|
||||
// for (let i of this.langs) {
|
||||
// if (!this.newValue[i] || /^\s*$/.test(this.newValue[i])) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.values.push(this.newValue)
|
||||
// this.update()
|
||||
// this.hideAddForm()
|
||||
// this.newValue = {}
|
||||
// },
|
||||
// saveEdit() {
|
||||
// for (let i of this.langs) {
|
||||
// if (!this.editValue[i] || /^\s*$/.test(this.editValue[i])) {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (let i in this.values) {
|
||||
// if (this.values[i].id === this.editValue.id) {
|
||||
// this.values[i] = this.editValue
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.update()
|
||||
// this.hideEditForm()
|
||||
// },
|
||||
// removeEdit() {
|
||||
// for (let i in this.values) {
|
||||
// if (this.values[i].id === this.editValue.id) {
|
||||
// this.values.splice(i, 1)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.update()
|
||||
// this.hideEditForm()
|
||||
// },
|
||||
// hideAddForm() {
|
||||
// this.addForm = false
|
||||
// },
|
||||
// hideEditForm() {
|
||||
// this.editForm = false
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
33
src/components/settings/MenuDisplay.vue
Normal file
33
src/components/settings/MenuDisplay.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import AlwaysDisplayImg from '../../img/admin/layout-always-displayed.svg'
|
||||
// import WideMenuImg from '../../img/admin/layout-big-menu.svg'
|
||||
// import DefaultImg from '../../img/admin/layout-default.svg'
|
||||
// import WithCategoriesImg from '../../img/admin/layout-width-categories.svg'
|
||||
|
||||
const choices = [
|
||||
// {id: 1, url: AlwaysDisplayImg},
|
||||
// {id: 2, url: WideMenuImg},
|
||||
// {id: 3, url: DefaultImg},
|
||||
// {id: 4, url: WithCategoriesImg},
|
||||
]
|
||||
|
||||
const carouselConfig = {
|
||||
itemsToShow: 1.3,
|
||||
wrapAround: true
|
||||
}
|
||||
|
||||
const { mode, alwaysDisplayed } = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
alwaysDisplayed: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
5
src/components/settings/TableContainer.vue
Normal file
5
src/components/settings/TableContainer.vue
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div class="side-menu-setting-table">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
33
src/components/settings/TableLabel.vue
Normal file
33
src/components/settings/TableLabel.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<div class="side-menu-setting-label" :class="{
|
||||
'side-menu-setting-label-short': short,
|
||||
'side-menu-setting-label--top': top,
|
||||
'side-menu-setting-label--middle': middle,
|
||||
}">
|
||||
{{ t('side_menu', label) }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { short, label } = defineProps({
|
||||
short: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
middle: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
top: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
5
src/components/settings/TableRow.vue
Normal file
5
src/components/settings/TableRow.vue
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div class="side-menu-setting-row">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
15
src/components/settings/TableValue.vue
Normal file
15
src/components/settings/TableValue.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div class="side-menu-setting-form" :class="{'side-menu-setting-form-long': long}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { long } = defineProps({
|
||||
long: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -5,7 +5,7 @@ const getActiveAppId = () => {
|
|||
|
||||
for (let id in apps) {
|
||||
if (apps[id].active) {
|
||||
return id
|
||||
return apps[id].id
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/lib/menu.js
Normal file
12
src/lib/menu.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const focusActiveApp = (menu) => {
|
||||
window.setTimeout(() => {
|
||||
const a = menu.querySelector('.side-menu-app.active a')
|
||||
|| menu.querySelector('.side-menu-app a')
|
||||
|
||||
if (a) {
|
||||
a.focus()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
export { focusActiveApp }
|
||||
|
|
@ -40,6 +40,7 @@ const display = ref(null)
|
|||
const hasPageLoader = ref(false)
|
||||
const isOpen = ref(false)
|
||||
const hasApps = ref(false)
|
||||
const openerHover = ref(false)
|
||||
|
||||
const toggleMenu = (value) => {
|
||||
isOpen.value = value
|
||||
|
|
@ -78,6 +79,10 @@ const createOpener = () => {
|
|||
}
|
||||
|
||||
opener.addEventListener('click', () => toggleMenu(true), true)
|
||||
|
||||
if (openerHover.value) {
|
||||
opener.addEventListener('mouseenter', () => toggleMenu(true), true)
|
||||
}
|
||||
}
|
||||
|
||||
const createLoader = () => {
|
||||
|
|
@ -111,6 +116,7 @@ onMounted(async () => {
|
|||
}
|
||||
|
||||
hasPageLoader.value = config.value['loader-enabled']
|
||||
openerHover.value = config.value['opener-hover']
|
||||
|
||||
if (hasApps.value) {
|
||||
createOpener()
|
||||
|
|
|
|||
|
|
@ -77,10 +77,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, useTemplateRef, onMounted } from 'vue'
|
||||
import { ref, useTemplateRef, onMounted, watch } from 'vue'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { getActiveAppId } from '../lib/app.js'
|
||||
import { focusActiveApp } from '../lib/menu.js'
|
||||
import { containsAppsMatchingSearch, isAppMatchingSearch } from '../lib/search.js'
|
||||
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
|
|
@ -108,6 +109,12 @@ const openerHover = ref(false)
|
|||
const menu = useTemplateRef('menu')
|
||||
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
||||
|
||||
watch(() => open, (val) => {
|
||||
if (val) {
|
||||
focusActiveApp(menu.value)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const config = await configStore.getConfig()
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
ref="menu"
|
||||
>
|
||||
<div
|
||||
v-if="settings || !openerHover || (!avatar && !alwaysDisplayed && logo) || avatar"
|
||||
class="side-menu-header"
|
||||
v-if="settings || displayLogo || open || !openerHover"
|
||||
>
|
||||
<SettingsButton
|
||||
v-if="settings && open"
|
||||
|
|
@ -32,26 +32,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
/>
|
||||
<AppSearch v-model="search" v-if="open" />
|
||||
<OpenerButton
|
||||
v-if="(!alwaysDisplayed && !openerHover) || isTouchDevice"
|
||||
v-if="!openerHover || isTouchDevice"
|
||||
@click="$emit('toggle')"
|
||||
/>
|
||||
<Logo
|
||||
v-if="!avatar && !alwaysDisplayed && logo"
|
||||
v-if="displayLogo"
|
||||
:classes="{ 'side-menu-logo': true, avatardiv: false }"
|
||||
:image="logo"
|
||||
:link="logoLink"
|
||||
/>
|
||||
<Logo
|
||||
v-if="avatar && !alwaysDisplayed"
|
||||
:classes="{ 'side-menu-logo': true, avatardiv: true }"
|
||||
:image="avatar"
|
||||
:image="useAvatarAsLogo ? avatar : logo"
|
||||
:link="logoLink"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ul
|
||||
class="side-menu-apps-list"
|
||||
:class="{ 'side-menu-apps-list--with-settings': !!settings }"
|
||||
:class="{ 'side-menu-apps-list--with-logo': displayLogo }"
|
||||
>
|
||||
<template
|
||||
v-for="(app, key) in apps"
|
||||
|
|
@ -59,7 +53,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
>
|
||||
<SideMenuApp
|
||||
v-if="isAppMatchingSearch(app, search)"
|
||||
:classes="{ 'side-menu-app': true, active: app.active }"
|
||||
:classes="{ 'side-menu-app': true, active: app.id === activeApp }"
|
||||
:icon="app.icon"
|
||||
:label="app.name"
|
||||
:href="app.href"
|
||||
|
|
@ -74,7 +68,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
import { ref, useTemplateRef, onMounted, watch } from 'vue'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { focusActiveApp } from '../lib/menu.js'
|
||||
import { isAppMatchingSearch } from '../lib/search.js'
|
||||
import { getActiveAppId } from '../lib/app.js'
|
||||
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
import SettingsButton from '../components/SettingsButton'
|
||||
|
|
@ -94,12 +90,15 @@ const navStore = useNavStore()
|
|||
const configStore = useConfigStore()
|
||||
const targetBlankApps = ref(null)
|
||||
const forceLightIcon = ref(null)
|
||||
const activeApp = ref(null)
|
||||
const avatar = ref(null)
|
||||
const logo = ref(null)
|
||||
const logoLink = ref(null)
|
||||
const settings = ref(null)
|
||||
const openerHover = ref(false)
|
||||
const alwaysDisplayed = ref(false)
|
||||
const displayLogo = ref(false)
|
||||
const useAvatarAsLogo = ref(false)
|
||||
const search = ref('')
|
||||
const apps = ref([])
|
||||
const menu = useTemplateRef('menu')
|
||||
|
|
@ -109,6 +108,12 @@ watch(apps, (val) => {
|
|||
document.querySelector('html').classList.toggle('side-menu-always-displayed', alwaysDisplayed.value && val.length)
|
||||
})
|
||||
|
||||
watch(() => open, (val) => {
|
||||
if (val) {
|
||||
focusActiveApp(menu.value)
|
||||
}
|
||||
})
|
||||
|
||||
function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) {
|
||||
const data = []
|
||||
|
||||
|
|
@ -136,14 +141,22 @@ function getFiltredAndSortedApps(items, order, topMenuApps, topSideMenuApps) {
|
|||
onMounted(async () => {
|
||||
const config = await configStore.getConfig()
|
||||
|
||||
alwaysDisplayed.value = config['always-displayed']
|
||||
targetBlankApps.value = config['target-blank-apps']
|
||||
forceLightIcon.value = config['force-light-icon']
|
||||
|
||||
avatar.value = config['avatar']
|
||||
logo.value = config['logo']
|
||||
useAvatarAsLogo.value = config['use-avatar']
|
||||
displayLogo.value = config['display-logo'] && !alwaysDisplayed.value && (
|
||||
(!useAvatarAsLogo.value && logo.value) ||
|
||||
(useAvatarAsLogo.value && avatar.value)
|
||||
)
|
||||
|
||||
logoLink.value = config['logo-link']
|
||||
settings.value = config['settings']
|
||||
openerHover.value = config['opener-hover'] && !isTouchDevice
|
||||
alwaysDisplayed.value = config['always-displayed']
|
||||
activeApp.value = getActiveAppId()
|
||||
|
||||
apps.value = getFiltredAndSortedApps(await navStore.getApps(), config['apps-order'], config['top-menu-apps'], config['top-side-menu-apps'])
|
||||
|
||||
|
|
@ -151,7 +164,7 @@ onMounted(async () => {
|
|||
menu.value.addEventListener('mouseleave', () => emit('close'))
|
||||
}
|
||||
|
||||
if (alwaysDisplayed.value) {
|
||||
if (alwaysDisplayed.value && openerHover.value) {
|
||||
menu.value.addEventListener('mouseenter', () => emit('open'))
|
||||
menu.value.addEventListener('mouseleave', () => emit('close'))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,10 +81,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, useTemplateRef, onMounted } from 'vue'
|
||||
import { ref, useTemplateRef, onMounted, watch } from 'vue'
|
||||
import { useNavStore } from '../store/nav.js'
|
||||
import { useConfigStore } from '../store/config.js'
|
||||
import { getActiveAppId } from '../lib/app.js'
|
||||
import { focusActiveApp } from '../lib/menu.js'
|
||||
import { containsAppsMatchingSearch, isAppMatchingSearch } from '../lib/search.js'
|
||||
|
||||
import OpenerButton from '../components/OpenerButton'
|
||||
|
|
@ -113,6 +114,12 @@ const openerHover = ref(false)
|
|||
const menu = useTemplateRef('menu')
|
||||
const isTouchDevice = window.matchMedia('(pointer: coarse)').matches
|
||||
|
||||
watch(() => open, (val) => {
|
||||
if (val) {
|
||||
focusActiveApp(menu.value)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const config = await configStore.getConfig()
|
||||
|
||||
|
|
|
|||
104
src/pages/AdminSettings.vue
Normal file
104
src/pages/AdminSettings.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<template>
|
||||
<NcContent app-name="side_menu">
|
||||
<NcAppContent>
|
||||
<div class="side-menu-setting">
|
||||
<NcButton
|
||||
v-for="item in menu"
|
||||
@click="setSection(item.section)"
|
||||
:variant="item.section === section ? 'primary' : 'secondary'"
|
||||
>{{ trans(item.label) }}</NcButton>
|
||||
</div>
|
||||
|
||||
<TableContainer :class="sectionClass('panel')">
|
||||
</TableContainer>
|
||||
|
||||
<TableContainer :class="sectionClass('color')">
|
||||
<TableRow>
|
||||
<TableLabel label="Background color" :short="true" :middle="true"></TableLabel>
|
||||
<TableValue>
|
||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
||||
</NcColorPicker>
|
||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
||||
</NcColorPicker>
|
||||
</TableValue>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableLabel label="Background color of current app" :short="true" :middle="true"></TableLabel>
|
||||
<TableValue>
|
||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
||||
</NcColorPicker>
|
||||
</TableValue>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableLabel label="Text color" :short="true" :middle="true"></TableLabel>
|
||||
<TableValue>
|
||||
<NcColorPicker v-model="color" class="side-menu-setting-color-picker">
|
||||
<div :style="{'background-color': color}" class="side-menu-setting-color-picker-value" />
|
||||
</NcColorPicker>
|
||||
</TableValue>
|
||||
</TableRow>
|
||||
</TableContainer>
|
||||
</NcAppContent>
|
||||
</NcContent>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding-top: calc(var(--default-grid-baseline) * 5);
|
||||
padding-left: calc(var(--default-grid-baseline) * 7);
|
||||
padding-right: calc(var(--default-grid-baseline) * 7);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
width: 100px;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
import { NcContent, NcAppContent, NcColorPicker, NcButton } from '@nextcloud/vue'
|
||||
import MenuDisplay from '../components/settings/MenuDisplay'
|
||||
import TableContainer from '../components/settings/TableContainer'
|
||||
import TableRow from '../components/settings/TableRow'
|
||||
import TableLabel from '../components/settings/TableLabel'
|
||||
import TableValue from '../components/settings/TableValue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const menu = [
|
||||
{label: 'Panel', section: 'panel'},
|
||||
{label: 'Colors', section: 'color'},
|
||||
]
|
||||
|
||||
const section = ref('panel')
|
||||
const color = ref('#ff9')
|
||||
|
||||
const setSection = (value) => {
|
||||
section.value = value
|
||||
}
|
||||
|
||||
const trans = (value) => {
|
||||
return t('side_menu', value)
|
||||
}
|
||||
|
||||
const sectionClass = (value) => {
|
||||
return {
|
||||
hidden: value !== section.value,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -114,6 +114,7 @@
|
|||
.side-menu-setting-table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.side-menu-setting-row {
|
||||
|
|
@ -141,6 +142,10 @@
|
|||
vertical-align: top;
|
||||
}
|
||||
|
||||
.side-menu-setting-label--middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.side-menu-setting-form {
|
||||
display: table-cell;
|
||||
min-width: 300px;
|
||||
|
|
@ -218,3 +223,23 @@
|
|||
border-color: #c681d4;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.side-menu-setting {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.side-menu-setting-color-picker {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
|
||||
&-value {
|
||||
cursor: pointer;
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,12 +41,40 @@
|
|||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.side-menu-opener {
|
||||
margin-top: 1px !important;
|
||||
}
|
||||
|
||||
&.side-menu-big {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&.side-menu-with-categories {
|
||||
max-width: 290px;
|
||||
height: 100vh;
|
||||
|
||||
.side-menu-categories {
|
||||
display: block;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.side-menu-category {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.side-menu-big, &.side-menu-with-categories {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#header {
|
||||
.side-menu-opener {
|
||||
margin-left: 0px;
|
||||
margin-top: -1px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,20 +139,19 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
#side-menu.hide-opener .side-menu-opener,
|
||||
.side-menu-opener.hide,
|
||||
#side-menu.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.side-menu-apps-list {
|
||||
height: calc(100vh - 150px);
|
||||
height: calc(100vh - 49px);
|
||||
top: 49px;
|
||||
z-index: 2200;
|
||||
position: fixed;
|
||||
top: 150px;
|
||||
width: 100%;
|
||||
max-width: 290px;
|
||||
overflow: auto;
|
||||
|
||||
&.side-menu-apps-list--with-logo {
|
||||
height: calc(100vh - 160px);
|
||||
top: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-menu-app-icon {
|
||||
|
|
@ -149,7 +176,8 @@
|
|||
|
||||
a:hover,
|
||||
a:focus,
|
||||
&:active {
|
||||
&:active,
|
||||
&.active {
|
||||
background: var(--side-menu-current-app-background-color, #444);
|
||||
}
|
||||
}
|
||||
|
|
@ -165,7 +193,6 @@
|
|||
}
|
||||
|
||||
.side-menu-header {
|
||||
height: 150px;
|
||||
width: 100%;
|
||||
z-index: 2300;
|
||||
max-width: 290px;
|
||||
|
|
@ -183,10 +210,6 @@
|
|||
max-width: 295px;
|
||||
}
|
||||
|
||||
#side-menu.hide-opener .side-menu-logo {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#side-menu-loader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
@ -202,28 +225,24 @@
|
|||
}
|
||||
}
|
||||
|
||||
#side-menu.side-menu-big,
|
||||
#side-menu.side-menu-with-categories {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.side-menu-big, .side-menu-with-categories {
|
||||
.side-menu-apps-list {
|
||||
height: auto;
|
||||
position: static;
|
||||
max-width: 100vw;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.side-menu-big .side-menu-header,
|
||||
.side-menu-with-categories .side-menu-header {
|
||||
height: auto;
|
||||
}
|
||||
.side-menu-app {
|
||||
a {
|
||||
padding: 7px 0 7px 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-menu-big .side-menu-apps-list,
|
||||
.side-menu-with-categories .side-menu-apps-list {
|
||||
height: auto;
|
||||
position: static;
|
||||
max-width: 100vw;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.side-menu-big .side-menu-app a,
|
||||
.side-menu-with-categories .side-menu-app a {
|
||||
padding: 7px 0 7px 7px;
|
||||
.side-menu-app-icon {
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-menu-categories-wrapper {
|
||||
|
|
@ -265,11 +284,6 @@
|
|||
stroke: var(--side-menu-text-color, #fff);
|
||||
}
|
||||
|
||||
.side-menu-with-categories .side-menu-app-icon,
|
||||
.side-menu-big .side-menu-app-icon {
|
||||
vertical-align: middle;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.side-menu-always-displayed {
|
||||
body {
|
||||
|
|
@ -287,12 +301,6 @@
|
|||
}
|
||||
|
||||
.side-menu-apps-list {
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.side-menu-apps-list--with-settings {
|
||||
height: calc(100vh - 49px);
|
||||
top: 49px;
|
||||
}
|
||||
|
|
@ -331,32 +339,6 @@
|
|||
right: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.side-menu-search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#body-settings,
|
||||
#body-settings.body-settings-side-menu {
|
||||
overflow-x: visible;
|
||||
}
|
||||
}
|
||||
|
||||
#side-menu.side-menu-with-categories {
|
||||
max-width: 290px;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.side-menu-with-categories {
|
||||
.side-menu-categories {
|
||||
display: block;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.side-menu-category {
|
||||
padding: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.app-menu {
|
||||
|
|
@ -390,10 +372,6 @@
|
|||
height: 100vh;
|
||||
}
|
||||
|
||||
#side-menu.hide-opener.side-menu-big .side-menu-search {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.side-menu-categories {
|
||||
display: block;
|
||||
padding: 0;
|
||||
|
|
|
|||
|
|
@ -8,65 +8,28 @@
|
|||
<?php endforeach; ?>
|
||||
}
|
||||
|
||||
<?php if (empty($_['top-menu-apps']) && empty($_['top-side-menu-apps'])): ?>
|
||||
#appmenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appmenu + nav {
|
||||
display: none;
|
||||
}
|
||||
<?php else: ?>
|
||||
.app-hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($_['opener-only']): ?>
|
||||
#nextcloud {
|
||||
display: none;
|
||||
}
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$_['display-logo']): ?>
|
||||
.side-menu-logo {
|
||||
display: none;
|
||||
<?php if ($_['size-text'] === 'hidden'): ?>
|
||||
#side-menu, .side-menu-apps-list {
|
||||
<?php if ($_['size-icon'] === 'big'): ?>
|
||||
width: 55px;
|
||||
<?php else: ?>
|
||||
width: 52px;
|
||||
<?php endif; ?>
|
||||
}
|
||||
|
||||
.side-menu-header {
|
||||
height: 50px;
|
||||
#side-menu .side-menu-opener {
|
||||
<?php if ($_['size-icon'] === 'big'): ?>
|
||||
margin-left: 1px;
|
||||
<?php else: ?>
|
||||
margin-left: 0px;
|
||||
<?php endif; ?>
|
||||
}
|
||||
|
||||
.side-menu-apps-list {
|
||||
height: calc(100vh - 49px);
|
||||
top: 49px;
|
||||
}
|
||||
|
||||
#side-menu.hide-opener .side-menu-header .side-menu-opener.side-menu-closer {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#side-menu.hide-opener.side-menu-with-categories .side-menu-search {
|
||||
float: none;
|
||||
}
|
||||
|
||||
<?php if ($_['size-text'] === 'hidden'): ?>
|
||||
#side-menu, .side-menu-apps-list {
|
||||
<?php if ($_['size-icon'] === 'big'): ?>
|
||||
width: 55px;
|
||||
<?php else: ?>
|
||||
width: 52px;
|
||||
<?php endif; ?>
|
||||
}
|
||||
|
||||
#side-menu .side-menu-opener {
|
||||
<?php if ($_['size-icon'] === 'big'): ?>
|
||||
margin-left: 1px;
|
||||
<?php else: ?>
|
||||
margin-left: 0px;
|
||||
<?php endif; ?>
|
||||
}
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($_['size-icon'] === 'hidden'): ?>
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
(function() {
|
||||
const body = document.querySelector('body')
|
||||
const html = document.querySelector('html')
|
||||
const nextcloud = document.querySelector('#nextcloud')
|
||||
const logo = document.querySelector('.header-left .logo')
|
||||
const isTouchDevice = window.matchMedia("(pointer: coarse)").matches
|
||||
|
||||
const sideMenuFocus = () => {
|
||||
let a = document.querySelector('#side-menu .side-menu-app.active a')
|
||||
|| document.querySelector('#side-menu .side-menu-app a')
|
||||
|
||||
if (a) {
|
||||
a.focus()
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelector('body').addEventListener('side-menu.apps', (e) => {
|
||||
const apps = e.detail.apps;
|
||||
})
|
||||
|
||||
body.addEventListener('side-menu.ready', () => {
|
||||
const sideMenu = document.querySelector('#side-menu')
|
||||
|
||||
|
||||
const sideMenuObserver = new MutationObserver((e) => {
|
||||
if (body.getAttribute('id') !== 'body-settings') {
|
||||
return
|
||||
}
|
||||
|
||||
body.classList.toggle('body-settings-side-menu', sideMenu.classList.contains('open'))
|
||||
})
|
||||
|
||||
sideMenuObserver.observe(sideMenu, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
childList: false,
|
||||
characterData: false
|
||||
})
|
||||
})
|
||||
})();
|
||||
File diff suppressed because it is too large
Load diff
1148
templates/settings/admin-form.php.bk
Normal file
1148
templates/settings/admin-form.php.bk
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -16,7 +16,7 @@ module.exports = {
|
|||
devtool: "inline-source-map",
|
||||
entry: {
|
||||
menu: path.resolve(path.join('src', 'menu.js')),
|
||||
// admin: path.resolve(path.join('src', 'admin.js')),
|
||||
admin: path.resolve(path.join('src', 'admin.js')),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('./js'),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ module.exports = {
|
|||
test: /\.scss$/,
|
||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
||||
},
|
||||
css: {
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
vue: {
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue