diff --git a/.drone.yml b/.drone.yml index 5980907..6a19e9d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -21,8 +21,8 @@ steps: - name: release image: deblan/devenv volumes: - - name: artefacts - path: /artefacts + - name: artifacts + path: /artifacts environment: APP_CERTIFICATE: from_secret: app_certificate @@ -30,13 +30,13 @@ steps: - mkdir -p "$HOME/.nextcloud/certificates" - echo "$APP_CERTIFICATE" > "$HOME/.nextcloud/certificates/side_menu.key" - export VERSION=$(grep "" appinfo/info.xml | grep -o "[0-9]*\.[0-9]*\.[0-9]*" --color=never) - - export RELEASE_DIRECTORY="/artefacts/deblan/side_menu" + - export RELEASE_DIRECTORY="/artifacts/deblan/side_menu" - make release when: branch: - master volumes: - - name: artefacts + - name: artifacts host: - path: /var/www/html/_artefacts + path: /var/www/html/artifacts diff --git a/CHANGELOG.md b/CHANGELOG.md index ee70394..7ed448a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ ## [Unreleased] +## 2.0.0 +### Fixed +- fix #66: removing usage of setInterval +- fix #73: icon background +### Changed +- fix #67: replace jQuery with Vanilla JS +### Removed +- Nextcloud 18 is not supported anymore + ## 1.28.0 ### Added - fix #63: add a new side menu with categories diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 7de95c4..a734e49 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,1027 +1,5 @@ +# Contributor Code of Conduct +This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NCoC/CODE_OF_CONDUCT.md at master · domgetter/NCoC · GitHub - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content - - - - - - - - -
- -
- - - - - -
- - - -
- - - - - - - - - -
-
-
- - - - - - - - - - - - -
- -
- -
-

- - - / - - NCoC - - -

- - -
- - - -
- - -
- - -
-
- - - - - - - Permalink - - - - - -
- -
-
- - - master - - - - -
- - - -
-
-
- -
- - - - Go to file - - -
- - -
- -
- - - -
- -
-
-
 
-
- -
-
 
- Cannot retrieve contributors at this time -
-
- - - - - - -
- -
-
- - 5 lines (3 sloc) - - 258 Bytes -
- -
- -
- Raw - Blame -
- - -
-
- - -
-

Contributor Code of Conduct

-

This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters.

-

For more information please visit the No Code of Conduct homepage.

-
-
- -
- - - - -
- - -
- - -
-
- - - - -
-
- -
-
- -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - - - - - +For more information please visit the [No Code of Conduct](https://github.com/domgetter/NCoC) homepage. diff --git a/Makefile b/Makefile index 09d3f22..124ddc9 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ release: npm-build translations test -d releases/$$VERSION && rm -fr releases/$$VERSION mkdir -p releases/$$VERSION/side_menu - cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots releases/$$VERSION/side_menu + cp -r README.md CHANGELOG.md appinfo css lib img l10n js src templates screenshots vendor releases/$$VERSION/side_menu cd releases/$$VERSION zip -r side_menu_v$$VERSION.zip side_menu tar cvzf side_menu_v$$VERSION.tar.gz side_menu diff --git a/appinfo/info.xml b/appinfo/info.xml index 97e6518..dec246d 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -26,7 +26,7 @@ If you like this application and if you want to support the development: * [Donate with liberapay](https://liberapay.com/deblan) * [Leave a comment](https://apps.nextcloud.com/apps/side_menu#comments) ]]> - 1.28.0 + 2.0.0 agpl Simon Vieille SideMenu @@ -46,7 +46,7 @@ If you like this application and if you want to support the development: https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/nc20_big_menu_responsive.png https://gitnet.fr/deblan/side_menu/raw/branch/master/screenshots/personal_settings.png - + diff --git a/css/admin.css b/css/admin.css index 41d9ec1..a15aa64 100644 --- a/css/admin.css +++ b/css/admin.css @@ -20,7 +20,7 @@ margin: 10px 0 10px 0; } -#side-menu-section input[type="checkbox"] { +#-dropside-menu-section input[type="checkbox"] { vertical-align: middle; } @@ -81,6 +81,12 @@ cursor: pointer; } +.side-menu-setting-list-drop { + background: yellow; + border-color: yellow; + height: 34px; +} + .side-menu-setting.arrow { color: #ccc; padding-right: 5px; diff --git a/css/sideMenu.css b/css/sideMenu.css index 08b0539..db83941 100644 --- a/css/sideMenu.css +++ b/css/sideMenu.css @@ -34,7 +34,7 @@ } #header .side-menu-opener { - margin-left: 5px; + margin-left: 0px; } .side-menu-settings { @@ -67,10 +67,19 @@ .side-menu-opener { background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); - height: 40px; - width: 40px; - border-radius: 0; - border: 0; + background-color: transparent !important; + height: 40px !important; + width: 40px !important; + border-radius: 0 !important; + border: 0 !important; + padding-right: 12px !important; + padding-left: 12px !important; + margin-left: 5px !important; + margin-left: 3px !important; +} + +.side-menu-opener:active { + background-color: var(--color-background-dark) !important; } .side-menu-closer { @@ -139,7 +148,6 @@ max-width: 250px; position: fixed; padding-top: 2px; - padding-left: 5px; top: 0; } diff --git a/src/SideMenu.js b/src/SideMenu.js index 441d0a4..a9848c4 100644 --- a/src/SideMenu.js +++ b/src/SideMenu.js @@ -41,7 +41,7 @@ const mountSideMenuComponent = () => { sideMenu.$mount('#side-menu') - $('body').trigger('side-menu.ready') + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready')) } else { window.setTimeout(mountSideMenuComponent, 50) } diff --git a/src/SideMenu.vue b/src/SideMenu.vue index 68fcccb..7ab3cab 100644 --- a/src/SideMenu.vue +++ b/src/SideMenu.vue @@ -122,13 +122,15 @@ export default { name: trim(element.querySelector('span').innerHTML), icon: svg, active: element.classList.contains('active') - }); + }) } } (function(apps) { window.setTimeout(function() { - jQuery('body').trigger('side-menu.apps', [apps]) + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })) }, 1000) })(this.apps) }, @@ -147,7 +149,7 @@ export default { that.logo = config['logo'] that.logoLink = config['logo-link'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/src/SideMenuBig.vue b/src/SideMenuBig.vue index 9f6d8f2..72e6321 100644 --- a/src/SideMenuBig.vue +++ b/src/SideMenuBig.vue @@ -97,8 +97,10 @@ export default { } } - jQuery('body').trigger('side-menu.apps', [apps]) - }); + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })) + }) }, retrieveActiveApp() { @@ -116,7 +118,7 @@ export default { that.targetBlankApps = config['target-blank-apps'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/src/SideMenuWithCategories.vue b/src/SideMenuWithCategories.vue index 6b63689..7accbf0 100644 --- a/src/SideMenuWithCategories.vue +++ b/src/SideMenuWithCategories.vue @@ -95,8 +95,10 @@ export default { } } - jQuery('body').trigger('side-menu.apps', [apps]) - }); + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.apps', { + detail: {apps: apps}, + })) + }) }, retrieveActiveApp() { @@ -114,7 +116,7 @@ export default { that.targetBlankApps = config['target-blank-apps'] that.settings = config['settings'] - }); + }) }, }, mounted() { diff --git a/src/admin.js b/src/admin.js index c9b3c4d..3058e24 100644 --- a/src/admin.js +++ b/src/admin.js @@ -17,13 +17,23 @@ let elements = [] -const selector = '#side-menu-message'; +const selector = '#side-menu-message' const userConfig = (name, value, callbacks) => { const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet') + const formData = new FormData() + formData.append('name', name) + formData.append('value', value) - $.post(url, {name: name, value: value}, callbacks.success) - .fail(callbacks.error) + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: formData + }) + .then(callbacks.success) + .catch(callbacks.error) } const appConfig = (name, value, callbacks) => { @@ -31,23 +41,29 @@ const appConfig = (name, value, callbacks) => { } const saveSettings = (key) => { - const element = elements.get(key) + const element = elements[key] + + if (!element) { + return + } + let value let name - if (jQuery(element).is('[data-checkbox]')) { - name = jQuery(element).attr('data-name') - const inputs = jQuery('input[name="' + name + '[]"]:checked') + if (element.hasAttribute('data-checkbox')) { + name = element.getAttribute('data-name') value = [] - inputs.each((i, v) => { - value.push(v.value) - }) + const inputs = document.querySelectorAll('input[name="' + name + '[]"]:checked') + + for (let input of inputs) { + value.push(input.value) + } value = JSON.stringify(value) } else { - name = jQuery(element).attr('name') - value = jQuery(element).val() + name = element.getAttribute('name') + value = element.value } const size = elements.length @@ -63,8 +79,8 @@ const saveSettings = (key) => { t('side_menu', (key + 1) + '/' + size) ) - if (key < size - 1) { - saveSettings(++key) + if (key < size) { + saveSettings(key + 1) } else { OC.msg.finishedSuccess(selector, t('side_menu', 'Saved')) } @@ -74,7 +90,7 @@ const saveSettings = (key) => { } } - if (jQuery(element).is('[data-personal]')) { + if (element.hasAttribute('data-personal')) { userConfig(name, value, callbacks) } else { appConfig(name, value, callbacks) @@ -82,84 +98,105 @@ const saveSettings = (key) => { } const elementToggler = (element) => { - jQuery(element).toggle() + let display = 'none' + + if (window.getComputedStyle(element).display === 'none') { + display = 'block' + } + + element.style.display = display } -jQuery(document).ready(() => { - elements = jQuery('.side-menu-setting') +document.addEventListener('DOMContentLoaded', () => { + elements = document.querySelectorAll('.side-menu-setting') - jQuery('#side-menu-save').on('click', (event) => { + document.querySelector('#side-menu-save').addEventListener('click', (event) => { event.preventDefault() OC.msg.startSaving(selector) saveSettings(0) - }); - - jQuery('.side-menu-display').on('click', (event) => { - var target = jQuery(event.target) - - jQuery('.side-menu-display').removeClass('is-active') - target.addClass('is-active') - - jQuery('#side-menu-always-displayed').val(target.attr('data-alwaysdiplayed')) - jQuery('#side-menu-big-menu').val(target.attr('data-bigmenu')) - jQuery('#side-menu-side-with-categories').val(target.attr('data-sidewithcategories')) }) - jQuery('.side-menu-setting-live').on('change', (event) => { - var target = jQuery(event.target) - var name = target.attr('name') - var value = target.val() + const displays = document.querySelectorAll('.side-menu-display') - if ('background-color-opacity' === name) { - return $('#side-menu-background-color, #side-menu-background-color-to').trigger('change'); - } else if ('dark-mode-background-color-opacity' === name) { - return $('#side-menu-dark-mode-background-color, #side-menu-dark-mode-background-color-to').trigger('change'); - } + for (let display of displays) { + display.addEventListener('click', (event) => { + const target = event.target - if (name === 'opener') { - var url = OC.generateUrl(`/apps/side_menu/img/${value}.svg`).replace('/index.php', '') + for (let d of displays) { + d.classList.toggle('is-active', d === display) + } - value = `url(${url})`; - } + 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') + }) + } - if (name === 'icon-invert-filter' || name === 'icon-opacity') { - value/=100; - } + for (let item of document.querySelectorAll('.side-menu-setting-live')) { + item.addEventListener('change', (event) => { + const target = event.target + const name = target.getAttribute('name') - if (['dark-mode-background-color', 'dark-mode-background-color-to'].indexOf(name) > -1) { - var opacity = parseInt($('#side-menu-dark-mode-background-color-opacity').val() * 255 / 100); + let value = target.value + let id = null - value = [value, opacity.toString(16)].join(''); - } else if (['background-color', 'background-color-to'].indexOf(name) > -1) { - var opacity = parseInt($('#side-menu-background-color-opacity').val() * 255 / 100); + 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' + } - value = [value, opacity.toString(16)].join(''); - } + if (id) { + document.querySelector(id).dispatchEvent(new CustomEvent('change')) - document.documentElement.style.setProperty('--side-menu-' + name, value) + 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' }) - jQuery('.side-menu-toggler').on('click', (event) => { - var target = jQuery(event.target) - var element = target.attr('data-target') + sortable('#categories-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => { + let value = [] - elementToggler(element) + 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) }) - - jQuery("#categories-list .side-menu-setting-list").sortable({ - forcePlaceholderSize: true, - placeholder: 'placeholder', - stop: function (event, ui) { - let value = [] - - jQuery('#categories-list .side-menu-setting-list-item').each(function() { - value.push(jQuery(this).attr('data-id')) - }); - - value = JSON.stringify(value) - - jQuery('input[name="categories-order"]').val(value) - } - }); -}); +}) diff --git a/templates/js/_alwaysDisplayed.js b/templates/js/_alwaysDisplayed.js index 2b58bc7..e37230a 100644 --- a/templates/js/_alwaysDisplayed.js +++ b/templates/js/_alwaysDisplayed.js @@ -1,21 +1,19 @@ -var alwaysDisplayed = function() { - var elements = document.querySelectorAll('*'); - var fixedElements = [] - - for (var i in elements) { - var element = elements[i] +const alwaysDisplayed = function() { + const elements = querySelectorAll('*') + const fixedElements = [] + for (var element of elements) { if (typeof element !== 'object') { continue } - var position = window.getComputedStyle(element, null).getPropertyValue('position'); + const position = window.getComputedStyle(element, null).getPropertyValue('position') if (position !== 'fixed') { continue } - var id = element.getAttribute('id') + const id = element.getAttribute('id') if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') { continue @@ -25,7 +23,21 @@ var alwaysDisplayed = function() { continue } - if (jQuery(element).parents('#side-menu').length) { + let elementIsInSideMenu = false + let parent = element.parentNode + + while (parent && !elementIsInSideMenu) { + try { + if (parent.getAttribute('id') === 'side-menu') { + elementIsInSideMenu = true + } + } catch (e) { + } + + parent = parent.parentNode + } + + if (elementIsInSideMenu) { continue } @@ -33,19 +45,19 @@ var alwaysDisplayed = function() { } for (var i in fixedElements) { - var element = fixedElements[i] - var computedStyle = window.getComputedStyle(element, null) - var left = computedStyle.getPropertyValue('left') - var right = computedStyle.getPropertyValue('right') + const element = fixedElements[i] + const computedStyle = window.getComputedStyle(element, null) + const left = computedStyle.getPropertyValue('left') + const right = computedStyle.getPropertyValue('right') if (right !== '0px') { - var intValue = parseInt(left.replace('px', '')) - element.style.setProperty('transform', 'translateX(' + (intValue + 50) + 'px)') + const intValue = parseInt(left.replace('px', '')) + 50 + element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)') } } } -let content = document.getElementById('content') +const content = querySelector('#content') if (content && content.classList.contains('app-settings')) { let loaded = false @@ -56,7 +68,7 @@ if (content && content.classList.contains('app-settings')) { } const observer = new MutationObserver(() => { if (loaded) { - return; + return } const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul') diff --git a/templates/js/_loaderEnabled.js b/templates/js/_loaderEnabled.js index b391e4b..1d5ff4e 100644 --- a/templates/js/_loaderEnabled.js +++ b/templates/js/_loaderEnabled.js @@ -1,15 +1,14 @@ -var pageLoader = jQuery('
') -var pageLoaderBar = jQuery('
') +let pageLoader = createElement('div', {id: 'side-menu-loader'}) +let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'}) -body.append(pageLoader) -pageLoader.append(pageLoaderBar) +pageLoader.appendChild(pageLoaderBar) +querySelector('body').appendChild(pageLoader) -var pageLoaderValue = 0 - -$(window).on('beforeunload', function() { - setInterval(function() { - pageLoaderBar.width(pageLoaderValue.toString() + '%') +let pageLoaderValue = 0 +window.addEventListener('beforeunload', () => { + setInterval(() => { + pageLoaderBar.style.width = pageLoaderValue.toString() + '%' pageLoaderValue = Math.min(pageLoaderValue + .2, 100) }, 25) }) diff --git a/templates/js/_topMenuApps.js b/templates/js/_topMenuApps.js index 57b7088..dee32c8 100644 --- a/templates/js/_topMenuApps.js +++ b/templates/js/_topMenuApps.js @@ -1,32 +1,55 @@ -var menuCache = null +let menuCache = null -var updateTopMenu = function() { - var breakpointMobileWidth = 1024 - var menu = jQuery('#appmenu') - var apps = menu.find('li') - var minAppsDesktop = 8 - var usePercentualAppMenuLimit = 0.8 - var isMobile = jQuery(window).width() < breakpointMobileWidth - var lastShownApp = null - var appShown = [] - var moreApps = jQuery('#more-apps') - var navigation = jQuery('#navigation') - var navigationApps = jQuery('#apps ul') - var appCount = null +const breakpointMobileWidth = 1024 +const usePercentualAppMenuLimit = 0.8 +const minAppsDesktop = 8 - var currentMenuCache = menu.html() + menu.next().html() +const handleMenuClick = (e, icon) => { + let element = e.target - if (currentMenuCache === menuCache) { + while (element.tagName !== 'LI') { + element = element.parentNode + } + + const a = querySelector('a', element) + + if (a.getAttribute('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey) { + for (let tag of ['svg', 'div']) { + let el = querySelector(tag, element) + + if (el) { + el.remove() + } + } + + const loader = createElement('div', {'class': icon}) + + a.insertBefore(loader, querySelector('span', a)) + } +} + +const updateTopMenu = function() { + const isMobile = window.innerWidth < breakpointMobileWidth + const menu = querySelector('#appmenu') + const moreApps = querySelector('#more-apps') + const navigation = querySelector('#navigation') + const navigationApps = querySelector('#apps ul') + + let apps = querySelectorAll('li', menu) + let lastShownApp = null + let appShown = [] + + if ((menu.innerHTML + menu.nextSibling.innerHTML) === menuCache) { return } - navigationApps.html('') + navigationAppsHtml = '' - apps.each(function(i, app) { - var dataId = app.getAttribute('data-id') + for (let app of apps) { + const dataId = app.getAttribute('data-id') if (dataId === null) { - return + continue } if (topMenuApps.indexOf(dataId) === -1) { @@ -35,26 +58,31 @@ var updateTopMenu = function() { } else { app.classList.remove('hidden') app.classList.add('app-external-site') + appShown.push(app) - navigationApps.append(app.outerHTML) + + navigationAppsHtml = navigationAppsHtml + app.outerHTML } if (targetBlankApps.indexOf(dataId) !== -1) { - jQuery(app).children('a').attr('target', '_blank'); + querySelector('a', app).setAttribute('target', '_blank') } - }) - var rightHeaderWidth = jQuery('.header-right').outerWidth() - var headerWidth = jQuery('header').outerWidth() - var availableWidth = headerWidth - jQuery('#nextcloud').outerWidth() - - jQuery('#header .side-menu-opener').outerWidth() - - (rightHeaderWidth > 230 ? rightHeaderWidth : 230) - - if (!isMobile) { - availableWidth = availableWidth * usePercentualAppMenuLimit } - appCount = Math.floor(availableWidth / jQuery('#appmenu li').width()) + navigationApps.innerHTML = navigationAppsHtml + + const rightHeaderWidth = querySelector('.header-right').offsetWidth + const headerWidth = querySelector('header').offsetWidth + + let availableWidth = headerWidth + + availableWidth -= nextcloud.offsetWidth + availableWidth -= querySelector('#header .side-menu-opener').offsetWidth + availableWidth -= rightHeaderWidth > 230 ? rightHeaderWidth : 230 + availableWidth *= isMobile ? usePercentualAppMenuLimit : 1 + + let appCount = Math.floor(availableWidth / querySelector('#appmenu li:not(.hidden)').offsetWidth) if (isMobile && appCount > minAppsDesktop) { appCount = minAppsDesktop @@ -62,111 +90,125 @@ var updateTopMenu = function() { appCount = minAppsDesktop } - if (appCount === 0) { - menu.addClass('hidden') - } - - menu.removeClass('hidden') - menu.css('opacity', 1) + menu.style.opacity = 1 if (appShown.length - 1 - appCount >= 1) { appCount-- } - moreApps.find('a').removeClass('active') + for (let item of querySelectorAll('a', moreApps)) { + item.classList.remove('active') + } - var k = 0 - var notInHeader = 0 - var name + let k = 0 + let notInHeader = 0 - jQuery(appShown).each(function(i, app) { - app = jQuery(app) - name = app.data('id') + + for (let app of appShown) { + const name = app.getAttribute('data-id') + const li = querySelector('#apps li[data-id=' + name + '].app-external-site') if (k < appCount && appCount > 0) { - app.removeClass('hidden') - lastShownApp = app + app.classList.remove('hidden') + li.classList.add('in-header') - jQuery('#apps li[data-id=' + name + '].app-external-site').addClass('in-header') + lastShownApp = app } else { - app.addClass('hidden') + app.classList.add('hidden') + li.classList.remove('in-header') + notInHeader++ - jQuery('#apps li[data-id=' + name + '].app-external-site').removeClass('in-header') + const a = querySelector('a', app) - if (appCount > 0 && app.children('a').hasClass('active')) { - lastShownApp.addClass('hidden') - app.removeClass('hidden') + if (appCount > 0 && a.classList.contains('active')) { + lastShownApp.classList.add('hidden') + app.classList.remove('hidden') notInHeader++ - jQuery('#apps li[data-id=' + name + '].app-external-site') - .removeClass('in-header') - .addClass('in-header') + li.classList.add('in-header') } } k++ - }) + } - // Hack for https://github.com/nextcloud/server/blob/23b0b63c213f5b31eecae817ffd4a9e26f6624d0/core/src/components/MainMenu.js#L74-L96 - menu.undelegate('li:not(#more-apps) > a', 'click') - menu.delegate('li:not(#more-apps) > a', 'click', function(e) { - var a = $(e.target) + // Hack for: + // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119 + // - https://github.com/nextcloud/server/blob/master/core/src/components/MainMenu.js#L97-L119 + jQuery(menu).undelegate('li:not(#more-apps) > a', 'click') + jQuery(navigation).undelegate('a', 'click') - if (!a.is('a')) { - a = a.closest('a') + const confs = [ + { + items: querySelectorAll('#navigation li'), + icon: 'icon-loading-small' + }, + { + items: querySelectorAll('li:not(#more-apps)', menu), + icon: OCA.Theming && OCA.Theming.inverted ? 'icon-loading-small' : 'icon-loading-small-dark' + }, + ] + + for (let conf of confs) { + for (let item of conf.items) { + item.addEventListener('click', (e) => { + handleMenuClick(e, conf.icon) + }) } + } - if (a.attr('target') !== '_blank' && e.which === 1 && !e.ctrlKey && !e.metaKey && a.parent('#more-apps').length === 0) { - a.find('svg').remove() - a.find('div').remove() - a.prepend(jQuery('
').addClass( - OCA.Theming && OCA.Theming.inverted - ? 'icon-loading-small' - : 'icon-loading-small-dark' - )) + for (let app of querySelectorAll('#apps li.app-external-site')) { + const appId = app.getAttribute('data-id') - window.location.href = a.attr('href') - } - }) - - jQuery('#apps li.app-external-site').each(function(i, app) { - app = jQuery(app) - var appId = app.attr('data-id') - - if (app.hasClass('in-header')) { - app.find('svg').find('defs').remove() + if (app.classList.contains('in-header')) { + for (let defs of querySelectorAll('svg defs', app)) { + defs.remove() + } } else { - var svg = app.find('svg'); + const svg = querySelector('svg', app) - if (svg.find('defs').length > 0) { - return; + if (querySelectorAll('svg defs', app).length > 0) { + continue } - var defs = ` + const defs = ` ` - svg.prepend(defs) - svg.find('image').attr('filter', `url(#invertMenuMore-${appId})`) + svg.innerHTML = defs + svg.innerHTML - var html = svg.get(0).innerHTML.replace(/fecolormatrix/g, 'feColorMatrix'); + for (let image of querySelectorAll('image', svg)) { + image.setAttribute('filter', `url(#invertMenuMore-${appId})`) + } - svg.html(html) + svg.innerHTML = svg.innerHTML.replace(/fecolormatrix/g, 'feColorMatrix') } - }) - - if (notInHeader === 0) { - moreApps.hide() - navigation.hide() - } else { - moreApps.show() } - menuCache = menu.html() + menu.next().html() + if (notInHeader === 0) { + moreApps.style.display = 'none' + navigation.style.display = 'none' + } else { + moreApps.style.display = 'flex' + } + + menuCache = menu.innerHTML + menu.nextSibling.innerHTML } -setInterval(updateTopMenu, 50) +for (let timeout of [300, 500, 700, 900, 1100]) { + setTimeout(updateTopMenu, timeout) +} + +let resizeTimeout = null; + +window.addEventListener('resize', () => { + if (resizeTimeout !== null) { + clearTimeout(resizeTimeout) + } + + resizeTimeout = setTimeout(updateTopMenu, 100) +}) diff --git a/templates/js/script.php b/templates/js/script.php index cf4b2ce..8f22477 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -13,63 +13,98 @@ if ($_['always-displayed']) { ?> (function() { - var sideMenuContainer = jQuery('
') - var sideMenuOpener = jQuery('') - var sideMenu = jQuery('
') - var body = jQuery('body') - var html = jQuery('html') - var isTouchDevice = window.matchMedia("(pointer: coarse)").matches + const querySelector = function(selector, element) { + if (element) { + return element.querySelector(selector) + } + + return document.querySelector(selector) + } + + const querySelectorAll = function(selector, element) { + if (element) { + return element.querySelectorAll(selector) + } + + return document.querySelectorAll(selector) + } + + const createElement = function(tagName, attributes) { + const element = document.createElement(tagName) + + if (typeof attributes === 'object') { + for (let i in attributes) { + element.setAttribute(i, attributes[i]) + } + } + + return element + } + + const sideMenuContainer = createElement('div', {id: 'side-menu-container'}) + const sideMenuOpener = createElement('button', {'class': 'side-menu-opener'}) + const sideMenu = createElement('div', {id: 'side-menu'}) + + const body = querySelector('body') + const html = querySelector('html') + const nextcloud = querySelector('#nextcloud') + + const isTouchDevice = window.matchMedia("(pointer: coarse)").matches + const targetBlankApps = - sideMenu.attr('data-bigmenu', '1') + sideMenu.setAttribute('data-bigmenu', '1') - sideMenu.attr('data-sidewithcategories', '1') + sideMenu.setAttribute('data-sidewithcategories', '1') - var targetBlankApps = ; + querySelector('body').addEventListener('side-menu.apps', (e) => { + const apps = e.detail.apps; - body.on('side-menu.apps', function(e, apps) { - sideMenu = jQuery('#side-menu') + const sideMenu = querySelector('#side-menu') if (apps.length === 0) { - sideMenu.removeClass('open') - sideMenu.addClass('hide') - sideMenuOpener.addClass('hide') + sideMenu.classList.remove('open') + sideMenu.classList.add('hide') + sideMenuOpener.classList.add('hide') } else { - sideMenu.removeClass('hide') - sideMenuOpener.removeClass('hide') + sideMenu.classList.remove('hide') + sideMenuOpener.classList.remove('hide') } if (apps.length === 0) { - html.removeClass('side-menu-always-displayed'); + html.classList.remove('side-menu-always-displayed') } else { - html.addClass('side-menu-always-displayed'); + html.classList.add('side-menu-always-displayed') } if (apps.length === 0) { - html.removeClass('side-menu-always-displayed'); + html.classList.remove('side-menu-always-displayed') } else { - html.addClass('side-menu-always-displayed'); + html.classList.add('side-menu-always-displayed') } }) - body.on('side-menu.ready', function() { - sideMenu = jQuery('#side-menu') + body.addEventListener('side-menu.ready', () => { + const sideMenu = querySelector('#side-menu') + const headerMenuOpener = querySelector('#header .side-menu-opener') + const sideMenuOpener = querySelectorAll('#side-menu .side-menu-opener') - var headerMenuOpener = jQuery('#header .side-menu-opener') - var sideMenuOpener = jQuery('#side-menu .side-menu-opener') + sideMenuFocus = () => { + let a = querySelector('.side-menu-app.active a', sideMenu) - sideMenuFocus = function() { - var a = sideMenu.find('.side-menu-app.active a') + if (!a) { + return + } if (a.length === 0) { - a = sideMenu.find('.side-menu-app:first-child a') + a = querySelector('.side-menu-app:first-child a', sideMenu) } if (a.length > 0) { @@ -77,79 +112,82 @@ if ($_['always-displayed']) { } } - - var sideMenuMouseLeave = function() { - sideMenu - .removeClass('open') - .off('mouseleave', sideMenuMouseLeave) + + const sideMenuMouseLeave = () => { + sideMenu.classList.remove('open') + sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave) } - var sideMenuMouseEnter = function() { - sideMenu.on('mouseleave', sideMenuMouseLeave) + const sideMenuMouseEnter = () => { + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) } - var sideMenuOpenerMouseEnter = function() { - sideMenu - .addClass('open') - .on('mouseenter', sideMenuMouseEnter) + const sideMenuOpenerMouseEnter = () => { + sideMenu.classList.add('open') + sideMenu.addEventListener('mouseenter', sideMenuMouseEnter) sideMenuFocus() } if (!isTouchDevice) { - headerMenuOpener.on('mouseenter', sideMenuOpenerMouseEnter) + headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter) - sideMenu.addClass('hide-opener') + sideMenu.classList.add('hide-opener') - sideMenu.on('mouseleave', sideMenuMouseLeave) - sideMenu.on('mouseenter', sideMenuOpenerMouseEnter) + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) + sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter) } - headerMenuOpener.on('click', function() { - sideMenu.addClass('open') - sideMenu.find('.side-menu-app.active a').focus() + headerMenuOpener.addEventListener('click', () => { + sideMenu.classList.add('open') + + const a = querySelector('.side-menu-app.active a', sideMenu) + + if (a !== null) { + a.focus() + } }) - - sideMenuOpener.on('click', function() { - sideMenu.toggleClass('open') + for (let opener of sideMenuOpener) { + opener.addEventListener('click', () => { + + sideMenu.classList.toggle('open') + + sideMenu.classList.remove('open') + }) - - sideMenuOpener.on('click', function() { - sideMenu.removeClass('open') - }) - + } - jQuery(document).keydown(function(e) { + document.addEventListener('keydown', (e) => { var key = e.key || e.keyCode if ((key === 'o' || key === 79) && e.ctrlKey === true) { e.preventDefault() - sideMenu.toggleClass('open') + sideMenu.classList.toggle('open') sideMenuFocus() } }) }) - body.append(sideMenuContainer) - sideMenuContainer.append(sideMenu) + body.appendChild(sideMenuContainer) + sideMenuContainer.appendChild(sideMenu) - sideMenuOpener.insertBefore('#nextcloud') + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud) - sideMenuOpener.insertAfter('#nextcloud') + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) - var topMenuApps = ; + const topMenuApps = diff --git a/templates/settings/admin-form.php b/templates/settings/admin-form.php index 0f2e509..2aa15fb 100644 --- a/templates/settings/admin-form.php +++ b/templates/settings/admin-form.php @@ -20,6 +20,7 @@ use OCP\IURLGenerator; use OCP\IConfig; use OCA\SideMenu\AppInfo\Application; +vendor_script('side_menu', 'html5sortable.min'); script('side_menu', 'admin'); style('side_menu', 'admin'); @@ -799,7 +800,7 @@ $choicesSizes = [ 🖱️ t('Show and hide the list of categories')); ?> -