diff --git a/.gitignore b/.gitignore index 89695bf..d10e458 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /releases /package-lock.json !/l10n/.gitkeep +/yarn*.log diff --git a/.stylelintrc.json b/.stylelintrc.json index 252cc97..f364910 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,5 +1,5 @@ { "rules": { - "indentation": 4 + "indentation": 2 } } diff --git a/.woodpecker.yml b/.woodpecker.yml index 8b8609f..aee34a9 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,21 +1,21 @@ pipeline: dependencies: - image: deblan/devenv + image: gitnet.fr/deblan/devenv commands: - - npm install + - make dep when: event: [tag, push, pull_request] branch: [master, develop, feature/*] build: - image: deblan/devenv + image: gitnet.fr/deblan/devenv commands: - - make npm-build + - make build when: event: [push, pull_request] package: - image: deblan/devenv + image: gitnet.fr/deblan/devenv volumes: - /var/www/html/artifacts:/var/www/html/artifacts secrets: [app_certificate] diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a79237..bee7dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,16 @@ ## [Unreleased] +## 3.0.0 +### Added +* Add compatibility with NC25 (#136/#135) +### Removed +* Nextcloud 20-24 are not supported anymore +* AppOrder is not supported anymore + ## 2.5.1 ### Fixed * fix icon render (#133) - ## 2.5.0 ### Changed * upgrade dependencies diff --git a/Makefile b/Makefile index 1639f5c..966581f 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,15 @@ -npm-build: +build: dep npm run build -npm-watch: +watch: dep npm run watch +dep: + npm i + npm link @nextcloud/vue || sudo npm link @nextcloud/vue + .ONESHELL: -release: npm-build translations +release: build translations if [ -z "$$VERSION" ]; then echo "VERSION required" exit 1 diff --git a/README.md b/README.md index 92f05f7..421e239 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,9 @@ If you are a developer: * fork the repository * install an instance of Nextcloud * go to `apps/` and clone your repository -* go to `apps/side_menu` and run `npm install` +* go to `apps/side_menu` and run `make dep` -Build javascripts using `make npm-build` (or `make npm-watch` to build them in real time). +Build javascripts using `make build` (or `make watch` to build them in real time). Then commit and create a pull request. diff --git a/appinfo/info.xml b/appinfo/info.xml index f4c1969..103f98a 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -11,7 +11,7 @@ This application is rather suitable for instances that activate a lot of applica Use the shortcut `Ctrl`+`o` to open and to hide the side menu. Use `tab` to navigate. -You can customize colors depending of the theme (Dark theme and Breeze Dark). Comptatible with AppOrder. +You can customize colors depending of the theme (Dark theme and Breeze Dark). You can report a bug or request a feature by opening an issue. @@ -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) ]]> - 2.5.1 + 3.0.0 agpl Simon Vieille SideMenu diff --git a/css/admin.css b/css/admin.css index b35455a..f32892e 100644 --- a/css/admin.css +++ b/css/admin.css @@ -16,131 +16,130 @@ */ #side-menu-section input[type="color"] { - width: 100px; - margin: 10px 0 10px 0; + width: 100px; + margin: 10px 0 10px 0; } #-dropside-menu-section input[type="checkbox"] { - vertical-align: middle; + vertical-align: middle; } #side-menu-section input[type="range"] { - vertical-align: middle; + vertical-align: middle; } #side-menu-section select { - margin: 10px 0 10px 0; + margin: 10px 0 10px 0; } .keyboard-key { - padding: 1px 9px; - margin: 0 2px; - background: #eee; - border: 1px solid #aaa; - color: #555; - border-radius: 3px; + padding: 1px 9px; + margin: 0 2px; + background: #eee; + border: 1px solid #aaa; + color: #555; + border-radius: 3px; } .side-menu-display { - padding: 10px; - border: 2px solid transparent; - max-width: 100%; - cursor: pointer; + padding: 10px; + border: 2px solid transparent; + max-width: 100%; + cursor: pointer; } .side-menu-display.is-active { - border: 2px solid #91cb7f; + border: 2px solid #91cb7f; } .info { - margin-top: 8px; - padding: 5px; - background: #91cb7f; - color: #fff; - border-radius: var(--border-radius); + margin-top: 8px; + padding: 5px; + background: #91cb7f; + color: #fff; + border-radius: var(--border-radius); } #side-menu-section h2 small { - font-size: 11px; - font-weight: normal; + font-size: 11px; + font-weight: normal; } .side-menu-toggler { - cursor: pointer; + cursor: pointer; } .side-menu-setting-list { - margin: 10px 4px 4px 0px; + margin: 10px 4px 4px 0px; } .side-menu-setting-list-item { - padding: 5px 10px; - border: 1px solid var(--color-border-dark); - max-width: 300px; - margin: -1px 0 0 0; - cursor: pointer; + padding: 5px 10px; + border: 1px solid var(--color-border-dark); + max-width: 300px; + margin: -1px 0 0 0; + cursor: pointer; } .side-menu-setting-list-drop { - background: yellow; - border-color: yellow; - height: 34px; + background: yellow; + border-color: yellow; + height: 34px; } .side-menu-setting.arrow { - color: #ccc; - padding-right: 5px; + color: #ccc; + padding-right: 5px; } .side-menu-setting-list-item input { - min-height: auto; - margin-top: -1px; + min-height: auto; + margin-top: -1px; } #apps-categories-custom-list select { - width: 100%; + width: 100%; } - .side-menu-setting-table { - display: table; - width: 100%; + display: table; + width: 100%; } .side-menu-setting-row { - display: table; + display: table; } .side-menu-setting-label { - display: table-cell; - width: 400px; - padding-right: 20px; + display: table-cell; + width: 400px; + padding-right: 20px; } .side-menu-setting-form { - display: table-cell; - min-width: 300px; + display: table-cell; + min-width: 300px; } .side-menu-setting-label-short { - width: 300px; + width: 300px; } .side-menu-setting-form-long { - width: 400px; + width: 400px; } #side-menu-save-progress { - display: inline-block; - width: 0; - height: 15px; - background: #fff; + display: inline-block; + width: 0; + height: 15px; + background: #fff; } .btn-reset { - display: inline-block; - cursor: pointer; - position: absolute; - margin-top: 17px; - margin-left: 5px; + display: inline-block; + cursor: pointer; + position: relative; + top: -8px; + left: 5px; } diff --git a/css/sideMenu.css b/css/sideMenu.css index 80ed6b7..a99de63 100644 --- a/css/sideMenu.css +++ b/css/sideMenu.css @@ -16,326 +16,325 @@ */ #side-menu { - position: fixed; - top: 0; - left: 0; - height: 100vh; - width: 100%; - max-width: 250px; - background: linear-gradient(90deg, var(--side-menu-background-color, #333) 0%, var(--side-menu-background-color-to, #333) 100%); - z-index: 3000; - color: var(--side-menu-text-color, #fff); - box-shadow: rgba(0, 0, 0, 0.22) 0px 25.6px 57.6px 0px, rgba(0, 0, 0, 0.18) 0px 4.8px 14.4px 0px; - display: none; + position: fixed; + top: 0; + left: 0; + height: 100vh; + width: 100%; + max-width: 290px; + background: linear-gradient(90deg, var(--side-menu-background-color, #333) 0%, var(--side-menu-background-color-to, #333) 100%); + z-index: 3000; + color: var(--side-menu-text-color, #fff); + box-shadow: rgba(0, 0, 0, 0.22) 0px 25.6px 57.6px 0px, rgba(0, 0, 0, 0.18) 0px 4.8px 14.4px 0px; + display: none; } #side-menu a { - transition: 0.2s; + transition: 0.2s; } #side-menu.open { - display: block; + display: block; } #header .side-menu-opener { - margin-left: 0px; + margin-left: 0px; } .side-menu-settings { - margin-right: 9px; - margin-top: 2px; - float: right; - line-height: 34px; - height: 28px; - display: none; + margin-right: 9px; + margin-top: 2px; + float: right; + line-height: 34px; + height: 28px; + display: none; } .side-menu-settings a { - color: var(--side-menu-text-color, #fff); - display: block; - padding: 4px 7px; + color: var(--side-menu-text-color, #fff); + display: block; + padding: 4px 7px; } .side-menu-settings:hover a, .side-menu-settings a:active, .side-menu-settings a:focus { - background: var(--side-menu-current-app-background-color, #444); + background: var(--side-menu-current-app-background-color, #444); } .side-menu-settings img { - vertical-align: bottom; - margin-left: 3px; - width: 32px; - height: 32px; + vertical-align: bottom; + margin-left: 3px; + width: 32px; + height: 32px; } #side-menu.open .side-menu-settings { - display: block; + display: block; } .side-menu-opener { - background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); - 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; + background: var(--side-menu-opener, url('../img/side-menu-opener.svg')); + 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, .side-menu-opener:focus { - background-color: var(--side-menu-current-app-background-color, #444) !important; + background-color: var(--side-menu-current-app-background-color, #444) !important; } .side-menu-closer { - background: url('../img/side-menu-opener-closer.svg'); - display: none; + background: url('../img/side-menu-opener-closer.svg'); + display: none; } #side-menu.hide-opener .side-menu-opener, .side-menu-opener.hide, #side-menu.hide { - display: none !important; + display: none !important; } .side-menu-apps-list { - height: calc(100vh - 150px); - z-index: 2200; - position: fixed; - top: 150px; - width: 100%; - max-width: 250px; - overflow: auto; + height: calc(100vh - 150px); + z-index: 2200; + position: fixed; + top: 150px; + width: 100%; + max-width: 290px; + overflow: auto; } .side-menu-app-icon { - width: 20px; - vertical-align: top; - margin-right: 10px; - filter: invert(var(--side-menu-icon-invert-filter, 0%)); - opacity: var(--side-menu-icon-opacity, 1); -} - -.side-menu-app-icon svg { - vertical-align: middle; - margin-top: -3px; -} - -.side-menu-app-icon .app-icon-notification { - display: none; + width: 20px; + vertical-align: middle; + margin-top: -4px; + margin-right: 10px; + filter: invert(var(--side-menu-icon-invert-filter, 0%)); + opacity: var(--side-menu-icon-opacity, 1); } .side-menu-app a { - line-height: 30px; - color: var(--side-menu-text-color, #fff); - display: block; - padding: 7px 0 5px 15px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + line-height: 30px; + color: var(--side-menu-text-color, #fff); + display: block; + padding: 7px 0 5px 15px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .side-menu-app a:hover, .side-menu-app.active, .side-menu-app a:focus { - background: var(--side-menu-current-app-background-color, #444); + background: var(--side-menu-current-app-background-color, #444); } .side-menu-logo { - text-align: center; + text-align: center; } .side-menu-logo img { - max-width: 60%; - max-height: 100px; + max-width: 60%; + max-height: 100px; } -.side-menu-header { - height: 150px; - width: 100%; - z-index: 2300; - max-width: 250px; - position: fixed; - padding-top: 2px; - top: 0; +.enu-header { + height: 150px; + width: 100%; + z-index: 2300; + max-width: 290px; + position: fixed; + padding-top: 2px; + top: 0; } #side-menu.side-menu-with-categories .side-menu-header { - max-width: 295px; + max-width: 295px; } #side-menu.hide-opener .side-menu-logo { - margin-top: 20px; + margin-top: 20px; } #side-menu-loader { - position: fixed; - top: 0; - left: 0; - width: 100%; - z-index: 3001; + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 3001; } #side-menu-loader-bar { - height: 4px; - background: var(--side-menu-loader-color, #0e75ac); - width: 0; - transition-property: width; + height: 4px; + background: var(--side-menu-loader-color, #0e75ac); + width: 0; + transition-property: width; } #side-menu.side-menu-big, #side-menu.side-menu-with-categories { - max-width: 100%; - height: auto; + max-width: 100%; + height: auto; } .side-menu-big .side-menu-header, .side-menu-with-categories .side-menu-header { - height: auto; + height: auto; } .side-menu-big .side-menu-apps-list, .side-menu-with-categories .side-menu-apps-list { - height: auto; - position: static; - max-width: 100vw; - overflow: auto; + 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; + padding: 7px 0 7px 7px; } .side-menu-categories-wrapper { - padding-bottom: 70px; + padding-bottom: 70px; } .side-menu-categories { - max-height: calc(100vh - 50px); - overflow: auto; - position: relative; - top: 50px; - display: flex; - flex-wrap: wrap; - justify-content: center; - padding: 0 10% 0 10%; + max-height: calc(100vh - 50px); + overflow: auto; + position: relative; + top: 50px; + display: flex; + flex-wrap: wrap; + justify-content: center; + padding: 0 10% 0 10%; } .side-menu-category { - padding: 10px 20px; - flex: 1 1 auto; + padding: 10px 20px; + flex: 1 1 auto; } .side-menu-category-title { - padding-left: 10px; - color: var(--side-menu-text-color, #fff); + padding-left: 10px; + color: var(--side-menu-text-color, #fff); } .side-menu-loader { - text-align: center; + text-align: center; } .side-menu-loader svg { - width: 38px; - margin: auto; - stroke: var(--side-menu-text-color, #fff); + width: 38px; + margin: auto; + 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 #header, -.side-menu-always-displayed body { - width: calc(100% - 50px) !important; + vertical-align: middle; + margin-top: -2px; } .side-menu-always-displayed body { - position: absolute; - left: 50px; + width: calc(100% - 50px) !important; +} + +.side-menu-always-displayed body { + position: absolute; + left: 50px; } .side-menu-always-displayed #side-menu { - display: block; + display: block; } .side-menu-always-displayed .side-menu-apps-list { - height: calc(100vh - 49px); - top: 49px; - overflow: hidden; + height: calc(100vh - 49px); + top: 49px; + overflow: hidden; } .side-menu-always-displayed .side-menu-apps-list:hover { - overflow: auto; + overflow: auto; } .side-menu-always-displayed #side-menu, .side-menu-always-displayed .side-menu-header, .side-menu-always-displayed .side-menu-apps-list { - width: 50px; + width: 50px; } .side-menu-always-displayed #side-menu .side-menu-app-text, .side-menu-always-displayed #header .side-menu-opener, .side-menu-always-displayed .side-menu-logo { - display: none; + display: none; } .side-menu-always-displayed #side-menu .side-menu-header { - height: 49px; + height: 49px; } .side-menu-always-displayed #side-menu.open, .side-menu-always-displayed #side-menu.open .side-menu-apps-list, .side-menu-always-displayed #side-menu.open .side-menu-header { - width: 100%; + width: 100%; } .side-menu-always-displayed #side-menu.open .side-menu-app-text { - display: inline; + display: inline; } .side-menu-always-displayed .app-navigation--close { - transform: translateX(calc(-100% + 50px)) !important; + transform: translateX(calc(-100% + 50px)) !important; } #side-menu.side-menu-with-categories { - max-width: 290px; - height: 100vh; + max-width: 290px; + height: 100vh; } .side-menu-with-categories .side-menu-categories { - display: block; - padding: 0; + display: block; + padding: 0; } .side-menu-with-categories .side-menu-category { - padding: 10px 0; + padding: 10px 0; } .side-menu-always-displayed #body-settings, #body-settings.body-settings-side-menu { - overflow-x: visible; + overflow-x: visible; +} + +.app-menu { + visibility: hidden; +} + +.app-menu.show { + visibility: visible; } @media screen and (max-width: 1024px) { - #side-menu.side-menu-big { - max-width: 290px; - height: 100vh; - } + #side-menu.side-menu-big { + max-width: 290px; + height: 100vh; + } - .side-menu-categories { - display: block; - padding: 0; - } + .side-menu-categories { + display: block; + padding: 0; + } - .side-menu-category { - padding: 10px 0; - } + .side-menu-category { + padding: 10px 0; + } } @media screen and (min-width: 1024px) { - .side-menu-closer { - display: block; - float: right; - margin-right: 9px; - } + .side-menu-closer { + display: block; + float: right; + margin-right: 9px; + } - .side-menu-big .side-menu-header { - max-width: 100%; - } + .side-menu-big .side-menu-header { + max-width: 100%; + } } diff --git a/package.json b/package.json index fb7e755..503c7d1 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,15 @@ "license": "agpl", "private": true, "scripts": { - "build": "NODE_ENV=production webpack --progress --config webpack.js", - "dev": "NODE_ENV=development webpack --progress --config webpack.js", - "watch": "NODE_ENV=development webpack --progress --watch --config webpack.js", - "lint": "eslint --ext .js,.vue src", - "lint:fix": "eslint --ext .js,.vue src --fix", - "stylelint": "stylelint src", - "stylelint:fix": "stylelint src --fix" + "build": "NODE_ENV=production ./node_modules/.bin/webpack-cli --progress --config webpack.js", + "dev": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --config webpack.js", + "watch": "NODE_ENV=development ./node_modules/.bin/webpack-cli --progress --watch --config webpack.js", + "lint": "./node_modules/.bin/eslint --ext .js,.vue src", + "lint:fix": "./node_modules/.bin/eslint --ext .js,.vue src --fix", + "stylelint": "./node_modules/.bin/stylelint src", + "stylelint:fix": "./node_modules/.bin/stylelint src --fix" }, "dependencies": { - "@nextcloud/axios": "^1.8.0", - "@nextcloud/vue": "^1.5.0", "axios": "^0.24.0", "trim": "^1.0.1", "vue": "^2.6.11" @@ -27,8 +25,12 @@ "@babel/core": "^7.9.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.9.0", + "@nextcloud/axios": "^1.8.0", "@nextcloud/browserslist-config": "^1.0.0", "@nextcloud/eslint-config": "^8.1.2", + "@nextcloud/initial-state": "^2.0.0", + "@nextcloud/l10n": "^1.6.0", + "@nextcloud/vue": "^7.0.0", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "css-loader": "^3.4.2", @@ -50,8 +52,9 @@ "stylelint-scss": "^4.0.0", "stylelint-webpack-plugin": "^3.3.0", "url-loader": "^4.0.0", - "vue-loader": "^15.9.1", - "vue-template-compiler": "^2.6.11", + "vue-loader": "^15", + "vue-style-loader": "^4.1.3", + "vue-template-compiler": "^2.7.13", "webpack": "^5.0.0", "webpack-cli": "^4.0.0", "webpack-merge": "^4.2.2", diff --git a/src/AdminCategoriesCustom.vue b/src/AdminCategoriesCustom.vue index ca5272a..b02283d 100644 --- a/src/AdminCategoriesCustom.vue +++ b/src/AdminCategoriesCustom.vue @@ -15,168 +15,168 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/AppMenu.vue b/src/AppMenu.vue new file mode 100644 index 0000000..17db6ad --- /dev/null +++ b/src/AppMenu.vue @@ -0,0 +1,296 @@ + + + + + + + + diff --git a/src/CloserButton.vue b/src/CloserButton.vue index f14e39b..99779f8 100644 --- a/src/CloserButton.vue +++ b/src/CloserButton.vue @@ -15,11 +15,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/Loader.vue b/src/Loader.vue index 7dbdb1d..d059f68 100644 --- a/src/Loader.vue +++ b/src/Loader.vue @@ -15,28 +15,28 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/Logo.vue b/src/Logo.vue index 0e76b5b..1aec19f 100644 --- a/src/Logo.vue +++ b/src/Logo.vue @@ -15,30 +15,30 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/OpenerButton.vue b/src/OpenerButton.vue index 7a134e8..32813e9 100644 --- a/src/OpenerButton.vue +++ b/src/OpenerButton.vue @@ -15,11 +15,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/PageLoader.js b/src/PageLoader.js new file mode 100644 index 0000000..3c8bd13 --- /dev/null +++ b/src/PageLoader.js @@ -0,0 +1,20 @@ +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 + .2, 100) + }, 25) + }) +} + +module.exports = PageLoader diff --git a/src/SettingsButton.vue b/src/SettingsButton.vue index 5f18927..38aaefc 100644 --- a/src/SettingsButton.vue +++ b/src/SettingsButton.vue @@ -15,33 +15,33 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/SideMenu.js b/src/SideMenu.js index a9848c4..e7b3675 100644 --- a/src/SideMenu.js +++ b/src/SideMenu.js @@ -16,35 +16,56 @@ */ import Vue from 'vue' +import AppMenu from './AppMenu.vue' import SideMenu from './SideMenu.vue' import SideMenuBig from './SideMenuBig.vue' import SideMenuWithCategories from './SideMenuWithCategories.vue' +import PageLoader from './PageLoader' +import SMcreateElement from './lib/createElement' Vue.prototype.OC = OC +Vue.prototype.t = OC.L10N.translate + +window.SMcreateElement = SMcreateElement +window.PageLoader = PageLoader const mountSideMenuComponent = () => { - const sideMenuContainer = document.querySelector('#side-menu') + const container = document.querySelector('#side-menu') - if (sideMenuContainer) { - let component + if (!container) { + return window.setTimeout(mountSideMenuComponent, 50) + } - if (sideMenuContainer.getAttribute('data-bigmenu')) { - component = SideMenuBig - } else if(sideMenuContainer.getAttribute('data-sidewithcategories')) { - component = SideMenuWithCategories - } else { - component = SideMenu - } - - const View = Vue.extend(component) - const sideMenu = new View({}) - - sideMenu.$mount('#side-menu') - - document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready')) + const component = (() => { + if (container.getAttribute('data-bigmenu')) { + return SideMenuBig + } else if(container.getAttribute('data-sidewithcategories')) { + return SideMenuWithCategories } else { - window.setTimeout(mountSideMenuComponent, 50) + return SideMenu } + })() + + const View = Vue.extend(component) + const App = new View({}) + + App.$mount('#side-menu') + + document.querySelector('body').dispatchEvent(new CustomEvent('side-menu.ready')) +} + +const mountAppMenu = () => { + const container = document.querySelector('#header .app-menu') + + if (!container) { + return window.setTimeout(mountAppMenu, 50) + } + + const View = Vue.extend(AppMenu) + const App = new View({}) + + App.$mount('#header .app-menu') } mountSideMenuComponent() +mountAppMenu() diff --git a/src/SideMenu.vue b/src/SideMenu.vue index b369f87..65707ef 100644 --- a/src/SideMenu.vue +++ b/src/SideMenu.vue @@ -15,40 +15,41 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/SideMenuApp.vue b/src/SideMenuApp.vue index 48e2a60..c910dce 100644 --- a/src/SideMenuApp.vue +++ b/src/SideMenuApp.vue @@ -15,38 +15,38 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/SideMenuBig.vue b/src/SideMenuBig.vue index d1f0b1f..cdf279d 100644 --- a/src/SideMenuBig.vue +++ b/src/SideMenuBig.vue @@ -15,41 +15,42 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/SideMenuBigApp.vue b/src/SideMenuBigApp.vue index bab78e6..a0fcdde 100644 --- a/src/SideMenuBigApp.vue +++ b/src/SideMenuBigApp.vue @@ -15,38 +15,38 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/SideMenuWithCategories.vue b/src/SideMenuWithCategories.vue index e0a3c41..e4cad94 100644 --- a/src/SideMenuWithCategories.vue +++ b/src/SideMenuWithCategories.vue @@ -15,39 +15,40 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . --> diff --git a/src/admin.js b/src/admin.js index 9231c9a..7671c56 100644 --- a/src/admin.js +++ b/src/admin.js @@ -26,235 +26,235 @@ let elements = [] const selector = '#side-menu-message' const userConfig = (name, value, callbacks) => { - const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet') - const formData = [] + const url = OC.generateUrl('/apps/side_menu/personalSetting/valueSet') + const formData = [] - formData.push('name=' + encodeURIComponent(name)) - formData.push('value=' + encodeURIComponent(value)) + 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) + 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) + OCP.AppConfig.setValue('side_menu', name, value, callbacks) } const saveSettings = (key) => { - const element = elements[key] + const element = elements[key] - if (!element) { - return + 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) } - let value - let name + value = JSON.stringify(value) + } else { + name = element.getAttribute('name') + value = element.value + } - if (element.hasAttribute('data-checkbox')) { - name = element.getAttribute('data-name') - value = [] + const size = elements.length - const inputs = document.querySelectorAll('input[name="' + name + '[]"]:checked') + if (name === 'cache') { + ++value + } - for (let input of inputs) { - value.push(input.value) - } + const progress = document.querySelector('#side-menu-save-progress') - value = JSON.stringify(value) - } else { - name = element.getAttribute('name') - value = element.value + 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 + '"')) } + } - 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) - } + if (element.hasAttribute('data-personal')) { + userConfig(name, value, callbacks) + } else { + appConfig(name, value, callbacks) + } } const elementToggler = (element) => { - let display = 'none' + let display = 'none' - if (window.getComputedStyle(element).display === 'none') { - display = 'block' - } + if (window.getComputedStyle(element).display === 'none') { + display = 'block' + } - element.style.display = display + element.style.display = display } const updateAppsCategoriesCustom = () => { - let values = {} + let values = {} - for (let item of document.querySelectorAll('.apps-categories-custom')) { - let app = item.getAttribute('data-app') - let value = item.value + for (let item of document.querySelectorAll('.apps-categories-custom')) { + let app = item.getAttribute('data-app') + let value = item.value - if (value) { - values[app] = value - } + if (value) { + values[app] = value } + } - document.querySelector('#apps-categories-custom').value = JSON.stringify(values) + document.querySelector('#apps-categories-custom').value = JSON.stringify(values) } document.addEventListener('DOMContentLoaded', () => { - $('*[data-toggle="tooltip"]').tooltip(); + $('*[data-toggle="tooltip"]').tooltip(); - if (document.querySelector('#side-menu-categories-custom')) { - const View = Vue.extend(AdminCategoriesCustom) - const adminCategoriesCustom = new View({}) + if (document.querySelector('#side-menu-categories-custom')) { + const View = Vue.extend(AdminCategoriesCustom) + const adminCategoriesCustom = new View({}) - adminCategoriesCustom.$mount('#side-menu-categories-custom') - } + adminCategoriesCustom.$mount('#side-menu-categories-custom') + } - elements = document.querySelectorAll('.side-menu-setting') + elements = document.querySelectorAll('.side-menu-setting') - document.querySelector('#side-menu-save').addEventListener('click', (event) => { - event.preventDefault() - OC.msg.startSaving(selector) + document.querySelector('#side-menu-save').addEventListener('click', (event) => { + event.preventDefault() + OC.msg.startSaving(selector) - saveSettings(0) + 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')) + + for (let i in values) { + document.querySelector(`#${i}`).value = values[i] + } }) + } - const resets = document.querySelectorAll('.btn-reset') + const displays = document.querySelectorAll('.side-menu-display') - for (let btn of resets) { - btn.addEventListener('click', (event) => { - const target = event.target - const values = JSON.parse(target.getAttribute('data-reset')) + for (let display of displays) { + display.addEventListener('click', (event) => { + const target = event.target - for (let i in values) { - document.querySelector(`#${i}`).value = values[i] - } - }) - } + for (let d of displays) { + d.classList.toggle('is-active', d === display) + } - 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' + 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') }) + } - try { - sortable('#categories-list .side-menu-setting-list')[0].addEventListener('sortstop', (e) => { - let value = [] + for (let item of document.querySelectorAll('.apps-categories-custom')) { + item.addEventListener('change', (event) => { + updateAppsCategoriesCustom() + }) + } - for (let item of document.querySelectorAll('#categories-list .side-menu-setting-list-item')) { - console.log(item.getAttribute('data-id')) - value.push(item.getAttribute('data-id')) - } + for (let item of document.querySelectorAll('.side-menu-setting-live')) { + item.addEventListener('change', (event) => { + const target = event.target + const name = target.getAttribute('name') - document.querySelector('input[name="categories-order"]').value = JSON.stringify(value) - }) - } catch (e) { - } + 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')) { + console.log(item.getAttribute('data-id')) + value.push(item.getAttribute('data-id')) + } + + document.querySelector('input[name="categories-order"]').value = JSON.stringify(value) + }) + } catch (e) { + } }) diff --git a/src/lib/createElement.js b/src/lib/createElement.js new file mode 100644 index 0000000..19c8207 --- /dev/null +++ b/src/lib/createElement.js @@ -0,0 +1,11 @@ +module.exports = (tagName, attributes) => { + const element = document.createElement(tagName) + + if (typeof attributes === 'object') { + for (let i in attributes) { + element.setAttribute(i, attributes[i]) + } + } + + return element +} diff --git a/templates/css/stylesheet.php b/templates/css/stylesheet.php index 379fb42..6582131 100644 --- a/templates/css/stylesheet.php +++ b/templates/css/stylesheet.php @@ -1,116 +1,128 @@ :root { - $value): ?> - - --side-menu-: url(''); - - --side-menu-: ; - - + $value): ?> + + --side-menu-: url(''); + + --side-menu-: ; + + } - #appmenu { - display: none; - } + #appmenu { + display: none; + } - #appmenu + nav { - display: none; - } + #appmenu + nav { + display: none; + } - .app-hidden { - opacity: 0; - } + .app-hidden { + opacity: 0; + } - #nextcloud { - display: none; - } + #nextcloud { + display: none; + } - .side-menu-logo { - display: none; + .side-menu-logo { + display: none; + } + + .side-menu-header { + height: 50px; + } + + .side-menu-apps-list { + height: calc(100vh - 49px); + top: 49px; + } + + #side-menu.hide-opener .side-menu-header { + visibility: hidden; + } + + + #side-menu, .side-menu-apps-list { + + width: 55px; + + width: 52px; + } - .side-menu-header { - height: 50px; + #side-menu .side-menu-opener { + + margin-left: 1px; + + margin-left: 0px; + } - - .side-menu-apps-list { - height: calc(100vh - 49px); - top: 49px; - } - - #side-menu.hide-opener .side-menu-header { - visibility: hidden; - } - - - #side-menu, .side-menu-apps-list { - - width: 55px; - - width: 52px; - - } - - #side-menu .side-menu-opener { - - margin-left: 1px; - - margin-left: 0px; - - } - + - .side-menu-app-icon { - display: none; - } + .side-menu-app-icon { + display: none; + } - .side-menu-app-icon svg { - width: 15px; - height: 15px; - } + .side-menu-app-icon svg { + width: 15px; + height: 15px; + } - img.side-menu-app-icon { - width: 15px; - height: 15px; - } + img.side-menu-app-icon { + width: 15px; + height: 15px; + } - .side-menu-app-icon svg { - width: 20px; - height: 20px; - } + .side-menu-app-icon svg { + width: 20px; + height: 20px; + } - img.side-menu-app-icon { - width: 20px; - height: 20px; - } + img.side-menu-app-icon { + width: 20px; + height: 20px; + } - .side-menu-app-icon svg { - width: 23px; - height: 23px; - } + .side-menu-app-icon svg { + width: 23px; + height: 23px; + } - img.side-menu-app-icon { - width: 23px; - height: 23px; - } + img.side-menu-app-icon { + width: 23px; + height: 23px; + } - .side-menu-app-text { - display: none; - } + .side-menu-app-text { + display: none; + } - .side-menu-app-text { - font-size: 12px; - } + .side-menu-app-text { + font-size: 12px; + } - .side-menu-app-text { - font-size: 16px; - } + .side-menu-app-text { + font-size: 16px; + } + + + + #content { + left: 53px; + width: calc(100% - (var(--body-container-margin) * 2) - 62px); + } + + #content-vue { + width: calc(100% - (var(--body-container-margin) * 2) - 60px); + margin-left: 11px; + } diff --git a/templates/js/_alwaysDisplayed.js b/templates/js/_alwaysDisplayed.js deleted file mode 100644 index 9e3727f..0000000 --- a/templates/js/_alwaysDisplayed.js +++ /dev/null @@ -1,86 +0,0 @@ -const alwaysDisplayed = function() { - const elements = querySelectorAll('*') - const fixedElements = [] - - for (let element of elements) { - if (typeof element !== 'object') { - continue - } - - const position = window.getComputedStyle(element, null).getPropertyValue('position') - - if (position !== 'fixed') { - continue - } - - const id = element.getAttribute('id') - - if (id === 'header' || id === 'side-menu' || id === 'side-menu-loader') { - continue - } - - if (element.classList.contains('oc-dialog')) { - continue - } - - 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 - } - - fixedElements.push(element) - } - - for (let i in fixedElements) { - const element = fixedElements[i] - const computedStyle = window.getComputedStyle(element, null) - const left = computedStyle.getPropertyValue('left') - const right = computedStyle.getPropertyValue('right') - - if (right !== '0px') { - const intValue = parseInt(left.replace('px', '')) + 50 - element.style.setProperty('transform', 'translateX(' + intValue.toString() + 'px)') - } - } -} - -const content = querySelector('#content') - -if (content && content.classList.contains('app-settings')) { - let loaded = false - const config = { - attributes: false, - childList: true, - subtree: true - } - const observer = new MutationObserver(() => { - if (loaded) { - return - } - - const element = content.querySelector('#app-category-your-apps') || content.querySelector('#app-navigation ul') - - if (element) { - loaded = true - - alwaysDisplayed() - } - }) - - observer.observe(content, config) -} else { - window.setTimeout(alwaysDisplayed, 200) -} diff --git a/templates/js/_loaderEnabled.js b/templates/js/_loaderEnabled.js deleted file mode 100644 index 1d5ff4e..0000000 --- a/templates/js/_loaderEnabled.js +++ /dev/null @@ -1,14 +0,0 @@ -let pageLoader = createElement('div', {id: 'side-menu-loader'}) -let pageLoaderBar = createElement('div', {id: 'side-menu-loader-bar'}) - -pageLoader.appendChild(pageLoaderBar) -querySelector('body').appendChild(pageLoader) - -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 deleted file mode 100644 index 455b09a..0000000 --- a/templates/js/_topMenuApps.js +++ /dev/null @@ -1,216 +0,0 @@ -let menuCache = null - -const breakpointMobileWidth = 1024 -const usePercentualAppMenuLimit = 0.8 -const minAppsDesktop = 8 - -const handleMenuClick = (e, icon) => { - let element = e.target - - 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 - } - - let navigationAppsHtml = '' - - for (let app of apps) { - const dataId = app.getAttribute('data-id') - - if (dataId === null) { - continue - } - - if (topMenuApps.indexOf(dataId) === -1 && topSideMenuApps.indexOf(dataId) === -1) { - app.classList.add('hidden') - app.classList.add('app-hidden') - } else { - app.classList.remove('hidden') - app.classList.add('app-external-site') - - if (topSideMenuApps.indexOf(dataId) !== -1) { - app.classList.add('app-top-side-menu') - } - - appShown.push(app) - - navigationAppsHtml = navigationAppsHtml + app.outerHTML - } - - if (targetBlankApps.indexOf(dataId) !== -1) { - querySelector('a', app).setAttribute('target', '_blank') - } - } - - 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 - } else if (!isMobile && appCount < minAppsDesktop) { - appCount = minAppsDesktop - } - - menu.style.opacity = 1 - - if (appShown.length - 1 - appCount >= 1) { - appCount-- - } - - for (let item of querySelectorAll('a', moreApps)) { - item.classList.remove('active') - } - - let k = 0 - let notInHeader = 0 - - 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.classList.remove('hidden') - li.classList.add('in-header') - - lastShownApp = app - } else { - app.classList.add('hidden') - li.classList.remove('in-header') - - notInHeader++ - - const a = querySelector('a', app) - - if (appCount > 0 && a.classList.contains('active')) { - lastShownApp.classList.add('hidden') - app.classList.remove('hidden') - notInHeader++ - - li.classList.add('in-header') - } - } - - k++ - } - - // 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') - - 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) - }) - } - } - - for (let app of querySelectorAll('#apps li.app-external-site')) { - const appId = app.getAttribute('data-id') - - if (app.classList.contains('in-header')) { - for (let defs of querySelectorAll('svg defs', app)) { - defs.remove() - } - } else { - const svg = querySelector('svg', app) - - if (querySelectorAll('svg defs', app).length > 0) { - continue - } - - const defs = ` - - - - - ` - - svg.innerHTML = defs + svg.innerHTML - - for (let image of querySelectorAll('image', svg)) { - image.setAttribute('filter', `url(#invertMenuMore-${appId})`) - } - - svg.innerHTML = svg.innerHTML.replace(/fecolormatrix/g, 'feColorMatrix') - } - } - - if (notInHeader === 0) { - moreApps.style.display = 'none' - navigation.style.display = 'none' - } else { - moreApps.style.display = 'flex' - } - - menuCache = menu.innerHTML + menu.nextSibling.innerHTML -} - -for (let i = 0; i < 4000; i+= 100) { - setTimeout(updateTopMenu, i) -} - -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 f62b7da..b942533 100644 --- a/templates/js/script.php +++ b/templates/js/script.php @@ -13,204 +13,168 @@ if ($_['always-displayed']) { ?> (function() { - const querySelector = function(selector, element) { - if (element) { - return element.querySelector(selector) + const sideMenuContainer = SMcreateElement('div', {id: 'side-menu-container'}) + const sideMenuOpener = SMcreateElement('button', {'class': 'side-menu-opener'}) + const sideMenu = SMcreateElement('div', {id: 'side-menu'}) + + const body = document.querySelector('body') + const html = document.querySelector('html') + const nextcloud = document.querySelector('#nextcloud') + + const isTouchDevice = window.matchMedia("(pointer: coarse)").matches + const targetBlankApps = + + window.topMenuApps = + window.topSideMenuApps = + + + sideMenu.setAttribute('data-bigmenu', '1') + + sideMenu.setAttribute('data-sidewithcategories', '1') + + + document.querySelector('body').addEventListener('side-menu.apps', (e) => { + const apps = e.detail.apps; + + + const sideMenu = document.querySelector('#side-menu') + + if (apps.length === 0) { + sideMenu.classList.remove('open') + sideMenu.classList.add('hide') + sideMenuOpener.classList.add('hide') + } else { + sideMenu.classList.remove('hide') + sideMenuOpener.classList.remove('hide') + } + + + if (apps.length === 0) { + html.classList.remove('side-menu-always-displayed') + } else { + html.classList.add('side-menu-always-displayed') } - - 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.setAttribute('data-bigmenu', '1') - - sideMenu.setAttribute('data-sidewithcategories', '1') - - - querySelector('body').addEventListener('side-menu.apps', (e) => { - const apps = e.detail.apps; - - - const sideMenu = querySelector('#side-menu') - - if (apps.length === 0) { - sideMenu.classList.remove('open') - sideMenu.classList.add('hide') - sideMenuOpener.classList.add('hide') - } else { - sideMenu.classList.remove('hide') - sideMenuOpener.classList.remove('hide') - } - - - if (apps.length === 0) { - html.classList.remove('side-menu-always-displayed') - } else { - html.classList.add('side-menu-always-displayed') - } - - - - if (apps.length === 0) { - html.classList.remove('side-menu-always-displayed') - } else { - html.classList.add('side-menu-always-displayed') - } - - - }) - - 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') - - sideMenuFocus = () => { - let a = querySelector('.side-menu-app.active a', sideMenu) - - if (!a) { - return - } - - if (a.length === 0) { - a = querySelector('.side-menu-app:first-child a', sideMenu) - } - - if (a.length > 0) { - a.focus() - } - } - - - const sideMenuMouseLeave = () => { - sideMenu.classList.remove('open') - sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave) - } - - const sideMenuMouseEnter = () => { - sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) - } - - const sideMenuOpenerMouseEnter = () => { - sideMenu.classList.add('open') - sideMenu.addEventListener('mouseenter', sideMenuMouseEnter) - - sideMenuFocus() - } - - if (!isTouchDevice) { - - headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter) - - sideMenu.classList.add('hide-opener') - - - sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) - sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter) - } - - - headerMenuOpener.addEventListener('click', () => { - sideMenu.classList.add('open') - - const a = querySelector('.side-menu-app.active a', sideMenu) - - if (a !== null) { - a.focus() - } - - headerMenuOpener.blur() - }) - - for (let opener of sideMenuOpener) { - opener.addEventListener('click', () => { - - sideMenu.classList.toggle('open') - - sideMenu.classList.remove('open') - - }) - } - - document.addEventListener('keydown', (e) => { - var key = e.key || e.keyCode - - if ((key === 'o' || key === 79) && e.ctrlKey === true) { - e.preventDefault() - - sideMenu.classList.toggle('open') - sideMenuFocus() - } - }) - - 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 - }) - }) - - body.appendChild(sideMenuContainer) - sideMenuContainer.appendChild(sideMenu) - - - - - - - nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud) + - nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) + + if (apps.length === 0) { + html.classList.remove('side-menu-always-displayed') + } else { + html.classList.add('side-menu-always-displayed') + } + + + }) + + body.addEventListener('side-menu.ready', () => { + const sideMenu = document.querySelector('#side-menu') + const headerMenuOpener = document.querySelector('#header .side-menu-opener') + const sideMenuOpener = document.querySelectorAll('#side-menu .side-menu-opener') + + sideMenuFocus = () => { + let a = document.querySelector('.side-menu-app.active a', sideMenu) + + if (!a) { + return + } + + if (a.length === 0) { + a = sideMenu.querySelector('.side-menu-app:first-child a') + } + + if (a.length > 0) { + a.focus() + } + } + + + const sideMenuMouseLeave = () => { + sideMenu.classList.remove('open') + sideMenu.removeEventListener('mouseleave', sideMenuMouseLeave) + } + + const sideMenuMouseEnter = () => { + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) + } + + const sideMenuOpenerMouseEnter = () => { + sideMenu.classList.add('open') + sideMenu.addEventListener('mouseenter', sideMenuMouseEnter) + + sideMenuFocus() + } + + if (!isTouchDevice) { + + headerMenuOpener.addEventListener('mouseenter', sideMenuOpenerMouseEnter) + + sideMenu.classList.add('hide-opener') + + + sideMenu.addEventListener('mouseleave', sideMenuMouseLeave) + sideMenu.addEventListener('mouseenter', sideMenuOpenerMouseEnter) + } - - const topMenuApps = - const topSideMenuApps = + headerMenuOpener.addEventListener('click', () => { + sideMenu.classList.add('open') - - + const a = sideMenu.querySelector('.side-menu-app.active a') - - - + if (a !== null) { + a.focus() + } + + headerMenuOpener.blur() + }) + + for (let opener of sideMenuOpener) { + opener.addEventListener('click', () => { + + sideMenu.classList.toggle('open') + + sideMenu.classList.remove('open') + + }) + } + + document.addEventListener('keydown', (e) => { + var key = e.key || e.keyCode + + if ((key === 'o' || key === 79) && e.ctrlKey === true) { + e.preventDefault() + + sideMenu.classList.toggle('open') + sideMenuFocus() + } + }) + + 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 + }) + }) + + body.appendChild(sideMenuContainer) + sideMenuContainer.appendChild(sideMenu) + + + PageLoader() + + + + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud) + + nextcloud.parentNode.insertBefore(sideMenuOpener, nextcloud.nextSibling) + })(); diff --git a/webpack.js b/webpack.js index 6175934..6e3eac8 100644 --- a/webpack.js +++ b/webpack.js @@ -3,6 +3,7 @@ const { VueLoaderPlugin } = require('vue-loader') const StyleLintPlugin = require('stylelint-webpack-plugin') module.exports = { + devtool: "source-map", entry: { 'admin': path.join(__dirname, 'src', 'admin.js'), 'sideMenu': path.join(__dirname, 'src', 'SideMenu.js'),